Browse Source

Bump libwebsockets to version 3.0.0

Fabio Alessandrelli 7 years ago
parent
commit
e56a3c1dc4
98 changed files with 19232 additions and 15685 deletions
  1. 10 8
      COPYRIGHT.txt
  2. 44 34
      modules/websocket/SCsub
  3. 2 1
      modules/websocket/lws_client.cpp
  4. 12 10
      thirdparty/README.md
  5. 17 16
      thirdparty/libwebsockets/LICENSE
  6. 0 1304
      thirdparty/libwebsockets/client/client.c
  7. 0 625
      thirdparty/libwebsockets/client/ssl-client.c
  8. 6 4
      thirdparty/libwebsockets/core/alloc.c
  9. 413 230
      thirdparty/libwebsockets/core/context.c
  10. 439 342
      thirdparty/libwebsockets/core/libwebsockets.c
  11. 308 0
      thirdparty/libwebsockets/core/output.c
  12. 228 174
      thirdparty/libwebsockets/core/pollfd.c
  13. 1751 0
      thirdparty/libwebsockets/core/private.h
  14. 987 0
      thirdparty/libwebsockets/core/service.c
  15. 43 0
      thirdparty/libwebsockets/event-libs/poll/poll.c
  16. 23 0
      thirdparty/libwebsockets/event-libs/poll/private.h
  17. 74 0
      thirdparty/libwebsockets/event-libs/private.h
  18. 0 473
      thirdparty/libwebsockets/ext/extension-permessage-deflate.c
  19. 0 41
      thirdparty/libwebsockets/ext/extension-permessage-deflate.h
  20. 0 344
      thirdparty/libwebsockets/ext/extension.c
  21. 0 280
      thirdparty/libwebsockets/handshake.c
  22. 0 805
      thirdparty/libwebsockets/lextable.h
  23. 513 278
      thirdparty/libwebsockets/libwebsockets.h
  24. 40 10
      thirdparty/libwebsockets/lws_config.h
  25. 2 0
      thirdparty/libwebsockets/lws_config_private.h
  26. 0 74
      thirdparty/libwebsockets/mbedtls_verify.diff
  27. 46 10
      thirdparty/libwebsockets/misc/base64-decode.c
  28. 1 1
      thirdparty/libwebsockets/misc/getifaddrs.c
  29. 48 11
      thirdparty/libwebsockets/misc/lejp.c
  30. 0 232
      thirdparty/libwebsockets/misc/lejp.h
  31. 1 1
      thirdparty/libwebsockets/misc/sha-1.c
  32. 0 883
      thirdparty/libwebsockets/output.c
  33. 211 92
      thirdparty/libwebsockets/plat/lws-plat-unix.c
  34. 147 63
      thirdparty/libwebsockets/plat/lws-plat-win.c
  35. 0 2615
      thirdparty/libwebsockets/private-libwebsockets.h
  36. 687 0
      thirdparty/libwebsockets/roles/h1/ops-h1.c
  37. 27 0
      thirdparty/libwebsockets/roles/h1/private.h
  38. 417 184
      thirdparty/libwebsockets/roles/http/client/client-handshake.c
  39. 1231 0
      thirdparty/libwebsockets/roles/http/client/client.c
  40. 94 42
      thirdparty/libwebsockets/roles/http/header.c
  41. 4 0
      thirdparty/libwebsockets/roles/http/lextable-strings.h
  42. 838 0
      thirdparty/libwebsockets/roles/http/lextable.h
  43. 257 0
      thirdparty/libwebsockets/roles/http/private.h
  44. 182 0
      thirdparty/libwebsockets/roles/http/server/access-log.c
  45. 7 8
      thirdparty/libwebsockets/roles/http/server/fops-zip.c
  46. 77 23
      thirdparty/libwebsockets/roles/http/server/lejp-conf.c
  47. 1139 0
      thirdparty/libwebsockets/roles/http/server/parsers.c
  48. 306 265
      thirdparty/libwebsockets/roles/http/server/server.c
  49. 177 0
      thirdparty/libwebsockets/roles/listen/ops-listen.c
  50. 81 0
      thirdparty/libwebsockets/roles/pipe/ops-pipe.c
  51. 282 0
      thirdparty/libwebsockets/roles/private.h
  52. 223 0
      thirdparty/libwebsockets/roles/raw/ops-raw.c
  53. 187 175
      thirdparty/libwebsockets/roles/ws/client-parser-ws.c
  54. 629 0
      thirdparty/libwebsockets/roles/ws/client-ws.c
  55. 1992 0
      thirdparty/libwebsockets/roles/ws/ops-ws.c
  56. 164 0
      thirdparty/libwebsockets/roles/ws/private.h
  57. 836 0
      thirdparty/libwebsockets/roles/ws/server-ws.c
  58. 0 1783
      thirdparty/libwebsockets/server/parsers.c
  59. 0 214
      thirdparty/libwebsockets/server/ranges.c
  60. 0 360
      thirdparty/libwebsockets/server/server-handshake.c
  61. 0 477
      thirdparty/libwebsockets/server/ssl-server.c
  62. 0 1714
      thirdparty/libwebsockets/service.c
  63. 0 973
      thirdparty/libwebsockets/ssl.c
  64. 202 0
      thirdparty/libwebsockets/tls/mbedtls/lws-genhash.c
  65. 329 0
      thirdparty/libwebsockets/tls/mbedtls/lws-genrsa.c
  66. 240 0
      thirdparty/libwebsockets/tls/mbedtls/mbedtls-client.c
  67. 694 0
      thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c
  68. 520 0
      thirdparty/libwebsockets/tls/mbedtls/ssl.c
  69. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl3.h
  70. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_cert.h
  71. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_code.h
  72. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_dbg.h
  73. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_lib.h
  74. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_methods.h
  75. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_pkey.h
  76. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_stack.h
  77. 2 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h
  78. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_x509.h
  79. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/tls1.h
  80. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/x509_vfy.h
  81. 17 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/openssl/ssl.h
  82. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/platform/ssl_pm.h
  83. 2 10
      thirdparty/libwebsockets/tls/mbedtls/wrapper/include/platform/ssl_port.h
  84. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_cert.c
  85. 71 14
      thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_lib.c
  86. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_methods.c
  87. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_pkey.c
  88. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_stack.c
  89. 25 1
      thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_x509.c
  90. 164 56
      thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_pm.c
  91. 0 0
      thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_port.c
  92. 281 0
      thirdparty/libwebsockets/tls/private.h
  93. 150 0
      thirdparty/libwebsockets/tls/tls-client.c
  94. 382 0
      thirdparty/libwebsockets/tls/tls-server.c
  95. 522 0
      thirdparty/libwebsockets/tls/tls.c
  96. 153 153
      thirdparty/libwebsockets/win32helpers/getopt.c
  97. 240 237
      thirdparty/libwebsockets/win32helpers/getopt_long.c
  98. 35 35
      thirdparty/libwebsockets/win32helpers/gettimeofday.c

+ 10 - 8
COPYRIGHT.txt

@@ -221,7 +221,7 @@ License: BSD-3-clause
 Files: ./thirdparty/libwebsockets/
 Files: ./thirdparty/libwebsockets/
 Comment: libwebsockets
 Comment: libwebsockets
 Copyright: 2010-2017, Andy Green
 Copyright: 2010-2017, Andy Green
-License: LGPL-2.1+SLE (lws)
+License: LGPL-2.1+SLE (libwebsockets)
 
 
 Files: ./thirdparty/mbedtls/
 Files: ./thirdparty/mbedtls/
 Comment: Mbed TLS
 Comment: Mbed TLS
@@ -1061,7 +1061,7 @@ License: ISC
  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 
-License: LGPL-2.1+SLE (lws)
+License: LGPL-2.1+SLE (libwebsockets)
  Libwebsockets and included programs are provided under the terms of the GNU
  Libwebsockets and included programs are provided under the terms of the GNU
  Library General Public License (LGPL) 2.1, with the following exceptions:
  Library General Public License (LGPL) 2.1, with the following exceptions:
  .
  .
@@ -1097,19 +1097,21 @@ License: LGPL-2.1+SLE (lws)
  .
  .
  Original liberal license retained
  Original liberal license retained
  .
  .
-   - lib/sha-1.c         - 3-clause BSD license retained, link to original
+   - lib/misc/sha-1.c    - 3-clause BSD license retained, link to original
    - win32port/zlib      - ZLIB license (see zlib.h)
    - win32port/zlib      - ZLIB license (see zlib.h)
+   - lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)
  .
  .
  Relicensed to libwebsocket license
  Relicensed to libwebsocket license
  .
  .
-   - lib/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
-   - lib/daemonize.c     - relicensed from Public Domain to LGPL2.1+SLE,
-                           link to original Public Domain version
+   - lib/misc/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
+   - lib/misc/daemonize.c     - relicensed from Public Domain to LGPL2.1+SLE,
+                                link to original Public Domain version
  .
  .
  Public Domain (CC-zero) to simplify reuse
  Public Domain (CC-zero) to simplify reuse
  .
  .
-   - test-server/*.c
-   - test-server/*.h
+   - test-apps/*.c
+   - test-apps/*.h
+   - minimal-examples/*
    - lwsws/*
    - lwsws/*
  .
  .
  ------ end of exceptions
  ------ end of exceptions

+ 44 - 34
modules/websocket/SCsub

@@ -10,44 +10,54 @@ env_lws = env_modules.Clone()
 thirdparty_dir = "#thirdparty/libwebsockets/"
 thirdparty_dir = "#thirdparty/libwebsockets/"
 helper_dir = "win32helpers/"
 helper_dir = "win32helpers/"
 thirdparty_sources = [
 thirdparty_sources = [
-    "client/client.c",
-    "client/client-handshake.c",
-    "client/client-parser.c",
-    "client/ssl-client.c",
-
-    "ext/extension.c",
-    "ext/extension-permessage-deflate.c",
-
-    "server/fops-zip.c",
-    "server/lejp-conf.c",
-    "server/parsers.c",
-    "server/ranges.c",
-    "server/server.c",
-    "server/server-handshake.c",
-    "server/ssl-server.c",
+
+    "core/alloc.c",
+    "core/context.c",
+    "core/libwebsockets.c",
+    "core/output.c",
+    "core/pollfd.c",
+    "core/service.c",
+
+    "event-libs/poll/poll.c",
 
 
     "misc/base64-decode.c",
     "misc/base64-decode.c",
     "misc/lejp.c",
     "misc/lejp.c",
     "misc/sha-1.c",
     "misc/sha-1.c",
 
 
-    "alloc.c",
-    "context.c",
-    "handshake.c",
-    "header.c",
-    "libwebsockets.c",
-    "output.c",
-    "pollfd.c",
-    "service.c",
-    "ssl.c",
-
-    "mbedtls_wrapper/library/ssl_cert.c",
-    "mbedtls_wrapper/library/ssl_pkey.c",
-    "mbedtls_wrapper/library/ssl_stack.c",
-    "mbedtls_wrapper/library/ssl_methods.c",
-    "mbedtls_wrapper/library/ssl_lib.c",
-    "mbedtls_wrapper/library/ssl_x509.c",
-    "mbedtls_wrapper/platform/ssl_port.c",
-    "mbedtls_wrapper/platform/ssl_pm.c",
+    "roles/h1/ops-h1.c",
+    "roles/http/header.c",
+    "roles/http/client/client.c",
+    "roles/http/client/client-handshake.c",
+    "roles/http/server/fops-zip.c",
+    "roles/http/server/lejp-conf.c",
+    "roles/http/server/parsers.c",
+    "roles/http/server/server.c",
+    "roles/listen/ops-listen.c",
+    "roles/pipe/ops-pipe.c",
+    "roles/raw/ops-raw.c",
+
+    "roles/ws/client-ws.c",
+    "roles/ws/client-parser-ws.c",
+    "roles/ws/ops-ws.c",
+    "roles/ws/server-ws.c",
+
+    "tls/tls.c",
+    "tls/tls-client.c",
+    "tls/tls-server.c",
+
+    "tls/mbedtls/wrapper/library/ssl_cert.c",
+    "tls/mbedtls/wrapper/library/ssl_pkey.c",
+    "tls/mbedtls/wrapper/library/ssl_stack.c",
+    "tls/mbedtls/wrapper/library/ssl_methods.c",
+    "tls/mbedtls/wrapper/library/ssl_lib.c",
+    "tls/mbedtls/wrapper/library/ssl_x509.c",
+    "tls/mbedtls/wrapper/platform/ssl_port.c",
+    "tls/mbedtls/wrapper/platform/ssl_pm.c",
+    "tls/mbedtls/lws-genhash.c",
+    "tls/mbedtls/mbedtls-client.c",
+    "tls/mbedtls/lws-genrsa.c",
+    "tls/mbedtls/ssl.c",
+    "tls/mbedtls/mbedtls-server.c"
 ]
 ]
 
 
 if env_lws["platform"] == "android": # Builtin getifaddrs
 if env_lws["platform"] == "android": # Builtin getifaddrs
@@ -67,7 +77,7 @@ else:
     env_lws.add_source_files(env.modules_sources, thirdparty_sources)
     env_lws.add_source_files(env.modules_sources, thirdparty_sources)
     env_lws.Append(CPPPATH=[thirdparty_dir])
     env_lws.Append(CPPPATH=[thirdparty_dir])
 
 
-    wrapper_includes = ["#thirdparty/libwebsockets/mbedtls_wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
+    wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
     env_lws.Prepend(CPPPATH=wrapper_includes)
     env_lws.Prepend(CPPPATH=wrapper_includes)
 
 
     if env['builtin_mbedtls']:
     if env['builtin_mbedtls']:

+ 2 - 1
modules/websocket/lws_client.cpp

@@ -32,6 +32,7 @@
 #include "lws_client.h"
 #include "lws_client.h"
 #include "core/io/ip.h"
 #include "core/io/ip.h"
 #include "core/io/stream_peer_ssl.h"
 #include "core/io/stream_peer_ssl.h"
+#include "tls/mbedtls/wrapper/include/openssl/ssl.h"
 
 
 Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
 Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
 
 
@@ -140,7 +141,7 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
 			destroy_context();
 			destroy_context();
 			return -1; // we should close the connection (would probably happen anyway)
 			return -1; // we should close the connection (would probably happen anyway)
 
 
-		case LWS_CALLBACK_CLOSED:
+		case LWS_CALLBACK_CLIENT_CLOSED:
 			peer_data->in_count = 0;
 			peer_data->in_count = 0;
 			peer_data->out_count = 0;
 			peer_data->out_count = 0;
 			peer_data->rbw.resize(0);
 			peer_data->rbw.resize(0);

+ 12 - 10
thirdparty/README.md

@@ -237,19 +237,21 @@ changes are marked with `// -- GODOT --` comments.
 ## libwebsockets
 ## libwebsockets
 
 
 - Upstream: https://github.com/warmcat/libwebsockets
 - Upstream: https://github.com/warmcat/libwebsockets
-- Version: 2.4.2
+- Version: 3.0.0
 - License: LGPLv2.1 + static linking exception
 - License: LGPLv2.1 + static linking exception
 
 
 File extracted from upstream source:
 File extracted from upstream source:
-- Everything in `lib/` except `minilex.c`, `http2/`, `event-libs/`.
-  - From `misc/` exclude `lws-genhash.c`, `lws-ring.c`, `romfs.{c,h}`, `smtp.c`.
-  - From `plat/` exclude `lws-plat-{esp*,optee}.c`.
-  - From `server/` exclude `access-log.c`, `cgi.c`, `daemonize.c`, `lws-spa.c`,
-`peer-limits.c`, `rewrite.c`
-- Also copy `win32helpers/` from `win32port/`
-- `mbedtls_wrapper/include/platform/ssl_port.h` has a small change to check for OSX and FreeBSD (missing `malloc.h`).
-  The bug is fixed in upstream master via `LWS_HAVE_MALLOC_H`, but not in the 2.4.1 branch (as the file structure has changed).
-- You might need to apply the patch in `thirdparty/libwebsockets/mbedtls_verify.diff` (port of PR 1215) to future `2.4.x` releases if it does not get cherry picked.
+- From `lib/` into `thirdparty/libwebsockets`:
+  - Everything from `core`
+  - From `event-libs` only the `poll` subfolder
+  - From `misc` only `base64-decode.c`, `getifaddrs.c`, `getifaddrs.h`, `lejp.c`, and `sha-1.c`
+  - From `plat` only `lws-plat-unix.c` and `lws-plat-win.c`
+  - From `roles` only `private.h`, `h1`, `http`, `listen`, `pipe`, `raw`, `ws`
+    - From `roles/http` exclude `minilex.c`
+    - From `roles/http/server` exclude `access-log.c`, `lws-spa.c`, `ranges.c`, and `rewrite.c`
+    - From `roles/ws` exclude `ext` folder.
+  - From `tls` exclude `openssl` folder.
+- Also copy `win32helpers/` from `win32port/` inside `thirdparty/libwebsockets`
 
 
 Important: `lws_config.h` and `lws_config_private.h` contains custom
 Important: `lws_config.h` and `lws_config_private.h` contains custom
 Godot build configurations, check them out when updating.
 Godot build configurations, check them out when updating.

+ 17 - 16
thirdparty/libwebsockets/LICENSE.txt → thirdparty/libwebsockets/LICENSE

@@ -33,19 +33,21 @@ to get original sources with the liberal terms.
 
 
 Original liberal license retained
 Original liberal license retained
 
 
-  - lib/sha-1.c         - 3-clause BSD license retained, link to original
+  - lib/misc/sha-1.c    - 3-clause BSD license retained, link to original
   - win32port/zlib      - ZLIB license (see zlib.h)
   - win32port/zlib      - ZLIB license (see zlib.h)
+  - lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)
 
 
 Relicensed to libwebsocket license
 Relicensed to libwebsocket license
 
 
-  - lib/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
-  - lib/daemonize.c     - relicensed from Public Domain to LGPL2.1+SLE,
-                          link to original Public Domain version
+  - lib/misc/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
+  - lib/misc/daemonize.c     - relicensed from Public Domain to LGPL2.1+SLE,
+                               link to original Public Domain version
 
 
 Public Domain (CC-zero) to simplify reuse
 Public Domain (CC-zero) to simplify reuse
 
 
-  - test-server/*.c
-  - test-server/*.h
+  - test-apps/*.c
+  - test-apps/*.h
+  - minimal-examples/*
   - lwsws/*
   - lwsws/*
 
 
 ------ end of exceptions
 ------ end of exceptions
@@ -107,7 +109,7 @@ modified by someone else and passed on, the recipients should know
 that what they have is not the original version, so that the original
 that what they have is not the original version, so that the original
 author's reputation will not be affected by problems that might be
 author's reputation will not be affected by problems that might be
 introduced by others.
 introduced by others.
-
+
   Finally, software patents pose a constant threat to the existence of
   Finally, software patents pose a constant threat to the existence of
 any free program.  We wish to make sure that a company cannot
 any free program.  We wish to make sure that a company cannot
 effectively restrict the users of a free program by obtaining a
 effectively restrict the users of a free program by obtaining a
@@ -163,7 +165,7 @@ modification follow.  Pay close attention to the difference between a
 "work based on the library" and a "work that uses the library".  The
 "work based on the library" and a "work that uses the library".  The
 former contains code derived from the library, whereas the latter must
 former contains code derived from the library, whereas the latter must
 be combined with the library in order to run.
 be combined with the library in order to run.
-
+
                   GNU LESSER GENERAL PUBLIC LICENSE
                   GNU LESSER GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
 
@@ -210,7 +212,7 @@ Library.
   You may charge a fee for the physical act of transferring a copy,
   You may charge a fee for the physical act of transferring a copy,
 and you may at your option offer warranty protection in exchange for a
 and you may at your option offer warranty protection in exchange for a
 fee.
 fee.
-
+
   2. You may modify your copy or copies of the Library or any portion
   2. You may modify your copy or copies of the Library or any portion
 of it, thus forming a work based on the Library, and copy and
 of it, thus forming a work based on the Library, and copy and
 distribute such modifications or work under the terms of Section 1
 distribute such modifications or work under the terms of Section 1
@@ -268,7 +270,7 @@ instead of to this License.  (If a newer version than version 2 of the
 ordinary GNU General Public License has appeared, then you can specify
 ordinary GNU General Public License has appeared, then you can specify
 that version instead if you wish.)  Do not make any other change in
 that version instead if you wish.)  Do not make any other change in
 these notices.
 these notices.
-
+
   Once this change is made in a given copy, it is irreversible for
   Once this change is made in a given copy, it is irreversible for
 that copy, so the ordinary GNU General Public License applies to all
 that copy, so the ordinary GNU General Public License applies to all
 subsequent copies and derivative works made from that copy.
 subsequent copies and derivative works made from that copy.
@@ -319,7 +321,7 @@ Library will still fall under Section 6.)
 distribute the object code for the work under the terms of Section 6.
 distribute the object code for the work under the terms of Section 6.
 Any executables containing that work also fall under Section 6,
 Any executables containing that work also fall under Section 6,
 whether or not they are linked directly with the Library itself.
 whether or not they are linked directly with the Library itself.
-
+
   6. As an exception to the Sections above, you may also combine or
   6. As an exception to the Sections above, you may also combine or
 link a "work that uses the Library" with the Library to produce a
 link a "work that uses the Library" with the Library to produce a
 work containing portions of the Library, and distribute that work
 work containing portions of the Library, and distribute that work
@@ -381,7 +383,7 @@ restrictions of other proprietary libraries that do not normally
 accompany the operating system.  Such a contradiction means you cannot
 accompany the operating system.  Such a contradiction means you cannot
 use both them and the Library together in an executable that you
 use both them and the Library together in an executable that you
 distribute.
 distribute.
-
+
   7. You may place library facilities that are a work based on the
   7. You may place library facilities that are a work based on the
 Library side-by-side in a single library together with other library
 Library side-by-side in a single library together with other library
 facilities not covered by this License, and distribute such a combined
 facilities not covered by this License, and distribute such a combined
@@ -422,7 +424,7 @@ subject to these terms and conditions.  You may not impose any further
 restrictions on the recipients' exercise of the rights granted herein.
 restrictions on the recipients' exercise of the rights granted herein.
 You are not responsible for enforcing compliance by third parties with
 You are not responsible for enforcing compliance by third parties with
 this License.
 this License.
-
+
   11. If, as a consequence of a court judgment or allegation of patent
   11. If, as a consequence of a court judgment or allegation of patent
 infringement or for any other reason (not limited to patent issues),
 infringement or for any other reason (not limited to patent issues),
 conditions are imposed on you (whether by court order, agreement or
 conditions are imposed on you (whether by court order, agreement or
@@ -474,7 +476,7 @@ conditions either of that version or of any later version published by
 the Free Software Foundation.  If the Library does not specify a
 the Free Software Foundation.  If the Library does not specify a
 license version number, you may choose any version ever published by
 license version number, you may choose any version ever published by
 the Free Software Foundation.
 the Free Software Foundation.
-
+
   14. If you wish to incorporate parts of the Library into other free
   14. If you wish to incorporate parts of the Library into other free
 programs whose distribution conditions are incompatible with these,
 programs whose distribution conditions are incompatible with these,
 write to the author to ask for permission.  For software which is
 write to the author to ask for permission.  For software which is
@@ -508,7 +510,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 DAMAGES.
 DAMAGES.
 
 
                      END OF TERMS AND CONDITIONS
                      END OF TERMS AND CONDITIONS
-
+
            How to Apply These Terms to Your New Libraries
            How to Apply These Terms to Your New Libraries
 
 
   If you develop a new library, and you want it to be of the greatest
   If you develop a new library, and you want it to be of the greatest
@@ -552,4 +554,3 @@ necessary.  Here is a sample; alter the names:
   Ty Coon, President of Vice
   Ty Coon, President of Vice
 
 
 That's all there is to it!
 That's all there is to it!
-

+ 0 - 1304
thirdparty/libwebsockets/client/client.c

@@ -1,1304 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2014 Andy Green <[email protected]>
- *
- *  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"
-
-int
-lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
-{
-	int m;
-
-	switch (wsi->mode) {
-	case LWSCM_WSCL_WAITING_PROXY_REPLY:
-	case LWSCM_WSCL_ISSUE_HANDSHAKE:
-	case LWSCM_WSCL_WAITING_SERVER_REPLY:
-	case LWSCM_WSCL_WAITING_EXTENSION_CONNECT:
-	case LWSCM_WS_CLIENT:
-		while (len) {
-			/*
-			 * we were accepting input but now we stopped doing so
-			 */
-			if (lws_is_flowcontrolled(wsi)) {
-				lwsl_debug("%s: caching %ld\n", __func__, (long)len);
-				lws_rxflow_cache(wsi, *buf, 0, len);
-				return 0;
-			}
-			if (wsi->u.ws.rx_draining_ext) {
-#if !defined(LWS_NO_CLIENT)
-				if (wsi->mode == LWSCM_WS_CLIENT)
-					m = lws_client_rx_sm(wsi, 0);
-				else
-#endif
-					m = lws_rx_sm(wsi, 0);
-				if (m < 0)
-					return -1;
-				continue;
-			}
-			/* account for what we're using in rxflow buffer */
-			if (wsi->rxflow_buffer)
-				wsi->rxflow_pos++;
-
-			if (lws_client_rx_sm(wsi, *(*buf)++)) {
-				lwsl_debug("client_rx_sm exited\n");
-				return -1;
-			}
-			len--;
-		}
-		lwsl_debug("%s: finished with %ld\n", __func__, (long)len);
-		return 0;
-	default:
-		break;
-	}
-
-	return 0;
-}
-
-LWS_VISIBLE LWS_EXTERN void
-lws_client_http_body_pending(struct lws *wsi, int something_left_to_send)
-{
-	wsi->client_http_body_pending = !!something_left_to_send;
-}
-
-int
-lws_client_socket_service(struct lws_context *context, struct lws *wsi,
-			  struct lws_pollfd *pollfd)
-{
-	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-	char *p = (char *)&pt->serv_buf[0];
-	const char *cce = NULL;
-	unsigned char c;
-	char *sb = p;
-	int n = 0;
-	ssize_t len = 0;
-#if defined(LWS_WITH_SOCKS5)
-	char conn_mode = 0, pending_timeout = 0;
-#endif
-
-	switch (wsi->mode) {
-
-	case LWSCM_WSCL_WAITING_CONNECT:
-
-		/*
-		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
-		 * timeout protection set in client-handshake.c
-		 */
-
-		if (!lws_client_connect_2(wsi)) {
-			/* closed */
-			lwsl_client("closed\n");
-			return -1;
-		}
-
-		/* either still pending connection, or changed mode */
-		return 0;
-
-#if defined(LWS_WITH_SOCKS5)
-	/* SOCKS Greeting Reply */
-	case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY:
-	case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY:
-	case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY:
-
-		/* handle proxy hung up on us */
-
-		if (pollfd->revents & LWS_POLLHUP) {
-			lwsl_warn("SOCKS connection %p (fd=%d) dead\n",
-				  (void *)wsi, pollfd->fd);
-			goto bail3;
-		}
-
-		n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
-		if (n < 0) {
-			if (LWS_ERRNO == LWS_EAGAIN) {
-				lwsl_debug("SOCKS read EAGAIN, retrying\n");
-				return 0;
-			}
-			lwsl_err("ERROR reading from SOCKS socket\n");
-			goto bail3;
-		}
-
-		switch (wsi->mode) {
-
-		case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY:
-			if (pt->serv_buf[0] != SOCKS_VERSION_5)
-				goto socks_reply_fail;
-
-			if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) {
-				lwsl_client("SOCKS greeting reply: No Auth Method\n");
-				socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
-				conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY;
-				pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
-				goto socks_send;
-			}
-
-			if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) {
-				lwsl_client("SOCKS greeting reply: User/Pw Method\n");
-				socks_generate_msg(wsi, SOCKS_MSG_USERNAME_PASSWORD, &len);
-				conn_mode = LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY;
-				pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY;
-				goto socks_send;
-			}
-			goto socks_reply_fail;
-
-		case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY:
-			if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 ||
-			    pt->serv_buf[1] != SOCKS_SUBNEGOTIATION_STATUS_SUCCESS)
-				goto socks_reply_fail;
-
-			lwsl_client("SOCKS password OK, sending connect\n");
-			socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
-			conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY;
-			pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
-socks_send:
-			n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len,
-				 MSG_NOSIGNAL);
-			if (n < 0) {
-				lwsl_debug("ERROR writing to socks proxy\n");
-				goto bail3;
-			}
-
-			lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT);
-			wsi->mode = conn_mode;
-			break;
-
-socks_reply_fail:
-			lwsl_notice("socks reply: v%d, err %d\n",
-				    pt->serv_buf[0], pt->serv_buf[1]);
-			goto bail3;
-
-		case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY:
-			if (pt->serv_buf[0] != SOCKS_VERSION_5 ||
-			    pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS)
-				goto socks_reply_fail;
-
-			lwsl_client("socks connect OK\n");
-
-			/* free stash since we are done with it */
-			lws_free_set_NULL(wsi->u.hdr.stash);
-			if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
-						  wsi->vhost->socks_proxy_address))
-				goto bail3;
-
-			wsi->c_port = wsi->vhost->socks_proxy_port;
-
-			/* clear his proxy connection timeout */
-			lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-			goto start_ws_handshake;
-		}
-		break;
-#endif
-
-	case LWSCM_WSCL_WAITING_PROXY_REPLY:
-
-		/* handle proxy hung up on us */
-
-		if (pollfd->revents & LWS_POLLHUP) {
-
-			lwsl_warn("Proxy connection %p (fd=%d) dead\n",
-				  (void *)wsi, pollfd->fd);
-
-			goto bail3;
-		}
-
-		n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
-		if (n < 0) {
-			if (LWS_ERRNO == LWS_EAGAIN) {
-				lwsl_debug("Proxy read returned EAGAIN... retrying\n");
-				return 0;
-			}
-			lwsl_err("ERROR reading from proxy socket\n");
-			goto bail3;
-		}
-
-		pt->serv_buf[13] = '\0';
-		if (strcmp(sb, "HTTP/1.0 200 ") &&
-		    strcmp(sb, "HTTP/1.1 200 ")) {
-			lwsl_err("ERROR proxy: %s\n", sb);
-			goto bail3;
-		}
-
-		/* clear his proxy connection timeout */
-
-		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
-		/* fallthru */
-
-	case LWSCM_WSCL_ISSUE_HANDSHAKE:
-
-		/*
-		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
-		 * timeout protection set in client-handshake.c
-		 *
-		 * take care of our lws_callback_on_writable
-		 * happening at a time when there's no real connection yet
-		 */
-#if defined(LWS_WITH_SOCKS5)
-start_ws_handshake:
-#endif
-		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
-			return -1;
-
-#ifdef LWS_OPENSSL_SUPPORT
-		/* we can retry this... just cook the SSL BIO the first time */
-
-		if (wsi->use_ssl && !wsi->ssl &&
-		    lws_ssl_client_bio_create(wsi) < 0) {
-			cce = "bio_create failed";
-			goto bail3;
-		}
-
-		if (wsi->use_ssl) {
-			n = lws_ssl_client_connect1(wsi);
-			if (!n)
-				return 0;
-			if (n < 0) {
-				cce = "lws_ssl_client_connect1 failed";
-				goto bail3;
-			}
-		} else
-			wsi->ssl = NULL;
-
-		/* fallthru */
-
-	case LWSCM_WSCL_WAITING_SSL:
-
-		if (wsi->use_ssl) {
-			n = lws_ssl_client_connect2(wsi);
-			if (!n)
-				return 0;
-			if (n < 0) {
-				cce = "lws_ssl_client_connect2 failed";
-				goto bail3;
-			}
-		} else
-			wsi->ssl = NULL;
-#endif
-
-		wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2;
-		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
-				context->timeout_secs);
-
-		/* fallthru */
-
-	case LWSCM_WSCL_ISSUE_HANDSHAKE2:
-		p = lws_generate_client_handshake(wsi, p);
-		if (p == NULL) {
-			if (wsi->mode == LWSCM_RAW)
-				return 0;
-
-			lwsl_err("Failed to generate handshake for client\n");
-			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-			return 0;
-		}
-
-		/* send our request to the server */
-		lws_latency_pre(context, wsi);
-
-		n = lws_ssl_capable_write(wsi, (unsigned char *)sb, p - sb);
-		lws_latency(context, wsi, "send lws_issue_raw", n,
-			    n == p - sb);
-		switch (n) {
-		case LWS_SSL_CAPABLE_ERROR:
-			lwsl_debug("ERROR writing to client socket\n");
-			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-			return 0;
-		case LWS_SSL_CAPABLE_MORE_SERVICE:
-			lws_callback_on_writable(wsi);
-			break;
-		}
-
-		if (wsi->client_http_body_pending) {
-			wsi->mode = LWSCM_WSCL_ISSUE_HTTP_BODY;
-			lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
-					context->timeout_secs);
-			/* user code must ask for writable callback */
-			break;
-		}
-
-		goto client_http_body_sent;
-
-	case LWSCM_WSCL_ISSUE_HTTP_BODY:
-		if (wsi->client_http_body_pending) {
-			lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
-					context->timeout_secs);
-			/* user code must ask for writable callback */
-			break;
-		}
-client_http_body_sent:
-		wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
-		wsi->u.hdr.lextable_pos = 0;
-		wsi->mode = LWSCM_WSCL_WAITING_SERVER_REPLY;
-		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
-				context->timeout_secs);
-		break;
-
-	case LWSCM_WSCL_WAITING_SERVER_REPLY:
-		/*
-		 * handle server hanging up on us...
-		 * but if there is POLLIN waiting, handle that first
-		 */
-		if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) ==
-								LWS_POLLHUP) {
-
-			lwsl_debug("Server connection %p (fd=%d) dead\n",
-				(void *)wsi, pollfd->fd);
-			cce = "Peer hung up";
-			goto bail3;
-		}
-
-		if (!(pollfd->revents & LWS_POLLIN))
-			break;
-
-		/* interpret the server response
-		 *
-		 *  HTTP/1.1 101 Switching Protocols
-		 *  Upgrade: websocket
-		 *  Connection: Upgrade
-		 *  Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
-		 *  Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
-		 *  Sec-WebSocket-Protocol: chat
-		 *
-		 * we have to take some care here to only take from the
-		 * socket bytewise.  The browser may (and has been seen to
-		 * in the case that onopen() performs websocket traffic)
-		 * coalesce both handshake response and websocket traffic
-		 * in one packet, since at that point the connection is
-		 * definitively ready from browser pov.
-		 */
-		len = 1;
-		while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE &&
-		       len > 0) {
-			n = lws_ssl_capable_read(wsi, &c, 1);
-			lws_latency(context, wsi, "send lws_issue_raw", n,
-				    n == 1);
-			switch (n) {
-			case 0:
-			case LWS_SSL_CAPABLE_ERROR:
-				cce = "read failed";
-				goto bail3;
-			case LWS_SSL_CAPABLE_MORE_SERVICE:
-				return 0;
-			}
-
-			if (lws_parse(wsi, c)) {
-				lwsl_warn("problems parsing header\n");
-				goto bail3;
-			}
-		}
-
-		/*
-		 * hs may also be coming in multiple packets, there is a 5-sec
-		 * libwebsocket timeout still active here too, so if parsing did
-		 * not complete just wait for next packet coming in this state
-		 */
-		if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
-			break;
-
-		/*
-		 * otherwise deal with the handshake.  If there's any
-		 * packet traffic already arrived we'll trigger poll() again
-		 * right away and deal with it that way
-		 */
-		return lws_client_interpret_server_handshake(wsi);
-
-bail3:
-		lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n");
-		if (cce)
-			lwsl_info("reason: %s\n", cce);
-		wsi->protocol->callback(wsi,
-			LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
-			wsi->user_space, (void *)cce, cce ? strlen(cce) : 0);
-		wsi->already_did_cce = 1;
-		lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-		return -1;
-
-	case LWSCM_WSCL_WAITING_EXTENSION_CONNECT:
-		lwsl_ext("LWSCM_WSCL_WAITING_EXTENSION_CONNECT\n");
-		break;
-
-	case LWSCM_WSCL_PENDING_CANDIDATE_CHILD:
-		lwsl_ext("LWSCM_WSCL_PENDING_CANDIDATE_CHILD\n");
-		break;
-	default:
-		break;
-	}
-
-	return 0;
-}
-
-/*
- * In-place str to lower case
- */
-
-static void
-strtolower(char *s)
-{
-	while (*s) {
-#ifdef LWS_PLAT_OPTEE
-		int tolower_optee(int c);
-		*s = tolower_optee((int)*s);
-#else
-		*s = tolower((int)*s);
-#endif
-		s++;
-	}
-}
-
-int LWS_WARN_UNUSED_RESULT
-lws_http_transaction_completed_client(struct lws *wsi)
-{
-	lwsl_debug("%s: wsi %p\n", __func__, wsi);
-	/* if we can't go back to accept new headers, drop the connection */
-	if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
-		lwsl_info("%s: %p: close connection\n", __func__, wsi);
-		return 1;
-	}
-
-	/* we don't support chained client connections yet */
-	return 1;
-#if 0
-	/* otherwise set ourselves up ready to go again */
-	wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
-	wsi->mode = LWSCM_HTTP_CLIENT_ACCEPTED;
-	wsi->u.http.rx_content_length = 0;
-	wsi->hdr_parsing_completed = 0;
-
-	/* He asked for it to stay alive indefinitely */
-	lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
-	/*
-	 * As client, nothing new is going to come until we ask for it
-	 * we can drop the ah, if any
-	 */
-	if (wsi->u.hdr.ah) {
-		lws_header_table_force_to_detachable_state(wsi);
-		lws_header_table_detach(wsi, 0);
-	}
-
-	/* If we're (re)starting on headers, need other implied init */
-	wsi->u.hdr.ues = URIES_IDLE;
-
-	lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi);
-
-	return 0;
-#endif
-}
-
-LWS_VISIBLE LWS_EXTERN unsigned int
-lws_http_client_http_response(struct lws *wsi)
-{
-	if (!wsi->u.http.ah)
-		return 0;
-
-	return wsi->u.http.ah->http_response;
-}
-
-int
-lws_client_interpret_server_handshake(struct lws *wsi)
-{
-	int n, len, okay = 0, port = 0, ssl = 0;
-	int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
-	struct lws_context *context = wsi->context;
-	const char *pc, *prot, *ads = NULL, *path, *cce = NULL;
-	struct allocated_headers *ah = NULL;
-	char *p, *q;
-	char new_path[300];
-#ifndef LWS_NO_EXTENSIONS
-	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-	char *sb = (char *)&pt->serv_buf[0];
-	const struct lws_ext_options *opts;
-	const struct lws_extension *ext;
-	char ext_name[128];
-	const char *c, *a;
-	char ignore;
-	int more = 1;
-	void *v;
-#endif
-	if (wsi->u.hdr.stash)
-		lws_free_set_NULL(wsi->u.hdr.stash);
-
-	ah = wsi->u.hdr.ah;
-	if (!wsi->do_ws) {
-		/* we are being an http client...
-		 */
-		lws_union_transition(wsi, LWSCM_HTTP_CLIENT_ACCEPTED);
-		wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
-		wsi->u.http.ah = ah;
-		ah->http_response = 0;
-	}
-
-	/*
-	 * well, what the server sent looked reasonable for syntax.
-	 * Now let's confirm it sent all the necessary headers
-	 *
-	 * http (non-ws) client will expect something like this
-	 *
-	 * HTTP/1.0.200
-	 * server:.libwebsockets
-	 * content-type:.text/html
-	 * content-length:.17703
-	 * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000
-	 *
-	 *
-	 *
-	 */
-
-	wsi->u.http.connection_type = HTTP_CONNECTION_KEEP_ALIVE;
-	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
-	if (wsi->do_ws && !p) {
-		lwsl_info("no URI\n");
-		cce = "HS: URI missing";
-		goto bail3;
-	}
-	if (!p) {
-		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0);
-		wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE;
-	}
-	if (!p) {
-		cce = "HS: URI missing";
-		lwsl_info("no URI\n");
-		goto bail3;
-	}
-	n = atoi(p);
-	if (ah)
-		ah->http_response = n;
-
-	if (n == 301 || n == 302 || n == 303 || n == 307 || n == 308) {
-		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION);
-		if (!p) {
-			cce = "HS: Redirect code but no Location";
-			goto bail3;
-		}
-
-		/* Relative reference absolute path */
-		if (p[0] == '/')
-		{
-#ifdef LWS_OPENSSL_SUPPORT
-			ssl = wsi->use_ssl;
-#endif
-			ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
-			port = wsi->c_port;
-			path = p + 1; /* +1 as lws_client_reset expects leading / to be omitted */
-		}
-		/* Absolute (Full) URI */
-		else if (strchr(p, ':'))
-		{
-			if (lws_parse_uri(p, &prot, &ads, &port, &path)) {
-				cce = "HS: URI did not parse";
-				goto bail3;
-			}
-
-			if (!strcmp(prot, "wss") || !strcmp(prot, "https"))
-				ssl = 1;
-		}
-		/* Relative reference relative path */
-		else
-		{
-			/* This doesn't try to calculate an absolute path, that will be left to the server */
-#ifdef LWS_OPENSSL_SUPPORT
-			ssl = wsi->use_ssl;
-#endif
-			ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
-			port = wsi->c_port;
-			path = new_path + 1; /* +1 as lws_client_reset expects leading / to be omitted */
-			strncpy(new_path, lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI), sizeof(new_path));
-			new_path[sizeof(new_path) - 1] = '\0';
-			q = strrchr(new_path, '/');
-			if (q)
-			{
-				strncpy(q + 1, p, sizeof(new_path) - (q - new_path) - 1);
-				new_path[sizeof(new_path) - 1] = '\0';
-			}
-			else
-			{
-				path = p;
-			}
-		}
-
-#ifdef LWS_OPENSSL_SUPPORT
-		if (wsi->use_ssl && !ssl) {
-			cce = "HS: Redirect attempted SSL downgrade";
-			goto bail3;
-		}
-#endif
-
-		if (!lws_client_reset(&wsi, ssl, ads, port, path, ads)) {
-			/* there are two ways to fail out with NULL return...
-			 * simple, early problem where the wsi is intact, or
-			 * we went through with the reconnect attempt and the
-			 * wsi is already closed.  In the latter case, the wsi
-			 * has beet set to NULL additionally.
-			 */
-			lwsl_err("Redirect failed\n");
-			cce = "HS: Redirect failed";
-			if (wsi)
-				goto bail3;
-
-			return 1;
-		}
-		return 0;
-	}
-
-	if (!wsi->do_ws) {
-
-#ifdef LWS_WITH_HTTP_PROXY
-		wsi->perform_rewrite = 0;
-		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
-			if (!strncmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE),
-				     "text/html", 9))
-				wsi->perform_rewrite = 1;
-		}
-#endif
-
-		/* allocate the per-connection user memory (if any) */
-		if (lws_ensure_user_space(wsi)) {
-			lwsl_err("Problem allocating wsi user mem\n");
-			cce = "HS: OOM";
-			goto bail2;
-		}
-
-		/* he may choose to send us stuff in chunked transfer-coding */
-		wsi->chunked = 0;
-		wsi->chunk_remaining = 0; /* ie, next thing is chunk size */
-		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING)) {
-			wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi,
-					       WSI_TOKEN_HTTP_TRANSFER_ENCODING),
-					"chunked");
-			/* first thing is hex, after payload there is crlf */
-			wsi->chunk_parser = ELCP_HEX;
-		}
-
-		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
-			wsi->u.http.rx_content_length =
-					atoll(lws_hdr_simple_ptr(wsi,
-						WSI_TOKEN_HTTP_CONTENT_LENGTH));
-			lwsl_notice("%s: incoming content length %llu\n", __func__,
-					(unsigned long long)wsi->u.http.rx_content_length);
-			wsi->u.http.rx_content_remain = wsi->u.http.rx_content_length;
-		} else /* can't do 1.1 without a content length or chunked */
-			if (!wsi->chunked)
-				wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE;
-
-		/*
-		 * we seem to be good to go, give client last chance to check
-		 * headers and OK it
-		 */
-		if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
-					    wsi->user_space, NULL, 0)) {
-
-			cce = "HS: disallowed by client filter";
-			goto bail2;
-		}
-
-		/* clear his proxy connection timeout */
-		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
-		wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
-
-		/* call him back to inform him he is up */
-		if (wsi->protocol->callback(wsi,
-					    LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
-					    wsi->user_space, NULL, 0)) {
-			cce = "HS: disallowed at ESTABLISHED";
-			goto bail3;
-		}
-
-		/* free up his parsing allocations */
-		lws_header_table_detach(wsi, 0);
-
-		lwsl_notice("%s: client connection up\n", __func__);
-
-		return 0;
-	}
-
-	if (p && !strncmp(p, "401", 3)) {
-		lwsl_warn(
-		       "lws_client_handshake: got bad HTTP response '%s'\n", p);
-		cce = "HS: ws upgrade unauthorized";
-		goto bail3;
-	}
-
-	if (p && strncmp(p, "101", 3)) {
-		lwsl_warn(
-		       "lws_client_handshake: got bad HTTP response '%s'\n", p);
-		cce = "HS: ws upgrade response not 101";
-		goto bail3;
-	}
-
-	if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
-		lwsl_info("no ACCEPT\n");
-		cce = "HS: ACCEPT missing";
-		goto bail3;
-	}
-
-	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
-	if (!p) {
-		lwsl_info("no UPGRADE\n");
-		cce = "HS: UPGRADE missing";
-		goto bail3;
-	}
-	strtolower(p);
-	if (strcmp(p, "websocket")) {
-		lwsl_warn(
-		      "lws_client_handshake: got bad Upgrade header '%s'\n", p);
-		cce = "HS: Upgrade to something other than websocket";
-		goto bail3;
-	}
-
-	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION);
-	if (!p) {
-		lwsl_info("no Connection hdr\n");
-		cce = "HS: CONNECTION missing";
-		goto bail3;
-	}
-	strtolower(p);
-	if (strcmp(p, "upgrade")) {
-		lwsl_warn("lws_client_int_s_hs: bad header %s\n", p);
-		cce = "HS: UPGRADE malformed";
-		goto bail3;
-	}
-
-	pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
-	if (!pc) {
-		lwsl_parser("lws_client_int_s_hs: no protocol list\n");
-	} else
-		lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc);
-
-	/*
-	 * confirm the protocol the server wants to talk was in the list
-	 * of protocols we offered
-	 */
-
-	len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
-	if (!len) {
-		lwsl_info("lws_client_int_s_hs: WSI_TOKEN_PROTOCOL is null\n");
-		/*
-		 * no protocol name to work from,
-		 * default to first protocol
-		 */
-		n = 0;
-		wsi->protocol = &wsi->vhost->protocols[0];
-		goto check_extensions;
-	}
-
-	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
-	len = strlen(p);
-
-	while (pc && *pc && !okay) {
-		if (!strncmp(pc, p, len) &&
-		    (pc[len] == ',' || pc[len] == '\0')) {
-			okay = 1;
-			continue;
-		}
-		while (*pc && *pc++ != ',')
-			;
-		while (*pc && *pc == ' ')
-			pc++;
-	}
-
-	if (!okay) {
-		lwsl_err("lws_client_int_s_hs: got bad protocol %s\n", p);
-		cce = "HS: PROTOCOL malformed";
-		goto bail2;
-	}
-
-	/*
-	 * identify the selected protocol struct and set it
-	 */
-	n = 0;
-	wsi->protocol = NULL;
-	while (wsi->vhost->protocols[n].callback && !wsi->protocol) {
-		if (strcmp(p, wsi->vhost->protocols[n].name) == 0) {
-			wsi->protocol = &wsi->vhost->protocols[n];
-			break;
-		}
-		n++;
-	}
-
-	if (wsi->protocol == NULL) {
-		lwsl_err("lws_client_int_s_hs: fail protocol %s\n", p);
-		cce = "HS: Cannot match protocol";
-		goto bail2;
-	}
-
-check_extensions:
-	/*
-	 * stitch protocol choice into the vh protocol linked list
-	 * We always insert ourselves at the start of the list
-	 *
-	 * X <-> B
-	 * X <-> pAn <-> pB
-	 */
-	//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);
-	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;
-
-#ifndef LWS_NO_EXTENSIONS
-	/* instantiate the accepted extensions */
-
-	if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
-		lwsl_ext("no client extensions allowed by server\n");
-		goto check_accept;
-	}
-
-	/*
-	 * break down the list of server accepted extensions
-	 * and go through matching them or identifying bogons
-	 */
-
-	if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size, WSI_TOKEN_EXTENSIONS) < 0) {
-		lwsl_warn("ext list from server failed to copy\n");
-		cce = "HS: EXT: list too big";
-		goto bail2;
-	}
-
-	c = sb;
-	n = 0;
-	ignore = 0;
-	a = NULL;
-	while (more) {
-
-		if (*c && (*c != ',' && *c != '\t')) {
-			if (*c == ';') {
-				ignore = 1;
-				if (!a)
-					a = 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 {
-			c++;
-			if (!n)
-				continue;
-		}
-
-		/* check we actually support it */
-
-		lwsl_notice("checking client ext %s\n", ext_name);
-
-		n = 0;
-		ext = wsi->vhost->extensions;
-		while (ext && ext->callback) {
-			if (strcmp(ext_name, ext->name)) {
-				ext++;
-				continue;
-			}
-
-			n = 1;
-			lwsl_notice("instantiating client ext %s\n", ext_name);
-
-			/* instantiate the extension on this conn */
-
-			wsi->active_extensions[wsi->count_act_ext] = ext;
-
-			/* allow him to construct his ext instance */
-
-			if (ext->callback(lws_get_context(wsi), ext, wsi,
-				      LWS_EXT_CB_CLIENT_CONSTRUCT,
-				      (void *)&wsi->act_ext_user[wsi->count_act_ext],
-				      (void *)&opts, 0)) {
-				lwsl_info(" ext %s failed construction\n", ext_name);
-				ext++;
-				continue;
-			}
-
-			/*
-			 * allow the user code to override ext defaults if it
-			 * wants to
-			 */
-			ext_name[0] = '\0';
-			if (user_callback_handle_rxflow(wsi->protocol->callback,
-					wsi, LWS_CALLBACK_WS_EXT_DEFAULTS,
-					(char *)ext->name, ext_name,
-					sizeof(ext_name))) {
-				cce = "HS: EXT: failed setting defaults";
-				goto bail2;
-			}
-
-			if (ext_name[0] &&
-			    lws_ext_parse_options(ext, wsi, wsi->act_ext_user[
-						  wsi->count_act_ext], opts, ext_name,
-						  strlen(ext_name))) {
-				lwsl_err("%s: unable to parse user defaults '%s'",
-					 __func__, ext_name);
-				cce = "HS: EXT: failed parsing defaults";
-				goto bail2;
-			}
-
-			/*
-			 * give the extension the server options
-			 */
-			if (a && lws_ext_parse_options(ext, wsi,
-					wsi->act_ext_user[wsi->count_act_ext],
-					opts, a, c - a)) {
-				lwsl_err("%s: unable to parse remote def '%s'",
-					 __func__, a);
-				cce = "HS: EXT: failed parsing options";
-				goto bail2;
-			}
-
-			if (ext->callback(lws_get_context(wsi), ext, wsi,
-					LWS_EXT_CB_OPTION_CONFIRM,
-				      wsi->act_ext_user[wsi->count_act_ext],
-				      NULL, 0)) {
-				lwsl_err("%s: ext %s rejects server options %s",
-					 __func__, ext->name, a);
-				cce = "HS: EXT: Rejects server options";
-				goto bail2;
-			}
-
-			wsi->count_act_ext++;
-
-			ext++;
-		}
-
-		if (n == 0) {
-			lwsl_warn("Unknown ext '%s'!\n", ext_name);
-			cce = "HS: EXT: unknown ext";
-			goto bail2;
-		}
-
-		a = NULL;
-		n = 0;
-	}
-
-check_accept:
-#endif
-
-	/*
-	 * Confirm his accept token is the one we precomputed
-	 */
-
-	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT);
-	if (strcmp(p, wsi->u.hdr.ah->initial_handshake_hash_base64)) {
-		lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p,
-				  wsi->u.hdr.ah->initial_handshake_hash_base64);
-		cce = "HS: Accept hash wrong";
-		goto bail2;
-	}
-
-	/* allocate the per-connection user memory (if any) */
-	if (lws_ensure_user_space(wsi)) {
-		lwsl_err("Problem allocating wsi user mem\n");
-		cce = "HS: OOM";
-		goto bail2;
-	}
-
-	/*
-	 * we seem to be good to go, give client last chance to check
-	 * headers and OK it
-	 */
-	if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
-				    wsi->user_space, NULL, 0)) {
-		cce = "HS: Rejected by filter cb";
-		goto bail2;
-	}
-
-	/* clear his proxy connection timeout */
-	lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
-	/* free up his parsing allocations */
-	lws_header_table_detach(wsi, 0);
-
-	lws_union_transition(wsi, LWSCM_WS_CLIENT);
-	wsi->state = LWSS_ESTABLISHED;
-	lws_restart_ws_ping_pong_timer(wsi);
-
-	wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
-
-	/*
-	 * create the frame buffer for this connection according to the
-	 * size mentioned in the protocol definition.  If 0 there, then
-	 * use a big default for compatibility
-	 */
-	n = wsi->protocol->rx_buffer_size;
-	if (!n)
-		n = context->pt_serv_buf_size;
-	n += LWS_PRE;
-	wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "client frame buffer");
-	if (!wsi->u.ws.rx_ubuf) {
-		lwsl_err("Out of Mem allocating rx buffer %d\n", n);
-		cce = "HS: OOM";
-		goto bail2;
-	}
-       wsi->u.ws.rx_ubuf_alloc = n;
-	lwsl_info("Allocating client RX buffer %d\n", n);
-
-#if !defined(LWS_WITH_ESP32)
-	if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&n,
-		       sizeof n)) {
-		lwsl_warn("Failed to set SNDBUF to %d", n);
-		cce = "HS: SO_SNDBUF failed";
-		goto bail3;
-	}
-#endif
-
-	lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);
-
-	/* call him back to inform him he is up */
-
-	if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED,
-				    wsi->user_space, NULL, 0)) {
-		cce = "HS: Rejected at CLIENT_ESTABLISHED";
-		goto bail3;
-	}
-#ifndef LWS_NO_EXTENSIONS
-	/*
-	 * inform all extensions, not just active ones since they
-	 * already know
-	 */
-	ext = wsi->vhost->extensions;
-
-	while (ext && ext->callback) {
-		v = NULL;
-		for (n = 0; n < wsi->count_act_ext; n++)
-			if (wsi->active_extensions[n] == ext)
-				v = wsi->act_ext_user[n];
-
-		ext->callback(context, ext, wsi,
-			  LWS_EXT_CB_ANY_WSI_ESTABLISHED, v, NULL, 0);
-		ext++;
-	}
-#endif
-
-	return 0;
-
-bail3:
-	close_reason = LWS_CLOSE_STATUS_NOSTATUS;
-
-bail2:
-	if (wsi->protocol)
-		wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
-				wsi->user_space, (void *)cce,
-				(unsigned int)strlen(cce));
-	wsi->already_did_cce = 1;
-
-	lwsl_info("closing connection due to bail2 connection error\n");
-
-	/* closing will free up his parsing allocations */
-	lws_close_free_wsi(wsi, close_reason);
-
-	return 1;
-}
-
-
-char *
-lws_generate_client_handshake(struct lws *wsi, char *pkt)
-{
-	char buf[128], hash[20], key_b64[40], *p = pkt;
-	struct lws_context *context = wsi->context;
-	const char *meth;
-	int n;
-#ifndef LWS_NO_EXTENSIONS
-	const struct lws_extension *ext;
-	int ext_count = 0;
-#endif
-	const char *pp = lws_hdr_simple_ptr(wsi,
-				_WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
-
-	meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
-	if (!meth) {
-		meth = "GET";
-		wsi->do_ws = 1;
-	} else {
-		wsi->do_ws = 0;
-	}
-
-	if (!strcmp(meth, "RAW")) {
-		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-		lwsl_notice("client transition to raw\n");
-
-		if (pp) {
-			const struct lws_protocols *pr;
-
-			pr = lws_vhost_name_to_protocol(wsi->vhost, pp);
-
-			if (!pr) {
-				lwsl_err("protocol %s not enabled on vhost\n",
-					 pp);
-				return NULL;
-			}
-
-			lws_bind_protocol(wsi, pr);
-		}
-
-		if ((wsi->protocol->callback)(wsi,
-				LWS_CALLBACK_RAW_ADOPT,
-				wsi->user_space, NULL, 0))
-			return NULL;
-
-		lws_header_table_force_to_detachable_state(wsi);
-		lws_union_transition(wsi, LWSCM_RAW);
-		lws_header_table_detach(wsi, 1);
-
-		return NULL;
-	}
-
-	if (wsi->do_ws) {
-		/*
-		 * create the random key
-		 */
-		n = lws_get_random(context, hash, 16);
-		if (n != 16) {
-			lwsl_err("Unable to read from random dev %s\n",
-				 SYSTEM_RANDOM_FILEPATH);
-			return NULL;
-		}
-
-		lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
-	}
-
-	/*
-	 * 04 example client handshake
-	 *
-	 * GET /chat HTTP/1.1
-	 * Host: server.example.com
-	 * Upgrade: websocket
-	 * Connection: Upgrade
-	 * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-	 * Sec-WebSocket-Origin: http://example.com
-	 * Sec-WebSocket-Protocol: chat, superchat
-	 * Sec-WebSocket-Version: 4
-	 */
-
-	p += sprintf(p, "%s %s HTTP/1.1\x0d\x0a", meth,
-		     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));
-
-	p += sprintf(p, "Pragma: no-cache\x0d\x0a"
-			"Cache-Control: no-cache\x0d\x0a");
-
-	p += sprintf(p, "Host: %s\x0d\x0a",
-		     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
-
-	if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) {
-		if (lws_check_opt(context->options, LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN))
-			p += sprintf(p, "Origin: %s\x0d\x0a",
-				     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN));
-		else
-			p += sprintf(p, "Origin: http://%s\x0d\x0a",
-				     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN));
-	}
-
-	if (wsi->do_ws) {
-		p += sprintf(p, "Upgrade: websocket\x0d\x0a"
-				"Connection: Upgrade\x0d\x0a"
-				"Sec-WebSocket-Key: ");
-		strcpy(p, key_b64);
-		p += strlen(key_b64);
-		p += sprintf(p, "\x0d\x0a");
-		if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
-			p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
-			     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));
-
-		/* tell the server what extensions we could support */
-
-#ifndef LWS_NO_EXTENSIONS
-		ext = wsi->vhost->extensions;
-		while (ext && ext->callback) {
-			n = lws_ext_cb_all_exts(context, wsi,
-				   LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION,
-				   (char *)ext->name, 0);
-			if (n) { /* an extension vetos us */
-				lwsl_ext("ext %s vetoed\n", (char *)ext->name);
-				ext++;
-				continue;
-			}
-			n = wsi->vhost->protocols[0].callback(wsi,
-				LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
-					wsi->user_space, (char *)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) {
-				ext++;
-				continue;
-			}
-
-			/* apply it */
-
-			if (ext_count)
-				*p++ = ',';
-			else
-				p += sprintf(p, "Sec-WebSocket-Extensions: ");
-			p += sprintf(p, "%s", ext->client_offer);
-			ext_count++;
-
-			ext++;
-		}
-		if (ext_count)
-			p += sprintf(p, "\x0d\x0a");
-#endif
-
-		if (wsi->ietf_spec_revision)
-			p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
-				     wsi->ietf_spec_revision);
-
-		/* prepare the expected server accept response */
-
-		key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
-		n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64);
-
-		lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);
-
-		lws_b64_encode_string(hash, 20,
-				      wsi->u.hdr.ah->initial_handshake_hash_base64,
-				      sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64));
-	}
-
-	/* give userland a chance to append, eg, cookies */
-
-	if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
-				wsi->user_space, &p, (pkt + context->pt_serv_buf_size) - p - 12))
-		return NULL;
-
-	p += sprintf(p, "\x0d\x0a");
-
-	return p;
-}
-

+ 0 - 625
thirdparty/libwebsockets/client/ssl-client.c

@@ -1,625 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <[email protected]>
- *
- *  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_MBEDTLS)
-	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_MBEDTLS)
-#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_MBEDTLS)
-	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_MBEDTLS)
-	SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, 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_MBEDTLS)
-	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_MBEDTLS)
-	BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */
-#endif
-#endif
-
-#if !defined(LWS_WITH_MBEDTLS)
-	SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index,
-			wsi);
-#endif
-
-	return 0;
-}
-
-#if defined(LWS_WITH_MBEDTLS)
-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: errno %d: %s\n",
-				 n, errno, ERR_error_string(n, sb));
-			lws_ssl_elaborate_error();
-#if defined(LWS_WITH_MBEDTLS)
-			if (n == SSL_ERROR_SYSCALL)
-				return -1;
-#endif
-		}
-
-		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 0;
-	}
-
-	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_MBEDTLS)
-	{
-		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;
-	const char *ca_filepath = info->ssl_ca_filepath;
-#if !defined(LWS_WITH_MBEDTLS)
-	const char *cipher_list = info->ssl_cipher_list;
-	const char *private_key_filepath = info->ssl_private_key_filepath;
-	const char *cert_filepath = info->ssl_cert_filepath;
-	int n;
-
-	if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW)
-		return 0;
-
-	/*
-	 *  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_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 (info->client_ssl_ca_filepath)
-		ca_filepath = info->client_ssl_ca_filepath;
-
-	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;
-	}
-
-	lwsl_notice("created client ssl context for %s\n", vhost->name);
-
-#ifdef SSL_OP_NO_COMPRESSION
-	SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION);
-#endif
-
-#if defined(LWS_WITH_MBEDTLS)
-	if (ca_filepath) {
-		lws_filepos_t len;
-		uint8_t *buf;
-		/*
-		 * prototype this here, the shim does not export it in the
-		 * header, and we need to use the shim unchanged for ESP32 case
-		 */
-		X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len);
-
-		if (alloc_file(vhost->context, ca_filepath, &buf, &len)) {
-			lwsl_err("Load CA cert file %s failed\n", ca_filepath);
-			return 1;
-		}
-
-		vhost->x509_client_CA = d2i_X509(NULL, buf, len);
-		free(buf);
-		if (!vhost->x509_client_CA) {
-			lwsl_err("client CA: x509 parse failed\n");
-			return 1;
-		}
-
-		SSL_CTX_add_client_CA(vhost->ssl_client_ctx,
-				      vhost->x509_client_CA);
-
-		lwsl_notice("client loaded CA for verification %s\n", ca_filepath);
-	}
-#else
-	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 LWS_OPENSSL_CLIENT_CERTS) -- "
-			    "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");
-
-	/*
-	 * callback allowing user code to load extra verification certs
-	 * helping the client to verify server identity
-	 */
-
-	/* 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;
-}

+ 6 - 4
thirdparty/libwebsockets/alloc.c → thirdparty/libwebsockets/core/alloc.c

@@ -1,4 +1,4 @@
-#include "private-libwebsockets.h"
+#include "core/private.h"
 
 
 #if defined(LWS_PLAT_OPTEE)
 #if defined(LWS_PLAT_OPTEE)
 
 
@@ -51,10 +51,12 @@ void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason))
 static void *_realloc(void *ptr, size_t size, const char *reason)
 static void *_realloc(void *ptr, size_t size, const char *reason)
 {
 {
 	if (size) {
 	if (size) {
-#if defined(LWS_PLAT_ESP32)
-		lwsl_notice("%s: size %lu: %s\n", __func__, (unsigned long)size, reason);
+#if defined(LWS_WITH_ESP32)
+		lwsl_notice("%s: size %lu: %s (free heap %d)\n", __func__,
+			    (unsigned long)size, reason, (unsigned int)esp_get_free_heap_size() - (int)size);
 #else
 #else
-		lwsl_debug("%s: size %lu: %s\n", __func__, (unsigned long)size, reason);
+		lwsl_debug("%s: size %lu: %s\n", __func__,
+			   (unsigned long)size, reason);
 #endif
 #endif
 #if defined(LWS_PLAT_OPTEE)
 #if defined(LWS_PLAT_OPTEE)
 		return (void *)TEE_Realloc(ptr, size);
 		return (void *)TEE_Realloc(ptr, size);

File diff suppressed because it is too large
+ 413 - 230
thirdparty/libwebsockets/core/context.c


File diff suppressed because it is too large
+ 439 - 342
thirdparty/libwebsockets/core/libwebsockets.c


+ 308 - 0
thirdparty/libwebsockets/core/output.c

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

+ 228 - 174
thirdparty/libwebsockets/pollfd.c → thirdparty/libwebsockets/core/pollfd.c

@@ -19,21 +19,31 @@
  *  MA  02110-1301  USA
  *  MA  02110-1301  USA
  */
  */
 
 
-#include "private-libwebsockets.h"
+#include "core/private.h"
 
 
 int
 int
 _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
 _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
 {
 {
+#if !defined(LWS_WITH_LIBUV) && !defined(LWS_WITH_LIBEV) && !defined(LWS_WITH_LIBEVENT)
+	volatile struct lws_context_per_thread *vpt;
+#endif
 	struct lws_context_per_thread *pt;
 	struct lws_context_per_thread *pt;
 	struct lws_context *context;
 	struct lws_context *context;
 	int ret = 0, pa_events = 1;
 	int ret = 0, pa_events = 1;
 	struct lws_pollfd *pfd;
 	struct lws_pollfd *pfd;
 	int sampled_tid, tid;
 	int sampled_tid, tid;
 
 
-	if (!wsi || wsi->position_in_fds_table < 0)
+	if (!wsi)
+		return 0;
+
+	assert(wsi->position_in_fds_table == LWS_NO_FDS_POS ||
+	       wsi->position_in_fds_table >= 0);
+
+	if (wsi->position_in_fds_table == LWS_NO_FDS_POS)
 		return 0;
 		return 0;
 
 
-	if (wsi->handling_pollout && !_and && _or == LWS_POLLOUT) {
+	if (((volatile struct lws *)wsi)->handling_pollout &&
+	    !_and && _or == LWS_POLLOUT) {
 		/*
 		/*
 		 * Happening alongside service thread handling POLLOUT.
 		 * Happening alongside service thread handling POLLOUT.
 		 * The danger is when he is finished, he will disable POLLOUT,
 		 * The danger is when he is finished, he will disable POLLOUT,
@@ -42,7 +52,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
 		 * Instead of changing the fds, inform the service thread
 		 * Instead of changing the fds, inform the service thread
 		 * what happened, and ask it to leave POLLOUT active on exit
 		 * what happened, and ask it to leave POLLOUT active on exit
 		 */
 		 */
-		wsi->leave_pollout_active = 1;
+		((volatile struct lws *)wsi)->leave_pollout_active = 1;
 		/*
 		/*
 		 * by definition service thread is not in poll wait, so no need
 		 * by definition service thread is not in poll wait, so no need
 		 * to cancel service
 		 * to cancel service
@@ -55,42 +65,106 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
 
 
 	context = wsi->context;
 	context = wsi->context;
 	pt = &context->pt[(int)wsi->tsi];
 	pt = &context->pt[(int)wsi->tsi];
-	assert(wsi->position_in_fds_table >= 0 &&
-	       wsi->position_in_fds_table < pt->fds_count);
+
+	assert(wsi->position_in_fds_table < (int)pt->fds_count);
+
+#if !defined(LWS_WITH_LIBUV) && \
+    !defined(LWS_WITH_LIBEV) && \
+    !defined(LWS_WITH_LIBEVENT)
+	/*
+	 * This only applies when we use the default poll() event loop.
+	 *
+	 * BSD can revert pa->events at any time, when the kernel decides to
+	 * exit from poll().  We can't protect against it using locking.
+	 *
+	 * Therefore we must check first if the service thread is in poll()
+	 * wait; if so, we know we must be being called from a foreign thread,
+	 * and we must keep a strictly ordered list of changes we made instead
+	 * of trying to apply them, since when poll() exits, which may happen
+	 * at any time it would revert our changes.
+	 *
+	 * The plat code will apply them when it leaves the poll() wait
+	 * before doing anything else.
+	 */
+
+	vpt = (volatile struct lws_context_per_thread *)pt;
+
+	vpt->foreign_spinlock = 1;
+	lws_memory_barrier();
+
+	if (vpt->inside_poll) {
+		struct lws_foreign_thread_pollfd *ftp, **ftp1;
+		/*
+		 * We are certainly a foreign thread trying to change events
+		 * while the service thread is in the poll() wait.
+		 *
+		 * Create a list of changes to be applied after poll() exit,
+		 * instead of trying to apply them now.
+		 */
+		ftp = lws_malloc(sizeof(*ftp), "ftp");
+		if (!ftp) {
+			vpt->foreign_spinlock = 0;
+			lws_memory_barrier();
+			ret = -1;
+			goto bail;
+		}
+
+		ftp->_and = _and;
+		ftp->_or = _or;
+		ftp->fd_index = wsi->position_in_fds_table;
+		ftp->next = NULL;
+
+		/* place at END of list to maintain order */
+		ftp1 = (struct lws_foreign_thread_pollfd **)
+						&vpt->foreign_pfd_list;
+		while (*ftp1)
+			ftp1 = &((*ftp1)->next);
+
+		*ftp1 = ftp;
+		vpt->foreign_spinlock = 0;
+		lws_memory_barrier();
+		lws_cancel_service_pt(wsi);
+
+		return 0;
+	}
+
+	vpt->foreign_spinlock = 0;
+	lws_memory_barrier();
+#endif
 
 
 	pfd = &pt->fds[wsi->position_in_fds_table];
 	pfd = &pt->fds[wsi->position_in_fds_table];
 	pa->fd = wsi->desc.sockfd;
 	pa->fd = wsi->desc.sockfd;
+	lwsl_debug("%s: wsi %p: fd %d events %d -> %d\n", __func__, wsi, pa->fd, pfd->events, (pfd->events & ~_and) | _or);
 	pa->prev_events = pfd->events;
 	pa->prev_events = pfd->events;
 	pa->events = pfd->events = (pfd->events & ~_and) | _or;
 	pa->events = pfd->events = (pfd->events & ~_and) | _or;
 
 
 	if (wsi->http2_substream)
 	if (wsi->http2_substream)
 		return 0;
 		return 0;
 
 
-	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD,
-					   wsi->user_space, (void *)pa, 0)) {
+	if (wsi->vhost &&
+	    wsi->vhost->protocols[0].callback(wsi,
+			    	    	      LWS_CALLBACK_CHANGE_MODE_POLL_FD,
+					      wsi->user_space, (void *)pa, 0)) {
 		ret = -1;
 		ret = -1;
 		goto bail;
 		goto bail;
 	}
 	}
 
 
-	if (_and & LWS_POLLIN) {
-		lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ);
-		lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ);
-		lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ);
-	}
-	if (_or & LWS_POLLIN) {
-		lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
-		lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
-		lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
-	}
-	if (_and & LWS_POLLOUT) {
-		lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
-		lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
-		lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
-	}
-	if (_or & LWS_POLLOUT) {
-		lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE);
-		lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE);
-		lws_libevent_io(wsi, LWS_EV_START | LWS_EV_WRITE);
+	if (context->event_loop_ops->io) {
+		if (_and & LWS_POLLIN)
+			context->event_loop_ops->io(wsi,
+					LWS_EV_STOP | LWS_EV_READ);
+
+		if (_or & LWS_POLLIN)
+			context->event_loop_ops->io(wsi,
+					LWS_EV_START | LWS_EV_READ);
+
+		if (_and & LWS_POLLOUT)
+			context->event_loop_ops->io(wsi,
+					LWS_EV_STOP | LWS_EV_WRITE);
+
+		if (_or & LWS_POLLOUT)
+			context->event_loop_ops->io(wsi,
+					LWS_EV_START | LWS_EV_WRITE);
 	}
 	}
 
 
 	/*
 	/*
@@ -100,20 +174,16 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
 	 *       ... and the service thread is waiting ...
 	 *       ... and the service thread is waiting ...
 	 *         then cancel it to force a restart with our changed events
 	 *         then cancel it to force a restart with our changed events
 	 */
 	 */
-#if LWS_POSIX
 	pa_events = pa->prev_events != pa->events;
 	pa_events = pa->prev_events != pa->events;
-#endif
 
 
 	if (pa_events) {
 	if (pa_events) {
-
 		if (lws_plat_change_pollfd(context, wsi, pfd)) {
 		if (lws_plat_change_pollfd(context, wsi, pfd)) {
 			lwsl_info("%s failed\n", __func__);
 			lwsl_info("%s failed\n", __func__);
 			ret = -1;
 			ret = -1;
 			goto bail;
 			goto bail;
 		}
 		}
-
 		sampled_tid = context->service_tid;
 		sampled_tid = context->service_tid;
-		if (sampled_tid) {
+		if (sampled_tid && wsi->vhost) {
 			tid = wsi->vhost->protocols[0].callback(wsi,
 			tid = wsi->vhost->protocols[0].callback(wsi,
 				     LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
 				     LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
 			if (tid == -1) {
 			if (tid == -1) {
@@ -124,34 +194,39 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
 				lws_cancel_service_pt(wsi);
 				lws_cancel_service_pt(wsi);
 		}
 		}
 	}
 	}
+
 bail:
 bail:
 	return ret;
 	return ret;
 }
 }
 
 
 #ifndef LWS_NO_SERVER
 #ifndef LWS_NO_SERVER
+/*
+ * Enable or disable listen sockets on this pt globally...
+ * it's modulated according to the pt having space for a new accept.
+ */
 static void
 static void
-lws_accept_modulation(struct lws_context_per_thread *pt, int allow)
+lws_accept_modulation(struct lws_context *context,
+		      struct lws_context_per_thread *pt, int allow)
 {
 {
-// multithread listen seems broken
-#if 0
 	struct lws_vhost *vh = context->vhost_list;
 	struct lws_vhost *vh = context->vhost_list;
 	struct lws_pollargs pa1;
 	struct lws_pollargs pa1;
 
 
 	while (vh) {
 	while (vh) {
-		if (allow)
-			_lws_change_pollfd(pt->wsi_listening,
+		if (vh->lserv_wsi) {
+			if (allow)
+				_lws_change_pollfd(vh->lserv_wsi,
 					   0, LWS_POLLIN, &pa1);
 					   0, LWS_POLLIN, &pa1);
-		else
-			_lws_change_pollfd(pt->wsi_listening,
+			else
+				_lws_change_pollfd(vh->lserv_wsi,
 					   LWS_POLLIN, 0, &pa1);
 					   LWS_POLLIN, 0, &pa1);
+		}
 		vh = vh->vhost_next;
 		vh = vh->vhost_next;
 	}
 	}
-#endif
 }
 }
 #endif
 #endif
 
 
 int
 int
-insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
+__insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
 {
 {
 	struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 };
 	struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 };
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
@@ -167,52 +242,46 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
 		return 1;
 		return 1;
 	}
 	}
 
 
-#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
-	if (wsi->desc.sockfd >= context->max_fds) {
-		lwsl_err("Socket fd %d is too high (%d)\n",
-			 wsi->desc.sockfd, context->max_fds);
+#if !defined(_WIN32)
+	if (wsi->desc.sockfd - lws_plat_socket_offset() >= context->max_fds) {
+		lwsl_err("Socket fd %d is too high (%d) offset %d\n",
+			 wsi->desc.sockfd, context->max_fds, lws_plat_socket_offset());
 		return 1;
 		return 1;
 	}
 	}
 #endif
 #endif
 
 
 	assert(wsi);
 	assert(wsi);
-	assert(wsi->vhost);
+	assert(wsi->event_pipe || wsi->vhost);
 	assert(lws_socket_is_valid(wsi->desc.sockfd));
 	assert(lws_socket_is_valid(wsi->desc.sockfd));
 
 
-	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
+	if (wsi->vhost &&
+	    wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
 					   wsi->user_space, (void *) &pa, 1))
 					   wsi->user_space, (void *) &pa, 1))
 		return -1;
 		return -1;
 
 
-	lws_pt_lock(pt);
 	pt->count_conns++;
 	pt->count_conns++;
 	insert_wsi(context, wsi);
 	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;
+	wsi->position_in_fds_table = pt->fds_count;
 
 
 	pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd;
 	pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd;
-#if LWS_POSIX
 	pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN;
 	pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN;
-#else
-	pt->fds[wsi->position_in_fds_table].events = 0;
-#endif
 	pa.events = pt->fds[pt->fds_count].events;
 	pa.events = pt->fds[pt->fds_count].events;
 
 
 	lws_plat_insert_socket_into_fds(context, wsi);
 	lws_plat_insert_socket_into_fds(context, wsi);
 
 
 	/* external POLL support via protocol 0 */
 	/* external POLL support via protocol 0 */
-	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
+	if (wsi->vhost &&
+	    wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
 					   wsi->user_space, (void *) &pa, 0))
 					   wsi->user_space, (void *) &pa, 0))
 		ret =  -1;
 		ret =  -1;
 #ifndef LWS_NO_SERVER
 #ifndef LWS_NO_SERVER
 	/* if no more room, defeat accepts on this thread */
 	/* if no more room, defeat accepts on this thread */
 	if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1)
 	if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1)
-		lws_accept_modulation(pt, 0);
+		lws_accept_modulation(context, pt, 0);
 #endif
 #endif
-	lws_pt_unlock(pt);
 
 
-	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
+	if (wsi->vhost &&
+	    wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
 					   wsi->user_space, (void *)&pa, 1))
 					   wsi->user_space, (void *)&pa, 1))
 		ret = -1;
 		ret = -1;
 
 
@@ -220,15 +289,13 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
 }
 }
 
 
 int
 int
-remove_wsi_socket_from_fds(struct lws *wsi)
+__remove_wsi_socket_from_fds(struct lws *wsi)
 {
 {
 	struct lws_context *context = wsi->context;
 	struct lws_context *context = wsi->context;
 	struct lws_pollargs pa = { wsi->desc.sockfd, 0, 0 };
 	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_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	struct lws *end_wsi;
 	struct lws *end_wsi;
 	int v;
 	int v;
-#endif
 	int m, ret = 0;
 	int m, ret = 0;
 
 
 	if (wsi->parent_carries_io) {
 	if (wsi->parent_carries_io) {
@@ -236,15 +303,16 @@ remove_wsi_socket_from_fds(struct lws *wsi)
 		return 0;
 		return 0;
 	}
 	}
 
 
-#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
-	if (wsi->desc.sockfd > context->max_fds) {
+#if !defined(_WIN32)
+	if (wsi->desc.sockfd - lws_plat_socket_offset() > context->max_fds) {
 		lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd,
 		lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd,
 			 context->max_fds);
 			 context->max_fds);
 		return 1;
 		return 1;
 	}
 	}
 #endif
 #endif
 
 
-	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
+	if (wsi->vhost &&
+	    wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
 					   wsi->user_space, (void *)&pa, 1))
 					   wsi->user_space, (void *)&pa, 1))
 		return -1;
 		return -1;
 
 
@@ -253,101 +321,114 @@ remove_wsi_socket_from_fds(struct lws *wsi)
 	/* the guy who is to be deleted's slot index in pt->fds */
 	/* the guy who is to be deleted's slot index in pt->fds */
 	m = wsi->position_in_fds_table;
 	m = wsi->position_in_fds_table;
 	
 	
-#if !defined(LWS_WITH_ESP8266)
-	lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE |
-			  LWS_EV_PREPARE_DELETION);
-	lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE |
-			  LWS_EV_PREPARE_DELETION);
+	/* these are the only valid possibilities for position_in_fds_table */
+	assert(m == LWS_NO_FDS_POS || (m >= 0 &&
+				       (unsigned int)m < pt->fds_count));
 
 
-	lws_pt_lock(pt);
+	if (context->event_loop_ops->io)
+		context->event_loop_ops->io(wsi,
+				  LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE |
+				  LWS_EV_PREPARE_DELETION);
 
 
 	lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n",
 	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,
 		  __func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table,
 		  pt->fds_count, pt->fds[pt->fds_count].fd);
 		  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;
+	if (m != LWS_NO_FDS_POS) {
+
+		/* have the last guy take up the now vacant slot */
+		pt->fds[m] = pt->fds[pt->fds_count - 1];
+		/* this decrements pt->fds_count */
+		lws_plat_delete_socket_from_fds(context, wsi, m);
+		v = (int) pt->fds[m].fd;
+		/* end guy's "position in fds table" is now the deletion guy's old one */
+		end_wsi = wsi_from_fd(context, v);
+		if (!end_wsi) {
+			lwsl_err("no wsi for fd %d at pos %d, pt->fds_count=%d\n",
+				 (int)pt->fds[m].fd, m, pt->fds_count);
+			assert(0);
+		} else
+			end_wsi->position_in_fds_table = m;
+
+		/* deletion guy's lws_lookup entry needs nuking */
+		delete_from_fd(context, wsi->desc.sockfd);
+
+		/* removed wsi has no position any more */
+		wsi->position_in_fds_table = LWS_NO_FDS_POS;
+	}
 
 
 	/* remove also from external POLL support via protocol 0 */
 	/* 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;
+	if (lws_socket_is_valid(wsi->desc.sockfd) && wsi->vhost &&
+	    wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
+					      wsi->user_space, (void *) &pa, 0))
+		ret = -1;
+
 #ifndef LWS_NO_SERVER
 #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);
+	if (!context->being_destroyed &&
+	    /* if this made some room, accept connects on this thread */
+	    (unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
+		lws_accept_modulation(context, pt, 1);
 #endif
 #endif
-	lws_pt_unlock(pt);
 
 
-	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
+	if (wsi->vhost &&
+	    wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
 					      wsi->user_space, (void *) &pa, 1))
 					      wsi->user_space, (void *) &pa, 1))
 		ret = -1;
 		ret = -1;
-#endif
+
 	return ret;
 	return ret;
 }
 }
 
 
 int
 int
-lws_change_pollfd(struct lws *wsi, int _and, int _or)
+__lws_change_pollfd(struct lws *wsi, int _and, int _or)
 {
 {
-	struct lws_context_per_thread *pt;
 	struct lws_context *context;
 	struct lws_context *context;
 	struct lws_pollargs pa;
 	struct lws_pollargs pa;
 	int ret = 0;
 	int ret = 0;
 
 
-	if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0)
-		return 1;
+	if (!wsi || (!wsi->protocol && !wsi->event_pipe) ||
+	    wsi->position_in_fds_table == LWS_NO_FDS_POS)
+		return 0;
 
 
 	context = lws_get_context(wsi);
 	context = lws_get_context(wsi);
 	if (!context)
 	if (!context)
 		return 1;
 		return 1;
 
 
-	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
-					      wsi->user_space,  (void *) &pa, 0))
+	if (wsi->vhost &&
+	    wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
+					      wsi->user_space, (void *) &pa, 0))
 		return -1;
 		return -1;
 
 
-	pt = &context->pt[(int)wsi->tsi];
-
-	lws_pt_lock(pt);
 	ret = _lws_change_pollfd(wsi, _and, _or, &pa);
 	ret = _lws_change_pollfd(wsi, _and, _or, &pa);
-	lws_pt_unlock(pt);
-	if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
+	if (wsi->vhost &&
+	    wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
 					   wsi->user_space, (void *) &pa, 0))
 					   wsi->user_space, (void *) &pa, 0))
 		ret = -1;
 		ret = -1;
 
 
 	return ret;
 	return ret;
 }
 }
 
 
+int
+lws_change_pollfd(struct lws *wsi, int _and, int _or)
+{
+	struct lws_context_per_thread *pt;
+	int ret = 0;
+
+	pt = &wsi->context->pt[(int)wsi->tsi];
+
+	lws_pt_lock(pt, __func__);
+	ret = __lws_change_pollfd(wsi, _and, _or);
+	lws_pt_unlock(pt);
+
+	return ret;
+}
+
 LWS_VISIBLE int
 LWS_VISIBLE int
 lws_callback_on_writable(struct lws *wsi)
 lws_callback_on_writable(struct lws *wsi)
 {
 {
 	struct lws_context_per_thread *pt;
 	struct lws_context_per_thread *pt;
-#ifdef LWS_WITH_HTTP2
-	struct lws *network_wsi, *wsi2;
-	int already;
-#endif
 	int n;
 	int n;
 
 
-	if (wsi->state == LWSS_SHUTDOWN)
+	if (lwsi_state(wsi) == LRS_SHUTDOWN)
 		return 0;
 		return 0;
 
 
 	if (wsi->socket_is_permanently_unusable)
 	if (wsi->socket_is_permanently_unusable)
@@ -375,72 +456,31 @@ lws_callback_on_writable(struct lws *wsi)
 #if defined(LWS_WITH_STATS)
 #if defined(LWS_WITH_STATS)
 	if (!wsi->active_writable_req_us) {
 	if (!wsi->active_writable_req_us) {
 		wsi->active_writable_req_us = time_in_microseconds();
 		wsi->active_writable_req_us = time_in_microseconds();
-		lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
+		lws_stats_atomic_bump(wsi->context, pt,
+				      LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
 	}
 	}
 #endif
 #endif
 
 
-#ifdef LWS_WITH_HTTP2
-	lwsl_info("%s: %p\n", __func__, wsi);
-
-	if (wsi->mode != LWSCM_HTTP2_SERVING)
-		goto network_sock;
 
 
-	if (wsi->u.h2.requested_POLLOUT) {
-		lwsl_info("already pending writable\n");
-		return 1;
+	if (wsi->role_ops->callback_on_writable) {
+		if (wsi->role_ops->callback_on_writable(wsi))
+			return 1;
+		wsi = lws_get_network_wsi(wsi);
 	}
 	}
 
 
-	/* is this for DATA or for control messages? */
-	if (wsi->upgraded_to_http2 && !wsi->u.h2.h2n->pps &&
-	    !lws_h2_tx_cr_get(wsi)) {
-		/*
-		 * other side is not able to cope with us sending DATA
-		 * anything so no matter if we have POLLOUT on our side if it's
-		 * DATA we want to send.
-		 *
-		 * Delay waiting for our POLLOUT until peer indicates he has
-		 * space for more using tx window command in http2 layer
-		 */
-		lwsl_notice("%s: %p: skint (%d)\n", __func__, wsi, wsi->u.h2.tx_cr);
-		wsi->u.h2.skint = 1;
-		return 0;
-	}
-
-	wsi->u.h2.skint = 0;
-	network_wsi = lws_get_network_wsi(wsi);
-	already = network_wsi->u.h2.requested_POLLOUT;
-
-	/* mark everybody above him as requesting pollout */
-
-	wsi2 = wsi;
-	while (wsi2) {
-		wsi2->u.h2.requested_POLLOUT = 1;
-		lwsl_info("mark %p pending writable\n", wsi2);
-		wsi2 = wsi2->u.h2.parent_wsi;
-	}
-
-	/* for network action, act only on the network wsi */
-
-	wsi = network_wsi;
-	if (already)
-		return 1;
-network_sock:
-#endif
-
-	if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0))
-		return 1;
-
-	if (wsi->position_in_fds_table < 0) {
-		lwsl_debug("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
+	if (wsi->position_in_fds_table == LWS_NO_FDS_POS) {
+		lwsl_debug("%s: failed to find socket %d\n", __func__,
+			   wsi->desc.sockfd);
 		return -1;
 		return -1;
 	}
 	}
 
 
-	if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
+	if (__lws_change_pollfd(wsi, 0, LWS_POLLOUT))
 		return -1;
 		return -1;
 
 
 	return 1;
 	return 1;
 }
 }
 
 
+
 /*
 /*
  * stitch protocol choice into the vh protocol linked list
  * stitch protocol choice into the vh protocol linked list
  * We always insert ourselves at the start of the list
  * We always insert ourselves at the start of the list
@@ -458,6 +498,8 @@ lws_same_vh_protocol_insert(struct lws *wsi, int n)
 		lwsl_notice("Attempted to attach wsi twice to same vh prot\n");
 		lwsl_notice("Attempted to attach wsi twice to same vh prot\n");
 	}
 	}
 
 
+	lws_vhost_lock(wsi->vhost);
+
 	wsi->same_vh_protocol_prev = &wsi->vhost->same_vh_protocol_list[n];
 	wsi->same_vh_protocol_prev = &wsi->vhost->same_vh_protocol_list[n];
 	/* old first guy is our next */
 	/* old first guy is our next */
 	wsi->same_vh_protocol_next =  wsi->vhost->same_vh_protocol_list[n];
 	wsi->same_vh_protocol_next =  wsi->vhost->same_vh_protocol_list[n];
@@ -468,6 +510,10 @@ lws_same_vh_protocol_insert(struct lws *wsi, int n)
 		/* old first guy points back to us now */
 		/* old first guy points back to us now */
 		wsi->same_vh_protocol_next->same_vh_protocol_prev =
 		wsi->same_vh_protocol_next->same_vh_protocol_prev =
 				&wsi->same_vh_protocol_next;
 				&wsi->same_vh_protocol_next;
+
+	wsi->on_same_vh_list = 1;
+
+	lws_vhost_unlock(wsi->vhost);
 }
 }
 
 
 void
 void
@@ -482,6 +528,11 @@ lws_same_vh_protocol_remove(struct lws *wsi)
 	 */
 	 */
 	lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi);
 	lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi);
 
 
+	if (!wsi->vhost || !wsi->on_same_vh_list)
+		return;
+
+	lws_vhost_lock(wsi->vhost);
+
 	if (wsi->same_vh_protocol_prev) {
 	if (wsi->same_vh_protocol_prev) {
 		assert (*(wsi->same_vh_protocol_prev) == wsi);
 		assert (*(wsi->same_vh_protocol_prev) == wsi);
 		lwsl_info("have prev %p, setting him to our next %p\n",
 		lwsl_info("have prev %p, setting him to our next %p\n",
@@ -493,13 +544,15 @@ lws_same_vh_protocol_remove(struct lws *wsi)
 	}
 	}
 
 
 	/* our next should point back to our prev */
 	/* our next should point back to our prev */
-	if (wsi->same_vh_protocol_next) {
+	if (wsi->same_vh_protocol_next)
 		wsi->same_vh_protocol_next->same_vh_protocol_prev =
 		wsi->same_vh_protocol_next->same_vh_protocol_prev =
 				wsi->same_vh_protocol_prev;
 				wsi->same_vh_protocol_prev;
-	}
 
 
 	wsi->same_vh_protocol_prev = NULL;
 	wsi->same_vh_protocol_prev = NULL;
 	wsi->same_vh_protocol_next = NULL;
 	wsi->same_vh_protocol_next = NULL;
+	wsi->on_same_vh_list = 0;
+
+	lws_vhost_unlock(wsi->vhost);
 }
 }
 
 
 
 
@@ -523,7 +576,8 @@ lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
 		assert(wsi->protocol == protocol);
 		assert(wsi->protocol == protocol);
 		assert(*wsi->same_vh_protocol_prev == wsi);
 		assert(*wsi->same_vh_protocol_prev == wsi);
 		if (wsi->same_vh_protocol_next)
 		if (wsi->same_vh_protocol_next)
-			assert(wsi->same_vh_protocol_next->same_vh_protocol_prev ==
+			assert(wsi->same_vh_protocol_next->
+					same_vh_protocol_prev ==
 					&wsi->same_vh_protocol_next);
 					&wsi->same_vh_protocol_next);
 
 
 		lws_callback_on_writable(wsi);
 		lws_callback_on_writable(wsi);

+ 1751 - 0
thirdparty/libwebsockets/core/private.h

@@ -0,0 +1,1751 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <[email protected]>
+ *
+ *  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 "lws_config.h"
+#include "lws_config_private.h"
+
+#if defined(LWS_WITH_CGI) && defined(LWS_HAVE_VFORK)
+ #define  _GNU_SOURCE
+#endif
+
+#if defined(__COVERITY__) && !defined(LWS_COVERITY_WORKAROUND)
+ #define LWS_COVERITY_WORKAROUND
+ typedef float _Float32;
+ typedef float _Float64;
+ typedef float _Float128;
+ typedef float _Float32x;
+ typedef float _Float64x;
+ typedef float _Float128x;
+#endif
+
+#ifdef LWS_HAVE_SYS_TYPES_H
+ #include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <inttypes.h>
+
+#if defined(LWS_WITH_ESP32)
+ #define MSG_NOSIGNAL 0
+ #define SOMAXCONN 3
+#endif
+
+#define STORE_IN_ROM
+#include <assert.h>
+#if LWS_MAX_SMP > 1
+ #include <pthread.h>
+#endif
+
+#ifdef LWS_HAVE_SYS_STAT_H
+ #include <sys/stat.h>
+#endif
+
+#if defined(WIN32) || defined(_WIN32)
+
+ #ifndef WIN32_LEAN_AND_MEAN
+  #define WIN32_LEAN_AND_MEAN
+ #endif
+
+ #if (WINVER < 0x0501)
+  #undef WINVER
+  #undef _WIN32_WINNT
+  #define WINVER 0x0501
+  #define _WIN32_WINNT WINVER
+ #endif
+
+ #define LWS_NO_DAEMONIZE
+ #define LWS_ERRNO WSAGetLastError()
+ #define LWS_EAGAIN WSAEWOULDBLOCK
+ #define LWS_EALREADY WSAEALREADY
+ #define LWS_EINPROGRESS WSAEINPROGRESS
+ #define LWS_EINTR WSAEINTR
+ #define LWS_EISCONN WSAEISCONN
+ #define LWS_EWOULDBLOCK WSAEWOULDBLOCK
+ #define MSG_NOSIGNAL 0
+ #define SHUT_RDWR SD_BOTH
+ #define SOL_TCP IPPROTO_TCP
+ #define SHUT_WR SD_SEND
+
+ #define compatible_close(fd) closesocket(fd)
+ #define lws_set_blocking_send(wsi) wsi->sock_send_blocking = 1
+ #define LWS_SOCK_INVALID (INVALID_SOCKET)
+
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #include <windows.h>
+ #include <tchar.h>
+ #ifdef LWS_HAVE_IN6ADDR_H
+  #include <in6addr.h>
+ #endif
+ #include <mstcpip.h>
+ #include <io.h>
+
+ #if !defined(LWS_HAVE_ATOLL)
+  #if defined(LWS_HAVE__ATOI64)
+   #define atoll _atoi64
+  #else
+   #warning No atoll or _atoi64 available, using atoi
+   #define atoll atoi
+  #endif
+ #endif
+
+ #ifndef __func__
+  #define __func__ __FUNCTION__
+ #endif
+
+ #ifdef LWS_HAVE__VSNPRINTF
+  #define vsnprintf _vsnprintf
+ #endif
+
+ /* we don't have an implementation for this on windows... */
+ int kill(int pid, int sig);
+ int fork(void);
+ #ifndef SIGINT
+  #define SIGINT 2
+ #endif
+
+#else /* not windows --> */
+
+ #include <fcntl.h>
+ #include <strings.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+
+ #ifndef __cplusplus
+  #include <errno.h>
+ #endif
+ #include <netdb.h>
+ #include <signal.h>
+ #include <sys/socket.h>
+
+ #if defined(LWS_BUILTIN_GETIFADDRS)
+  #include "./misc/getifaddrs.h"
+ #else
+  #if !defined(LWS_WITH_ESP32)
+   #if defined(__HAIKU__)
+    #define _BSD_SOURCE
+   #endif
+   #include <ifaddrs.h>
+  #endif
+ #endif
+ #if defined (__ANDROID__)
+  #include <syslog.h>
+  #include <sys/resource.h>
+ #elif defined (__sun) || defined(__HAIKU__) || defined(__QNX__)
+  #include <syslog.h>
+ #else
+  #if !defined(LWS_WITH_ESP32)
+   #include <sys/syslog.h>
+  #endif
+ #endif
+ #include <netdb.h>
+ #if !defined(LWS_WITH_ESP32)
+  #include <sys/mman.h>
+  #include <sys/un.h>
+  #include <netinet/in.h>
+  #include <netinet/tcp.h>
+  #include <arpa/inet.h>
+  #include <poll.h>
+ #endif
+ #ifndef LWS_NO_FORK
+  #ifdef LWS_HAVE_SYS_PRCTL_H
+   #include <sys/prctl.h>
+  #endif
+ #endif
+
+ #include <sys/time.h>
+
+ #define LWS_ERRNO errno
+ #define LWS_EAGAIN EAGAIN
+ #define LWS_EALREADY EALREADY
+ #define LWS_EINPROGRESS EINPROGRESS
+ #define LWS_EINTR EINTR
+ #define LWS_EISCONN EISCONN
+ #define LWS_EWOULDBLOCK EWOULDBLOCK
+
+ #define lws_set_blocking_send(wsi)
+
+ #define LWS_SOCK_INVALID (-1)
+#endif /* not windows */
+
+#ifndef LWS_HAVE_BZERO
+ #ifndef bzero
+  #define bzero(b, len) (memset((b), '\0', (len)), (void) 0)
+ #endif
+#endif
+
+#ifndef LWS_HAVE_STRERROR
+ #define strerror(x) ""
+#endif
+
+
+#define lws_socket_is_valid(x) (x != LWS_SOCK_INVALID)
+
+#include "libwebsockets.h"
+
+#include "tls/private.h"
+
+#if defined(WIN32) || defined(_WIN32)
+ #include <gettimeofday.h>
+
+ #ifndef BIG_ENDIAN
+  #define BIG_ENDIAN    4321  /* to show byte order (taken from gcc) */
+ #endif
+ #ifndef LITTLE_ENDIAN
+  #define LITTLE_ENDIAN 1234
+ #endif
+ #ifndef BYTE_ORDER
+  #define BYTE_ORDER LITTLE_ENDIAN
+ #endif
+
+ #undef __P
+ #ifndef __P
+  #if __STDC__
+   #define __P(protos) protos
+  #else
+   #define __P(protos) ()
+  #endif
+ #endif
+
+#else /* not windows */
+ static inline int compatible_close(int fd) { return close(fd); }
+
+ #include <sys/stat.h>
+ #include <sys/time.h>
+
+ #if defined(__APPLE__)
+  #include <machine/endian.h>
+ #elif defined(__FreeBSD__)
+  #include <sys/endian.h>
+ #elif defined(__linux__)
+  #include <endian.h>
+ #endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__QNX__)
+	#include <gulliver.h>
+	#if defined(__LITTLEENDIAN__)
+		#define BYTE_ORDER __LITTLEENDIAN__
+		#define LITTLE_ENDIAN __LITTLEENDIAN__
+		#define BIG_ENDIAN 4321  /* to show byte order (taken from gcc); for suppres warning that BIG_ENDIAN is not defined. */
+	#endif
+	#if defined(__BIGENDIAN__)
+		#define BYTE_ORDER __BIGENDIAN__
+		#define LITTLE_ENDIAN 1234  /* to show byte order (taken from gcc); for suppres warning that LITTLE_ENDIAN is not defined. */
+		#define BIG_ENDIAN __BIGENDIAN__
+	#endif
+#endif
+
+#if defined(__sun) && defined(__GNUC__)
+
+ #include <arpa/nameser_compat.h>
+
+ #if !defined (BYTE_ORDER)
+  #define BYTE_ORDER __BYTE_ORDER__
+ #endif
+
+ #if !defined(LITTLE_ENDIAN)
+  #define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
+ #endif
+
+ #if !defined(BIG_ENDIAN)
+  #define BIG_ENDIAN __ORDER_BIG_ENDIAN__
+ #endif
+
+#endif /* sun + GNUC */
+
+#if !defined(BYTE_ORDER)
+ #define BYTE_ORDER __BYTE_ORDER
+#endif
+#if !defined(LITTLE_ENDIAN)
+ #define LITTLE_ENDIAN __LITTLE_ENDIAN
+#endif
+#if !defined(BIG_ENDIAN)
+ #define BIG_ENDIAN __BIG_ENDIAN
+#endif
+
+
+/*
+ * Mac OSX as well as iOS do not define the MSG_NOSIGNAL flag,
+ * but happily have something equivalent in the SO_NOSIGPIPE flag.
+ */
+#ifdef __APPLE__
+#define MSG_NOSIGNAL SO_NOSIGPIPE
+#endif
+
+/*
+ * Solaris 11.X only supports POSIX 2001, MSG_NOSIGNAL appears in
+ * POSIX 2008.
+ */
+#ifdef __sun
+ #define MSG_NOSIGNAL 0
+#endif
+
+#ifdef _WIN32
+ #ifndef FD_HASHTABLE_MODULUS
+  #define FD_HASHTABLE_MODULUS 32
+ #endif
+#endif
+
+#ifndef LWS_DEF_HEADER_LEN
+#define LWS_DEF_HEADER_LEN 4096
+#endif
+#ifndef LWS_DEF_HEADER_POOL
+#define LWS_DEF_HEADER_POOL 4
+#endif
+#ifndef LWS_MAX_PROTOCOLS
+#define LWS_MAX_PROTOCOLS 5
+#endif
+#ifndef LWS_MAX_EXTENSIONS_ACTIVE
+#define LWS_MAX_EXTENSIONS_ACTIVE 1
+#endif
+#ifndef LWS_MAX_EXT_OFFERS
+#define LWS_MAX_EXT_OFFERS 8
+#endif
+#ifndef SPEC_LATEST_SUPPORTED
+#define SPEC_LATEST_SUPPORTED 13
+#endif
+#ifndef AWAITING_TIMEOUT
+#define AWAITING_TIMEOUT 20
+#endif
+#ifndef CIPHERS_LIST_STRING
+#define CIPHERS_LIST_STRING "DEFAULT"
+#endif
+#ifndef LWS_SOMAXCONN
+#define LWS_SOMAXCONN SOMAXCONN
+#endif
+
+#define MAX_WEBSOCKET_04_KEY_LEN 128
+
+#ifndef SYSTEM_RANDOM_FILEPATH
+#define SYSTEM_RANDOM_FILEPATH "/dev/urandom"
+#endif
+
+#define LWS_H2_RX_SCRATCH_SIZE 512
+
+
+
+/*
+ * All lws_tls...() functions must return this type, converting the
+ * native backend result and doing the extra work to determine which one
+ * as needed.
+ *
+ * Native TLS backend return codes are NOT ALLOWED outside the backend.
+ *
+ * Non-SSL mode also uses these types.
+ */
+enum lws_ssl_capable_status {
+	LWS_SSL_CAPABLE_ERROR = -1,		 /* it failed */
+	LWS_SSL_CAPABLE_DONE = 0,		 /* it succeeded */
+	LWS_SSL_CAPABLE_MORE_SERVICE_READ = -2,	 /* retry WANT_READ */
+	LWS_SSL_CAPABLE_MORE_SERVICE_WRITE = -3,  /* retry WANT_WRITE */
+	LWS_SSL_CAPABLE_MORE_SERVICE = -4,	 /* general retry */
+};
+
+#if defined(__clang__)
+#define lws_memory_barrier() __sync_synchronize()
+#elif defined(__GNUC__)
+#define lws_memory_barrier() __sync_synchronize()
+#else
+#define lws_memory_barrier()
+#endif
+
+/*
+ *
+ *  ------ roles ------
+ *
+ */
+
+#include "roles/private.h"
+
+/* null-terminated array of pointers to roles lws built with */
+extern const struct lws_role_ops *available_roles[];
+
+#define LWS_FOR_EVERY_AVAILABLE_ROLE_START(xx) { \
+		const struct lws_role_ops **ppxx = available_roles; \
+		while (*ppxx) { \
+			const struct lws_role_ops *xx = *ppxx++;
+
+#define LWS_FOR_EVERY_AVAILABLE_ROLE_END }}
+
+/*
+ *
+ *  ------ event_loop ops ------
+ *
+ */
+
+#include "event-libs/private.h"
+
+/* enums of socks version */
+enum socks_version {
+	SOCKS_VERSION_4 = 4,
+	SOCKS_VERSION_5 = 5
+};
+
+/* enums of subnegotiation version */
+enum socks_subnegotiation_version {
+	SOCKS_SUBNEGOTIATION_VERSION_1 = 1,
+};
+
+/* enums of socks commands */
+enum socks_command {
+	SOCKS_COMMAND_CONNECT = 1,
+	SOCKS_COMMAND_BIND = 2,
+	SOCKS_COMMAND_UDP_ASSOCIATE = 3
+};
+
+/* enums of socks address type */
+enum socks_atyp {
+	SOCKS_ATYP_IPV4 = 1,
+	SOCKS_ATYP_DOMAINNAME = 3,
+	SOCKS_ATYP_IPV6 = 4
+};
+
+/* enums of socks authentication methods */
+enum socks_auth_method {
+	SOCKS_AUTH_NO_AUTH = 0,
+	SOCKS_AUTH_GSSAPI = 1,
+	SOCKS_AUTH_USERNAME_PASSWORD = 2
+};
+
+/* enums of subnegotiation status */
+enum socks_subnegotiation_status {
+	SOCKS_SUBNEGOTIATION_STATUS_SUCCESS = 0,
+};
+
+/* enums of socks request reply */
+enum socks_request_reply {
+	SOCKS_REQUEST_REPLY_SUCCESS = 0,
+	SOCKS_REQUEST_REPLY_FAILURE_GENERAL = 1,
+	SOCKS_REQUEST_REPLY_CONNECTION_NOT_ALLOWED = 2,
+	SOCKS_REQUEST_REPLY_NETWORK_UNREACHABLE = 3,
+	SOCKS_REQUEST_REPLY_HOST_UNREACHABLE = 4,
+	SOCKS_REQUEST_REPLY_CONNECTION_REFUSED = 5,
+	SOCKS_REQUEST_REPLY_TTL_EXPIRED = 6,
+	SOCKS_REQUEST_REPLY_COMMAND_NOT_SUPPORTED = 7,
+	SOCKS_REQUEST_REPLY_ATYP_NOT_SUPPORTED = 8
+};
+
+/* enums used to generate socks messages */
+enum socks_msg_type {
+	/* greeting */
+	SOCKS_MSG_GREETING,
+	/* credential, user name and password */
+	SOCKS_MSG_USERNAME_PASSWORD,
+	/* connect command */
+	SOCKS_MSG_CONNECT
+};
+
+enum {
+	LWS_RXFLOW_ALLOW = (1 << 0),
+	LWS_RXFLOW_PENDING_CHANGE = (1 << 1),
+};
+
+struct lws_ring {
+	void *buf;
+	void (*destroy_element)(void *element);
+	uint32_t buflen;
+	uint32_t element_len;
+	uint32_t head;
+	uint32_t oldest_tail;
+};
+
+struct lws_protocols;
+struct lws;
+
+struct lws_io_watcher {
+#ifdef LWS_WITH_LIBEV
+	struct lws_io_watcher_libev ev;
+#endif
+#ifdef LWS_WITH_LIBUV
+	struct lws_io_watcher_libuv uv;
+#endif
+#ifdef LWS_WITH_LIBEVENT
+	struct lws_io_watcher_libevent event;
+#endif
+	struct lws_context *context;
+
+	uint8_t actual_events;
+};
+
+struct lws_signal_watcher {
+#ifdef LWS_WITH_LIBEV
+	struct lws_signal_watcher_libev ev;
+#endif
+#ifdef LWS_WITH_LIBUV
+	struct lws_signal_watcher_libuv uv;
+#endif
+#ifdef LWS_WITH_LIBEVENT
+	struct lws_signal_watcher_libevent event;
+#endif
+	struct lws_context *context;
+};
+
+#ifdef _WIN32
+#define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS)
+struct lws_fd_hashtable {
+	struct lws **wsi;
+	int length;
+};
+#endif
+
+struct lws_foreign_thread_pollfd {
+	struct lws_foreign_thread_pollfd *next;
+	int fd_index;
+	int _and;
+	int _or;
+};
+
+
+#define LWS_HRTIMER_NOWAIT (0x7fffffffffffffffll)
+
+/*
+ * so we can have n connections being serviced simultaneously,
+ * these things need to be isolated per-thread.
+ */
+
+struct lws_context_per_thread {
+#if LWS_MAX_SMP > 1
+	pthread_mutex_t lock;
+	pthread_mutex_t lock_stats;
+	pthread_t lock_owner;
+	const char *last_lock_reason;
+#endif
+
+	struct lws_context *context;
+
+	/*
+	 * usable by anything in the service code, but only if the scope
+	 * does not last longer than the service action (since next service
+	 * of any socket can likewise use it and overwrite)
+	 */
+	unsigned char *serv_buf;
+
+	struct lws_dll_lws dll_head_timeout;
+	struct lws_dll_lws dll_head_hrtimer;
+	struct lws_dll_lws dll_head_buflist; /* guys with pending rxflow */
+
+#if defined(LWS_WITH_TLS)
+	struct lws_pt_tls tls;
+#endif
+
+	struct lws_pollfd *fds;
+	volatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list;
+#ifdef _WIN32
+	WSAEVENT *events;
+#endif
+	lws_sockfd_type dummy_pipe_fds[2];
+	struct lws *pipe_wsi;
+
+	/* --- role based members --- */
+
+#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
+	struct lws_pt_role_ws ws;
+#endif
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	struct lws_pt_role_http http;
+#endif
+
+	/* --- event library based members --- */
+
+#if defined(LWS_WITH_LIBEV)
+	struct lws_pt_eventlibs_libev ev;
+#endif
+#if defined(LWS_WITH_LIBUV)
+	struct lws_pt_eventlibs_libuv uv;
+#endif
+#if defined(LWS_WITH_LIBEVENT)
+	struct lws_pt_eventlibs_libevent event;
+#endif
+
+#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
+	struct lws_signal_watcher w_sigint;
+#endif
+
+	/* --- */
+
+	unsigned long count_conns;
+	unsigned int fds_count;
+
+	volatile unsigned char inside_poll;
+	volatile unsigned char foreign_spinlock;
+
+	unsigned char tid;
+
+	unsigned char lock_depth;
+	unsigned char inside_service:1;
+	unsigned char event_loop_foreign:1;
+	unsigned char event_loop_destroy_processing_done:1;
+};
+
+struct lws_conn_stats {
+	unsigned long long rx, tx;
+	unsigned long h1_conn, h1_trans, h2_trans, ws_upg, h2_alpn, h2_subs,
+		      h2_upg, rejected;
+};
+
+void
+lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs);
+
+struct lws_timed_vh_protocol {
+	struct lws_timed_vh_protocol *next;
+	const struct lws_protocols *protocol;
+	time_t time;
+	int reason;
+};
+
+/*
+ * virtual host -related context information
+ *   vhostwide SSL context
+ *   vhostwide proxy
+ *
+ * hierarchy:
+ *
+ * context -> vhost -> wsi
+ *
+ * incoming connection non-SSL vhost binding:
+ *
+ *    listen socket -> wsi -> select vhost after first headers
+ *
+ * incoming connection SSL vhost binding:
+ *
+ *    SSL SNI -> wsi -> bind after SSL negotiation
+ */
+
+
+struct lws_vhost {
+#if !defined(LWS_WITHOUT_CLIENT)
+	char proxy_basic_auth_token[128];
+#endif
+#if LWS_MAX_SMP > 1
+	pthread_mutex_t lock;
+#endif
+
+#if defined(LWS_ROLE_H2)
+	struct lws_vhost_role_h2 h2;
+#endif
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	struct lws_vhost_role_http http;
+#endif
+#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
+	struct lws_vhost_role_ws ws;
+#endif
+
+#if defined(LWS_WITH_SOCKS5)
+	char socks_proxy_address[128];
+	char socks_user[96];
+	char socks_password[96];
+#endif
+#if defined(LWS_WITH_LIBEV)
+	struct lws_io_watcher w_accept;
+#endif
+	struct lws_conn_stats conn_stats;
+	struct lws_context *context;
+	struct lws_vhost *vhost_next;
+
+	struct lws *lserv_wsi;
+	const char *name;
+	const char *iface;
+
+#if !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32)
+	int bind_iface;
+#endif
+	const struct lws_protocols *protocols;
+	void **protocol_vh_privs;
+	const struct lws_protocol_vhost_options *pvo;
+	const struct lws_protocol_vhost_options *headers;
+	struct lws **same_vh_protocol_list;
+	struct lws_vhost *no_listener_vhost_list;
+#if !defined(LWS_NO_CLIENT)
+	struct lws_dll_lws dll_active_client_conns;
+#endif
+
+#if defined(LWS_WITH_TLS)
+	struct lws_vhost_tls tls;
+#endif
+
+	struct lws_timed_vh_protocol *timed_vh_protocol_list;
+	void *user;
+
+	int listen_port;
+
+#if defined(LWS_WITH_SOCKS5)
+	unsigned int socks_proxy_port;
+#endif
+	unsigned int options;
+	int count_protocols;
+	int ka_time;
+	int ka_probes;
+	int ka_interval;
+	int keepalive_timeout;
+	int timeout_secs_ah_idle;
+
+#ifdef LWS_WITH_ACCESS_LOG
+	int log_fd;
+#endif
+
+	unsigned int created_vhost_protocols:1;
+	unsigned int being_destroyed:1;
+
+	unsigned char default_protocol_index;
+	unsigned char raw_protocol_index;
+};
+
+struct lws_deferred_free
+{
+	struct lws_deferred_free *next;
+	time_t deadline;
+	void *payload;
+};
+
+typedef union {
+#ifdef LWS_WITH_IPV6
+	struct sockaddr_in6 sa6;
+#endif
+	struct sockaddr_in sa4;
+} sockaddr46;
+
+
+#if defined(LWS_WITH_PEER_LIMITS)
+struct lws_peer {
+	struct lws_peer *next;
+	struct lws_peer *peer_wait_list;
+
+	time_t time_created;
+	time_t time_closed_all;
+
+	uint8_t addr[32];
+	uint32_t hash;
+	uint32_t count_wsi;
+	uint32_t total_wsi;
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	struct lws_peer_role_http http;
+#endif
+
+	uint8_t af;
+};
+#endif
+
+/*
+ * the rest is managed per-context, that includes
+ *
+ *  - processwide single fd -> wsi lookup
+ *  - contextwide headers pool
+ */
+
+struct lws_context {
+	time_t last_timeout_check_s;
+	time_t last_ws_ping_pong_check_s;
+	time_t time_up;
+	time_t time_discontiguity;
+	time_t time_fixup;
+	const struct lws_plat_file_ops *fops;
+	struct lws_plat_file_ops fops_platform;
+	struct lws_context **pcontext_finalize;
+
+	const struct lws_tls_ops *tls_ops;
+
+#if defined(LWS_WITH_HTTP2)
+	struct http2_settings set;
+#endif
+#if defined(LWS_WITH_ZIP_FOPS)
+	struct lws_plat_file_ops fops_zip;
+#endif
+	struct lws_context_per_thread pt[LWS_MAX_SMP];
+	struct lws_conn_stats conn_stats;
+#if LWS_MAX_SMP > 1
+	pthread_mutex_t lock;
+	int lock_depth;
+#endif
+#ifdef _WIN32
+/* different implementation between unix and windows */
+	struct lws_fd_hashtable fd_hashtable[FD_HASHTABLE_MODULUS];
+#else
+	struct lws **lws_lookup;  /* fd to wsi */
+#endif
+	struct lws_vhost *vhost_list;
+	struct lws_vhost *no_listener_vhost_list;
+	struct lws_vhost *vhost_pending_destruction_list;
+	struct lws_plugin *plugin_list;
+	struct lws_deferred_free *deferred_free_list;
+#if defined(LWS_WITH_PEER_LIMITS)
+	struct lws_peer **pl_hash_table;
+	struct lws_peer *peer_wait_list;
+	time_t next_cull;
+#endif
+
+	void *external_baggage_free_on_destroy;
+	const struct lws_token_limits *token_limits;
+	void *user_space;
+	const struct lws_protocol_vhost_options *reject_service_keywords;
+	lws_reload_func deprecation_cb;
+	void (*eventlib_signal_cb)(void *event_lib_handle, int signum);
+
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+	cap_value_t caps[4];
+	char count_caps;
+#endif
+
+#if defined(LWS_WITH_LIBEV)
+	struct lws_context_eventlibs_libev ev;
+#endif
+#if defined(LWS_WITH_LIBUV)
+	struct lws_context_eventlibs_libuv uv;
+#endif
+#if defined(LWS_WITH_LIBEVENT)
+	struct lws_context_eventlibs_libevent event;
+#endif
+	struct lws_event_loop_ops *event_loop_ops;
+
+
+#if defined(LWS_WITH_TLS)
+	struct lws_context_tls tls;
+#endif
+
+	char canonical_hostname[128];
+	const char *server_string;
+
+#ifdef LWS_LATENCY
+	unsigned long worst_latency;
+	char worst_latency_info[256];
+#endif
+
+#if defined(LWS_WITH_STATS)
+	uint64_t lws_stats[LWSSTATS_SIZE];
+	uint64_t last_dump;
+	int updated;
+#endif
+#if defined(LWS_WITH_ESP32)
+	unsigned long time_last_state_dump;
+	uint32_t last_free_heap;
+#endif
+
+	int max_fds;
+	int count_event_loop_static_asset_handles;
+	int started_with_parent;
+	int uid, gid;
+
+	int fd_random;
+
+	int count_wsi_allocated;
+	int count_cgi_spawned;
+	unsigned int options;
+	unsigned int fd_limit_per_thread;
+	unsigned int timeout_secs;
+	unsigned int pt_serv_buf_size;
+	int max_http_header_data;
+	int simultaneous_ssl_restriction;
+	int simultaneous_ssl;
+#if defined(LWS_WITH_PEER_LIMITS)
+	uint32_t pl_hash_elements;	/* protected by context->lock */
+	uint32_t count_peers;		/* protected by context->lock */
+	unsigned short ip_limit_ah;
+	unsigned short ip_limit_wsi;
+#endif
+	unsigned int deprecated:1;
+	unsigned int being_destroyed:1;
+	unsigned int being_destroyed1:1;
+	unsigned int being_destroyed2:1;
+	unsigned int requested_kill:1;
+	unsigned int protocol_init_done:1;
+	unsigned int doing_protocol_init:1;
+	unsigned int done_protocol_destroy_cb:1;
+	unsigned int finalize_destroy_after_internal_loops_stopped:1;
+	/*
+	 * set to the Thread ID that's doing the service loop just before entry
+	 * to poll indicates service thread likely idling in poll()
+	 * volatile because other threads may check it as part of processing
+	 * for pollfd event change.
+	 */
+	volatile int service_tid;
+	int service_tid_detected;
+
+	short max_http_header_pool;
+	short count_threads;
+	short plugin_protocol_count;
+	short plugin_extension_count;
+	short server_string_len;
+	unsigned short ws_ping_pong_interval;
+	unsigned short deprecation_pending_listen_close_count;
+
+	uint8_t max_fi;
+};
+
+int
+lws_check_deferred_free(struct lws_context *context, int force);
+
+#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x]
+#define lws_get_vh_protocol(vh, x) vh->protocols[x]
+
+LWS_EXTERN void
+__lws_close_free_wsi_final(struct lws *wsi);
+LWS_EXTERN void
+lws_libuv_closehandle(struct lws *wsi);
+LWS_EXTERN int
+lws_libuv_check_watcher_active(struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_plat_plugins_init(struct lws_context * context, const char * const *d);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_plat_plugins_destroy(struct lws_context * context);
+
+LWS_EXTERN void
+lws_restart_ws_ping_pong_timer(struct lws *wsi);
+
+struct lws *
+lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd);
+
+int
+lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max);
+
+void
+lws_vhost_destroy1(struct lws_vhost *vh);
+
+enum {
+	LWS_EV_READ = (1 << 0),
+	LWS_EV_WRITE = (1 << 1),
+	LWS_EV_START = (1 << 2),
+	LWS_EV_STOP = (1 << 3),
+
+	LWS_EV_PREPARE_DELETION = (1 << 31),
+};
+
+
+#if defined(LWS_WITH_ESP32)
+LWS_EXTERN int
+lws_find_string_in_file(const char *filename, const char *string, int stringlen);
+#endif
+
+#ifdef LWS_WITH_IPV6
+#define LWS_IPV6_ENABLED(vh) \
+	(!lws_check_opt(vh->context->options, LWS_SERVER_OPTION_DISABLE_IPV6) && \
+	 !lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_IPV6))
+#else
+#define LWS_IPV6_ENABLED(context) (0)
+#endif
+
+#ifdef LWS_WITH_UNIX_SOCK
+#define LWS_UNIX_SOCK_ENABLED(vhost) \
+	(vhost->options & LWS_SERVER_OPTION_UNIX_SOCK)
+#else
+#define LWS_UNIX_SOCK_ENABLED(vhost) (0)
+#endif
+
+enum uri_path_states {
+	URIPS_IDLE,
+	URIPS_SEEN_SLASH,
+	URIPS_SEEN_SLASH_DOT,
+	URIPS_SEEN_SLASH_DOT_DOT,
+};
+
+enum uri_esc_states {
+	URIES_IDLE,
+	URIES_SEEN_PERCENT,
+	URIES_SEEN_PERCENT_H1,
+};
+
+
+#ifndef LWS_NO_CLIENT
+struct client_info_stash {
+	char *address;
+	char *path;
+	char *host;
+	char *origin;
+	char *protocol;
+	char *method;
+	char *iface;
+	char *alpn;
+};
+#endif
+
+
+signed char char_to_hex(const char c);
+
+
+struct lws_buflist {
+	struct lws_buflist *next;
+
+	size_t len;
+	size_t pos;
+
+	uint8_t buf[1]; /* true length of this is set by the oversize malloc */
+};
+
+#define lws_wsi_is_udp(___wsi) (!!___wsi->udp)
+
+#define LWS_H2_FRAME_HEADER_LENGTH 9
+
+
+struct lws {
+	/* structs */
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	struct _lws_http_mode_related http;
+#endif
+#if defined(LWS_ROLE_H2)
+	struct _lws_h2_related h2;
+#endif
+#if defined(LWS_ROLE_WS)
+	struct _lws_websocket_related *ws; /* allocated if we upgrade to ws */
+#endif
+
+	const struct lws_role_ops *role_ops;
+	lws_wsi_state_t	wsistate;
+	lws_wsi_state_t wsistate_pre_close;
+
+	/* lifetime members */
+
+#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
+	struct lws_io_watcher w_read;
+#endif
+#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBEVENT)
+	struct lws_io_watcher w_write;
+#endif
+
+	/* pointers */
+
+	struct lws_context *context;
+	struct lws_vhost *vhost;
+	struct lws *parent; /* points to parent, if any */
+	struct lws *child_list; /* points to first child */
+	struct lws *sibling_list; /* subsequent children at same level */
+
+	const struct lws_protocols *protocol;
+	struct lws **same_vh_protocol_prev, *same_vh_protocol_next;
+
+	struct lws_dll_lws dll_timeout;
+	struct lws_dll_lws dll_hrtimer;
+	struct lws_dll_lws dll_buflist; /* guys with pending rxflow */
+
+#if defined(LWS_WITH_PEER_LIMITS)
+	struct lws_peer *peer;
+#endif
+
+	struct lws_udp *udp;
+#ifndef LWS_NO_CLIENT
+	struct client_info_stash *stash;
+	char *client_hostname_copy;
+	struct lws_dll_lws dll_active_client_conns;
+	struct lws_dll_lws dll_client_transaction_queue_head;
+	struct lws_dll_lws dll_client_transaction_queue;
+#endif
+	void *user_space;
+	void *opaque_parent_data;
+
+	struct lws_buflist *buflist;
+
+	/* truncated send handling */
+	unsigned char *trunc_alloc; /* non-NULL means buffering in progress */
+
+#if defined(LWS_WITH_TLS)
+	struct lws_lws_tls tls;
+#endif
+
+	lws_sock_file_fd_type desc; /* .filefd / .sockfd */
+#if defined(LWS_WITH_STATS)
+	uint64_t active_writable_req_us;
+#if defined(LWS_WITH_TLS)
+	uint64_t accept_start_us;
+#endif
+#endif
+
+	lws_usec_t pending_timer; /* hrtimer fires */
+	time_t pending_timeout_set; /* second-resolution timeout start */
+
+#ifdef LWS_LATENCY
+	unsigned long action_start;
+	unsigned long latency_start;
+#endif
+
+	/* ints */
+#define LWS_NO_FDS_POS (-1)
+	int position_in_fds_table;
+	unsigned int trunc_alloc_len; /* size of malloc */
+	unsigned int trunc_offset; /* where we are in terms of spilling */
+	unsigned int trunc_len; /* how much is buffered */
+#ifndef LWS_NO_CLIENT
+	int chunk_remaining;
+#endif
+	unsigned int cache_secs;
+
+	unsigned int hdr_parsing_completed:1;
+	unsigned int http2_substream:1;
+	unsigned int upgraded_to_http2:1;
+	unsigned int h2_stream_carries_ws:1;
+	unsigned int seen_nonpseudoheader:1;
+	unsigned int listener:1;
+	unsigned int user_space_externally_allocated:1;
+	unsigned int socket_is_permanently_unusable:1;
+	unsigned int rxflow_change_to:2;
+	unsigned int conn_stat_done:1;
+	unsigned int cache_reuse:1;
+	unsigned int cache_revalidate:1;
+	unsigned int cache_intermediaries:1;
+	unsigned int favoured_pollin:1;
+	unsigned int sending_chunked:1;
+	unsigned int interpreting:1;
+	unsigned int already_did_cce:1;
+	unsigned int told_user_closed:1;
+	unsigned int told_event_loop_closed:1;
+	unsigned int waiting_to_send_close_frame:1;
+	unsigned int close_needs_ack:1;
+	unsigned int ipv6:1;
+	unsigned int parent_carries_io:1;
+	unsigned int parent_pending_cb_on_writable:1;
+	unsigned int cgi_stdout_zero_length:1;
+	unsigned int seen_zero_length_recv:1;
+	unsigned int rxflow_will_be_applied:1;
+	unsigned int event_pipe:1;
+	unsigned int on_same_vh_list:1;
+	unsigned int handling_404:1;
+	unsigned int protocol_bind_balance:1;
+
+	unsigned int could_have_pending:1; /* detect back-to-back writes */
+	unsigned int outer_will_close:1;
+
+#ifdef LWS_WITH_ACCESS_LOG
+	unsigned int access_log_pending:1;
+#endif
+#ifndef LWS_NO_CLIENT
+	unsigned int do_ws:1; /* whether we are doing http or ws flow */
+	unsigned int chunked:1; /* if the clientside connection is chunked */
+	unsigned int client_rx_avail:1;
+	unsigned int client_http_body_pending:1;
+	unsigned int transaction_from_pipeline_queue:1;
+	unsigned int keepalive_active:1;
+	unsigned int keepalive_rejected:1;
+	unsigned int client_pipeline:1;
+	unsigned int client_h2_alpn:1;
+	unsigned int client_h2_substream:1;
+#endif
+
+#ifdef _WIN32
+	unsigned int sock_send_blocking:1;
+#endif
+
+#ifndef LWS_NO_CLIENT
+	unsigned short c_port;
+#endif
+	unsigned short pending_timeout_limit;
+
+	/* chars */
+
+	char lws_rx_parse_state; /* enum lws_rx_parse_state */
+	char rx_frame_type; /* enum lws_write_protocol */
+	char pending_timeout; /* enum pending_timeout */
+	char tsi; /* thread service index we belong to */
+	char protocol_interpret_idx;
+	char redirects;
+	uint8_t rxflow_bitmap;
+#ifdef LWS_WITH_CGI
+	char cgi_channel; /* which of stdin/out/err */
+	char hdr_state;
+#endif
+#ifndef LWS_NO_CLIENT
+	char chunk_parser; /* enum lws_chunk_parser */
+#endif
+#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT)
+	char reason_bf; /* internal writeable callback reason bitfield */
+#endif
+#if defined(LWS_WITH_STATS) && defined(LWS_WITH_TLS)
+	char seen_rx;
+#endif
+	uint8_t ws_over_h2_count;
+	/* volatile to make sure code is aware other thread can change */
+	volatile char handling_pollout;
+	volatile char leave_pollout_active;
+};
+
+#define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap))
+
+void
+lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt);
+
+LWS_EXTERN int log_level;
+
+LWS_EXTERN int
+lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
+		const char *iface);
+
+#if defined(LWS_WITH_IPV6)
+LWS_EXTERN unsigned long
+lws_get_addr_scope(const char *ipaddr);
+#endif
+
+LWS_EXTERN void
+lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller);
+LWS_EXTERN void
+__lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller);
+
+LWS_EXTERN void
+__lws_free_wsi(struct lws *wsi);
+
+LWS_EXTERN int
+__remove_wsi_socket_from_fds(struct lws *wsi);
+LWS_EXTERN int
+lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len);
+
+#ifndef LWS_LATENCY
+static inline void
+lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
+	    int ret, int completion) {
+	do {
+		(void)context; (void)wsi; (void)action; (void)ret;
+		(void)completion;
+	} while (0);
+}
+static inline void
+lws_latency_pre(struct lws_context *context, struct lws *wsi) {
+	do { (void)context; (void)wsi; } while (0);
+}
+#else
+#define lws_latency_pre(_context, _wsi) lws_latency(_context, _wsi, NULL, 0, 0)
+extern void
+lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
+	    int ret, int completion);
+#endif
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ws_client_rx_sm(struct lws *wsi, unsigned char c);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_parse(struct lws *wsi, unsigned char *buf, int *len);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_parse_urldecode(struct lws *wsi, uint8_t *_c);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_http_action(struct lws *wsi);
+
+LWS_EXTERN int
+lws_b64_selftest(void);
+
+LWS_EXTERN int
+lws_service_flag_pending(struct lws_context *context, int tsi);
+
+LWS_EXTERN int
+lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p);
+
+#if defined(_WIN32)
+LWS_EXTERN struct lws *
+wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd);
+
+LWS_EXTERN int
+insert_wsi(struct lws_context *context, struct lws *wsi);
+
+LWS_EXTERN int
+delete_from_fd(struct lws_context *context, lws_sockfd_type fd);
+#else
+#define wsi_from_fd(A,B)  A->lws_lookup[B - lws_plat_socket_offset()]
+#define insert_wsi(A,B)   assert(A->lws_lookup[B->desc.sockfd - lws_plat_socket_offset()] == 0); A->lws_lookup[B->desc.sockfd - lws_plat_socket_offset()]=B
+#define delete_from_fd(A,B) A->lws_lookup[B - lws_plat_socket_offset()]=0
+#endif
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+__insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len);
+
+LWS_EXTERN void
+lws_remove_from_timeout_list(struct lws *wsi);
+
+LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
+lws_client_connect_2(struct lws *wsi);
+
+LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT
+lws_client_reset(struct lws **wsi, int ssl, const char *address, int port,
+		 const char *path, const char *host);
+
+LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
+lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi);
+
+LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
+lws_generate_client_handshake(struct lws *wsi, char *pkt);
+
+LWS_EXTERN int
+lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd);
+
+LWS_EXTERN struct lws *
+lws_client_connect_via_info2(struct lws *wsi);
+
+
+
+LWS_EXTERN void
+lws_client_stash_destroy(struct lws *wsi);
+
+/*
+ * EXTENSIONS
+ */
+
+#if defined(LWS_WITHOUT_EXTENSIONS)
+#define lws_any_extension_handled(_a, _b, _c, _d) (0)
+#define lws_ext_cb_active(_a, _b, _c, _d) (0)
+#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0)
+#define lws_issue_raw_ext_access lws_issue_raw
+#define lws_context_init_extensions(_a, _b)
+#endif
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_client_interpret_server_handshake(struct lws *wsi);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len);
+
+LWS_EXTERN void
+lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state,
+			struct lws_role_ops *ops);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+user_callback_handle_rxflow(lws_callback_function, struct lws *wsi,
+			    enum lws_callback_reasons reason, void *user,
+			    void *in, size_t len);
+
+LWS_EXTERN int
+lws_plat_socket_offset(void);
+
+LWS_EXTERN int
+lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd);
+
+LWS_EXTERN int
+lws_plat_check_connection_error(struct lws *wsi);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_header_table_attach(struct lws *wsi, int autoservice);
+
+LWS_EXTERN int
+lws_header_table_detach(struct lws *wsi, int autoservice);
+LWS_EXTERN int
+__lws_header_table_detach(struct lws *wsi, int autoservice);
+
+LWS_EXTERN void
+lws_header_table_reset(struct lws *wsi, int autoservice);
+
+void
+__lws_header_table_reset(struct lws *wsi, int autoservice);
+
+LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
+lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ensure_user_space(struct lws *wsi);
+
+LWS_EXTERN int
+lws_change_pollfd(struct lws *wsi, int _and, int _or);
+
+#ifndef LWS_NO_SERVER
+ int _lws_vhost_init_server(const struct lws_context_creation_info *info,
+			      struct lws_vhost *vhost);
+ LWS_EXTERN struct lws_vhost *
+ lws_select_vhost(struct lws_context *context, int port, const char *servername);
+ LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+ lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len);
+ LWS_EXTERN void
+ lws_server_get_canonical_hostname(struct lws_context *context,
+				   const struct lws_context_creation_info *info);
+#else
+ #define _lws_vhost_init_server(_a, _b) (0)
+ #define lws_parse_ws(_a, _b, _c) (0)
+ #define lws_server_get_canonical_hostname(_a, _b)
+#endif
+
+#ifndef LWS_NO_DAEMONIZE
+ LWS_EXTERN int get_daemonize_pid();
+#else
+ #define get_daemonize_pid() (0)
+#endif
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+interface_to_sa(struct lws_vhost *vh, const char *ifname,
+		struct sockaddr_in *addr, size_t addrlen);
+LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
+
+#if !defined(LWS_WITH_TLS)
+ #define LWS_SSL_ENABLED(context) (0)
+ #define lws_context_init_server_ssl(_a, _b) (0)
+ #define lws_ssl_destroy(_a)
+ #define lws_context_init_alpn(_a)
+ #define lws_ssl_capable_read lws_ssl_capable_read_no_ssl
+ #define lws_ssl_capable_write lws_ssl_capable_write_no_ssl
+ #define lws_ssl_pending lws_ssl_pending_no_ssl
+ #define lws_server_socket_service_ssl(_b, _c) (0)
+ #define lws_ssl_close(_a) (0)
+ #define lws_ssl_context_destroy(_a)
+ #define lws_ssl_SSL_CTX_destroy(_a)
+ #define lws_ssl_remove_wsi_from_buffered_list(_a)
+ #define __lws_ssl_remove_wsi_from_buffered_list(_a)
+ #define lws_context_init_ssl_library(_a)
+ #define lws_tls_check_all_cert_lifetimes(_a)
+ #define lws_tls_acme_sni_cert_destroy(_a)
+#endif
+
+
+#if LWS_MAX_SMP > 1
+
+static LWS_INLINE void
+lws_pt_mutex_init(struct lws_context_per_thread *pt)
+{
+	pthread_mutex_init(&pt->lock, NULL);
+	pthread_mutex_init(&pt->lock_stats, NULL);
+}
+
+static LWS_INLINE void
+lws_pt_mutex_destroy(struct lws_context_per_thread *pt)
+{
+	pthread_mutex_destroy(&pt->lock_stats);
+	pthread_mutex_destroy(&pt->lock);
+}
+
+static LWS_INLINE void
+lws_pt_lock(struct lws_context_per_thread *pt, const char *reason)
+{
+	if (pt->lock_owner == pthread_self()) {
+		pt->lock_depth++;
+		return;
+	}
+	pthread_mutex_lock(&pt->lock);
+	pt->last_lock_reason = reason;
+	pt->lock_owner = pthread_self();
+	//lwsl_notice("tid %d: lock %s\n", pt->tid, reason);
+}
+
+static LWS_INLINE void
+lws_pt_unlock(struct lws_context_per_thread *pt)
+{
+	if (pt->lock_depth) {
+		pt->lock_depth--;
+		return;
+	}
+	pt->last_lock_reason = "free";
+	pt->lock_owner = 0;
+	//lwsl_notice("tid %d: unlock %s\n", pt->tid, pt->last_lock_reason);
+	pthread_mutex_unlock(&pt->lock);
+}
+
+static LWS_INLINE void
+lws_pt_stats_lock(struct lws_context_per_thread *pt)
+{
+	pthread_mutex_lock(&pt->lock_stats);
+}
+
+static LWS_INLINE void
+lws_pt_stats_unlock(struct lws_context_per_thread *pt)
+{
+	pthread_mutex_unlock(&pt->lock_stats);
+}
+
+static LWS_INLINE void
+lws_context_lock(struct lws_context *context)
+{
+	pthread_mutex_lock(&context->lock);
+}
+
+static LWS_INLINE void
+lws_context_unlock(struct lws_context *context)
+{
+	pthread_mutex_unlock(&context->lock);
+}
+
+static LWS_INLINE void
+lws_vhost_lock(struct lws_vhost *vhost)
+{
+	pthread_mutex_lock(&vhost->lock);
+}
+
+static LWS_INLINE void
+lws_vhost_unlock(struct lws_vhost *vhost)
+{
+	pthread_mutex_unlock(&vhost->lock);
+}
+
+
+#else
+#define lws_pt_mutex_init(_a) (void)(_a)
+#define lws_pt_mutex_destroy(_a) (void)(_a)
+#define lws_pt_lock(_a, b) (void)(_a)
+#define lws_pt_unlock(_a) (void)(_a)
+#define lws_context_lock(_a) (void)(_a)
+#define lws_context_unlock(_a) (void)(_a)
+#define lws_vhost_lock(_a) (void)(_a)
+#define lws_vhost_unlock(_a) (void)(_a)
+#define lws_pt_stats_lock(_a) (void)(_a)
+#define lws_pt_stats_unlock(_a) (void)(_a)
+#endif
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_pending_no_ssl(struct lws *wsi);
+
+int
+lws_tls_check_cert_lifetime(struct lws_vhost *vhost);
+
+int lws_jws_selftest(void);
+
+
+#ifndef LWS_NO_CLIENT
+LWS_EXTERN int lws_client_socket_service(struct lws *wsi,
+					 struct lws_pollfd *pollfd,
+					 struct lws *wsi_conn);
+LWS_EXTERN struct lws *
+lws_client_wsi_effective(struct lws *wsi);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_http_transaction_completed_client(struct lws *wsi);
+#if !defined(LWS_WITH_TLS)
+	#define lws_context_init_client_ssl(_a, _b) (0)
+#endif
+LWS_EXTERN void
+lws_decode_ssl_error(void);
+#else
+#define lws_context_init_client_ssl(_a, _b) (0)
+#endif
+
+LWS_EXTERN int
+__lws_rx_flow_control(struct lws *wsi);
+
+LWS_EXTERN int
+_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa);
+
+#ifndef LWS_NO_SERVER
+LWS_EXTERN int
+lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len);
+#else
+#define lws_server_socket_service(_b, _c) (0)
+#define lws_handshake_server(_a, _b, _c) (0)
+#endif
+
+#ifdef LWS_WITH_ACCESS_LOG
+LWS_EXTERN int
+lws_access_log(struct lws *wsi);
+LWS_EXTERN void
+lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth);
+#else
+#define lws_access_log(_a)
+#endif
+
+LWS_EXTERN int
+lws_cgi_kill_terminated(struct lws_context_per_thread *pt);
+
+LWS_EXTERN void
+lws_cgi_remove_and_kill(struct lws *wsi);
+
+int
+lws_protocol_init(struct lws_context *context);
+
+int
+lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p);
+
+const struct lws_http_mount *
+lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len);
+
+/*
+ * custom allocator
+ */
+LWS_EXTERN void *
+lws_realloc(void *ptr, size_t size, const char *reason);
+
+LWS_EXTERN void * LWS_WARN_UNUSED_RESULT
+lws_zalloc(size_t size, const char *reason);
+
+#ifdef LWS_PLAT_OPTEE
+void *lws_malloc(size_t size, const char *reason);
+void lws_free(void *p);
+#define lws_free_set_NULL(P)    do { lws_free(P); (P) = NULL; } while(0)
+#else
+#define lws_malloc(S, R)	lws_realloc(NULL, S, R)
+#define lws_free(P)	lws_realloc(P, 0, "lws_free")
+#define lws_free_set_NULL(P)	do { lws_realloc(P, 0, "free"); (P) = NULL; } while(0)
+#endif
+
+int
+lws_plat_pipe_create(struct lws *wsi);
+int
+lws_plat_pipe_signal(struct lws *wsi);
+void
+lws_plat_pipe_close(struct lws *wsi);
+int
+lws_create_event_pipes(struct lws_context *context);
+
+const struct lws_plat_file_ops *
+lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path,
+		    const char **vpath);
+
+/* lws_plat_ */
+LWS_EXTERN void
+lws_plat_delete_socket_from_fds(struct lws_context *context,
+				struct lws *wsi, int m);
+LWS_EXTERN void
+lws_plat_insert_socket_into_fds(struct lws_context *context,
+				struct lws *wsi);
+LWS_EXTERN void
+lws_plat_service_periodic(struct lws_context *context);
+
+LWS_EXTERN int
+lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi,
+		       struct lws_pollfd *pfd);
+LWS_EXTERN void
+lws_add_wsi_to_draining_ext_list(struct lws *wsi);
+LWS_EXTERN void
+lws_remove_wsi_from_draining_ext_list(struct lws *wsi);
+LWS_EXTERN int
+lws_plat_context_early_init(void);
+LWS_EXTERN void
+lws_plat_context_early_destroy(struct lws_context *context);
+LWS_EXTERN void
+lws_plat_context_late_destroy(struct lws_context *context);
+LWS_EXTERN int
+lws_poll_listen_fd(struct lws_pollfd *fd);
+LWS_EXTERN int
+lws_plat_service(struct lws_context *context, int timeout_ms);
+LWS_EXTERN LWS_VISIBLE int
+_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi);
+LWS_EXTERN int
+lws_plat_init(struct lws_context *context,
+	      const struct lws_context_creation_info *info);
+LWS_EXTERN void
+lws_plat_drop_app_privileges(const struct lws_context_creation_info *info);
+LWS_EXTERN unsigned long long
+time_in_microseconds(void);
+LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT
+lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_plat_inet_pton(int af, const char *src, void *dst);
+
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len);
+LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
+		                lws_filepos_t *amount);
+
+
+LWS_EXTERN void
+lws_same_vh_protocol_remove(struct lws *wsi);
+LWS_EXTERN void
+lws_same_vh_protocol_insert(struct lws *wsi, int n);
+
+LWS_EXTERN int
+lws_broadcast(struct lws_context *context, int reason, void *in, size_t len);
+
+#if defined(LWS_WITH_STATS)
+ void
+ lws_stats_atomic_bump(struct lws_context * context,
+		struct lws_context_per_thread *pt, int index, uint64_t bump);
+ void
+ lws_stats_atomic_max(struct lws_context * context,
+		struct lws_context_per_thread *pt, int index, uint64_t val);
+#else
+ static inline uint64_t lws_stats_atomic_bump(struct lws_context * context,
+		struct lws_context_per_thread *pt, int index, uint64_t bump) {
+	(void)context; (void)pt; (void)index; (void)bump; return 0; }
+ static inline uint64_t lws_stats_atomic_max(struct lws_context * context,
+		struct lws_context_per_thread *pt, int index, uint64_t val) {
+	(void)context; (void)pt; (void)index; (void)val; return 0; }
+#endif
+
+/* socks */
+void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
+			ssize_t *msg_len);
+
+#if defined(LWS_WITH_PEER_LIMITS)
+void
+lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer);
+int
+lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer);
+void
+lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer);
+void
+lws_peer_cull_peer_wait_list(struct lws_context *context);
+struct lws_peer *
+lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd);
+void
+lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer,
+		 struct lws *wsi);
+void
+lws_peer_dump_from_wsi(struct lws *wsi);
+#endif
+
+
+void
+__lws_remove_from_timeout_list(struct lws *wsi);
+
+lws_usec_t
+__lws_hrtimer_service(struct lws_context_per_thread *pt);
+
+void
+__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
+int
+__lws_change_pollfd(struct lws *wsi, int _and, int _or);
+
+
+int
+lws_callback_as_writeable(struct lws *wsi);
+int
+lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi,
+		       struct lws_tokens *ebuf);
+int
+lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used,
+			  int buffered);
+
+
+char *
+lws_generate_client_ws_handshake(struct lws *wsi, char *p);
+int
+lws_client_ws_upgrade(struct lws *wsi, const char **cce);
+int
+lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi);
+int
+lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len);
+int
+lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn);
+int
+lws_tls_server_conn_alpn(struct lws *wsi);
+
+int
+lws_ws_client_rx_sm_block(struct lws *wsi, unsigned char **buf, size_t len);
+void
+lws_destroy_event_pipe(struct lws *wsi);
+void
+lws_context_destroy2(struct lws_context *context);
+
+#ifdef __cplusplus
+};
+#endif

+ 987 - 0
thirdparty/libwebsockets/core/service.c

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

+ 43 - 0
thirdparty/libwebsockets/event-libs/poll/poll.c

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

+ 23 - 0
thirdparty/libwebsockets/event-libs/poll/private.h

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

+ 74 - 0
thirdparty/libwebsockets/event-libs/private.h

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

+ 0 - 473
thirdparty/libwebsockets/ext/extension-permessage-deflate.c

@@ -1,473 +0,0 @@
-/*
- * ./lib/extension-permessage-deflate.c
- *
- *  Copyright (C) 2016 Andy Green <[email protected]>
- *
- *  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_info(" 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_info(" permessage-deflate requires the protocol (%s) to have an RX buffer >= 128\n",
-					wsi->protocol->name);
-			return -1;
-		}
-
-		/* fill in **user */
-		priv = lws_zalloc(sizeof(*priv), "pmd priv");
-		*((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]), "pmd rx inflate buf");
-		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]), "pmd tx deflate buf");
-		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;
-}
-

+ 0 - 41
thirdparty/libwebsockets/ext/extension-permessage-deflate.h

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

+ 0 - 344
thirdparty/libwebsockets/ext/extension.c

@@ -1,344 +0,0 @@
-#include "private-libwebsockets.h"
-
-#include "extension-permessage-deflate.h"
-
-LWS_VISIBLE void
-lws_context_init_extensions(struct lws_context_creation_info *info,
-			    struct lws_context *context)
-{
-	lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
-}
-
-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)
-{
-	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_cb_active(struct lws *wsi, int reason, void *arg, int len)
-{
-	int n, m, handled = 0;
-
-	for (n = 0; n < wsi->count_act_ext; n++) {
-		m = wsi->active_extensions[n]->callback(lws_get_context(wsi),
-			wsi->active_extensions[n], wsi, reason,
-			wsi->act_ext_user[n], arg, len);
-		if (m < 0) {
-			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_cb_all_exts(struct lws_context *context, struct lws *wsi,
-			int reason, void *arg, int len)
-{
-	int n = 0, m, handled = 0;
-	const struct lws_extension *ext;
-
-	if (!wsi || !wsi->vhost)
-		return 0;
-
-	ext = wsi->vhost->extensions;
-
-	while (ext && ext->callback && !handled) {
-		m = ext->callback(context, ext, wsi, reason,
-				  (void *)(lws_intptr_t)n, arg, len);
-		if (m < 0) {
-			lwsl_ext("Ext '%s' failed to handle callback %d!\n",
-				 wsi->active_extensions[n]->name, reason);
-			return -1;
-		}
-		if (m)
-			handled = 1;
-
-		ext++;
-		n++;
-	}
-
-	return 0;
-}
-
-int
-lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
-{
-	struct lws_tokens eff_buf;
-	int ret, m, n = 0;
-
-	eff_buf.token = (char *)buf;
-	eff_buf.token_len = len;
-
-	/*
-	 * while we have original buf to spill ourselves, or extensions report
-	 * more in their pipeline
-	 */
-
-	ret = 1;
-	while (ret == 1) {
-
-		/* default to nobody has more to spill */
-
-		ret = 0;
-
-		/* show every extension the new incoming data */
-		m = lws_ext_cb_active(wsi,
-			       LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0);
-		if (m < 0)
-			return -1;
-		if (m) /* handled */
-			ret = 1;
-
-		if ((char *)buf != eff_buf.token)
-			/*
-			 * extension recreated it:
-			 * need to buffer this if not all sent
-			 */
-			wsi->u.ws.clean_buffer = 0;
-
-		/* assuming they left us something to send, send it */
-
-		if (eff_buf.token_len) {
-			n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
-							    eff_buf.token_len);
-			if (n < 0) {
-				lwsl_info("closing from ext access\n");
-				return -1;
-			}
-
-			/* always either sent it all or privately buffered */
-			if (wsi->u.ws.clean_buffer)
-				len = n;
-		}
-
-		lwsl_parser("written %d bytes to client\n", n);
-
-		/* no extension has more to spill?  Then we can go */
-
-		if (!ret)
-			break;
-
-		/* we used up what we had */
-
-		eff_buf.token = NULL;
-		eff_buf.token_len = 0;
-
-		/*
-		 * Did that leave the pipe choked?
-		 * Or we had to hold on to some of it?
-		 */
-
-		if (!lws_send_pipe_choked(wsi) && !wsi->trunc_len)
-			/* no we could add more, lets's do that */
-			continue;
-
-		lwsl_debug("choked\n");
-
-		/*
-		 * Yes, he's choked.  Don't spill the rest now get a callback
-		 * when he is ready to send and take care of it there
-		 */
-		lws_callback_on_writable(wsi);
-		wsi->extension_data_pending = 1;
-		ret = 0;
-	}
-
-	return len;
-}
-
-int
-lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
-			  void *v, size_t len)
-{
-	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_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->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);
-}

+ 0 - 280
thirdparty/libwebsockets/handshake.c

@@ -1,280 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <[email protected]>
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public
- *  License as published by the Free Software Foundation:
- *  version 2.1 of the License.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- *  MA  02110-1301  USA
- */
-
-#include "private-libwebsockets.h"
-
-/*
- * -04 of the protocol (actually the 80th version) has a radically different
- * handshake.  The 04 spec gives the following idea
- *
- *    The handshake from the client looks as follows:
- *
- *      GET /chat HTTP/1.1
- *      Host: server.example.com
- *      Upgrade: websocket
- *      Connection: Upgrade
- *      Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
- *      Sec-WebSocket-Origin: http://example.com
- *      Sec-WebSocket-Protocol: chat, superchat
- *	Sec-WebSocket-Version: 4
- *
- *  The handshake from the server looks as follows:
- *
- *       HTTP/1.1 101 Switching Protocols
- *       Upgrade: websocket
- *       Connection: Upgrade
- *       Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
- *       Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
- *       Sec-WebSocket-Protocol: chat
- */
-
-#ifndef min
-#define min(a, b) ((a) < (b) ? (a) : (b))
-#endif
-
-/*
- * We have to take care about parsing because the headers may be split
- * into multiple fragments.  They may contain unknown headers with arbitrary
- * argument lengths.  So, we parse using a single-character at a time state
- * machine that is completely independent of packet size.
- *
- * Returns <0 for error or length of chars consumed from buf (up to len)
- */
-
-LWS_VISIBLE int
-lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
-{
-	unsigned char *last_char, *oldbuf = buf;
-	lws_filepos_t body_chunk_len;
-	size_t n;
-
-	switch (wsi->state) {
-#ifdef LWS_WITH_HTTP2
-	case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
-	case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
-	case LWSS_HTTP2_ESTABLISHED:
-		n = 0;
-		//lwsl_debug("%s: starting new block of %d\n", __func__, (int)len);
-		/*
-		 * wsi here is always the network connection wsi, not a stream
-		 * wsi.
-		 */
-		while (n < len) {
-			/*
-			 * we were accepting input but now we stopped doing so
-			 */
-			if (lws_is_flowcontrolled(wsi)) {
-				lws_rxflow_cache(wsi, buf, n, len);
-
-				return 1;
-			}
-
-			/* account for what we're using in rxflow buffer */
-			if (wsi->rxflow_buffer) {
-				wsi->rxflow_pos++;
-				assert(wsi->rxflow_pos <= wsi->rxflow_len);
-			}
-
-			if (lws_h2_parser(wsi, buf[n++])) {
-				lwsl_debug("%s: http2_parser bailed\n", __func__);
-				goto bail;
-			}
-		}
-		lwsl_debug("%s: used up block of %d\n", __func__, (int)len);
-		break;
-#endif
-
-	case LWSS_HTTP_ISSUING_FILE:
-		return 0;
-
-	case LWSS_CLIENT_HTTP_ESTABLISHED:
-		break;
-
-	case LWSS_HTTP:
-		wsi->hdr_parsing_completed = 0;
-
-		/* fallthru */
-
-	case LWSS_HTTP_HEADERS:
-		if (!wsi->u.hdr.ah) {
-			lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__);
-			assert(0);
-		}
-		lwsl_parser("issuing %d bytes to parser\n", (int)len);
-
-		lwsl_hexdump(buf, (size_t)len);
-
-		if (lws_handshake_client(wsi, &buf, (size_t)len))
-			goto bail;
-
-		last_char = buf;
-		if (lws_handshake_server(wsi, &buf, (size_t)len))
-			/* Handshake indicates this session is done. */
-			goto bail;
-
-		/* we might have transitioned to RAW */
-		if (wsi->mode == LWSCM_RAW)
-			 /* we gave the read buffer to RAW handler already */
-			goto read_ok;
-
-		/*
-		 * It's possible that we've exhausted our data already, or
-		 * rx flow control has stopped us dealing with this early,
-		 * but lws_handshake_server doesn't update len for us.
-		 * Figure out how much was read, so that we can proceed
-		 * appropriately:
-		 */
-		len -= (buf - last_char);
-		lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
-
-		if (!wsi->hdr_parsing_completed)
-			/* More header content on the way */
-			goto read_ok;
-
-		switch (wsi->state) {
-			case LWSS_HTTP:
-			case LWSS_HTTP_HEADERS:
-				goto read_ok;
-			case LWSS_HTTP_ISSUING_FILE:
-				goto read_ok;
-			case LWSS_HTTP_BODY:
-				wsi->u.http.rx_content_remain =
-						wsi->u.http.rx_content_length;
-				if (wsi->u.http.rx_content_remain)
-					goto http_postbody;
-
-				/* there is no POST content */
-				goto postbody_completion;
-			default:
-				break;
-		}
-		break;
-
-	case LWSS_HTTP_BODY:
-http_postbody:
-		//lwsl_notice("http post body\n");
-		while (len && wsi->u.http.rx_content_remain) {
-			/* Copy as much as possible, up to the limit of:
-			 * what we have in the read buffer (len)
-			 * remaining portion of the POST body (content_remain)
-			 */
-			body_chunk_len = min(wsi->u.http.rx_content_remain, len);
-			wsi->u.http.rx_content_remain -= body_chunk_len;
-			len -= body_chunk_len;
-#ifdef LWS_WITH_CGI
-			if (wsi->cgi) {
-				struct lws_cgi_args args;
-
-				args.ch = LWS_STDIN;
-				args.stdwsi = &wsi->cgi->stdwsi[0];
-				args.data = buf;
-				args.len = body_chunk_len;
-
-				/* returns how much used */
-				n = user_callback_handle_rxflow(
-					wsi->protocol->callback,
-					wsi, LWS_CALLBACK_CGI_STDIN_DATA,
-					wsi->user_space,
-					(void *)&args, 0);
-				if ((int)n < 0)
-					goto bail;
-			} else {
-#endif
-				n = wsi->protocol->callback(wsi,
-					LWS_CALLBACK_HTTP_BODY, wsi->user_space,
-					buf, (size_t)body_chunk_len);
-				if (n)
-					goto bail;
-				n = (size_t)body_chunk_len;
-#ifdef LWS_WITH_CGI
-			}
-#endif
-			buf += n;
-
-			if (wsi->u.http.rx_content_remain)  {
-				lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
-						wsi->context->timeout_secs);
-				break;
-			}
-			/* he sent all the content in time */
-postbody_completion:
-#ifdef LWS_WITH_CGI
-			/*
-			 * If we're running a cgi, we can't let him off the
-			 * hook just because he sent his POST data
-			 */
-			if (wsi->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
-			{
-				lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
-				n = wsi->protocol->callback(wsi,
-					LWS_CALLBACK_HTTP_BODY_COMPLETION,
-					wsi->user_space, NULL, 0);
-				if (n)
-					goto bail;
-
-				if (wsi->http2_substream)
-					wsi->state = LWSS_HTTP2_ESTABLISHED;
-			}
-
-			break;
-		}
-		break;
-
-	case LWSS_ESTABLISHED:
-	case LWSS_AWAITING_CLOSE_ACK:
-	case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
-	case LWSS_SHUTDOWN:
-		if (lws_handshake_client(wsi, &buf, (size_t)len))
-			goto bail;
-		switch (wsi->mode) {
-		case LWSCM_WS_SERVING:
-
-			if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
-				lwsl_info("interpret_incoming_packet has bailed\n");
-				goto bail;
-			}
-			break;
-		}
-		break;
-	default:
-		lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
-		break;
-	}
-
-read_ok:
-	/* Nothing more to do for now */
-	lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf));
-
-	return buf - oldbuf;
-
-bail:
-	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-
-	return -1;
-}

+ 0 - 805
thirdparty/libwebsockets/lextable.h

@@ -1,805 +0,0 @@
-/* pos 0000:   0 */    0x67 /* 'g' */, 0x40, 0x00  /* (to 0x0040 state   1) */,
-                       0x70 /* 'p' */, 0x42, 0x00  /* (to 0x0045 state   5) */,
-                       0x6F /* 'o' */, 0x51, 0x00  /* (to 0x0057 state  10) */,
-                       0x68 /* 'h' */, 0x5D, 0x00  /* (to 0x0066 state  18) */,
-                       0x63 /* 'c' */, 0x69, 0x00  /* (to 0x0075 state  23) */,
-                       0x75 /* 'u' */, 0x8A, 0x00  /* (to 0x0099 state  34) */,
-                       0x73 /* 's' */, 0xA0, 0x00  /* (to 0x00B2 state  48) */,
-                       0x0D /* '.' */, 0xD9, 0x00  /* (to 0x00EE state  68) */,
-                       0x61 /* 'a' */, 0x31, 0x01  /* (to 0x0149 state 129) */,
-                       0x69 /* 'i' */, 0x70, 0x01  /* (to 0x018B state 163) */,
-                       0x64 /* 'd' */, 0x19, 0x02  /* (to 0x0237 state 265) */,
-                       0x72 /* 'r' */, 0x22, 0x02  /* (to 0x0243 state 270) */,
-                       0x3A /* ':' */, 0x53, 0x02  /* (to 0x0277 state 299) */,
-                       0x65 /* 'e' */, 0xDF, 0x02  /* (to 0x0306 state 409) */,
-                       0x66 /* 'f' */, 0xFB, 0x02  /* (to 0x0325 state 425) */,
-                       0x6C /* 'l' */, 0x1D, 0x03  /* (to 0x034A state 458) */,
-                       0x6D /* 'm' */, 0x40, 0x03  /* (to 0x0370 state 484) */,
-                       0x74 /* 't' */, 0xAF, 0x03  /* (to 0x03E2 state 578) */,
-                       0x76 /* 'v' */, 0xD0, 0x03  /* (to 0x0406 state 606) */,
-                       0x77 /* 'w' */, 0xDD, 0x03  /* (to 0x0416 state 614) */,
-                       0x78 /* 'x' */, 0x04, 0x04  /* (to 0x0440 state 650) */,
-                       0x08, /* fail */
-/* pos 0040:   1 */    0xE5 /* 'e' -> */,
-/* pos 0041:   2 */    0xF4 /* 't' -> */,
-/* pos 0042:   3 */    0xA0 /* ' ' -> */,
-/* pos 0043:   4 */    0x00, 0x00                  /* - terminal marker  0 - */,
-/* pos 0045:   5 */    0x6F /* 'o' */, 0x0D, 0x00  /* (to 0x0052 state   6) */,
-                       0x72 /* 'r' */, 0x95, 0x01  /* (to 0x01DD state 211) */,
-                       0x61 /* 'a' */, 0xDD, 0x03  /* (to 0x0428 state 631) */,
-                       0x75 /* 'u' */, 0xDF, 0x03  /* (to 0x042D state 635) */,
-                       0x08, /* fail */
-/* pos 0052:   6 */    0xF3 /* 's' -> */,
-/* pos 0053:   7 */    0xF4 /* 't' -> */,
-/* pos 0054:   8 */    0xA0 /* ' ' -> */,
-/* pos 0055:   9 */    0x00, 0x01                  /* - terminal marker  1 - */,
-/* pos 0057:  10 */    0x70 /* 'p' */, 0x07, 0x00  /* (to 0x005E state  11) */,
-                       0x72 /* 'r' */, 0x51, 0x00  /* (to 0x00AB state  42) */,
-                       0x08, /* fail */
-/* pos 005e:  11 */    0xF4 /* 't' -> */,
-/* pos 005f:  12 */    0xE9 /* 'i' -> */,
-/* pos 0060:  13 */    0xEF /* 'o' -> */,
-/* pos 0061:  14 */    0xEE /* 'n' -> */,
-/* pos 0062:  15 */    0xF3 /* 's' -> */,
-/* pos 0063:  16 */    0xA0 /* ' ' -> */,
-/* pos 0064:  17 */    0x00, 0x02                  /* - terminal marker  2 - */,
-/* pos 0066:  18 */    0x6F /* 'o' */, 0x0A, 0x00  /* (to 0x0070 state  19) */,
-                       0x74 /* 't' */, 0xBF, 0x00  /* (to 0x0128 state 110) */,
-                       0x65 /* 'e' */, 0xF8, 0x03  /* (to 0x0464 state 676) */,
-                       0x08, /* fail */
-/* pos 0070:  19 */    0xF3 /* 's' -> */,
-/* pos 0071:  20 */    0xF4 /* 't' -> */,
-/* pos 0072:  21 */    0xBA /* ':' -> */,
-/* pos 0073:  22 */    0x00, 0x03                  /* - terminal marker  3 - */,
-/* pos 0075:  23 */    0x6F /* 'o' */, 0x07, 0x00  /* (to 0x007C state  24) */,
-                       0x61 /* 'a' */, 0x72, 0x01  /* (to 0x01EA state 217) */,
-                       0x08, /* fail */
-/* pos 007c:  24 */    0x6E /* 'n' */, 0x07, 0x00  /* (to 0x0083 state  25) */,
-                       0x6F /* 'o' */, 0x87, 0x01  /* (to 0x0206 state 243) */,
-                       0x08, /* fail */
-/* pos 0083:  25 */    0x6E /* 'n' */, 0x07, 0x00  /* (to 0x008A state  26) */,
-                       0x74 /* 't' */, 0x86, 0x01  /* (to 0x020C state 248) */,
-                       0x08, /* fail */
-/* pos 008a:  26 */    0xE5 /* 'e' -> */,
-/* pos 008b:  27 */    0xE3 /* 'c' -> */,
-/* pos 008c:  28 */    0xF4 /* 't' -> */,
-/* pos 008d:  29 */    0x69 /* 'i' */, 0x07, 0x00  /* (to 0x0094 state  30) */,
-                       0x20 /* ' ' */, 0xD2, 0x03  /* (to 0x0462 state 675) */,
-                       0x08, /* fail */
-/* pos 0094:  30 */    0xEF /* 'o' -> */,
-/* pos 0095:  31 */    0xEE /* 'n' -> */,
-/* pos 0096:  32 */    0xBA /* ':' -> */,
-/* pos 0097:  33 */    0x00, 0x04                  /* - terminal marker  4 - */,
-/* pos 0099:  34 */    0x70 /* 'p' */, 0x0A, 0x00  /* (to 0x00A3 state  35) */,
-                       0x73 /* 's' */, 0x5F, 0x03  /* (to 0x03FB state 596) */,
-                       0x72 /* 'r' */, 0x97, 0x03  /* (to 0x0436 state 642) */,
-                       0x08, /* fail */
-/* pos 00a3:  35 */    0xE7 /* 'g' -> */,
-/* pos 00a4:  36 */    0xF2 /* 'r' -> */,
-/* pos 00a5:  37 */    0xE1 /* 'a' -> */,
-/* pos 00a6:  38 */    0xE4 /* 'd' -> */,
-/* pos 00a7:  39 */    0xE5 /* 'e' -> */,
-/* pos 00a8:  40 */    0xBA /* ':' -> */,
-/* pos 00a9:  41 */    0x00, 0x05                  /* - terminal marker  5 - */,
-/* pos 00ab:  42 */    0xE9 /* 'i' -> */,
-/* pos 00ac:  43 */    0xE7 /* 'g' -> */,
-/* pos 00ad:  44 */    0xE9 /* 'i' -> */,
-/* pos 00ae:  45 */    0xEE /* 'n' -> */,
-/* pos 00af:  46 */    0xBA /* ':' -> */,
-/* pos 00b0:  47 */    0x00, 0x06                  /* - terminal marker  6 - */,
-/* pos 00b2:  48 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x00B9 state  49) */,
-                       0x74 /* 't' */, 0x13, 0x03  /* (to 0x03C8 state 553) */,
-                       0x08, /* fail */
-/* pos 00b9:  49 */    0x63 /* 'c' */, 0x0A, 0x00  /* (to 0x00C3 state  50) */,
-                       0x72 /* 'r' */, 0xFC, 0x02  /* (to 0x03B8 state 539) */,
-                       0x74 /* 't' */, 0xFF, 0x02  /* (to 0x03BE state 544) */,
-                       0x08, /* fail */
-/* pos 00c3:  50 */    0xAD /* '-' -> */,
-/* pos 00c4:  51 */    0xF7 /* 'w' -> */,
-/* pos 00c5:  52 */    0xE5 /* 'e' -> */,
-/* pos 00c6:  53 */    0xE2 /* 'b' -> */,
-/* pos 00c7:  54 */    0xF3 /* 's' -> */,
-/* pos 00c8:  55 */    0xEF /* 'o' -> */,
-/* pos 00c9:  56 */    0xE3 /* 'c' -> */,
-/* pos 00ca:  57 */    0xEB /* 'k' -> */,
-/* pos 00cb:  58 */    0xE5 /* 'e' -> */,
-/* pos 00cc:  59 */    0xF4 /* 't' -> */,
-/* pos 00cd:  60 */    0xAD /* '-' -> */,
-/* pos 00ce:  61 */    0x64 /* 'd' */, 0x19, 0x00  /* (to 0x00E7 state  62) */,
-                       0x65 /* 'e' */, 0x20, 0x00  /* (to 0x00F1 state  70) */,
-                       0x6B /* 'k' */, 0x29, 0x00  /* (to 0x00FD state  81) */,
-                       0x70 /* 'p' */, 0x38, 0x00  /* (to 0x010F state  88) */,
-                       0x61 /* 'a' */, 0x3F, 0x00  /* (to 0x0119 state  97) */,
-                       0x6E /* 'n' */, 0x44, 0x00  /* (to 0x0121 state 104) */,
-                       0x76 /* 'v' */, 0x86, 0x01  /* (to 0x0266 state 284) */,
-                       0x6F /* 'o' */, 0x8C, 0x01  /* (to 0x026F state 292) */,
-                       0x08, /* fail */
-/* pos 00e7:  62 */    0xF2 /* 'r' -> */,
-/* pos 00e8:  63 */    0xE1 /* 'a' -> */,
-/* pos 00e9:  64 */    0xE6 /* 'f' -> */,
-/* pos 00ea:  65 */    0xF4 /* 't' -> */,
-/* pos 00eb:  66 */    0xBA /* ':' -> */,
-/* pos 00ec:  67 */    0x00, 0x07                  /* - terminal marker  7 - */,
-/* pos 00ee:  68 */    0x8A /* '.' -> */,
-/* pos 00ef:  69 */    0x00, 0x08                  /* - terminal marker  8 - */,
-/* pos 00f1:  70 */    0xF8 /* 'x' -> */,
-/* pos 00f2:  71 */    0xF4 /* 't' -> */,
-/* pos 00f3:  72 */    0xE5 /* 'e' -> */,
-/* pos 00f4:  73 */    0xEE /* 'n' -> */,
-/* pos 00f5:  74 */    0xF3 /* 's' -> */,
-/* pos 00f6:  75 */    0xE9 /* 'i' -> */,
-/* pos 00f7:  76 */    0xEF /* 'o' -> */,
-/* pos 00f8:  77 */    0xEE /* 'n' -> */,
-/* pos 00f9:  78 */    0xF3 /* 's' -> */,
-/* pos 00fa:  79 */    0xBA /* ':' -> */,
-/* pos 00fb:  80 */    0x00, 0x09                  /* - terminal marker  9 - */,
-/* pos 00fd:  81 */    0xE5 /* 'e' -> */,
-/* pos 00fe:  82 */    0xF9 /* 'y' -> */,
-/* pos 00ff:  83 */    0x31 /* '1' */, 0x0A, 0x00  /* (to 0x0109 state  84) */,
-                       0x32 /* '2' */, 0x0A, 0x00  /* (to 0x010C state  86) */,
-                       0x3A /* ':' */, 0x5F, 0x01  /* (to 0x0264 state 283) */,
-                       0x08, /* fail */
-/* pos 0109:  84 */    0xBA /* ':' -> */,
-/* pos 010a:  85 */    0x00, 0x0A                  /* - terminal marker 10 - */,
-/* pos 010c:  86 */    0xBA /* ':' -> */,
-/* pos 010d:  87 */    0x00, 0x0B                  /* - terminal marker 11 - */,
-/* pos 010f:  88 */    0xF2 /* 'r' -> */,
-/* pos 0110:  89 */    0xEF /* 'o' -> */,
-/* pos 0111:  90 */    0xF4 /* 't' -> */,
-/* pos 0112:  91 */    0xEF /* 'o' -> */,
-/* pos 0113:  92 */    0xE3 /* 'c' -> */,
-/* pos 0114:  93 */    0xEF /* 'o' -> */,
-/* pos 0115:  94 */    0xEC /* 'l' -> */,
-/* pos 0116:  95 */    0xBA /* ':' -> */,
-/* pos 0117:  96 */    0x00, 0x0C                  /* - terminal marker 12 - */,
-/* pos 0119:  97 */    0xE3 /* 'c' -> */,
-/* pos 011a:  98 */    0xE3 /* 'c' -> */,
-/* pos 011b:  99 */    0xE5 /* 'e' -> */,
-/* pos 011c: 100 */    0xF0 /* 'p' -> */,
-/* pos 011d: 101 */    0xF4 /* 't' -> */,
-/* pos 011e: 102 */    0xBA /* ':' -> */,
-/* pos 011f: 103 */    0x00, 0x0D                  /* - terminal marker 13 - */,
-/* pos 0121: 104 */    0xEF /* 'o' -> */,
-/* pos 0122: 105 */    0xEE /* 'n' -> */,
-/* pos 0123: 106 */    0xE3 /* 'c' -> */,
-/* pos 0124: 107 */    0xE5 /* 'e' -> */,
-/* pos 0125: 108 */    0xBA /* ':' -> */,
-/* pos 0126: 109 */    0x00, 0x0E                  /* - terminal marker 14 - */,
-/* pos 0128: 110 */    0xF4 /* 't' -> */,
-/* pos 0129: 111 */    0xF0 /* 'p' -> */,
-/* pos 012a: 112 */    0x2F /* '/' */, 0x07, 0x00  /* (to 0x0131 state 113) */,
-                       0x32 /* '2' */, 0x10, 0x00  /* (to 0x013D state 118) */,
-                       0x08, /* fail */
-/* pos 0131: 113 */    0xB1 /* '1' -> */,
-/* pos 0132: 114 */    0xAE /* '.' -> */,
-/* pos 0133: 115 */    0x31 /* '1' */, 0x07, 0x00  /* (to 0x013A state 116) */,
-                       0x30 /* '0' */, 0x1B, 0x03  /* (to 0x0451 state 660) */,
-                       0x08, /* fail */
-/* pos 013a: 116 */    0xA0 /* ' ' -> */,
-/* pos 013b: 117 */    0x00, 0x0F                  /* - terminal marker 15 - */,
-/* pos 013d: 118 */    0xAD /* '-' -> */,
-/* pos 013e: 119 */    0xF3 /* 's' -> */,
-/* pos 013f: 120 */    0xE5 /* 'e' -> */,
-/* pos 0140: 121 */    0xF4 /* 't' -> */,
-/* pos 0141: 122 */    0xF4 /* 't' -> */,
-/* pos 0142: 123 */    0xE9 /* 'i' -> */,
-/* pos 0143: 124 */    0xEE /* 'n' -> */,
-/* pos 0144: 125 */    0xE7 /* 'g' -> */,
-/* pos 0145: 126 */    0xF3 /* 's' -> */,
-/* pos 0146: 127 */    0xBA /* ':' -> */,
-/* pos 0147: 128 */    0x00, 0x10                  /* - terminal marker 16 - */,
-/* pos 0149: 129 */    0x63 /* 'c' */, 0x0D, 0x00  /* (to 0x0156 state 130) */,
-                       0x75 /* 'u' */, 0xAC, 0x00  /* (to 0x01F8 state 230) */,
-                       0x67 /* 'g' */, 0x7D, 0x01  /* (to 0x02CC state 358) */,
-                       0x6C /* 'l' */, 0x7E, 0x01  /* (to 0x02D0 state 361) */,
-                       0x08, /* fail */
-/* pos 0156: 130 */    0xE3 /* 'c' -> */,
-/* pos 0157: 131 */    0xE5 /* 'e' -> */,
-/* pos 0158: 132 */    0x70 /* 'p' */, 0x07, 0x00  /* (to 0x015F state 133) */,
-                       0x73 /* 's' */, 0x0E, 0x00  /* (to 0x0169 state 136) */,
-                       0x08, /* fail */
-/* pos 015f: 133 */    0xF4 /* 't' -> */,
-/* pos 0160: 134 */    0x3A /* ':' */, 0x07, 0x00  /* (to 0x0167 state 135) */,
-                       0x2D /* '-' */, 0x59, 0x00  /* (to 0x01BC state 192) */,
-                       0x08, /* fail */
-/* pos 0167: 135 */    0x00, 0x11                  /* - terminal marker 17 - */,
-/* pos 0169: 136 */    0xF3 /* 's' -> */,
-/* pos 016a: 137 */    0xAD /* '-' -> */,
-/* pos 016b: 138 */    0xE3 /* 'c' -> */,
-/* pos 016c: 139 */    0xEF /* 'o' -> */,
-/* pos 016d: 140 */    0xEE /* 'n' -> */,
-/* pos 016e: 141 */    0xF4 /* 't' -> */,
-/* pos 016f: 142 */    0xF2 /* 'r' -> */,
-/* pos 0170: 143 */    0xEF /* 'o' -> */,
-/* pos 0171: 144 */    0xEC /* 'l' -> */,
-/* pos 0172: 145 */    0xAD /* '-' -> */,
-/* pos 0173: 146 */    0x72 /* 'r' */, 0x07, 0x00  /* (to 0x017A state 147) */,
-                       0x61 /* 'a' */, 0x48, 0x01  /* (to 0x02BE state 345) */,
-                       0x08, /* fail */
-/* pos 017a: 147 */    0xE5 /* 'e' -> */,
-/* pos 017b: 148 */    0xF1 /* 'q' -> */,
-/* pos 017c: 149 */    0xF5 /* 'u' -> */,
-/* pos 017d: 150 */    0xE5 /* 'e' -> */,
-/* pos 017e: 151 */    0xF3 /* 's' -> */,
-/* pos 017f: 152 */    0xF4 /* 't' -> */,
-/* pos 0180: 153 */    0xAD /* '-' -> */,
-/* pos 0181: 154 */    0xE8 /* 'h' -> */,
-/* pos 0182: 155 */    0xE5 /* 'e' -> */,
-/* pos 0183: 156 */    0xE1 /* 'a' -> */,
-/* pos 0184: 157 */    0xE4 /* 'd' -> */,
-/* pos 0185: 158 */    0xE5 /* 'e' -> */,
-/* pos 0186: 159 */    0xF2 /* 'r' -> */,
-/* pos 0187: 160 */    0xF3 /* 's' -> */,
-/* pos 0188: 161 */    0xBA /* ':' -> */,
-/* pos 0189: 162 */    0x00, 0x12                  /* - terminal marker 18 - */,
-/* pos 018b: 163 */    0xE6 /* 'f' -> */,
-/* pos 018c: 164 */    0xAD /* '-' -> */,
-/* pos 018d: 165 */    0x6D /* 'm' */, 0x0D, 0x00  /* (to 0x019A state 166) */,
-                       0x6E /* 'n' */, 0x20, 0x00  /* (to 0x01B0 state 181) */,
-                       0x72 /* 'r' */, 0x9E, 0x01  /* (to 0x0331 state 435) */,
-                       0x75 /* 'u' */, 0xA2, 0x01  /* (to 0x0338 state 441) */,
-                       0x08, /* fail */
-/* pos 019a: 166 */    0x6F /* 'o' */, 0x07, 0x00  /* (to 0x01A1 state 167) */,
-                       0x61 /* 'a' */, 0x8E, 0x01  /* (to 0x032B state 430) */,
-                       0x08, /* fail */
-/* pos 01a1: 167 */    0xE4 /* 'd' -> */,
-/* pos 01a2: 168 */    0xE9 /* 'i' -> */,
-/* pos 01a3: 169 */    0xE6 /* 'f' -> */,
-/* pos 01a4: 170 */    0xE9 /* 'i' -> */,
-/* pos 01a5: 171 */    0xE5 /* 'e' -> */,
-/* pos 01a6: 172 */    0xE4 /* 'd' -> */,
-/* pos 01a7: 173 */    0xAD /* '-' -> */,
-/* pos 01a8: 174 */    0xF3 /* 's' -> */,
-/* pos 01a9: 175 */    0xE9 /* 'i' -> */,
-/* pos 01aa: 176 */    0xEE /* 'n' -> */,
-/* pos 01ab: 177 */    0xE3 /* 'c' -> */,
-/* pos 01ac: 178 */    0xE5 /* 'e' -> */,
-/* pos 01ad: 179 */    0xBA /* ':' -> */,
-/* pos 01ae: 180 */    0x00, 0x13                  /* - terminal marker 19 - */,
-/* pos 01b0: 181 */    0xEF /* 'o' -> */,
-/* pos 01b1: 182 */    0xEE /* 'n' -> */,
-/* pos 01b2: 183 */    0xE5 /* 'e' -> */,
-/* pos 01b3: 184 */    0xAD /* '-' -> */,
-/* pos 01b4: 185 */    0xED /* 'm' -> */,
-/* pos 01b5: 186 */    0xE1 /* 'a' -> */,
-/* pos 01b6: 187 */    0xF4 /* 't' -> */,
-/* pos 01b7: 188 */    0xE3 /* 'c' -> */,
-/* pos 01b8: 189 */    0xE8 /* 'h' -> */,
-/* pos 01b9: 190 */    0xBA /* ':' -> */,
-/* pos 01ba: 191 */    0x00, 0x14                  /* - terminal marker 20 - */,
-/* pos 01bc: 192 */    0x65 /* 'e' */, 0x0D, 0x00  /* (to 0x01C9 state 193) */,
-                       0x6C /* 'l' */, 0x14, 0x00  /* (to 0x01D3 state 202) */,
-                       0x63 /* 'c' */, 0xEB, 0x00  /* (to 0x02AD state 330) */,
-                       0x72 /* 'r' */, 0xF1, 0x00  /* (to 0x02B6 state 338) */,
-                       0x08, /* fail */
-/* pos 01c9: 193 */    0xEE /* 'n' -> */,
-/* pos 01ca: 194 */    0xE3 /* 'c' -> */,
-/* pos 01cb: 195 */    0xEF /* 'o' -> */,
-/* pos 01cc: 196 */    0xE4 /* 'd' -> */,
-/* pos 01cd: 197 */    0xE9 /* 'i' -> */,
-/* pos 01ce: 198 */    0xEE /* 'n' -> */,
-/* pos 01cf: 199 */    0xE7 /* 'g' -> */,
-/* pos 01d0: 200 */    0xBA /* ':' -> */,
-/* pos 01d1: 201 */    0x00, 0x15                  /* - terminal marker 21 - */,
-/* pos 01d3: 202 */    0xE1 /* 'a' -> */,
-/* pos 01d4: 203 */    0xEE /* 'n' -> */,
-/* pos 01d5: 204 */    0xE7 /* 'g' -> */,
-/* pos 01d6: 205 */    0xF5 /* 'u' -> */,
-/* pos 01d7: 206 */    0xE1 /* 'a' -> */,
-/* pos 01d8: 207 */    0xE7 /* 'g' -> */,
-/* pos 01d9: 208 */    0xE5 /* 'e' -> */,
-/* pos 01da: 209 */    0xBA /* ':' -> */,
-/* pos 01db: 210 */    0x00, 0x16                  /* - terminal marker 22 - */,
-/* pos 01dd: 211 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x01E4 state 212) */,
-                       0x6F /* 'o' */, 0x9E, 0x01  /* (to 0x037E state 497) */,
-                       0x08, /* fail */
-/* pos 01e4: 212 */    0xE7 /* 'g' -> */,
-/* pos 01e5: 213 */    0xED /* 'm' -> */,
-/* pos 01e6: 214 */    0xE1 /* 'a' -> */,
-/* pos 01e7: 215 */    0xBA /* ':' -> */,
-/* pos 01e8: 216 */    0x00, 0x17                  /* - terminal marker 23 - */,
-/* pos 01ea: 217 */    0xE3 /* 'c' -> */,
-/* pos 01eb: 218 */    0xE8 /* 'h' -> */,
-/* pos 01ec: 219 */    0xE5 /* 'e' -> */,
-/* pos 01ed: 220 */    0xAD /* '-' -> */,
-/* pos 01ee: 221 */    0xE3 /* 'c' -> */,
-/* pos 01ef: 222 */    0xEF /* 'o' -> */,
-/* pos 01f0: 223 */    0xEE /* 'n' -> */,
-/* pos 01f1: 224 */    0xF4 /* 't' -> */,
-/* pos 01f2: 225 */    0xF2 /* 'r' -> */,
-/* pos 01f3: 226 */    0xEF /* 'o' -> */,
-/* pos 01f4: 227 */    0xEC /* 'l' -> */,
-/* pos 01f5: 228 */    0xBA /* ':' -> */,
-/* pos 01f6: 229 */    0x00, 0x18                  /* - terminal marker 24 - */,
-/* pos 01f8: 230 */    0xF4 /* 't' -> */,
-/* pos 01f9: 231 */    0xE8 /* 'h' -> */,
-/* pos 01fa: 232 */    0xEF /* 'o' -> */,
-/* pos 01fb: 233 */    0xF2 /* 'r' -> */,
-/* pos 01fc: 234 */    0xE9 /* 'i' -> */,
-/* pos 01fd: 235 */    0xFA /* 'z' -> */,
-/* pos 01fe: 236 */    0xE1 /* 'a' -> */,
-/* pos 01ff: 237 */    0xF4 /* 't' -> */,
-/* pos 0200: 238 */    0xE9 /* 'i' -> */,
-/* pos 0201: 239 */    0xEF /* 'o' -> */,
-/* pos 0202: 240 */    0xEE /* 'n' -> */,
-/* pos 0203: 241 */    0xBA /* ':' -> */,
-/* pos 0204: 242 */    0x00, 0x19                  /* - terminal marker 25 - */,
-/* pos 0206: 243 */    0xEB /* 'k' -> */,
-/* pos 0207: 244 */    0xE9 /* 'i' -> */,
-/* pos 0208: 245 */    0xE5 /* 'e' -> */,
-/* pos 0209: 246 */    0xBA /* ':' -> */,
-/* pos 020a: 247 */    0x00, 0x1A                  /* - terminal marker 26 - */,
-/* pos 020c: 248 */    0xE5 /* 'e' -> */,
-/* pos 020d: 249 */    0xEE /* 'n' -> */,
-/* pos 020e: 250 */    0xF4 /* 't' -> */,
-/* pos 020f: 251 */    0xAD /* '-' -> */,
-/* pos 0210: 252 */    0x6C /* 'l' */, 0x10, 0x00  /* (to 0x0220 state 253) */,
-                       0x74 /* 't' */, 0x1E, 0x00  /* (to 0x0231 state 260) */,
-                       0x64 /* 'd' */, 0xC0, 0x00  /* (to 0x02D6 state 366) */,
-                       0x65 /* 'e' */, 0xCA, 0x00  /* (to 0x02E3 state 378) */,
-                       0x72 /* 'r' */, 0xE3, 0x00  /* (to 0x02FF state 403) */,
-                       0x08, /* fail */
-/* pos 0220: 253 */    0x65 /* 'e' */, 0x0A, 0x00  /* (to 0x022A state 254) */,
-                       0x61 /* 'a' */, 0xCA, 0x00  /* (to 0x02ED state 387) */,
-                       0x6F /* 'o' */, 0xD0, 0x00  /* (to 0x02F6 state 395) */,
-                       0x08, /* fail */
-/* pos 022a: 254 */    0xEE /* 'n' -> */,
-/* pos 022b: 255 */    0xE7 /* 'g' -> */,
-/* pos 022c: 256 */    0xF4 /* 't' -> */,
-/* pos 022d: 257 */    0xE8 /* 'h' -> */,
-/* pos 022e: 258 */    0xBA /* ':' -> */,
-/* pos 022f: 259 */    0x00, 0x1B                  /* - terminal marker 27 - */,
-/* pos 0231: 260 */    0xF9 /* 'y' -> */,
-/* pos 0232: 261 */    0xF0 /* 'p' -> */,
-/* pos 0233: 262 */    0xE5 /* 'e' -> */,
-/* pos 0234: 263 */    0xBA /* ':' -> */,
-/* pos 0235: 264 */    0x00, 0x1C                  /* - terminal marker 28 - */,
-/* pos 0237: 265 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x023E state 266) */,
-                       0x65 /* 'e' */, 0xF6, 0x01  /* (to 0x0430 state 637) */,
-                       0x08, /* fail */
-/* pos 023e: 266 */    0xF4 /* 't' -> */,
-/* pos 023f: 267 */    0xE5 /* 'e' -> */,
-/* pos 0240: 268 */    0xBA /* ':' -> */,
-/* pos 0241: 269 */    0x00, 0x1D                  /* - terminal marker 29 - */,
-/* pos 0243: 270 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x024A state 271) */,
-                       0x65 /* 'e' */, 0x0A, 0x00  /* (to 0x0250 state 276) */,
-                       0x08, /* fail */
-/* pos 024a: 271 */    0xEE /* 'n' -> */,
-/* pos 024b: 272 */    0xE7 /* 'g' -> */,
-/* pos 024c: 273 */    0xE5 /* 'e' -> */,
-/* pos 024d: 274 */    0xBA /* ':' -> */,
-/* pos 024e: 275 */    0x00, 0x1E                  /* - terminal marker 30 - */,
-/* pos 0250: 276 */    0x66 /* 'f' */, 0x07, 0x00  /* (to 0x0257 state 277) */,
-                       0x74 /* 't' */, 0x5A, 0x01  /* (to 0x03AD state 529) */,
-                       0x08, /* fail */
-/* pos 0257: 277 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x025E state 278) */,
-                       0x72 /* 'r' */, 0x4D, 0x01  /* (to 0x03A7 state 524) */,
-                       0x08, /* fail */
-/* pos 025e: 278 */    0xF2 /* 'r' -> */,
-/* pos 025f: 279 */    0xE5 /* 'e' -> */,
-/* pos 0260: 280 */    0xF2 /* 'r' -> */,
-/* pos 0261: 281 */    0xBA /* ':' -> */,
-/* pos 0262: 282 */    0x00, 0x1F                  /* - terminal marker 31 - */,
-/* pos 0264: 283 */    0x00, 0x20                  /* - terminal marker 32 - */,
-/* pos 0266: 284 */    0xE5 /* 'e' -> */,
-/* pos 0267: 285 */    0xF2 /* 'r' -> */,
-/* pos 0268: 286 */    0xF3 /* 's' -> */,
-/* pos 0269: 287 */    0xE9 /* 'i' -> */,
-/* pos 026a: 288 */    0xEF /* 'o' -> */,
-/* pos 026b: 289 */    0xEE /* 'n' -> */,
-/* pos 026c: 290 */    0xBA /* ':' -> */,
-/* pos 026d: 291 */    0x00, 0x21                  /* - terminal marker 33 - */,
-/* pos 026f: 292 */    0xF2 /* 'r' -> */,
-/* pos 0270: 293 */    0xE9 /* 'i' -> */,
-/* pos 0271: 294 */    0xE7 /* 'g' -> */,
-/* pos 0272: 295 */    0xE9 /* 'i' -> */,
-/* pos 0273: 296 */    0xEE /* 'n' -> */,
-/* pos 0274: 297 */    0xBA /* ':' -> */,
-/* pos 0275: 298 */    0x00, 0x22                  /* - terminal marker 34 - */,
-/* pos 0277: 299 */    0x61 /* 'a' */, 0x0D, 0x00  /* (to 0x0284 state 300) */,
-                       0x6D /* 'm' */, 0x14, 0x00  /* (to 0x028E state 309) */,
-                       0x70 /* 'p' */, 0x18, 0x00  /* (to 0x0295 state 315) */,
-                       0x73 /* 's' */, 0x1A, 0x00  /* (to 0x029A state 319) */,
-                       0x08, /* fail */
-/* pos 0284: 300 */    0xF5 /* 'u' -> */,
-/* pos 0285: 301 */    0xF4 /* 't' -> */,
-/* pos 0286: 302 */    0xE8 /* 'h' -> */,
-/* pos 0287: 303 */    0xEF /* 'o' -> */,
-/* pos 0288: 304 */    0xF2 /* 'r' -> */,
-/* pos 0289: 305 */    0xE9 /* 'i' -> */,
-/* pos 028a: 306 */    0xF4 /* 't' -> */,
-/* pos 028b: 307 */    0xF9 /* 'y' -> */,
-/* pos 028c: 308 */    0x00, 0x23                  /* - terminal marker 35 - */,
-/* pos 028e: 309 */    0xE5 /* 'e' -> */,
-/* pos 028f: 310 */    0xF4 /* 't' -> */,
-/* pos 0290: 311 */    0xE8 /* 'h' -> */,
-/* pos 0291: 312 */    0xEF /* 'o' -> */,
-/* pos 0292: 313 */    0xE4 /* 'd' -> */,
-/* pos 0293: 314 */    0x00, 0x24                  /* - terminal marker 36 - */,
-/* pos 0295: 315 */    0xE1 /* 'a' -> */,
-/* pos 0296: 316 */    0xF4 /* 't' -> */,
-/* pos 0297: 317 */    0xE8 /* 'h' -> */,
-/* pos 0298: 318 */    0x00, 0x25                  /* - terminal marker 37 - */,
-/* pos 029a: 319 */    0x63 /* 'c' */, 0x07, 0x00  /* (to 0x02A1 state 320) */,
-                       0x74 /* 't' */, 0x0A, 0x00  /* (to 0x02A7 state 325) */,
-                       0x08, /* fail */
-/* pos 02a1: 320 */    0xE8 /* 'h' -> */,
-/* pos 02a2: 321 */    0xE5 /* 'e' -> */,
-/* pos 02a3: 322 */    0xED /* 'm' -> */,
-/* pos 02a4: 323 */    0xE5 /* 'e' -> */,
-/* pos 02a5: 324 */    0x00, 0x26                  /* - terminal marker 38 - */,
-/* pos 02a7: 325 */    0xE1 /* 'a' -> */,
-/* pos 02a8: 326 */    0xF4 /* 't' -> */,
-/* pos 02a9: 327 */    0xF5 /* 'u' -> */,
-/* pos 02aa: 328 */    0xF3 /* 's' -> */,
-/* pos 02ab: 329 */    0x00, 0x27                  /* - terminal marker 39 - */,
-/* pos 02ad: 330 */    0xE8 /* 'h' -> */,
-/* pos 02ae: 331 */    0xE1 /* 'a' -> */,
-/* pos 02af: 332 */    0xF2 /* 'r' -> */,
-/* pos 02b0: 333 */    0xF3 /* 's' -> */,
-/* pos 02b1: 334 */    0xE5 /* 'e' -> */,
-/* pos 02b2: 335 */    0xF4 /* 't' -> */,
-/* pos 02b3: 336 */    0xBA /* ':' -> */,
-/* pos 02b4: 337 */    0x00, 0x28                  /* - terminal marker 40 - */,
-/* pos 02b6: 338 */    0xE1 /* 'a' -> */,
-/* pos 02b7: 339 */    0xEE /* 'n' -> */,
-/* pos 02b8: 340 */    0xE7 /* 'g' -> */,
-/* pos 02b9: 341 */    0xE5 /* 'e' -> */,
-/* pos 02ba: 342 */    0xF3 /* 's' -> */,
-/* pos 02bb: 343 */    0xBA /* ':' -> */,
-/* pos 02bc: 344 */    0x00, 0x29                  /* - terminal marker 41 - */,
-/* pos 02be: 345 */    0xEC /* 'l' -> */,
-/* pos 02bf: 346 */    0xEC /* 'l' -> */,
-/* pos 02c0: 347 */    0xEF /* 'o' -> */,
-/* pos 02c1: 348 */    0xF7 /* 'w' -> */,
-/* pos 02c2: 349 */    0xAD /* '-' -> */,
-/* pos 02c3: 350 */    0xEF /* 'o' -> */,
-/* pos 02c4: 351 */    0xF2 /* 'r' -> */,
-/* pos 02c5: 352 */    0xE9 /* 'i' -> */,
-/* pos 02c6: 353 */    0xE7 /* 'g' -> */,
-/* pos 02c7: 354 */    0xE9 /* 'i' -> */,
-/* pos 02c8: 355 */    0xEE /* 'n' -> */,
-/* pos 02c9: 356 */    0xBA /* ':' -> */,
-/* pos 02ca: 357 */    0x00, 0x2A                  /* - terminal marker 42 - */,
-/* pos 02cc: 358 */    0xE5 /* 'e' -> */,
-/* pos 02cd: 359 */    0xBA /* ':' -> */,
-/* pos 02ce: 360 */    0x00, 0x2B                  /* - terminal marker 43 - */,
-/* pos 02d0: 361 */    0xEC /* 'l' -> */,
-/* pos 02d1: 362 */    0xEF /* 'o' -> */,
-/* pos 02d2: 363 */    0xF7 /* 'w' -> */,
-/* pos 02d3: 364 */    0xBA /* ':' -> */,
-/* pos 02d4: 365 */    0x00, 0x2C                  /* - terminal marker 44 - */,
-/* pos 02d6: 366 */    0xE9 /* 'i' -> */,
-/* pos 02d7: 367 */    0xF3 /* 's' -> */,
-/* pos 02d8: 368 */    0xF0 /* 'p' -> */,
-/* pos 02d9: 369 */    0xEF /* 'o' -> */,
-/* pos 02da: 370 */    0xF3 /* 's' -> */,
-/* pos 02db: 371 */    0xE9 /* 'i' -> */,
-/* pos 02dc: 372 */    0xF4 /* 't' -> */,
-/* pos 02dd: 373 */    0xE9 /* 'i' -> */,
-/* pos 02de: 374 */    0xEF /* 'o' -> */,
-/* pos 02df: 375 */    0xEE /* 'n' -> */,
-/* pos 02e0: 376 */    0xBA /* ':' -> */,
-/* pos 02e1: 377 */    0x00, 0x2D                  /* - terminal marker 45 - */,
-/* pos 02e3: 378 */    0xEE /* 'n' -> */,
-/* pos 02e4: 379 */    0xE3 /* 'c' -> */,
-/* pos 02e5: 380 */    0xEF /* 'o' -> */,
-/* pos 02e6: 381 */    0xE4 /* 'd' -> */,
-/* pos 02e7: 382 */    0xE9 /* 'i' -> */,
-/* pos 02e8: 383 */    0xEE /* 'n' -> */,
-/* pos 02e9: 384 */    0xE7 /* 'g' -> */,
-/* pos 02ea: 385 */    0xBA /* ':' -> */,
-/* pos 02eb: 386 */    0x00, 0x2E                  /* - terminal marker 46 - */,
-/* pos 02ed: 387 */    0xEE /* 'n' -> */,
-/* pos 02ee: 388 */    0xE7 /* 'g' -> */,
-/* pos 02ef: 389 */    0xF5 /* 'u' -> */,
-/* pos 02f0: 390 */    0xE1 /* 'a' -> */,
-/* pos 02f1: 391 */    0xE7 /* 'g' -> */,
-/* pos 02f2: 392 */    0xE5 /* 'e' -> */,
-/* pos 02f3: 393 */    0xBA /* ':' -> */,
-/* pos 02f4: 394 */    0x00, 0x2F                  /* - terminal marker 47 - */,
-/* pos 02f6: 395 */    0xE3 /* 'c' -> */,
-/* pos 02f7: 396 */    0xE1 /* 'a' -> */,
-/* pos 02f8: 397 */    0xF4 /* 't' -> */,
-/* pos 02f9: 398 */    0xE9 /* 'i' -> */,
-/* pos 02fa: 399 */    0xEF /* 'o' -> */,
-/* pos 02fb: 400 */    0xEE /* 'n' -> */,
-/* pos 02fc: 401 */    0xBA /* ':' -> */,
-/* pos 02fd: 402 */    0x00, 0x30                  /* - terminal marker 48 - */,
-/* pos 02ff: 403 */    0xE1 /* 'a' -> */,
-/* pos 0300: 404 */    0xEE /* 'n' -> */,
-/* pos 0301: 405 */    0xE7 /* 'g' -> */,
-/* pos 0302: 406 */    0xE5 /* 'e' -> */,
-/* pos 0303: 407 */    0xBA /* ':' -> */,
-/* pos 0304: 408 */    0x00, 0x31                  /* - terminal marker 49 - */,
-/* pos 0306: 409 */    0x74 /* 't' */, 0x07, 0x00  /* (to 0x030D state 410) */,
-                       0x78 /* 'x' */, 0x09, 0x00  /* (to 0x0312 state 414) */,
-                       0x08, /* fail */
-/* pos 030d: 410 */    0xE1 /* 'a' -> */,
-/* pos 030e: 411 */    0xE7 /* 'g' -> */,
-/* pos 030f: 412 */    0xBA /* ':' -> */,
-/* pos 0310: 413 */    0x00, 0x32                  /* - terminal marker 50 - */,
-/* pos 0312: 414 */    0xF0 /* 'p' -> */,
-/* pos 0313: 415 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x031A state 416) */,
-                       0x69 /* 'i' */, 0x09, 0x00  /* (to 0x031F state 420) */,
-                       0x08, /* fail */
-/* pos 031a: 416 */    0xE3 /* 'c' -> */,
-/* pos 031b: 417 */    0xF4 /* 't' -> */,
-/* pos 031c: 418 */    0xBA /* ':' -> */,
-/* pos 031d: 419 */    0x00, 0x33                  /* - terminal marker 51 - */,
-/* pos 031f: 420 */    0xF2 /* 'r' -> */,
-/* pos 0320: 421 */    0xE5 /* 'e' -> */,
-/* pos 0321: 422 */    0xF3 /* 's' -> */,
-/* pos 0322: 423 */    0xBA /* ':' -> */,
-/* pos 0323: 424 */    0x00, 0x34                  /* - terminal marker 52 - */,
-/* pos 0325: 425 */    0xF2 /* 'r' -> */,
-/* pos 0326: 426 */    0xEF /* 'o' -> */,
-/* pos 0327: 427 */    0xED /* 'm' -> */,
-/* pos 0328: 428 */    0xBA /* ':' -> */,
-/* pos 0329: 429 */    0x00, 0x35                  /* - terminal marker 53 - */,
-/* pos 032b: 430 */    0xF4 /* 't' -> */,
-/* pos 032c: 431 */    0xE3 /* 'c' -> */,
-/* pos 032d: 432 */    0xE8 /* 'h' -> */,
-/* pos 032e: 433 */    0xBA /* ':' -> */,
-/* pos 032f: 434 */    0x00, 0x36                  /* - terminal marker 54 - */,
-/* pos 0331: 435 */    0xE1 /* 'a' -> */,
-/* pos 0332: 436 */    0xEE /* 'n' -> */,
-/* pos 0333: 437 */    0xE7 /* 'g' -> */,
-/* pos 0334: 438 */    0xE5 /* 'e' -> */,
-/* pos 0335: 439 */    0xBA /* ':' -> */,
-/* pos 0336: 440 */    0x00, 0x37                  /* - terminal marker 55 - */,
-/* pos 0338: 441 */    0xEE /* 'n' -> */,
-/* pos 0339: 442 */    0xED /* 'm' -> */,
-/* pos 033a: 443 */    0xEF /* 'o' -> */,
-/* pos 033b: 444 */    0xE4 /* 'd' -> */,
-/* pos 033c: 445 */    0xE9 /* 'i' -> */,
-/* pos 033d: 446 */    0xE6 /* 'f' -> */,
-/* pos 033e: 447 */    0xE9 /* 'i' -> */,
-/* pos 033f: 448 */    0xE5 /* 'e' -> */,
-/* pos 0340: 449 */    0xE4 /* 'd' -> */,
-/* pos 0341: 450 */    0xAD /* '-' -> */,
-/* pos 0342: 451 */    0xF3 /* 's' -> */,
-/* pos 0343: 452 */    0xE9 /* 'i' -> */,
-/* pos 0344: 453 */    0xEE /* 'n' -> */,
-/* pos 0345: 454 */    0xE3 /* 'c' -> */,
-/* pos 0346: 455 */    0xE5 /* 'e' -> */,
-/* pos 0347: 456 */    0xBA /* ':' -> */,
-/* pos 0348: 457 */    0x00, 0x38                  /* - terminal marker 56 - */,
-/* pos 034a: 458 */    0x61 /* 'a' */, 0x0A, 0x00  /* (to 0x0354 state 459) */,
-                       0x69 /* 'i' */, 0x15, 0x00  /* (to 0x0362 state 472) */,
-                       0x6F /* 'o' */, 0x17, 0x00  /* (to 0x0367 state 476) */,
-                       0x08, /* fail */
-/* pos 0354: 459 */    0xF3 /* 's' -> */,
-/* pos 0355: 460 */    0xF4 /* 't' -> */,
-/* pos 0356: 461 */    0xAD /* '-' -> */,
-/* pos 0357: 462 */    0xED /* 'm' -> */,
-/* pos 0358: 463 */    0xEF /* 'o' -> */,
-/* pos 0359: 464 */    0xE4 /* 'd' -> */,
-/* pos 035a: 465 */    0xE9 /* 'i' -> */,
-/* pos 035b: 466 */    0xE6 /* 'f' -> */,
-/* pos 035c: 467 */    0xE9 /* 'i' -> */,
-/* pos 035d: 468 */    0xE5 /* 'e' -> */,
-/* pos 035e: 469 */    0xE4 /* 'd' -> */,
-/* pos 035f: 470 */    0xBA /* ':' -> */,
-/* pos 0360: 471 */    0x00, 0x39                  /* - terminal marker 57 - */,
-/* pos 0362: 472 */    0xEE /* 'n' -> */,
-/* pos 0363: 473 */    0xEB /* 'k' -> */,
-/* pos 0364: 474 */    0xBA /* ':' -> */,
-/* pos 0365: 475 */    0x00, 0x3A                  /* - terminal marker 58 - */,
-/* pos 0367: 476 */    0xE3 /* 'c' -> */,
-/* pos 0368: 477 */    0xE1 /* 'a' -> */,
-/* pos 0369: 478 */    0xF4 /* 't' -> */,
-/* pos 036a: 479 */    0xE9 /* 'i' -> */,
-/* pos 036b: 480 */    0xEF /* 'o' -> */,
-/* pos 036c: 481 */    0xEE /* 'n' -> */,
-/* pos 036d: 482 */    0xBA /* ':' -> */,
-/* pos 036e: 483 */    0x00, 0x3B                  /* - terminal marker 59 - */,
-/* pos 0370: 484 */    0xE1 /* 'a' -> */,
-/* pos 0371: 485 */    0xF8 /* 'x' -> */,
-/* pos 0372: 486 */    0xAD /* '-' -> */,
-/* pos 0373: 487 */    0xE6 /* 'f' -> */,
-/* pos 0374: 488 */    0xEF /* 'o' -> */,
-/* pos 0375: 489 */    0xF2 /* 'r' -> */,
-/* pos 0376: 490 */    0xF7 /* 'w' -> */,
-/* pos 0377: 491 */    0xE1 /* 'a' -> */,
-/* pos 0378: 492 */    0xF2 /* 'r' -> */,
-/* pos 0379: 493 */    0xE4 /* 'd' -> */,
-/* pos 037a: 494 */    0xF3 /* 's' -> */,
-/* pos 037b: 495 */    0xBA /* ':' -> */,
-/* pos 037c: 496 */    0x00, 0x3C                  /* - terminal marker 60 - */,
-/* pos 037e: 497 */    0xF8 /* 'x' -> */,
-/* pos 037f: 498 */    0xF9 /* 'y' -> */,
-/* pos 0380: 499 */    0x2D /* '-' */, 0x07, 0x00  /* (to 0x0387 state 500) */,
-                       0x20 /* ' ' */, 0xBB, 0x00  /* (to 0x043E state 649) */,
-                       0x08, /* fail */
-/* pos 0387: 500 */    0xE1 /* 'a' -> */,
-/* pos 0388: 501 */    0xF5 /* 'u' -> */,
-/* pos 0389: 502 */    0xF4 /* 't' -> */,
-/* pos 038a: 503 */    0xE8 /* 'h' -> */,
-/* pos 038b: 504 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x0392 state 505) */,
-                       0x6F /* 'o' */, 0x0E, 0x00  /* (to 0x039C state 514) */,
-                       0x08, /* fail */
-/* pos 0392: 505 */    0xEE /* 'n' -> */,
-/* pos 0393: 506 */    0xF4 /* 't' -> */,
-/* pos 0394: 507 */    0xE9 /* 'i' -> */,
-/* pos 0395: 508 */    0xE3 /* 'c' -> */,
-/* pos 0396: 509 */    0xE1 /* 'a' -> */,
-/* pos 0397: 510 */    0xF4 /* 't' -> */,
-/* pos 0398: 511 */    0xE5 /* 'e' -> */,
-/* pos 0399: 512 */    0xBA /* ':' -> */,
-/* pos 039a: 513 */    0x00, 0x3D                  /* - terminal marker 61 - */,
-/* pos 039c: 514 */    0xF2 /* 'r' -> */,
-/* pos 039d: 515 */    0xE9 /* 'i' -> */,
-/* pos 039e: 516 */    0xFA /* 'z' -> */,
-/* pos 039f: 517 */    0xE1 /* 'a' -> */,
-/* pos 03a0: 518 */    0xF4 /* 't' -> */,
-/* pos 03a1: 519 */    0xE9 /* 'i' -> */,
-/* pos 03a2: 520 */    0xEF /* 'o' -> */,
-/* pos 03a3: 521 */    0xEE /* 'n' -> */,
-/* pos 03a4: 522 */    0xBA /* ':' -> */,
-/* pos 03a5: 523 */    0x00, 0x3E                  /* - terminal marker 62 - */,
-/* pos 03a7: 524 */    0xE5 /* 'e' -> */,
-/* pos 03a8: 525 */    0xF3 /* 's' -> */,
-/* pos 03a9: 526 */    0xE8 /* 'h' -> */,
-/* pos 03aa: 527 */    0xBA /* ':' -> */,
-/* pos 03ab: 528 */    0x00, 0x3F                  /* - terminal marker 63 - */,
-/* pos 03ad: 529 */    0xF2 /* 'r' -> */,
-/* pos 03ae: 530 */    0xF9 /* 'y' -> */,
-/* pos 03af: 531 */    0xAD /* '-' -> */,
-/* pos 03b0: 532 */    0xE1 /* 'a' -> */,
-/* pos 03b1: 533 */    0xE6 /* 'f' -> */,
-/* pos 03b2: 534 */    0xF4 /* 't' -> */,
-/* pos 03b3: 535 */    0xE5 /* 'e' -> */,
-/* pos 03b4: 536 */    0xF2 /* 'r' -> */,
-/* pos 03b5: 537 */    0xBA /* ':' -> */,
-/* pos 03b6: 538 */    0x00, 0x40                  /* - terminal marker 64 - */,
-/* pos 03b8: 539 */    0xF6 /* 'v' -> */,
-/* pos 03b9: 540 */    0xE5 /* 'e' -> */,
-/* pos 03ba: 541 */    0xF2 /* 'r' -> */,
-/* pos 03bb: 542 */    0xBA /* ':' -> */,
-/* pos 03bc: 543 */    0x00, 0x41                  /* - terminal marker 65 - */,
-/* pos 03be: 544 */    0xAD /* '-' -> */,
-/* pos 03bf: 545 */    0xE3 /* 'c' -> */,
-/* pos 03c0: 546 */    0xEF /* 'o' -> */,
-/* pos 03c1: 547 */    0xEF /* 'o' -> */,
-/* pos 03c2: 548 */    0xEB /* 'k' -> */,
-/* pos 03c3: 549 */    0xE9 /* 'i' -> */,
-/* pos 03c4: 550 */    0xE5 /* 'e' -> */,
-/* pos 03c5: 551 */    0xBA /* ':' -> */,
-/* pos 03c6: 552 */    0x00, 0x42                  /* - terminal marker 66 - */,
-/* pos 03c8: 553 */    0xF2 /* 'r' -> */,
-/* pos 03c9: 554 */    0xE9 /* 'i' -> */,
-/* pos 03ca: 555 */    0xE3 /* 'c' -> */,
-/* pos 03cb: 556 */    0xF4 /* 't' -> */,
-/* pos 03cc: 557 */    0xAD /* '-' -> */,
-/* pos 03cd: 558 */    0xF4 /* 't' -> */,
-/* pos 03ce: 559 */    0xF2 /* 'r' -> */,
-/* pos 03cf: 560 */    0xE1 /* 'a' -> */,
-/* pos 03d0: 561 */    0xEE /* 'n' -> */,
-/* pos 03d1: 562 */    0xF3 /* 's' -> */,
-/* pos 03d2: 563 */    0xF0 /* 'p' -> */,
-/* pos 03d3: 564 */    0xEF /* 'o' -> */,
-/* pos 03d4: 565 */    0xF2 /* 'r' -> */,
-/* pos 03d5: 566 */    0xF4 /* 't' -> */,
-/* pos 03d6: 567 */    0xAD /* '-' -> */,
-/* pos 03d7: 568 */    0xF3 /* 's' -> */,
-/* pos 03d8: 569 */    0xE5 /* 'e' -> */,
-/* pos 03d9: 570 */    0xE3 /* 'c' -> */,
-/* pos 03da: 571 */    0xF5 /* 'u' -> */,
-/* pos 03db: 572 */    0xF2 /* 'r' -> */,
-/* pos 03dc: 573 */    0xE9 /* 'i' -> */,
-/* pos 03dd: 574 */    0xF4 /* 't' -> */,
-/* pos 03de: 575 */    0xF9 /* 'y' -> */,
-/* pos 03df: 576 */    0xBA /* ':' -> */,
-/* pos 03e0: 577 */    0x00, 0x43                  /* - terminal marker 67 - */,
-/* pos 03e2: 578 */    0x72 /* 'r' */, 0x07, 0x00  /* (to 0x03E9 state 579) */,
-                       0x65 /* 'e' */, 0x84, 0x00  /* (to 0x0469 state 680) */,
-                       0x08, /* fail */
-/* pos 03e9: 579 */    0xE1 /* 'a' -> */,
-/* pos 03ea: 580 */    0xEE /* 'n' -> */,
-/* pos 03eb: 581 */    0xF3 /* 's' -> */,
-/* pos 03ec: 582 */    0xE6 /* 'f' -> */,
-/* pos 03ed: 583 */    0xE5 /* 'e' -> */,
-/* pos 03ee: 584 */    0xF2 /* 'r' -> */,
-/* pos 03ef: 585 */    0xAD /* '-' -> */,
-/* pos 03f0: 586 */    0xE5 /* 'e' -> */,
-/* pos 03f1: 587 */    0xEE /* 'n' -> */,
-/* pos 03f2: 588 */    0xE3 /* 'c' -> */,
-/* pos 03f3: 589 */    0xEF /* 'o' -> */,
-/* pos 03f4: 590 */    0xE4 /* 'd' -> */,
-/* pos 03f5: 591 */    0xE9 /* 'i' -> */,
-/* pos 03f6: 592 */    0xEE /* 'n' -> */,
-/* pos 03f7: 593 */    0xE7 /* 'g' -> */,
-/* pos 03f8: 594 */    0xBA /* ':' -> */,
-/* pos 03f9: 595 */    0x00, 0x44                  /* - terminal marker 68 - */,
-/* pos 03fb: 596 */    0xE5 /* 'e' -> */,
-/* pos 03fc: 597 */    0xF2 /* 'r' -> */,
-/* pos 03fd: 598 */    0xAD /* '-' -> */,
-/* pos 03fe: 599 */    0xE1 /* 'a' -> */,
-/* pos 03ff: 600 */    0xE7 /* 'g' -> */,
-/* pos 0400: 601 */    0xE5 /* 'e' -> */,
-/* pos 0401: 602 */    0xEE /* 'n' -> */,
-/* pos 0402: 603 */    0xF4 /* 't' -> */,
-/* pos 0403: 604 */    0xBA /* ':' -> */,
-/* pos 0404: 605 */    0x00, 0x45                  /* - terminal marker 69 - */,
-/* pos 0406: 606 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x040D state 607) */,
-                       0x69 /* 'i' */, 0x09, 0x00  /* (to 0x0412 state 611) */,
-                       0x08, /* fail */
-/* pos 040d: 607 */    0xF2 /* 'r' -> */,
-/* pos 040e: 608 */    0xF9 /* 'y' -> */,
-/* pos 040f: 609 */    0xBA /* ':' -> */,
-/* pos 0410: 610 */    0x00, 0x46                  /* - terminal marker 70 - */,
-/* pos 0412: 611 */    0xE1 /* 'a' -> */,
-/* pos 0413: 612 */    0xBA /* ':' -> */,
-/* pos 0414: 613 */    0x00, 0x47                  /* - terminal marker 71 - */,
-/* pos 0416: 614 */    0xF7 /* 'w' -> */,
-/* pos 0417: 615 */    0xF7 /* 'w' -> */,
-/* pos 0418: 616 */    0xAD /* '-' -> */,
-/* pos 0419: 617 */    0xE1 /* 'a' -> */,
-/* pos 041a: 618 */    0xF5 /* 'u' -> */,
-/* pos 041b: 619 */    0xF4 /* 't' -> */,
-/* pos 041c: 620 */    0xE8 /* 'h' -> */,
-/* pos 041d: 621 */    0xE5 /* 'e' -> */,
-/* pos 041e: 622 */    0xEE /* 'n' -> */,
-/* pos 041f: 623 */    0xF4 /* 't' -> */,
-/* pos 0420: 624 */    0xE9 /* 'i' -> */,
-/* pos 0421: 625 */    0xE3 /* 'c' -> */,
-/* pos 0422: 626 */    0xE1 /* 'a' -> */,
-/* pos 0423: 627 */    0xF4 /* 't' -> */,
-/* pos 0424: 628 */    0xE5 /* 'e' -> */,
-/* pos 0425: 629 */    0xBA /* ':' -> */,
-/* pos 0426: 630 */    0x00, 0x48                  /* - terminal marker 72 - */,
-/* pos 0428: 631 */    0xF4 /* 't' -> */,
-/* pos 0429: 632 */    0xE3 /* 'c' -> */,
-/* pos 042a: 633 */    0xE8 /* 'h' -> */,
-/* pos 042b: 634 */    0x00, 0x49                  /* - terminal marker 73 - */,
-/* pos 042d: 635 */    0xF4 /* 't' -> */,
-/* pos 042e: 636 */    0x00, 0x4A                  /* - terminal marker 74 - */,
-/* pos 0430: 637 */    0xEC /* 'l' -> */,
-/* pos 0431: 638 */    0xE5 /* 'e' -> */,
-/* pos 0432: 639 */    0xF4 /* 't' -> */,
-/* pos 0433: 640 */    0xE5 /* 'e' -> */,
-/* pos 0434: 641 */    0x00, 0x4B                  /* - terminal marker 75 - */,
-/* pos 0436: 642 */    0xE9 /* 'i' -> */,
-/* pos 0437: 643 */    0xAD /* '-' -> */,
-/* pos 0438: 644 */    0xE1 /* 'a' -> */,
-/* pos 0439: 645 */    0xF2 /* 'r' -> */,
-/* pos 043a: 646 */    0xE7 /* 'g' -> */,
-/* pos 043b: 647 */    0xF3 /* 's' -> */,
-/* pos 043c: 648 */    0x00, 0x4C                  /* - terminal marker 76 - */,
-/* pos 043e: 649 */    0x00, 0x4D                  /* - terminal marker 77 - */,
-/* pos 0440: 650 */    0xAD /* '-' -> */,
-/* pos 0441: 651 */    0x72 /* 'r' */, 0x07, 0x00  /* (to 0x0448 state 652) */,
-                       0x66 /* 'f' */, 0x10, 0x00  /* (to 0x0454 state 662) */,
-                       0x08, /* fail */
-/* pos 0448: 652 */    0xE5 /* 'e' -> */,
-/* pos 0449: 653 */    0xE1 /* 'a' -> */,
-/* pos 044a: 654 */    0xEC /* 'l' -> */,
-/* pos 044b: 655 */    0xAD /* '-' -> */,
-/* pos 044c: 656 */    0xE9 /* 'i' -> */,
-/* pos 044d: 657 */    0xF0 /* 'p' -> */,
-/* pos 044e: 658 */    0xBA /* ':' -> */,
-/* pos 044f: 659 */    0x00, 0x4E                  /* - terminal marker 78 - */,
-/* pos 0451: 660 */    0xA0 /* ' ' -> */,
-/* pos 0452: 661 */    0x00, 0x4F                  /* - terminal marker 79 - */,
-/* pos 0454: 662 */    0xEF /* 'o' -> */,
-/* pos 0455: 663 */    0xF2 /* 'r' -> */,
-/* pos 0456: 664 */    0xF7 /* 'w' -> */,
-/* pos 0457: 665 */    0xE1 /* 'a' -> */,
-/* pos 0458: 666 */    0xF2 /* 'r' -> */,
-/* pos 0459: 667 */    0xE4 /* 'd' -> */,
-/* pos 045a: 668 */    0xE5 /* 'e' -> */,
-/* pos 045b: 669 */    0xE4 /* 'd' -> */,
-/* pos 045c: 670 */    0xAD /* '-' -> */,
-/* pos 045d: 671 */    0xE6 /* 'f' -> */,
-/* pos 045e: 672 */    0xEF /* 'o' -> */,
-/* pos 045f: 673 */    0xF2 /* 'r' -> */,
-/* pos 0460: 674 */    0x00, 0x50                  /* - terminal marker 80 - */,
-/* pos 0462: 675 */    0x00, 0x51                  /* - terminal marker 81 - */,
-/* pos 0464: 676 */    0xE1 /* 'a' -> */,
-/* pos 0465: 677 */    0xE4 /* 'd' -> */,
-/* pos 0466: 678 */    0xA0 /* ' ' -> */,
-/* pos 0467: 679 */    0x00, 0x52                  /* - terminal marker 82 - */,
-/* pos 0469: 680 */    0xBA /* ':' -> */,
-/* pos 046a: 681 */    0x00, 0x53                  /* - terminal marker 83 - */,
-/* total size 1132 bytes */

File diff suppressed because it is too large
+ 513 - 278
thirdparty/libwebsockets/libwebsockets.h


+ 40 - 10
thirdparty/libwebsockets/lws_config.h

@@ -14,6 +14,12 @@
 
 
 #define LWS_INSTALL_DATADIR "/usr/local/share"
 #define LWS_INSTALL_DATADIR "/usr/local/share"
 
 
+#define LWS_ROLE_H1
+#define LWS_ROLE_WS
+#define LWS_ROLE_RAW
+/* #undef LWS_ROLE_H2 */
+/* #undef LWS_ROLE_CGI */
+
 /* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL.
 /* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL.
  * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
  * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
 /* #undef USE_WOLFSSL */
 /* #undef USE_WOLFSSL */
@@ -25,26 +31,26 @@
 
 
 #define LWS_WITH_MBEDTLS
 #define LWS_WITH_MBEDTLS
 /* #undef LWS_WITH_POLARSSL */
 /* #undef LWS_WITH_POLARSSL */
-/* #undef LWS_WITH_ESP8266 */
 /* #undef LWS_WITH_ESP32 */
 /* #undef LWS_WITH_ESP32 */
 
 
 /* #undef LWS_WITH_PLUGINS */
 /* #undef LWS_WITH_PLUGINS */
 /* #undef LWS_WITH_NO_LOGS */
 /* #undef LWS_WITH_NO_LOGS */
 
 
 /* The Libwebsocket version */
 /* The Libwebsocket version */
-#define LWS_LIBRARY_VERSION "2.4.2"
+#define LWS_LIBRARY_VERSION "3.0.0"
 
 
-#define LWS_LIBRARY_VERSION_MAJOR 2
-#define LWS_LIBRARY_VERSION_MINOR 4
-#define LWS_LIBRARY_VERSION_PATCH 2
+#define LWS_LIBRARY_VERSION_MAJOR 3
+#define LWS_LIBRARY_VERSION_MINOR 0
+#define LWS_LIBRARY_VERSION_PATCH 0
 /* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
 /* 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
 #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 */
 /* The current git commit hash that we're building from */
-#define LWS_BUILD_HASH "8964ce9db75a98e463dfafd2e89f2bc8a95ec6ed"
+#define LWS_BUILD_HASH "v2.0.0-948-geaa935a8"
 
 
-/* Build with OpenSSL support */
+/* Build with OpenSSL support ... alias of LWS_WITH_TLS for compatibility*/
 #define LWS_OPENSSL_SUPPORT
 #define LWS_OPENSSL_SUPPORT
+#define LWS_WITH_TLS
 
 
 /* The client should load and trust CA root certs it finds in the OS */
 /* The client should load and trust CA root certs it finds in the OS */
 /* #undef LWS_SSL_CLIENT_USE_OS_CA_CERTS */
 /* #undef LWS_SSL_CLIENT_USE_OS_CA_CERTS */
@@ -53,7 +59,13 @@
 /* #undef LWS_OPENSSL_CLIENT_CERTS "../share" */
 /* #undef LWS_OPENSSL_CLIENT_CERTS "../share" */
 
 
 /* Turn off websocket extensions */
 /* Turn off websocket extensions */
-/* #undef LWS_NO_EXTENSIONS */
+#define LWS_WITHOUT_EXTENSIONS
+
+/* notice if client or server gone */
+/* #undef LWS_WITHOUT_SERVER */
+/* #undef LWS_WITHOUT_CLIENT */
+
+#define LWS_WITH_POLL
 
 
 /* Enable libev io loop */
 /* Enable libev io loop */
 /* #undef LWS_WITH_LIBEV */
 /* #undef LWS_WITH_LIBEV */
@@ -99,8 +111,11 @@
 /* #undef LWS_HAVE_SSL_CTX_set1_param */
 /* #undef LWS_HAVE_SSL_CTX_set1_param */
 #define LWS_HAVE_X509_VERIFY_PARAM_set1_host
 #define LWS_HAVE_X509_VERIFY_PARAM_set1_host
 /* #undef LWS_HAVE_RSA_SET0_KEY */
 /* #undef LWS_HAVE_RSA_SET0_KEY */
+/* #undef LWS_HAVE_X509_get_key_usage */
+/* #undef LWS_HAVE_SSL_CTX_get0_certificate */
 
 
 /* #undef LWS_HAVE_UV_VERSION_H */
 /* #undef LWS_HAVE_UV_VERSION_H */
+/* #undef LWS_HAVE_PTHREAD_H */
 
 
 /* CGI apis */
 /* CGI apis */
 /* #undef LWS_WITH_CGI */
 /* #undef LWS_WITH_CGI */
@@ -112,7 +127,7 @@
 /* #undef LWS_WITH_HTTP_PROXY */
 /* #undef LWS_WITH_HTTP_PROXY */
 
 
 /* HTTP Ranges support */
 /* HTTP Ranges support */
-#define LWS_WITH_RANGES
+/* #undef LWS_WITH_RANGES */
 
 
 /* Http access log support */
 /* Http access log support */
 /* #undef LWS_WITH_ACCESS_LOG */
 /* #undef LWS_WITH_ACCESS_LOG */
@@ -134,7 +149,7 @@
 /* #undef LWS_PLAT_OPTEE */
 /* #undef LWS_PLAT_OPTEE */
 
 
 /* ZIP FOPS */
 /* ZIP FOPS */
-#define LWS_WITH_ZIP_FOPS
+/* #undef LWS_WITH_ZIP_FOPS */
 #define LWS_HAVE_STDINT_H
 #define LWS_HAVE_STDINT_H
 
 
 /* #undef LWS_AVOID_SIGPIPE_IGN */
 /* #undef LWS_AVOID_SIGPIPE_IGN */
@@ -151,11 +166,26 @@
 /* #undef LWS_HAVE__ATOI64 */
 /* #undef LWS_HAVE__ATOI64 */
 /* #undef LWS_HAVE__STAT32I64 */
 /* #undef LWS_HAVE__STAT32I64 */
 
 
+/* #undef LWS_WITH_JWS */
+/* #undef LWS_WITH_ACME */
+/* #undef LWS_WITH_SELFTESTS */
+
+#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
+#define LWS_HAVE_MALLOC_H
+#endif
+
+#if !defined(IPHONE_ENABLED) && !defined(OSX_ENABLED)
+#define LWS_HAVE_PIPE2
+#endif
+
 /* OpenSSL various APIs */
 /* OpenSSL various APIs */
 
 
 #define LWS_HAVE_TLS_CLIENT_METHOD
 #define LWS_HAVE_TLS_CLIENT_METHOD
 /* #undef LWS_HAVE_TLSV1_2_CLIENT_METHOD */
 /* #undef LWS_HAVE_TLSV1_2_CLIENT_METHOD */
 /* #undef LWS_HAVE_SSL_SET_INFO_CALLBACK */
 /* #undef LWS_HAVE_SSL_SET_INFO_CALLBACK */
+/* #undef LWS_HAVE_SSL_EXTRA_CHAIN_CERTS */
+/* #undef LWS_HAVE_SSL_get0_alpn_selected */
+/* #undef LWS_HAVE_SSL_set_alpn_protos */
 
 
 #define LWS_HAS_INTPTR_T
 #define LWS_HAS_INTPTR_T
 
 

+ 2 - 0
thirdparty/libwebsockets/lws_config_private.h

@@ -100,6 +100,8 @@
 /* Define to 1 if you have the <unistd.h> header file. */
 /* Define to 1 if you have the <unistd.h> header file. */
 #define LWS_HAVE_UNISTD_H
 #define LWS_HAVE_UNISTD_H
 
 
+#define LWS_HAVE_TCP_USER_TIMEOUT
+
 /* Define to 1 if you have the `vfork' function. */
 /* Define to 1 if you have the `vfork' function. */
 #define LWS_HAVE_VFORK
 #define LWS_HAVE_VFORK
 
 

+ 0 - 74
thirdparty/libwebsockets/mbedtls_verify.diff

@@ -1,74 +0,0 @@
-diff --git a/thirdparty/libwebsockets/client/ssl-client.c b/thirdparty/libwebsockets/client/ssl-client.c
-index 6626e0844..962c6e3cb 100644
---- a/thirdparty/libwebsockets/client/ssl-client.c
-+++ b/thirdparty/libwebsockets/client/ssl-client.c
-@@ -176,11 +176,7 @@ lws_ssl_client_bio_create(struct lws *wsi)
- #endif
- #else
- #if defined(LWS_WITH_MBEDTLS)
--	if (wsi->vhost->x509_client_CA)
--		SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
--	else
--		SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback);
--
-+	SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
- #else
- #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
- 	SSL_set_tlsext_host_name(wsi->ssl, hostname);
-diff --git a/thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_pm.c b/thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_pm.c
-index 63504919c..4e3d61109 100644
---- a/thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_pm.c
-+++ b/thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_pm.c
-@@ -218,7 +218,7 @@ static int ssl_pm_reload_crt(SSL *ssl)
-     struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm;
- 
-     if (ssl->verify_mode == SSL_VERIFY_PEER)
--        mode = MBEDTLS_SSL_VERIFY_REQUIRED;
-+        mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
-     else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
-         mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
-     else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE)
-@@ -712,11 +712,39 @@ long ssl_pm_get_verify_result(const SSL *ssl)
-     struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
- 
-     ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
--    if (ret) {
--        SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret);
-+
-+    if (!ret)
-+            return X509_V_OK;
-+
-+    if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
-+        (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
-+        // Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification
-+        verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
-+
-+    else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
-+        verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
-+
-+    else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
-+        (ret & MBEDTLS_X509_BADCRL_BAD_KEY))
-+        verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
-+
-+    else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
-+        (ret & MBEDTLS_X509_BADCRL_BAD_MD))
-+        verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
-+
-+    else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
-+        (ret & MBEDTLS_X509_BADCRL_FUTURE))
-+        verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
-+
-+    else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
-+        (ret & MBEDTLS_X509_BADCRL_EXPIRED))
-+        verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
-+
-+    else
-         verify_result = X509_V_ERR_UNSPECIFIED;
--    } else
--        verify_result = X509_V_OK;
-+
-+    SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
-+              "mbedtls_ssl_get_verify_result() return 0x%x", ret);
- 
-     return verify_result;
- }

+ 46 - 10
thirdparty/libwebsockets/misc/base64-decode.c

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

+ 1 - 1
thirdparty/libwebsockets/misc/getifaddrs.c

@@ -43,7 +43,7 @@
 #include <string.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
 #include <unistd.h>
-#include "private-libwebsockets.h"
+#include "core/private.h"
 
 
 #ifdef LWS_HAVE_SYS_SOCKIO_H
 #ifdef LWS_HAVE_SYS_SOCKIO_H
 #include <sys/sockio.h>
 #include <sys/sockio.h>

+ 48 - 11
thirdparty/libwebsockets/misc/lejp.c

@@ -1,14 +1,26 @@
 /*
 /*
  * Lightweight Embedded JSON Parser
  * Lightweight Embedded JSON Parser
  *
  *
- * Copyright (C) 2013 Andy Green <[email protected]>
- * This code is licensed under LGPL 2.1
- * http://www.gnu.org/licenses/lgpl-2.1.html
+ * Copyright (C) 2013-2017 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
  */
  */
 
 
+#include <libwebsockets.h>
 #include <string.h>
 #include <string.h>
-#include "lejp.h"
-
 #include <stdio.h>
 #include <stdio.h>
 
 
 /**
 /**
@@ -148,7 +160,8 @@ lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len)
 
 
 	n = ctx->wild[wildcard];
 	n = ctx->wild[wildcard];
 
 
-	while (--len && n < ctx->ppos && (n == ctx->wild[wildcard] || ctx->path[n] != '.'))
+	while (--len && n < ctx->ppos &&
+	       (n == ctx->wild[wildcard] || ctx->path[n] != '.'))
 		*dest++ = ctx->path[n++];
 		*dest++ = ctx->path[n++];
 
 
 	*dest = '\0';
 	*dest = '\0';
@@ -186,7 +199,6 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
 
 
 	while (len--) {
 	while (len--) {
 		c = *json++;
 		c = *json++;
-
 		s = ctx->st[ctx->sp].s;
 		s = ctx->st[ctx->sp].s;
 
 
 		/* skip whitespace unless we should care */
 		/* skip whitespace unless we should care */
@@ -411,6 +423,26 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
 				}
 				}
 				goto add_stack_level;
 				goto add_stack_level;
 
 
+			case ']':
+				/* pop */
+				ctx->sp--;
+				if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) {
+					ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY;
+					goto reject;
+				}
+				/* drop the path [n] bit */
+				ctx->ppos = ctx->st[ctx->sp - 1].p;
+				ctx->ipos = ctx->st[ctx->sp - 1].i;
+				ctx->path[ctx->ppos] = '\0';
+				if (ctx->path_match &&
+					       ctx->ppos <= ctx->path_match_len)
+					/*
+					 * we shrank the path to be
+					 * smaller than the matching point
+					 */
+					ctx->path_match = 0;
+				goto array_end;
+
 			case 't': /* true */
 			case 't': /* true */
 				ctx->uni = 0;
 				ctx->uni = 0;
 				ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
 				ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
@@ -582,8 +614,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
 					goto reject;
 					goto reject;
 				}
 				}
 				/* drop the path [n] bit */
 				/* drop the path [n] bit */
-				ctx->ppos = ctx->st[ctx->sp - 1].p;
-				ctx->ipos = ctx->st[ctx->sp - 1].i;
+				if (ctx->sp) {
+					ctx->ppos = ctx->st[ctx->sp - 1].p;
+					ctx->ipos = ctx->st[ctx->sp - 1].i;
+				}
 				ctx->path[ctx->ppos] = '\0';
 				ctx->path[ctx->ppos] = '\0';
 				if (ctx->path_match &&
 				if (ctx->path_match &&
 					       ctx->ppos <= ctx->path_match_len)
 					       ctx->ppos <= ctx->path_match_len)
@@ -609,8 +643,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
 				}
 				}
 				/* pop */
 				/* pop */
 				ctx->sp--;
 				ctx->sp--;
-				ctx->ppos = ctx->st[ctx->sp - 1].p;
-				ctx->ipos = ctx->st[ctx->sp - 1].i;
+				if (ctx->sp) {
+					ctx->ppos = ctx->st[ctx->sp - 1].p;
+					ctx->ipos = ctx->st[ctx->sp - 1].i;
+				}
 				ctx->path[ctx->ppos] = '\0';
 				ctx->path[ctx->ppos] = '\0';
 				if (ctx->path_match &&
 				if (ctx->path_match &&
 					       ctx->ppos <= ctx->path_match_len)
 					       ctx->ppos <= ctx->path_match_len)
@@ -631,6 +667,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
 			goto reject;
 			goto reject;
 
 
 		case LEJP_MP_ARRAY_END:
 		case LEJP_MP_ARRAY_END:
+array_end:
 			ctx->path[ctx->ppos] = '\0';
 			ctx->path[ctx->ppos] = '\0';
 			if (c == ',') {
 			if (c == ',') {
 				/* increment this stack level's index */
 				/* increment this stack level's index */

+ 0 - 232
thirdparty/libwebsockets/misc/lejp.h

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

+ 1 - 1
thirdparty/libwebsockets/misc/sha-1.c

@@ -32,7 +32,7 @@
  * implemented by Jun-ichiro itojun Itoh <[email protected]>
  * implemented by Jun-ichiro itojun Itoh <[email protected]>
  */
  */
 
 
-#include "private-libwebsockets.h"
+#include "core/private.h"
 
 
 #ifdef LWS_HAVE_SYS_TYPES_H
 #ifdef LWS_HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #include <sys/types.h>

+ 0 - 883
thirdparty/libwebsockets/output.c

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

+ 211 - 92
thirdparty/libwebsockets/plat/lws-plat-unix.c

@@ -19,7 +19,8 @@
  *  MA  02110-1301  USA
  *  MA  02110-1301  USA
  */
  */
 
 
-#include "private-libwebsockets.h"
+#define _GNU_SOURCE
+#include "core/private.h"
 
 
 #include <pwd.h>
 #include <pwd.h>
 #include <grp.h>
 #include <grp.h>
@@ -29,6 +30,56 @@
 #endif
 #endif
 #include <dirent.h>
 #include <dirent.h>
 
 
+int
+lws_plat_socket_offset(void)
+{
+	return 0;
+}
+
+int
+lws_plat_pipe_create(struct lws *wsi)
+{
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+#if defined(LWS_HAVE_PIPE2)
+	return pipe2(pt->dummy_pipe_fds, O_NONBLOCK);
+#else
+	return pipe(pt->dummy_pipe_fds);
+#endif
+}
+
+int
+lws_plat_pipe_signal(struct lws *wsi)
+{
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+	char buf = 0;
+	int n;
+
+	n = write(pt->dummy_pipe_fds[1], &buf, 1);
+
+	return n != 1;
+}
+
+void
+lws_plat_pipe_close(struct lws *wsi)
+{
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+	if (pt->dummy_pipe_fds[0] && pt->dummy_pipe_fds[0] != -1)
+		close(pt->dummy_pipe_fds[0]);
+	if (pt->dummy_pipe_fds[1] && pt->dummy_pipe_fds[1] != -1)
+		close(pt->dummy_pipe_fds[1]);
+
+	pt->dummy_pipe_fds[0] = pt->dummy_pipe_fds[1] = -1;
+}
+
+#ifdef __QNX__
+# include "netinet/tcp_var.h"
+# define TCP_KEEPINTVL TCPCTL_KEEPINTVL
+# define TCP_KEEPIDLE  TCPCTL_KEEPIDLE
+# define TCP_KEEPCNT   TCPCTL_KEEPCNT
+#endif
+
 unsigned long long time_in_microseconds(void)
 unsigned long long time_in_microseconds(void)
 {
 {
 	struct timeval tv;
 	struct timeval tv;
@@ -52,6 +103,10 @@ lws_send_pipe_choked(struct lws *wsi)
 #if defined(LWS_WITH_HTTP2)
 #if defined(LWS_WITH_HTTP2)
 	wsi_eff = lws_get_network_wsi(wsi);
 	wsi_eff = lws_get_network_wsi(wsi);
 #endif
 #endif
+
+	/* the fact we checked implies we avoided back-to-back writes */
+	wsi_eff->could_have_pending = 0;
+
 	/* treat the fact we got a truncated send pending as if we're choked */
 	/* treat the fact we got a truncated send pending as if we're choked */
 	if (wsi_eff->trunc_len)
 	if (wsi_eff->trunc_len)
 		return 1;
 		return 1;
@@ -77,29 +132,6 @@ lws_poll_listen_fd(struct lws_pollfd *fd)
 	return poll(fd, 1, 0);
 	return poll(fd, 1, 0);
 }
 }
 
 
-LWS_VISIBLE void
-lws_cancel_service_pt(struct lws *wsi)
-{
-	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-	char buf = 0;
-
-	if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
-		lwsl_err("Cannot write to dummy pipe");
-}
-
-LWS_VISIBLE void
-lws_cancel_service(struct lws_context *context)
-{
-	struct lws_context_per_thread *pt = &context->pt[0];
-	char buf = 0, m = context->count_threads;
-
-	while (m--) {
-		if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
-			lwsl_err("Cannot write to dummy pipe");
-		pt++;
-	}
-}
-
 LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
 LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
 {
 {
 	int syslog_level = LOG_DEBUG;
 	int syslog_level = LOG_DEBUG;
@@ -124,9 +156,10 @@ LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
 LWS_VISIBLE LWS_EXTERN int
 LWS_VISIBLE LWS_EXTERN int
 _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 {
 {
+	volatile struct lws_foreign_thread_pollfd *ftp, *next;
+	volatile struct lws_context_per_thread *vpt;
 	struct lws_context_per_thread *pt;
 	struct lws_context_per_thread *pt;
 	int n = -1, m, c;
 	int n = -1, m, c;
-	char buf;
 
 
 	/* stay dead once we are dead */
 	/* stay dead once we are dead */
 
 
@@ -134,15 +167,15 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 		return 1;
 		return 1;
 
 
 	pt = &context->pt[tsi];
 	pt = &context->pt[tsi];
+	vpt = (volatile struct lws_context_per_thread *)pt;
 
 
 	lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1);
 	lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1);
 
 
 	if (timeout_ms < 0)
 	if (timeout_ms < 0)
 		goto faked_service;
 		goto faked_service;
 
 
-	lws_libev_run(context, tsi);
-	lws_libuv_run(context, tsi);
-	lws_libevent_run(context, tsi);
+	if (context->event_loop_ops->run_pt)
+		context->event_loop_ops->run_pt(context, tsi);
 
 
 	if (!context->service_tid_detected) {
 	if (!context->service_tid_detected) {
 		struct lws _lws;
 		struct lws _lws;
@@ -169,15 +202,73 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 			timeout_ms = 0;
 			timeout_ms = 0;
 	}
 	}
 
 
+	if (timeout_ms) {
+		lws_pt_lock(pt, __func__);
+		/* don't stay in poll wait longer than next hr timeout */
+		lws_usec_t t =  __lws_hrtimer_service(pt);
+		if ((lws_usec_t)timeout_ms * 1000 > t)
+			timeout_ms = t / 1000;
+		lws_pt_unlock(pt);
+	}
+
+	vpt->inside_poll = 1;
+	lws_memory_barrier();
 	n = poll(pt->fds, pt->fds_count, timeout_ms);
 	n = poll(pt->fds, pt->fds_count, timeout_ms);
+	vpt->inside_poll = 0;
+	lws_memory_barrier();
 
 
-#ifdef LWS_OPENSSL_SUPPORT
-	if (!n && !pt->rx_draining_ext_list &&
-	    !lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) {
-#else
-	if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
+	/* Collision will be rare and brief.  Just spin until it completes */
+	while (vpt->foreign_spinlock)
+		;
+
+	/*
+	 * At this point we are not inside a foreign thread pollfd change,
+	 * and we have marked ourselves as outside the poll() wait.  So we
+	 * are the only guys that can modify the lws_foreign_thread_pollfd
+	 * list on the pt.  Drain the list and apply the changes to the
+	 * affected pollfds in the correct order.
+	 */
+
+	lws_pt_lock(pt, __func__);
+
+	ftp = vpt->foreign_pfd_list;
+	//lwsl_notice("cleared list %p\n", ftp);
+	while (ftp) {
+		struct lws *wsi;
+		struct lws_pollfd *pfd;
+
+		next = ftp->next;
+		pfd = &vpt->fds[ftp->fd_index];
+		if (lws_socket_is_valid(pfd->fd)) {
+			wsi = wsi_from_fd(context, pfd->fd);
+			if (wsi)
+				__lws_change_pollfd(wsi, ftp->_and, ftp->_or);
+		}
+		lws_free((void *)ftp);
+		ftp = next;
+	}
+	vpt->foreign_pfd_list = NULL;
+	lws_memory_barrier();
+
+	/* we have come out of a poll wait... check the hrtimer list */
+
+	__lws_hrtimer_service(pt);
+
+	lws_pt_unlock(pt);
+
+	m = 0;
+#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
+	m |= !!pt->ws.rx_draining_ext_list;
 #endif
 #endif
+
+	if (pt->context->tls_ops &&
+	    pt->context->tls_ops->fake_POLLIN_for_buffered)
+		m |= pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
+
+	if (!m && !n) { /* nothing to do */
 		lws_service_fd_tsi(context, NULL, tsi);
 		lws_service_fd_tsi(context, NULL, tsi);
+		lws_service_do_ripe_rxflow(pt);
+
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -194,18 +285,12 @@ faked_service:
 			c = n;
 			c = n;
 
 
 	/* any socket with events to service? */
 	/* any socket with events to service? */
-	for (n = 0; n < pt->fds_count && c; n++) {
+	for (n = 0; n < (int)pt->fds_count && c; n++) {
 		if (!pt->fds[n].revents)
 		if (!pt->fds[n].revents)
 			continue;
 			continue;
 
 
 		c--;
 		c--;
 
 
-		if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) {
-			if (read(pt->fds[n].fd, &buf, 1) != 1)
-				lwsl_err("Cannot read from dummy pipe.");
-			continue;
-		}
-
 		m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
 		m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
 		if (m < 0)
 		if (m < 0)
 			return -1;
 			return -1;
@@ -214,6 +299,8 @@ faked_service:
 			n--;
 			n--;
 	}
 	}
 
 
+	lws_service_do_ripe_rxflow(pt);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -262,6 +349,14 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
 		 */
 		 */
 #else
 #else
 		/* set the keepalive conditions we want on it too */
 		/* set the keepalive conditions we want on it too */
+
+#if defined(LWS_HAVE_TCP_USER_TIMEOUT)
+		optval = 1000 * (vhost->ka_time +
+				 (vhost->ka_interval * vhost->ka_probes));
+		if (setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT,
+			       (const void *)&optval, optlen) < 0)
+			return 1;
+#endif
 		optval = vhost->ka_time;
 		optval = vhost->ka_time;
 		if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
 		if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
 			       (const void *)&optval, optlen) < 0)
 			       (const void *)&optval, optlen) < 0)
@@ -292,7 +387,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
 
 
 	/* Disable Nagle */
 	/* Disable Nagle */
 	optval = 1;
 	optval = 1;
-#if defined (__sun)
+#if defined (__sun) || defined(__QNX__)
 	if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
 	if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
 		return 1;
 		return 1;
 #elif !defined(__APPLE__) && \
 #elif !defined(__APPLE__) && \
@@ -317,7 +412,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
 
 
 #if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
 #if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
 static void
 static void
-_lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
+_lws_plat_apply_caps(int mode, const cap_value_t *cv, int count)
 {
 {
 	cap_t caps;
 	cap_t caps;
 
 
@@ -334,7 +429,7 @@ _lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
 #endif
 #endif
 
 
 LWS_VISIBLE void
 LWS_VISIBLE void
-lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
+lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
 {
 {
 #if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
 #if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
 	int n;
 	int n;
@@ -449,8 +544,8 @@ lws_plat_plugins_init(struct lws_context * context, const char * const *d)
 			}
 			}
 			plugin->list = context->plugin_list;
 			plugin->list = context->plugin_list;
 			context->plugin_list = plugin;
 			context->plugin_list = plugin;
-			strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1);
-			plugin->name[sizeof(plugin->name) - 1] = '\0';
+			lws_strncpy(plugin->name, namelist[i]->d_name,
+				    sizeof(plugin->name));
 			plugin->l = l;
 			plugin->l = l;
 			plugin->caps = lcaps;
 			plugin->caps = lcaps;
 			context->plugin_protocol_count += lcaps.count_protocols;
 			context->plugin_protocol_count += lcaps.count_protocols;
@@ -543,9 +638,6 @@ lws_plat_context_early_destroy(struct lws_context *context)
 LWS_VISIBLE void
 LWS_VISIBLE void
 lws_plat_context_late_destroy(struct lws_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
 #ifdef LWS_WITH_PLUGINS
 	if (context->plugin_list)
 	if (context->plugin_list)
 		lws_plat_plugins_destroy(context);
 		lws_plat_plugins_destroy(context);
@@ -554,13 +646,6 @@ lws_plat_context_late_destroy(struct lws_context *context)
 	if (context->lws_lookup)
 	if (context->lws_lookup)
 		lws_free(context->lws_lookup);
 		lws_free(context->lws_lookup);
 
 
-	while (m--) {
-		if (pt->dummy_pipe_fds[0])
-			close(pt->dummy_pipe_fds[0]);
-		if (pt->dummy_pipe_fds[1])
-			close(pt->dummy_pipe_fds[1]);
-		pt++;
-	}
 	if (!context->fd_random)
 	if (!context->fd_random)
 		lwsl_err("ZERO RANDOM FD\n");
 		lwsl_err("ZERO RANDOM FD\n");
 	if (context->fd_random != LWS_INVALID_FILE)
 	if (context->fd_random != LWS_INVALID_FILE)
@@ -573,7 +658,7 @@ LWS_VISIBLE int
 lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
 lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
 		    size_t addrlen)
 		    size_t addrlen)
 {
 {
-	int rc = -1;
+	int rc = LWS_ITOSA_NOT_EXIST;
 
 
 	struct ifaddrs *ifr;
 	struct ifaddrs *ifr;
 	struct ifaddrs *ifc;
 	struct ifaddrs *ifc;
@@ -586,12 +671,19 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
 		if (!ifc->ifa_addr)
 		if (!ifc->ifa_addr)
 			continue;
 			continue;
 
 
-		lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname);
+		lwsl_debug(" interface %s vs %s (fam %d) ipv6 %d\n", ifc->ifa_name, ifname, ifc->ifa_addr->sa_family, ipv6);
 
 
 		if (strcmp(ifc->ifa_name, ifname))
 		if (strcmp(ifc->ifa_name, ifname))
 			continue;
 			continue;
 
 
 		switch (ifc->ifa_addr->sa_family) {
 		switch (ifc->ifa_addr->sa_family) {
+#if defined(AF_PACKET)
+		case AF_PACKET:
+			/* interface exists but is not usable */
+			rc = LWS_ITOSA_NOT_USABLE;
+			continue;
+#endif
+
 		case AF_INET:
 		case AF_INET:
 #ifdef LWS_WITH_IPV6
 #ifdef LWS_WITH_IPV6
 			if (ipv6) {
 			if (ipv6) {
@@ -619,20 +711,20 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
 		default:
 		default:
 			continue;
 			continue;
 		}
 		}
-		rc = 0;
+		rc = LWS_ITOSA_USABLE;
 	}
 	}
 
 
 	freeifaddrs(ifr);
 	freeifaddrs(ifr);
 
 
-	if (rc == -1) {
+	if (rc) {
 		/* check if bind to IP address */
 		/* check if bind to IP address */
 #ifdef LWS_WITH_IPV6
 #ifdef LWS_WITH_IPV6
 		if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
 		if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
-			rc = 0;
+			rc = LWS_ITOSA_USABLE;
 		else
 		else
 #endif
 #endif
 		if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1)
 		if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1)
-			rc = 0;
+			rc = LWS_ITOSA_USABLE;
 	}
 	}
 
 
 	return rc;
 	return rc;
@@ -643,9 +735,8 @@ lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
 {
 {
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 
 
-	lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
-	lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
-	lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
+	if (context->event_loop_ops->io)
+		context->event_loop_ops->io(wsi, LWS_EV_START | LWS_EV_READ);
 
 
 	pt->fds[pt->fds_count++].revents = 0;
 	pt->fds[pt->fds_count++].revents = 0;
 }
 }
@@ -656,9 +747,9 @@ lws_plat_delete_socket_from_fds(struct lws_context *context,
 {
 {
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 
 
-	lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
-	lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
-	lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
+	if (context->event_loop_ops->io)
+		context->event_loop_ops->io(wsi,
+				LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
 
 
 	pt->fds_count--;
 	pt->fds_count--;
 }
 }
@@ -739,7 +830,8 @@ _lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
 {
 {
 	lws_fileofs_t r;
 	lws_fileofs_t r;
 
 
-	if (offset > 0 && offset > fop_fd->len - fop_fd->pos)
+	if (offset > 0 &&
+	    offset > (lws_fileofs_t)fop_fd->len - (lws_fileofs_t)fop_fd->pos)
 		offset = fop_fd->len - fop_fd->pos;
 		offset = fop_fd->len - fop_fd->pos;
 
 
 	if ((lws_fileofs_t)fop_fd->pos + offset < 0)
 	if ((lws_fileofs_t)fop_fd->pos + offset < 0)
@@ -793,13 +885,11 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
 	return 0;
 	return 0;
 }
 }
 
 
-
 LWS_VISIBLE int
 LWS_VISIBLE int
 lws_plat_init(struct lws_context *context,
 lws_plat_init(struct lws_context *context,
-	      struct lws_context_creation_info *info)
+	      const struct lws_context_creation_info *info)
 {
 {
-	struct lws_context_per_thread *pt = &context->pt[0];
-	int n = context->count_threads, fd;
+	int fd;
 
 
 	/* master context has the global fd lookup array */
 	/* master context has the global fd lookup array */
 	context->lws_lookup = lws_zalloc(sizeof(struct lws *) *
 	context->lws_lookup = lws_zalloc(sizeof(struct lws *) *
@@ -821,26 +911,6 @@ lws_plat_init(struct lws_context *context,
 		return 1;
 		return 1;
 	}
 	}
 
 
-	if (!lws_libev_init_fd_table(context) &&
-	    !lws_libuv_init_fd_table(context) &&
-	    !lws_libevent_init_fd_table(context)) {
-		/* otherwise libev/uv/event 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
 #ifdef LWS_WITH_PLUGINS
 	if (info->plugin_dirs)
 	if (info->plugin_dirs)
 		lws_plat_plugins_init(context, info->plugin_dirs);
 		lws_plat_plugins_init(context, info->plugin_dirs);
@@ -848,3 +918,52 @@ lws_plat_init(struct lws_context *context,
 
 
 	return 0;
 	return 0;
 }
 }
+
+LWS_VISIBLE int
+lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
+			int len)
+{
+	int n;
+
+	n = write(fd, buf, len);
+
+	fsync(fd);
+	lseek(fd, 0, SEEK_SET);
+
+	return n != len;
+}
+
+LWS_VISIBLE int
+lws_plat_write_file(const char *filename, void *buf, int len)
+{
+	int m, fd;
+
+	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+
+	if (fd == -1)
+		return 1;
+
+	m = write(fd, buf, len);
+	close(fd);
+
+	return m != len;
+}
+
+LWS_VISIBLE int
+lws_plat_read_file(const char *filename, void *buf, int len)
+{
+	int n, fd = open(filename, O_RDONLY);
+	if (fd == -1)
+		return -1;
+
+	n = read(fd, buf, len);
+	close(fd);
+
+	return n;
+}
+
+LWS_VISIBLE int
+lws_plat_recommended_rsa_bits(void)
+{
+	return 4096;
+}

+ 147 - 63
thirdparty/libwebsockets/plat/lws-plat-win.c

@@ -1,7 +1,34 @@
 #ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
 #ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
 #define _WINSOCK_DEPRECATED_NO_WARNINGS
 #define _WINSOCK_DEPRECATED_NO_WARNINGS
 #endif
 #endif
-#include "private-libwebsockets.h"
+#include "core/private.h"
+
+int
+lws_plat_socket_offset(void)
+{
+	return 0;
+}
+
+int
+lws_plat_pipe_create(struct lws *wsi)
+{
+	return 1;
+}
+
+int
+lws_plat_pipe_signal(struct lws *wsi)
+{
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+	WSASetEvent(pt->events[0]); /* trigger the cancel event */
+
+	return 0;
+}
+
+void
+lws_plat_pipe_close(struct lws *wsi)
+{
+}
 
 
 unsigned long long
 unsigned long long
 time_in_microseconds()
 time_in_microseconds()
@@ -19,9 +46,10 @@ time_in_microseconds()
 #endif
 #endif
 
 
 	/*
 	/*
-	 * As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a
-	 * ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can
-	 * prevent alignment faults on 64-bit Windows).
+	 * As per Windows documentation for FILETIME, copy the resulting
+	 * FILETIME structure to a ULARGE_INTEGER structure using memcpy
+	 * (using memcpy instead of direct assignment can prevent alignment
+	 * faults on 64-bit Windows).
 	 */
 	 */
 	memcpy(&datetime, &filetime, sizeof(datetime));
 	memcpy(&datetime, &filetime, sizeof(datetime));
 
 
@@ -81,7 +109,7 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
 		if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) {
 		if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) {
 			while (n < context->fd_hashtable[h].length) {
 			while (n < context->fd_hashtable[h].length) {
 				context->fd_hashtable[h].wsi[n] =
 				context->fd_hashtable[h].wsi[n] =
-						context->fd_hashtable[h].wsi[n + 1];
+					context->fd_hashtable[h].wsi[n + 1];
 				n++;
 				n++;
 			}
 			}
 			context->fd_hashtable[h].length--;
 			context->fd_hashtable[h].length--;
@@ -94,8 +122,8 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
 	return 1;
 	return 1;
 }
 }
 
 
-LWS_VISIBLE int lws_get_random(struct lws_context *context,
-								 void *buf, int len)
+LWS_VISIBLE int
+lws_get_random(struct lws_context *context, void *buf, int len)
 {
 {
 	int n;
 	int n;
 	char *p = (char *)buf;
 	char *p = (char *)buf;
@@ -106,16 +134,25 @@ LWS_VISIBLE int lws_get_random(struct lws_context *context,
 	return n;
 	return n;
 }
 }
 
 
-LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi)
-{
+LWS_VISIBLE int
+lws_send_pipe_choked(struct lws *wsi)
+{	struct lws *wsi_eff = wsi;
+
+#if defined(LWS_WITH_HTTP2)
+	wsi_eff = lws_get_network_wsi(wsi);
+#endif
+	/* the fact we checked implies we avoided back-to-back writes */
+	wsi_eff->could_have_pending = 0;
+
 	/* treat the fact we got a truncated send pending as if we're choked */
 	/* treat the fact we got a truncated send pending as if we're choked */
-	if (wsi->trunc_len)
+	if (wsi_eff->trunc_len)
 		return 1;
 		return 1;
 
 
-	return (int)wsi->sock_send_blocking;
+	return (int)wsi_eff->sock_send_blocking;
 }
 }
 
 
-LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
+LWS_VISIBLE int
+lws_poll_listen_fd(struct lws_pollfd *fd)
 {
 {
 	fd_set readfds;
 	fd_set readfds;
 	struct timeval tv = { 0, 0 };
 	struct timeval tv = { 0, 0 };
@@ -125,29 +162,11 @@ LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
 	FD_ZERO(&readfds);
 	FD_ZERO(&readfds);
 	FD_SET(fd->fd, &readfds);
 	FD_SET(fd->fd, &readfds);
 
 
-	return select(fd->fd + 1, &readfds, NULL, NULL, &tv);
+	return select(((int)fd->fd) + 1, &readfds, NULL, NULL, &tv);
 }
 }
 
 
 LWS_VISIBLE void
 LWS_VISIBLE void
-lws_cancel_service(struct lws_context *context)
-{
-	struct lws_context_per_thread *pt = &context->pt[0];
-	int n = context->count_threads;
-
-	while (n--) {
-		WSASetEvent(pt->events[0]);
-		pt++;
-	}
-}
-
-LWS_VISIBLE void
-lws_cancel_service_pt(struct lws *wsi)
-{
-	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-	WSASetEvent(pt->events[0]);
-}
-
-LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
+lwsl_emit_syslog(int level, const char *line)
 {
 {
 	lwsl_emit_stderr(level, line);
 	lwsl_emit_stderr(level, line);
 }
 }
@@ -182,9 +201,8 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 		context->service_tid_detected = 1;
 		context->service_tid_detected = 1;
 	}
 	}
 
 
-	if (timeout_ms < 0)
-	{
-			if (lws_service_flag_pending(context, tsi)) {
+	if (timeout_ms < 0) {
+		if (lws_service_flag_pending(context, tsi)) {
 			/* any socket with events to service? */
 			/* any socket with events to service? */
 			for (n = 0; n < (int)pt->fds_count; n++) {
 			for (n = 0; n < (int)pt->fds_count; n++) {
 				if (!pt->fds[n].revents)
 				if (!pt->fds[n].revents)
@@ -201,6 +219,9 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 		return 0;
 		return 0;
 	}
 	}
 
 
+	if (context->event_loop_ops->run_pt)
+		context->event_loop_ops->run_pt(context, tsi);
+
 	for (i = 0; i < pt->fds_count; ++i) {
 	for (i = 0; i < pt->fds_count; ++i) {
 		pfd = &pt->fds[i];
 		pfd = &pt->fds[i];
 
 
@@ -220,6 +241,9 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 		if (n)
 		if (n)
 			i--;
 			i--;
 
 
+		/*
+		 * any wsi has truncated, force him signalled
+		 */
 		if (wsi->trunc_len)
 		if (wsi->trunc_len)
 			WSASetEvent(pt->events[0]);
 			WSASetEvent(pt->events[0]);
 	}
 	}
@@ -236,29 +260,44 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 			timeout_ms = 0;
 			timeout_ms = 0;
 	}
 	}
 
 
-	ev = WSAWaitForMultipleEvents( 1,  pt->events , FALSE, timeout_ms, FALSE);
+	if (timeout_ms) {
+		lws_pt_lock(pt, __func__);
+		/* don't stay in poll wait longer than next hr timeout */
+		lws_usec_t t =  __lws_hrtimer_service(pt);
+
+		if ((lws_usec_t)timeout_ms * 1000 > t)
+			timeout_ms = (int)(t / 1000);
+		lws_pt_unlock(pt);
+	}
+
+	ev = WSAWaitForMultipleEvents(1, pt->events, FALSE, timeout_ms, FALSE);
 	if (ev == WSA_WAIT_EVENT_0) {
 	if (ev == WSA_WAIT_EVENT_0) {
-		unsigned int eIdx;
+		unsigned int eIdx, err;
 
 
 		WSAResetEvent(pt->events[0]);
 		WSAResetEvent(pt->events[0]);
 
 
+		if (pt->context->tls_ops &&
+		    pt->context->tls_ops->fake_POLLIN_for_buffered)
+			pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
+
 		for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) {
 		for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) {
-			if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0, &networkevents) == SOCKET_ERROR) {
-				lwsl_err("WSAEnumNetworkEvents() failed with error %d\n", LWS_ERRNO);
+			if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0,
+					&networkevents) == SOCKET_ERROR) {
+				lwsl_err("WSAEnumNetworkEvents() failed "
+					 "with error %d\n", LWS_ERRNO);
 				return -1;
 				return -1;
 			}
 			}
 
 
 			pfd = &pt->fds[eIdx];
 			pfd = &pt->fds[eIdx];
 			pfd->revents = (short)networkevents.lNetworkEvents;
 			pfd->revents = (short)networkevents.lNetworkEvents;
 
 
+			err = networkevents.iErrorCode[FD_CONNECT_BIT];
+
 			if ((networkevents.lNetworkEvents & FD_CONNECT) &&
 			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]);
+			     err && err != LWS_EALREADY &&
+			     err != LWS_EINPROGRESS && err != LWS_EWOULDBLOCK &&
+			     err != WSAEINVAL) {
+				lwsl_debug("Unable to connect errno=%d\n", err);
 				pfd->revents |= LWS_POLLHUP;
 				pfd->revents |= LWS_POLLHUP;
 			}
 			}
 
 
@@ -269,21 +308,19 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
 			}
 			}
 			 /* if something closed, retry this slot */
 			 /* if something closed, retry this slot */
 			if (pfd->revents & LWS_POLLHUP)
 			if (pfd->revents & LWS_POLLHUP)
-					--eIdx;
+				--eIdx;
 
 
-			if( pfd->revents != 0 ) {
+			if (pfd->revents)
 				lws_service_fd_tsi(context, pfd, tsi);
 				lws_service_fd_tsi(context, pfd, tsi);
-
-			}
 		}
 		}
 	}
 	}
 
 
 	context->service_tid = 0;
 	context->service_tid = 0;
 
 
-	if (ev == WSA_WAIT_TIMEOUT) {
+	if (ev == WSA_WAIT_TIMEOUT)
 		lws_service_fd(context, NULL);
 		lws_service_fd(context, NULL);
-	}
-	return 0;;
+
+	return 0;
 }
 }
 
 
 LWS_VISIBLE int
 LWS_VISIBLE int
@@ -309,7 +346,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
 		/* enable keepalive on this socket */
 		/* enable keepalive on this socket */
 		optval = 1;
 		optval = 1;
 		if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
 		if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
-						 (const char *)&optval, optlen) < 0)
+			       (const char *)&optval, optlen) < 0)
 			return 1;
 			return 1;
 
 
 		alive.onoff = TRUE;
 		alive.onoff = TRUE;
@@ -317,7 +354,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
 		alive.keepaliveinterval = vhost->ka_interval;
 		alive.keepaliveinterval = vhost->ka_interval;
 
 
 		if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
 		if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
-						  NULL, 0, &dwBytesRet, NULL, NULL))
+			     NULL, 0, &dwBytesRet, NULL, NULL))
 			return 1;
 			return 1;
 	}
 	}
 
 
@@ -343,7 +380,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
 }
 }
 
 
 LWS_VISIBLE void
 LWS_VISIBLE void
-lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
+lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
 {
 {
 }
 }
 
 
@@ -406,7 +443,7 @@ lws_interface_to_sa(int ipv6,
 
 
 	if (ipv6) {
 	if (ipv6) {
 		if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) {
 		if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) {
-			return 0;
+			return LWS_ITOSA_USABLE;
 		}
 		}
 	}
 	}
 #endif
 #endif
@@ -420,11 +457,11 @@ lws_interface_to_sa(int ipv6,
 	}
 	}
 
 
 	if (address == INADDR_NONE)
 	if (address == INADDR_NONE)
-		return -1;
+		return LWS_ITOSA_NOT_EXIST;
 
 
-	addr->sin_addr.s_addr = (lws_intptr_t)address;
+	addr->sin_addr.s_addr = (unsigned long)(lws_intptr_t)address;
 
 
-	return 0;
+	return LWS_ITOSA_USABLE;
 }
 }
 
 
 LWS_VISIBLE void
 LWS_VISIBLE void
@@ -542,7 +579,7 @@ LWS_VISIBLE int
 lws_plat_inet_pton(int af, const char *src, void *dst)
 lws_plat_inet_pton(int af, const char *src, void *dst)
 {
 {
 	WCHAR *buffer;
 	WCHAR *buffer;
-	DWORD bufferlen = strlen(src) + 1;
+	DWORD bufferlen = (int)strlen(src) + 1;
 	BOOL ok = FALSE;
 	BOOL ok = FALSE;
 
 
 	buffer = lws_malloc(bufferlen * 2, "inet_pton");
 	buffer = lws_malloc(bufferlen * 2, "inet_pton");
@@ -692,7 +729,7 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
 
 
 LWS_VISIBLE int
 LWS_VISIBLE int
 lws_plat_init(struct lws_context *context,
 lws_plat_init(struct lws_context *context,
-		  struct lws_context_creation_info *info)
+	      const struct lws_context_creation_info *info)
 {
 {
 	struct lws_context_per_thread *pt = &context->pt[0];
 	struct lws_context_per_thread *pt = &context->pt[0];
 	int i, n = context->count_threads;
 	int i, n = context->count_threads;
@@ -715,7 +752,7 @@ lws_plat_init(struct lws_context *context,
 		}
 		}
 
 
 		pt->fds_count = 0;
 		pt->fds_count = 0;
-		pt->events[0] = WSACreateEvent();
+		pt->events[0] = WSACreateEvent(); /* the cancel event */
 
 
 		pt++;
 		pt++;
 	}
 	}
@@ -743,3 +780,50 @@ int fork(void)
 	exit(0);
 	exit(0);
 }
 }
 
 
+LWS_VISIBLE int
+lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
+			int len)
+{
+	int n;
+
+	n = write(fd, buf, len);
+
+	lseek(fd, 0, SEEK_SET);
+
+	return n != len;
+}
+
+LWS_VISIBLE int
+lws_plat_write_file(const char *filename, void *buf, int len)
+{
+	int m, fd;
+
+	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+
+	if (fd == -1)
+		return -1;
+
+	m = write(fd, buf, len);
+	close(fd);
+
+	return m != len;
+}
+
+LWS_VISIBLE int
+lws_plat_read_file(const char *filename, void *buf, int len)
+{
+	int n, fd = open(filename, O_RDONLY);
+	if (fd == -1)
+		return -1;
+
+	n = read(fd, buf, len);
+	close(fd);
+
+	return n;
+}
+
+LWS_VISIBLE int
+lws_plat_recommended_rsa_bits(void)
+{
+	return 4096;
+}

+ 0 - 2615
thirdparty/libwebsockets/private-libwebsockets.h

@@ -1,2615 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010 - 2016 Andy Green <[email protected]>
- *
- *  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 "lws_config.h"
-#include "lws_config_private.h"
-
-
-#if defined(LWS_WITH_CGI) && defined(LWS_HAVE_VFORK)
-#define  _GNU_SOURCE
-#endif
-
-#if defined(__COVERITY__)
-typedef struct { long double x, y; } _Float128;
-#endif
-
-#ifdef LWS_HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <ctype.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <inttypes.h>
-
-#if defined(LWS_WITH_ESP32)
-#define MSG_NOSIGNAL 0
-#define SOMAXCONN 3
-#endif
-
-#if defined(LWS_WITH_ESP8266)
-#include <user_interface.h>
-#define assert(n)
-
-/* rom-provided stdc functions for free, ensure use these instead of libc ones */
-
-int ets_vsprintf(char *str, const char *format, va_list argptr);
-int ets_vsnprintf(char *buffer, size_t sizeOfBuffer,  const char *format, va_list argptr);
-int ets_snprintf(char *str, size_t size, const char *format, ...);
-int ets_sprintf(char *str, const char *format, ...);
-int os_printf_plus(const char *format, ...);
-#undef malloc
-#undef realloc
-#undef free
-void *pvPortMalloc(size_t s, const char *f, int line);
-#define malloc(s) pvPortMalloc(s, "", 0)
-void *pvPortRealloc(void *p, size_t s, const char *f, int line);
-#define realloc(p, s) pvPortRealloc(p, s, "", 0)
-void vPortFree(void *p, const char *f, int line);
-#define free(p) vPortFree(p, "", 0)
-#undef memcpy
-void *ets_memcpy(void *dest, const void *src, size_t n);
-#define memcpy ets_memcpy
-void *ets_memset(void *dest, int v, size_t n);
-#define memset ets_memset
-char *ets_strcpy(char *dest, const char *src);
-#define strcpy ets_strcpy
-char *ets_strncpy(char *dest, const char *src, size_t n);
-#define strncpy ets_strncpy
-char *ets_strstr(const char *haystack, const char *needle);
-#define strstr ets_strstr
-int ets_strcmp(const char *s1, const char *s2);
-int ets_strncmp(const char *s1, const char *s2, size_t n);
-#define strcmp ets_strcmp
-#define strncmp ets_strncmp
-size_t ets_strlen(const char *s);
-#define strlen ets_strlen
-void *ets_memmove(void *dest, const void *src, size_t n);
-#define memmove ets_memmove
-char *ets_strchr(const char *s, int c);
-#define strchr_ets_strchr
-#undef _DEBUG
-#include <osapi.h>
-
-#else
-#define STORE_IN_ROM
-#include <assert.h>
-#endif
-#if LWS_MAX_SMP > 1
-#include <pthread.h>
-#endif
-
-#ifdef LWS_HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-
-#if defined(WIN32) || defined(_WIN32)
-
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-
-#if (WINVER < 0x0501)
-#undef WINVER
-#undef _WIN32_WINNT
-#define WINVER 0x0501
-#define _WIN32_WINNT WINVER
-#endif
-#define LWS_NO_DAEMONIZE
-#define LWS_ERRNO WSAGetLastError()
-#define LWS_EAGAIN WSAEWOULDBLOCK
-#define LWS_EALREADY WSAEALREADY
-#define LWS_EINPROGRESS WSAEINPROGRESS
-#define LWS_EINTR WSAEINTR
-#define LWS_EISCONN WSAEISCONN
-#define LWS_EWOULDBLOCK WSAEWOULDBLOCK
-#define MSG_NOSIGNAL 0
-#define SHUT_RDWR SD_BOTH
-#define SOL_TCP IPPROTO_TCP
-#define SHUT_WR SD_SEND
-
-#define compatible_close(fd) closesocket(fd)
-#define lws_set_blocking_send(wsi) wsi->sock_send_blocking = 1
-#define lws_socket_is_valid(x) (!!x)
-#define LWS_SOCK_INVALID 0
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <windows.h>
-#include <tchar.h>
-#ifdef LWS_HAVE_IN6ADDR_H
-#include <in6addr.h>
-#endif
-#include <mstcpip.h>
-#include <io.h>
-
-#if !defined(LWS_HAVE_ATOLL)
-#if defined(LWS_HAVE__ATOI64)
-#define atoll _atoi64
-#else
-#warning No atoll or _atoi64 available, using atoi
-#define atoll atoi
-#endif
-#endif
-
-#ifndef __func__
-#define __func__ __FUNCTION__
-#endif
-
-#ifdef LWS_HAVE__VSNPRINTF
-#define vsnprintf _vsnprintf
-#endif
-
-/* we don't have an implementation for this on windows... */
-int kill(int pid, int sig);
-int fork(void);
-#ifndef SIGINT
-#define SIGINT 2
-#endif
-
-#else /* not windows --> */
-
-#include <fcntl.h>
-#include <strings.h>
-#include <unistd.h>
-#include <sys/types.h>
-
-#ifndef __cplusplus
-#include <errno.h>
-#endif
-#include <netdb.h>
-#include <signal.h>
-#ifdef LWS_WITH_ESP8266
-#include <sockets.h>
-#define vsnprintf ets_vsnprintf
-#define snprintf ets_snprintf
-#define sprintf ets_sprintf
-
-int kill(int pid, int sig);
-
-#else
-#include <sys/socket.h>
-#endif
-#ifdef LWS_WITH_HTTP_PROXY
-#include <hubbub/hubbub.h>
-#include <hubbub/parser.h>
-#endif
-#if defined(LWS_BUILTIN_GETIFADDRS)
- #include "./misc/getifaddrs.h"
-#else
- #if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
- #if defined(__HAIKU__)
-   #define _BSD_SOURCE
- #endif
- #include <ifaddrs.h>
- #endif
-#endif
-#if defined (__ANDROID__)
-#include <syslog.h>
-#include <sys/resource.h>
-#elif defined (__sun) || defined(__HAIKU__)
-#include <syslog.h>
-#else
-#if !defined(LWS_WITH_ESP8266)  && !defined(LWS_WITH_ESP32)
-#include <sys/syslog.h>
-#endif
-#endif
-#include <netdb.h>
-#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
-#include <sys/mman.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <poll.h>
-#endif
-#ifdef LWS_WITH_LIBEV
-#include <ev.h>
-#endif
-#ifdef LWS_WITH_LIBUV
-#include <uv.h>
-#endif
-#ifdef LWS_WITH_LIBEVENT
-#include <event2/event.h>
-#endif
-
-#ifndef LWS_NO_FORK
-#ifdef LWS_HAVE_SYS_PRCTL_H
-#include <sys/prctl.h>
-#endif
-#endif
-
-#include <sys/time.h>
-
-#define LWS_ERRNO errno
-#define LWS_EAGAIN EAGAIN
-#define LWS_EALREADY EALREADY
-#define LWS_EINPROGRESS EINPROGRESS
-#define LWS_EINTR EINTR
-#define LWS_EISCONN EISCONN
-#define LWS_EWOULDBLOCK EWOULDBLOCK
-
-#define lws_set_blocking_send(wsi)
-
-#if defined(LWS_WITH_ESP8266)
-#define lws_socket_is_valid(x) ((x) != NULL)
-#define LWS_SOCK_INVALID (NULL)
-struct lws;
-const char *
-lws_plat_get_peer_simple(struct lws *wsi, char *name, int namelen);
-#else
-#define lws_socket_is_valid(x) (x >= 0)
-#define LWS_SOCK_INVALID (-1)
-#endif
-#endif
-
-#ifndef LWS_HAVE_BZERO
-#ifndef bzero
-#define bzero(b, len) (memset((b), '\0', (len)), (void) 0)
-#endif
-#endif
-
-#ifndef LWS_HAVE_STRERROR
-#define strerror(x) ""
-#endif
-
-#ifdef LWS_OPENSSL_SUPPORT
-
-#ifdef USE_WOLFSSL
-#ifdef USE_OLD_CYASSL
-#include <cyassl/openssl/ssl.h>
-#include <cyassl/error-ssl.h>
-#else
-#include <wolfssl/openssl/ssl.h>
-#include <wolfssl/error-ssl.h>
-#define OPENSSL_NO_TLSEXT
-#endif /* not USE_OLD_CYASSL */
-#else
-#if defined(LWS_WITH_ESP32)
-#define OPENSSL_NO_TLSEXT
-#else
-#if defined(LWS_WITH_MBEDTLS)
-#include <mbedtls/ssl.h>
-#include <mbedtls/x509_crt.h>
-#else
-#include <openssl/ssl.h>
-#include <openssl/evp.h>
-#include <openssl/err.h>
-#include <openssl/md5.h>
-#include <openssl/sha.h>
-#ifdef LWS_HAVE_OPENSSL_ECDH_H
-#include <openssl/ecdh.h>
-#endif
-#include <openssl/x509v3.h>
-#endif
-#if defined(OPENSSL_VERSION_NUMBER)
-#if (OPENSSL_VERSION_NUMBER < 0x0009080afL)
-/* later openssl defines this to negate the presence of tlsext... but it was only
- * introduced at 0.9.8j.  Earlier versions don't know it exists so don't
- * define it... making it look like the feature exists...
- */
-#define OPENSSL_NO_TLSEXT
-#endif
-#endif
-#endif /* not ESP32 */
-#endif /* not USE_WOLFSSL */
-#endif
-
-#include "libwebsockets.h"
-#if defined(WIN32) || defined(_WIN32)
-#else
-static inline int compatible_close(int fd) { return close(fd); }
-#endif
-
-#if defined(WIN32) || defined(_WIN32)
-#include <gettimeofday.h>
-#endif
-
-#if defined(LWS_WITH_ESP8266)
-#undef compatible_close
-#define compatible_close(fd) { fd->state=ESPCONN_CLOSE; espconn_delete(fd); }
-lws_sockfd_type
-esp8266_create_tcp_stream_socket(void);
-void
-esp8266_tcp_stream_bind(lws_sockfd_type fd, int port, struct lws *wsi);
-#ifndef BIG_ENDIAN
-#define BIG_ENDIAN    4321  /* to show byte order (taken from gcc) */
-#endif
-#ifndef LITTLE_ENDIAN
-#define LITTLE_ENDIAN 1234
-#endif
-#ifndef BYTE_ORDER
-#define BYTE_ORDER LITTLE_ENDIAN
-#endif
-#endif
-
-
-#if defined(WIN32) || defined(_WIN32)
-
-#ifndef BIG_ENDIAN
-#define BIG_ENDIAN    4321  /* to show byte order (taken from gcc) */
-#endif
-#ifndef LITTLE_ENDIAN
-#define LITTLE_ENDIAN 1234
-#endif
-#ifndef BYTE_ORDER
-#define BYTE_ORDER LITTLE_ENDIAN
-#endif
-
-#undef __P
-#ifndef __P
-#if __STDC__
-#define __P(protos) protos
-#else
-#define __P(protos) ()
-#endif
-#endif
-
-#else
-
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#if defined(__APPLE__)
-#include <machine/endian.h>
-#elif defined(__FreeBSD__)
-#include <sys/endian.h>
-#elif defined(__linux__)
-#include <endian.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if defined(__QNX__)
-	#include <gulliver.h>
-	#if defined(__LITTLEENDIAN__)
-		#define BYTE_ORDER __LITTLEENDIAN__
-		#define LITTLE_ENDIAN __LITTLEENDIAN__
-		#define BIG_ENDIAN 4321  /* to show byte order (taken from gcc); for suppres warning that BIG_ENDIAN is not defined. */
-	#endif
-	#if defined(__BIGENDIAN__)
-		#define BYTE_ORDER __BIGENDIAN__
-		#define LITTLE_ENDIAN 1234  /* to show byte order (taken from gcc); for suppres warning that LITTLE_ENDIAN is not defined. */
-		#define BIG_ENDIAN __BIGENDIAN__
-	#endif
-#endif
-
-#if defined(__sun) && defined(__GNUC__)
-
-#include <arpa/nameser_compat.h>
-
-#if !defined (BYTE_ORDER)
-# define BYTE_ORDER __BYTE_ORDER__
-#endif
-
-#if !defined(LITTLE_ENDIAN)
-# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
-#endif
-
-#if !defined(BIG_ENDIAN)
-# define BIG_ENDIAN __ORDER_BIG_ENDIAN__
-#endif
-
-#endif /* sun + GNUC */
-
-#if !defined(BYTE_ORDER)
-# define BYTE_ORDER __BYTE_ORDER
-#endif
-#if !defined(LITTLE_ENDIAN)
-# define LITTLE_ENDIAN __LITTLE_ENDIAN
-#endif
-#if !defined(BIG_ENDIAN)
-# define BIG_ENDIAN __BIG_ENDIAN
-#endif
-
-#endif
-
-/*
- * Mac OSX as well as iOS do not define the MSG_NOSIGNAL flag,
- * but happily have something equivalent in the SO_NOSIGPIPE flag.
- */
-#ifdef __APPLE__
-#define MSG_NOSIGNAL SO_NOSIGPIPE
-#endif
-
-/*
- * Solaris 11.X only supports POSIX 2001, MSG_NOSIGNAL appears in
- * POSIX 2008.
- */
-#ifdef __sun
-#define MSG_NOSIGNAL 0
-#endif
-
-#ifdef _WIN32
-#ifndef FD_HASHTABLE_MODULUS
-#define FD_HASHTABLE_MODULUS 32
-#endif
-#endif
-
-#ifndef LWS_DEF_HEADER_LEN
-#define LWS_DEF_HEADER_LEN 4096
-#endif
-#ifndef LWS_DEF_HEADER_POOL
-#define LWS_DEF_HEADER_POOL 4
-#endif
-#ifndef LWS_MAX_PROTOCOLS
-#define LWS_MAX_PROTOCOLS 5
-#endif
-#ifndef LWS_MAX_EXTENSIONS_ACTIVE
-#define LWS_MAX_EXTENSIONS_ACTIVE 2
-#endif
-#ifndef LWS_MAX_EXT_OFFERS
-#define LWS_MAX_EXT_OFFERS 8
-#endif
-#ifndef SPEC_LATEST_SUPPORTED
-#define SPEC_LATEST_SUPPORTED 13
-#endif
-#ifndef AWAITING_TIMEOUT
-#define AWAITING_TIMEOUT 20
-#endif
-#ifndef CIPHERS_LIST_STRING
-#define CIPHERS_LIST_STRING "DEFAULT"
-#endif
-#ifndef LWS_SOMAXCONN
-#define LWS_SOMAXCONN SOMAXCONN
-#endif
-
-#define MAX_WEBSOCKET_04_KEY_LEN 128
-
-#ifndef SYSTEM_RANDOM_FILEPATH
-#define SYSTEM_RANDOM_FILEPATH "/dev/urandom"
-#endif
-
-enum lws_websocket_opcodes_07 {
-	LWSWSOPC_CONTINUATION = 0,
-	LWSWSOPC_TEXT_FRAME = 1,
-	LWSWSOPC_BINARY_FRAME = 2,
-
-	LWSWSOPC_NOSPEC__MUX = 7,
-
-	/* control extensions 8+ */
-
-	LWSWSOPC_CLOSE = 8,
-	LWSWSOPC_PING = 9,
-	LWSWSOPC_PONG = 0xa,
-};
-
-
-enum lws_connection_states {
-	LWSS_HTTP,
-	LWSS_HTTP_ISSUING_FILE,
-	LWSS_HTTP_HEADERS,
-	LWSS_HTTP_BODY,
-	LWSS_DEAD_SOCKET,
-	LWSS_ESTABLISHED,
-	LWSS_CLIENT_HTTP_ESTABLISHED,
-	LWSS_CLIENT_UNCONNECTED,
-	LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION,
-	LWSS_RETURNED_CLOSE_ALREADY,
-	LWSS_AWAITING_CLOSE_ACK,
-	LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE,
-	LWSS_SHUTDOWN,
-
-	LWSS_HTTP2_AWAIT_CLIENT_PREFACE,
-	LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS,
-	LWSS_HTTP2_ESTABLISHED,
-
-	LWSS_CGI,
-};
-
-enum http_version {
-	HTTP_VERSION_1_0,
-	HTTP_VERSION_1_1,
-	HTTP_VERSION_2
-};
-
-enum http_connection_type {
-	HTTP_CONNECTION_CLOSE,
-	HTTP_CONNECTION_KEEP_ALIVE
-};
-
-enum lws_rx_parse_state {
-	LWS_RXPS_NEW,
-
-	LWS_RXPS_04_mask_1,
-	LWS_RXPS_04_mask_2,
-	LWS_RXPS_04_mask_3,
-
-	LWS_RXPS_04_FRAME_HDR_1,
-	LWS_RXPS_04_FRAME_HDR_LEN,
-	LWS_RXPS_04_FRAME_HDR_LEN16_2,
-	LWS_RXPS_04_FRAME_HDR_LEN16_1,
-	LWS_RXPS_04_FRAME_HDR_LEN64_8,
-	LWS_RXPS_04_FRAME_HDR_LEN64_7,
-	LWS_RXPS_04_FRAME_HDR_LEN64_6,
-	LWS_RXPS_04_FRAME_HDR_LEN64_5,
-	LWS_RXPS_04_FRAME_HDR_LEN64_4,
-	LWS_RXPS_04_FRAME_HDR_LEN64_3,
-	LWS_RXPS_04_FRAME_HDR_LEN64_2,
-	LWS_RXPS_04_FRAME_HDR_LEN64_1,
-
-	LWS_RXPS_07_COLLECT_FRAME_KEY_1,
-	LWS_RXPS_07_COLLECT_FRAME_KEY_2,
-	LWS_RXPS_07_COLLECT_FRAME_KEY_3,
-	LWS_RXPS_07_COLLECT_FRAME_KEY_4,
-
-	LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED
-};
-
-#define LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP 32
-
-enum connection_mode {
-	LWSCM_HTTP_SERVING,
-	LWSCM_HTTP_SERVING_ACCEPTED, /* actual HTTP service going on */
-	LWSCM_PRE_WS_SERVING_ACCEPT,
-
-	LWSCM_WS_SERVING,
-	LWSCM_WS_CLIENT,
-
-	LWSCM_HTTP2_SERVING,
-
-	/* transient, ssl delay hiding */
-	LWSCM_SSL_ACK_PENDING,
-	LWSCM_SSL_INIT,
-	/* as above, but complete into LWSCM_RAW */
-	LWSCM_SSL_ACK_PENDING_RAW,
-	LWSCM_SSL_INIT_RAW,
-
-	/* special internal types */
-	LWSCM_SERVER_LISTENER,
-	LWSCM_CGI, /* stdin, stdout, stderr for another cgi master wsi */
-	LWSCM_RAW, /* raw with bulk handling */
-	LWSCM_RAW_FILEDESC, /* raw without bulk handling */
-
-	/* HTTP Client related */
-	LWSCM_HTTP_CLIENT = LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP,
-	LWSCM_HTTP_CLIENT_ACCEPTED, /* actual HTTP service going on */
-	LWSCM_WSCL_WAITING_CONNECT,
-	LWSCM_WSCL_WAITING_PROXY_REPLY,
-	LWSCM_WSCL_ISSUE_HANDSHAKE,
-	LWSCM_WSCL_ISSUE_HANDSHAKE2,
-	LWSCM_WSCL_ISSUE_HTTP_BODY,
-	LWSCM_WSCL_WAITING_SSL,
-	LWSCM_WSCL_WAITING_SERVER_REPLY,
-	LWSCM_WSCL_WAITING_EXTENSION_CONNECT,
-	LWSCM_WSCL_PENDING_CANDIDATE_CHILD,
-	LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY,
-	LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY,
-	LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY,
-
-	/****** add new things just above ---^ ******/
-
-
-};
-
-/* enums of socks version */
-enum socks_version {
-	SOCKS_VERSION_4 = 4,
-	SOCKS_VERSION_5 = 5
-};
-
-/* enums of subnegotiation version */
-enum socks_subnegotiation_version {
-	SOCKS_SUBNEGOTIATION_VERSION_1 = 1,
-};
-
-/* enums of socks commands */
-enum socks_command {
-	SOCKS_COMMAND_CONNECT = 1,
-	SOCKS_COMMAND_BIND = 2,
-	SOCKS_COMMAND_UDP_ASSOCIATE = 3
-};
-
-/* enums of socks address type */
-enum socks_atyp {
-	SOCKS_ATYP_IPV4 = 1,
-	SOCKS_ATYP_DOMAINNAME = 3,
-	SOCKS_ATYP_IPV6 = 4
-};
-
-/* enums of socks authentication methods */
-enum socks_auth_method {
-	SOCKS_AUTH_NO_AUTH = 0,
-	SOCKS_AUTH_GSSAPI = 1,
-	SOCKS_AUTH_USERNAME_PASSWORD = 2
-};
-
-/* enums of subnegotiation status */
-enum socks_subnegotiation_status {
-	SOCKS_SUBNEGOTIATION_STATUS_SUCCESS = 0,
-};
-
-/* enums of socks request reply */
-enum socks_request_reply {
-	SOCKS_REQUEST_REPLY_SUCCESS = 0,
-	SOCKS_REQUEST_REPLY_FAILURE_GENERAL = 1,
-	SOCKS_REQUEST_REPLY_CONNECTION_NOT_ALLOWED = 2,
-	SOCKS_REQUEST_REPLY_NETWORK_UNREACHABLE = 3,
-	SOCKS_REQUEST_REPLY_HOST_UNREACHABLE = 4,
-	SOCKS_REQUEST_REPLY_CONNECTION_REFUSED = 5,
-	SOCKS_REQUEST_REPLY_TTL_EXPIRED = 6,
-	SOCKS_REQUEST_REPLY_COMMAND_NOT_SUPPORTED = 7,
-	SOCKS_REQUEST_REPLY_ATYP_NOT_SUPPORTED = 8
-};
-
-/* enums used to generate socks messages */
-enum socks_msg_type {
-	/* greeting */
-	SOCKS_MSG_GREETING,
-	/* credential, user name and password */
-	SOCKS_MSG_USERNAME_PASSWORD,
-	/* connect command */
-	SOCKS_MSG_CONNECT
-};
-
-enum {
-	LWS_RXFLOW_ALLOW = (1 << 0),
-	LWS_RXFLOW_PENDING_CHANGE = (1 << 1),
-};
-
-struct lws_ring {
-	void *buf;
-	void (*destroy_element)(void *element);
-	size_t buflen;
-	size_t element_len;
-	uint32_t head;
-	uint32_t oldest_tail;
-};
-
-/* this is not usable directly by user code any more, lws_close_reason() */
-#define LWS_WRITE_CLOSE 4
-
-struct lws_protocols;
-struct lws;
-
-#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
-
-struct lws_io_watcher {
-#ifdef LWS_WITH_LIBEV
-	ev_io ev_watcher;
-#endif
-#ifdef LWS_WITH_LIBUV
-	uv_poll_t uv_watcher;
-#endif
-#ifdef LWS_WITH_LIBEVENT
-	struct event *event_watcher;
-#endif
-	struct lws_context *context;
-
-	uint8_t actual_events;
-};
-
-struct lws_signal_watcher {
-#ifdef LWS_WITH_LIBEV
-	ev_signal ev_watcher;
-#endif
-#ifdef LWS_WITH_LIBUV
-	uv_signal_t uv_watcher;
-#endif
-#ifdef LWS_WITH_LIBEVENT
-	struct event *event_watcher;
-#endif
-	struct lws_context *context;
-};
-#endif
-
-#ifdef _WIN32
-#define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS)
-struct lws_fd_hashtable {
-	struct lws **wsi;
-	int length;
-};
-#endif
-
-/*
- * This is totally opaque to code using the library.  It's exported as a
- * forward-reference pointer-only declaration; the user can use the pointer with
- * other APIs to get information out of it.
- */
-
-#if defined(LWS_WITH_ESP32)
-typedef uint16_t ah_data_idx_t;
-#else
-typedef uint32_t ah_data_idx_t;
-#endif
-
-struct lws_fragments {
-	ah_data_idx_t	offset;
-	uint16_t	len;
-	uint8_t		nfrag; /* which ah->frag[] continues this content, or 0 */
-	uint8_t		flags; /* only http2 cares */
-};
-
-/*
- * these are assigned from a pool held in the context.
- * Both client and server mode uses them for http header analysis
- */
-
-struct allocated_headers {
-	struct allocated_headers *next; /* linked list */
-	struct lws *wsi; /* owner */
-	char *data; /* prepared by context init to point to dedicated storage */
-	ah_data_idx_t data_length;
-	/*
-	 * the randomly ordered fragments, indexed by frag_index and
-	 * lws_fragments->nfrag for continuation.
-	 */
-	struct lws_fragments frags[WSI_TOKEN_COUNT];
-	time_t assigned;
-	/*
-	 * for each recognized token, frag_index says which frag[] his data
-	 * starts in (0 means the token did not appear)
-	 * the actual header data gets dumped as it comes in, into data[]
-	 */
-	uint8_t frag_index[WSI_TOKEN_COUNT];
-#if defined(LWS_WITH_ESP32)
-	uint8_t rx[256];
-#else
-	uint8_t rx[2048];
-#endif
-
-	int16_t rxpos;
-	int16_t rxlen;
-	uint32_t pos;
-	uint32_t http_response;
-	int hdr_token_idx;
-
-#ifndef LWS_NO_CLIENT
-	char initial_handshake_hash_base64[30];
-#endif
-
-	uint8_t in_use;
-	uint8_t nfrag;
-};
-
-/*
- * so we can have n connections being serviced simultaneously,
- * these things need to be isolated per-thread.
- */
-
-struct lws_context_per_thread {
-#if LWS_MAX_SMP > 1
-	pthread_mutex_t lock;
-#endif
-	struct lws_pollfd *fds;
-#if defined(LWS_WITH_ESP8266)
-	struct lws **lws_vs_fds_index;
-#endif
-	struct lws *rx_draining_ext_list;
-	struct lws *tx_draining_ext_list;
-	struct lws *timeout_list;
-#if defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
-	struct lws_context *context;
-#endif
-#ifdef LWS_WITH_CGI
-	struct lws_cgi *cgi_list;
-#endif
-	void *http_header_data;
-	struct allocated_headers *ah_list;
-	struct lws *ah_wait_list;
-	int ah_wait_list_length;
-#ifdef LWS_OPENSSL_SUPPORT
-	struct lws *pending_read_list; /* linked list */
-#endif
-#if defined(LWS_WITH_LIBEV)
-	struct ev_loop *io_loop_ev;
-#endif
-#if defined(LWS_WITH_LIBUV)
-	uv_loop_t *io_loop_uv;
-	uv_signal_t signals[8];
-	uv_timer_t uv_timeout_watcher;
-	uv_idle_t uv_idle;
-#endif
-#if defined(LWS_WITH_LIBEVENT)
-	struct event_base *io_loop_event_base;
-#endif
-#if defined(LWS_WITH_LIBEV)
-	struct lws_io_watcher w_accept;
-#endif
-#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
-	struct lws_signal_watcher w_sigint;
-	unsigned char ev_loop_foreign:1;
-#endif
-
-	unsigned long count_conns;
-	/*
-	 * usable by anything in the service code, but only if the scope
-	 * does not last longer than the service action (since next service
-	 * of any socket can likewise use it and overwrite)
-	 */
-	unsigned char *serv_buf;
-#ifdef _WIN32
-	WSAEVENT *events;
-#else
-	lws_sockfd_type dummy_pipe_fds[2];
-#endif
-	unsigned int fds_count;
-	uint32_t ah_pool_length;
-
-	short ah_count_in_use;
-	unsigned char tid;
-	unsigned char lock_depth;
-};
-
-struct lws_conn_stats {
-	unsigned long long rx, tx;
-	unsigned long h1_conn, h1_trans, h2_trans, ws_upg, h2_alpn, h2_subs,
-		      h2_upg, rejected;
-};
-
-void
-lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs);
-
-
-enum lws_h2_settings {
-	H2SET_HEADER_TABLE_SIZE = 1,
-	H2SET_ENABLE_PUSH,
-	H2SET_MAX_CONCURRENT_STREAMS,
-	H2SET_INITIAL_WINDOW_SIZE,
-	H2SET_MAX_FRAME_SIZE,
-	H2SET_MAX_HEADER_LIST_SIZE,
-
-	H2SET_COUNT /* always last */
-};
-
-struct http2_settings {
-	uint32_t s[H2SET_COUNT];
-};
-
-/*
- * virtual host -related context information
- *   vhostwide SSL context
- *   vhostwide proxy
- *
- * hierarchy:
- *
- * context -> vhost -> wsi
- *
- * incoming connection non-SSL vhost binding:
- *
- *    listen socket -> wsi -> select vhost after first headers
- *
- * incoming connection SSL vhost binding:
- *
- *    SSL SNI -> wsi -> bind after SSL negotiation
- */
-
-struct lws_vhost {
-#if !defined(LWS_WITH_ESP8266)
-	char http_proxy_address[128];
-	char proxy_basic_auth_token[128];
-#if defined(LWS_WITH_HTTP2)
-	struct http2_settings set;
-#endif
-#if defined(LWS_WITH_SOCKS5)
-	char socks_proxy_address[128];
-	char socks_user[96];
-	char socks_password[96];
-#endif
-#endif
-#if defined(LWS_WITH_ESP8266)
-	/* listen sockets need a place to hang their hat */
-	esp_tcp tcp;
-#endif
-	struct lws_conn_stats conn_stats;
-	struct lws_context *context;
-	struct lws_vhost *vhost_next;
-	const struct lws_http_mount *mount_list;
-	struct lws *lserv_wsi;
-	const char *name;
-	const char *iface;
-#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32)
-	int bind_iface;
-#endif
-	const struct lws_protocols *protocols;
-	void **protocol_vh_privs;
-	const struct lws_protocol_vhost_options *pvo;
-	const struct lws_protocol_vhost_options *headers;
-	struct lws **same_vh_protocol_list;
-#ifdef LWS_OPENSSL_SUPPORT
-	SSL_CTX *ssl_ctx;
-	SSL_CTX *ssl_client_ctx;
-#endif
-#if defined(LWS_WITH_MBEDTLS)
-	X509 *x509_client_CA;
-#endif
-#ifndef LWS_NO_EXTENSIONS
-	const struct lws_extension *extensions;
-#endif
-	void *user;
-
-	int listen_port;
-	unsigned int http_proxy_port;
-#if defined(LWS_WITH_SOCKS5)
-	unsigned int socks_proxy_port;
-#endif
-	unsigned int options;
-	int count_protocols;
-	int ka_time;
-	int ka_probes;
-	int ka_interval;
-	int keepalive_timeout;
-	int timeout_secs_ah_idle;
-	int ssl_info_event_mask;
-#ifdef LWS_WITH_ACCESS_LOG
-	int log_fd;
-#endif
-
-#ifdef LWS_OPENSSL_SUPPORT
-	int use_ssl;
-	int allow_non_ssl_on_ssl_port;
-	unsigned int user_supplied_ssl_ctx:1;
-#endif
-
-	unsigned int created_vhost_protocols:1;
-	unsigned int being_destroyed:1;
-
-	unsigned char default_protocol_index;
-	unsigned char raw_protocol_index;
-};
-
-struct lws_deferred_free
-{
-	struct lws_deferred_free *next;
-	time_t deadline;
-	void *payload;
-};
-
-typedef union {
-#ifdef LWS_WITH_IPV6
-	struct sockaddr_in6 sa6;
-#endif
-	struct sockaddr_in sa4;
-} sockaddr46;
-
-
-#if defined(LWS_WITH_PEER_LIMITS)
-struct lws_peer {
-	struct lws_peer *next;
-	struct lws_peer *peer_wait_list;
-
-	time_t time_created;
-	time_t time_closed_all;
-
-	uint8_t addr[32];
-	uint32_t hash;
-	uint32_t count_wsi;
-	uint32_t count_ah;
-
-	uint32_t total_wsi;
-	uint32_t total_ah;
-
-	uint8_t af;
-};
-#endif
-
-/*
- * the rest is managed per-context, that includes
- *
- *  - processwide single fd -> wsi lookup
- *  - contextwide headers pool
- */
-
-struct lws_context {
-	time_t last_timeout_check_s;
-	time_t last_ws_ping_pong_check_s;
-	time_t time_up;
-	const struct lws_plat_file_ops *fops;
-	struct lws_plat_file_ops fops_platform;
-#if defined(LWS_WITH_HTTP2)
-	struct http2_settings set;
-#endif
-#if defined(LWS_WITH_ZIP_FOPS)
-	struct lws_plat_file_ops fops_zip;
-#endif
-	struct lws_context_per_thread pt[LWS_MAX_SMP];
-	struct lws_conn_stats conn_stats;
-#if LWS_MAX_SMP > 1
-	pthread_mutex_t lock;
-	int lock_depth;
-#endif
-#ifdef _WIN32
-/* different implementation between unix and windows */
-	struct lws_fd_hashtable fd_hashtable[FD_HASHTABLE_MODULUS];
-#else
-#if defined(LWS_WITH_ESP8266)
-	struct espconn **connpool; /* .reverse points to the wsi */
-	void *rxd;
-	int rxd_len;
-	os_timer_t to_timer;
-#else
-	struct lws **lws_lookup;  /* fd to wsi */
-#endif
-#endif
-	struct lws_vhost *vhost_list;
-	struct lws_vhost *vhost_pending_destruction_list;
-	struct lws_plugin *plugin_list;
-	struct lws_deferred_free *deferred_free_list;
-#if defined(LWS_WITH_PEER_LIMITS)
-	struct lws_peer **pl_hash_table;
-	struct lws_peer *peer_wait_list;
-	time_t next_cull;
-#endif
-
-	void *external_baggage_free_on_destroy;
-	const struct lws_token_limits *token_limits;
-	void *user_space;
-	const char *server_string;
-	const struct lws_protocol_vhost_options *reject_service_keywords;
-	lws_reload_func deprecation_cb;
-
-#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
-	cap_value_t caps[4];
-	char count_caps;
-#endif
-
-#if defined(LWS_WITH_LIBEV)
-	lws_ev_signal_cb_t * lws_ev_sigint_cb;
-#endif
-#if defined(LWS_WITH_LIBUV)
-	uv_signal_cb lws_uv_sigint_cb;
-	uv_loop_t pu_loop;
-#endif
-#if defined(LWS_WITH_LIBEVENT)
-	lws_event_signal_cb_t * lws_event_sigint_cb;
-#endif
-	char canonical_hostname[128];
-#ifdef LWS_LATENCY
-	unsigned long worst_latency;
-	char worst_latency_info[256];
-#endif
-
-#if defined(LWS_WITH_STATS)
-	uint64_t lws_stats[LWSSTATS_SIZE];
-	uint64_t last_dump;
-	int updated;
-#endif
-#if defined(LWS_WITH_ESP32)
-	unsigned long time_last_state_dump;
-	uint32_t last_free_heap;
-#endif
-
-	int max_fds;
-#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
-	int use_ev_sigint;
-#endif
-	int started_with_parent;
-	int uid, gid;
-
-	int fd_random;
-
-	int count_wsi_allocated;
-	int count_cgi_spawned;
-	unsigned int options;
-	unsigned int fd_limit_per_thread;
-	unsigned int timeout_secs;
-	unsigned int pt_serv_buf_size;
-	int max_http_header_data;
-	int simultaneous_ssl_restriction;
-	int simultaneous_ssl;
-#if defined(LWS_WITH_PEER_LIMITS)
-	uint32_t pl_hash_elements;	/* protected by context->lock */
-	uint32_t count_peers;		/* protected by context->lock */
-	unsigned short ip_limit_ah;
-	unsigned short ip_limit_wsi;
-#endif
-	unsigned int deprecated:1;
-	unsigned int being_destroyed:1;
-	unsigned int being_destroyed1:1;
-	unsigned int requested_kill:1;
-	unsigned int protocol_init_done:1;
-	unsigned int ssl_gate_accepts:1;
-	unsigned int doing_protocol_init;
-	/*
-	 * set to the Thread ID that's doing the service loop just before entry
-	 * to poll indicates service thread likely idling in poll()
-	 * volatile because other threads may check it as part of processing
-	 * for pollfd event change.
-	 */
-	volatile int service_tid;
-	int service_tid_detected;
-
-	short max_http_header_pool;
-	short count_threads;
-	short plugin_protocol_count;
-	short plugin_extension_count;
-	short server_string_len;
-	unsigned short ws_ping_pong_interval;
-	unsigned short deprecation_pending_listen_close_count;
-
-	uint8_t max_fi;
-};
-
-int
-lws_check_deferred_free(struct lws_context *context, int force);
-
-#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x]
-#define lws_get_vh_protocol(vh, x) vh->protocols[x]
-
-LWS_EXTERN void
-lws_close_free_wsi_final(struct lws *wsi);
-LWS_EXTERN void
-lws_libuv_closehandle(struct lws *wsi);
-LWS_EXTERN void
-lws_libuv_closehandle_manually(struct lws *wsi);
-LWS_EXTERN int
-lws_libuv_check_watcher_active(struct lws *wsi);
-
-LWS_VISIBLE LWS_EXTERN int
-lws_plat_plugins_init(struct lws_context * context, const char * const *d);
-
-LWS_VISIBLE LWS_EXTERN int
-lws_plat_plugins_destroy(struct lws_context * context);
-
-LWS_EXTERN void
-lws_restart_ws_ping_pong_timer(struct lws *wsi);
-
-struct lws *
-lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd);
-
-
-enum {
-	LWS_EV_READ = (1 << 0),
-	LWS_EV_WRITE = (1 << 1),
-	LWS_EV_START = (1 << 2),
-	LWS_EV_STOP = (1 << 3),
-
-	LWS_EV_PREPARE_DELETION = (1 << 31),
-};
-
-#if defined(LWS_WITH_LIBEV)
-LWS_EXTERN void
-lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc);
-LWS_EXTERN void
-lws_libev_io(struct lws *wsi, int flags);
-LWS_EXTERN int
-lws_libev_init_fd_table(struct lws_context *context);
-LWS_EXTERN void
-lws_libev_destroyloop(struct lws_context *context, int tsi);
-LWS_EXTERN void
-lws_libev_run(const struct lws_context *context, int tsi);
-#define LWS_LIBEV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV)
-LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info);
-#else
-#define lws_libev_accept(_a, _b) ((void) 0)
-#define lws_libev_io(_a, _b) ((void) 0)
-#define lws_libev_init_fd_table(_a) (0)
-#define lws_libev_run(_a, _b) ((void) 0)
-#define lws_libev_destroyloop(_a, _b) ((void) 0)
-#define LWS_LIBEV_ENABLED(context) (0)
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
-#define lws_feature_status_libev(_a) \
-			lwsl_info("libev support not compiled in\n")
-#else
-#define lws_feature_status_libev(_a)
-#endif
-#endif
-
-#if defined(LWS_WITH_LIBUV)
-LWS_EXTERN void
-lws_libuv_accept(struct lws *new_wsi, lws_sock_file_fd_type desc);
-LWS_EXTERN void
-lws_libuv_io(struct lws *wsi, int flags);
-LWS_EXTERN int
-lws_libuv_init_fd_table(struct lws_context *context);
-LWS_EXTERN void
-lws_libuv_run(const struct lws_context *context, int tsi);
-LWS_EXTERN void
-lws_libuv_destroyloop(struct lws_context *context, int tsi);
-LWS_EXTERN int
-lws_uv_initvhost(struct lws_vhost* vh, struct lws*);
-#define LWS_LIBUV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)
-LWS_EXTERN void lws_feature_status_libuv(struct lws_context_creation_info *info);
-#else
-#define lws_libuv_accept(_a, _b) ((void) 0)
-#define lws_libuv_io(_a, _b) ((void) 0)
-#define lws_libuv_init_fd_table(_a) (0)
-#define lws_libuv_run(_a, _b) ((void) 0)
-#define lws_libuv_destroyloop(_a, _b) ((void) 0)
-#define LWS_LIBUV_ENABLED(context) (0)
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
-#define lws_feature_status_libuv(_a) \
-			lwsl_notice("libuv support not compiled in\n")
-#else
-#define lws_feature_status_libuv(_a)
-#endif
-#endif
-
-#if defined(LWS_WITH_LIBEVENT)
-LWS_EXTERN void
-lws_libevent_accept(struct lws *new_wsi, lws_sock_file_fd_type desc);
-LWS_EXTERN void
-lws_libevent_io(struct lws *wsi, int flags);
-LWS_EXTERN int
-lws_libevent_init_fd_table(struct lws_context *context);
-LWS_EXTERN void
-lws_libevent_destroyloop(struct lws_context *context, int tsi);
-LWS_EXTERN void
-lws_libevent_run(const struct lws_context *context, int tsi);
-#define LWS_LIBEVENT_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT)
-LWS_EXTERN void lws_feature_status_libevent(struct lws_context_creation_info *info);
-#else
-#define lws_libevent_accept(_a, _b) ((void) 0)
-#define lws_libevent_io(_a, _b) ((void) 0)
-#define lws_libevent_init_fd_table(_a) (0)
-#define lws_libevent_run(_a, _b) ((void) 0)
-#define lws_libevent_destroyloop(_a, _b) ((void) 0)
-#define LWS_LIBEVENT_ENABLED(context) (0)
-#if LWS_POSIX && !defined(LWS_WITH_ESP32)
-#define lws_feature_status_libevent(_a) \
-			lwsl_notice("libevent support not compiled in\n")
-#else
-#define lws_feature_status_libevent(_a)
-#endif
-#endif
-
-
-#ifdef LWS_WITH_IPV6
-#define LWS_IPV6_ENABLED(vh) \
-	(!lws_check_opt(vh->context->options, LWS_SERVER_OPTION_DISABLE_IPV6) && \
-	 !lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_IPV6))
-#else
-#define LWS_IPV6_ENABLED(context) (0)
-#endif
-
-#ifdef LWS_WITH_UNIX_SOCK
-#define LWS_UNIX_SOCK_ENABLED(vhost) \
-	(vhost->options & LWS_SERVER_OPTION_UNIX_SOCK)
-#else
-#define LWS_UNIX_SOCK_ENABLED(vhost) (0)
-#endif
-
-enum uri_path_states {
-	URIPS_IDLE,
-	URIPS_SEEN_SLASH,
-	URIPS_SEEN_SLASH_DOT,
-	URIPS_SEEN_SLASH_DOT_DOT,
-};
-
-enum uri_esc_states {
-	URIES_IDLE,
-	URIES_SEEN_PERCENT,
-	URIES_SEEN_PERCENT_H1,
-};
-
-/* notice that these union members:
- *
- *  hdr
- *  http
- *  http2
- *
- * all have a pointer to allocated_headers struct as their first member.
- *
- * It means for allocated_headers access, the three union paths can all be
- * used interchangeably to access the same data
- */
-
-
-#ifndef LWS_NO_CLIENT
-struct client_info_stash {
-	char address[256];
-	char path[4096];
-	char host[256];
-	char origin[256];
-	char protocol[256];
-	char method[16];
-	char iface[16];
-};
-#endif
-
-struct _lws_header_related {
-	/* MUST be first in struct */
-	struct allocated_headers *ah;
-	struct lws *ah_wait_list;
-	unsigned char *preamble_rx;
-#ifndef LWS_NO_CLIENT
-	struct client_info_stash *stash;
-#endif
-	unsigned int preamble_rx_len;
-	enum uri_path_states ups;
-	enum uri_esc_states ues;
-	short lextable_pos;
-	unsigned int current_token_limit;
-
-	char esc_stash;
-	char post_literal_equal;
-	unsigned char parser_state; /* enum lws_token_indexes */
-};
-
-#if defined(LWS_WITH_RANGES)
-enum range_states {
-	LWSRS_NO_ACTIVE_RANGE,
-	LWSRS_BYTES_EQ,
-	LWSRS_FIRST,
-	LWSRS_STARTING,
-	LWSRS_ENDING,
-	LWSRS_COMPLETED,
-	LWSRS_SYNTAX,
-};
-
-struct lws_range_parsing {
-	unsigned long long start, end, extent, agg, budget;
-	const char buf[128];
-	int pos;
-	enum range_states state;
-	char start_valid, end_valid, ctr, count_ranges, did_try, inside, send_ctr;
-};
-
-int
-lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp, unsigned long long extent);
-int
-lws_ranges_next(struct lws_range_parsing *rp);
-void
-lws_ranges_reset(struct lws_range_parsing *rp);
-#endif
-
-struct _lws_http_mode_related {
-	/* MUST be first in struct */
-	struct allocated_headers *ah; /* mirroring  _lws_header_related */
-	struct lws *ah_wait_list;
-	unsigned char *preamble_rx;
-#ifndef LWS_NO_CLIENT
-	struct client_info_stash *stash;
-#endif
-	unsigned int preamble_rx_len;
-	struct lws *new_wsi_list;
-	lws_filepos_t filepos;
-	lws_filepos_t filelen;
-	lws_fop_fd_t fop_fd;
-
-#if defined(LWS_WITH_RANGES)
-	struct lws_range_parsing range;
-	char multipart_content_type[64];
-#endif
-
-	enum http_version request_version;
-	enum http_connection_type connection_type;
-	lws_filepos_t tx_content_length;
-	lws_filepos_t tx_content_remain;
-	lws_filepos_t rx_content_length;
-	lws_filepos_t rx_content_remain;
-};
-
-#define LWS_H2_FRAME_HEADER_LENGTH 9
-
-#ifdef LWS_WITH_HTTP2
-
-enum lws_h2_wellknown_frame_types {
-	LWS_H2_FRAME_TYPE_DATA,
-	LWS_H2_FRAME_TYPE_HEADERS,
-	LWS_H2_FRAME_TYPE_PRIORITY,
-	LWS_H2_FRAME_TYPE_RST_STREAM,
-	LWS_H2_FRAME_TYPE_SETTINGS,
-	LWS_H2_FRAME_TYPE_PUSH_PROMISE,
-	LWS_H2_FRAME_TYPE_PING,
-	LWS_H2_FRAME_TYPE_GOAWAY,
-	LWS_H2_FRAME_TYPE_WINDOW_UPDATE,
-	LWS_H2_FRAME_TYPE_CONTINUATION,
-
-	LWS_H2_FRAME_TYPE_COUNT /* always last */
-};
-
-enum lws_h2_flags {
-	LWS_H2_FLAG_END_STREAM = 1,
-	LWS_H2_FLAG_END_HEADERS = 4,
-	LWS_H2_FLAG_PADDED = 8,
-	LWS_H2_FLAG_PRIORITY = 0x20,
-
-	LWS_H2_FLAG_SETTINGS_ACK = 1,
-};
-
-enum lws_h2_errors {
-	H2_ERR_NO_ERROR,		   /* Graceful shutdown */
-	H2_ERR_PROTOCOL_ERROR,	   /* Protocol error detected */
-	H2_ERR_INTERNAL_ERROR,	   /* Implementation fault */
-	H2_ERR_FLOW_CONTROL_ERROR,  /* Flow-control limits exceeded */
-	H2_ERR_SETTINGS_TIMEOUT,	   /* Settings not acknowledged */
-	H2_ERR_STREAM_CLOSED,	   /* Frame received for closed stream */
-	H2_ERR_FRAME_SIZE_ERROR,	   /* Frame size incorrect */
-	H2_ERR_REFUSED_STREAM,	   /* Stream not processed */
-	H2_ERR_CANCEL,		   /* Stream cancelled */
-	H2_ERR_COMPRESSION_ERROR,   /* Compression state not updated */
-	H2_ERR_CONNECT_ERROR,	   /* TCP connection error for CONNECT method */
-	H2_ERR_ENHANCE_YOUR_CALM,   /* Processing capacity exceeded */
-	H2_ERR_INADEQUATE_SECURITY, /* Negotiated TLS parameters not acceptable */
-	H2_ERR_HTTP_1_1_REQUIRED,   /* Use HTTP/1.1 for the request */
-};
-
-enum lws_h2_states {
-	LWS_H2_STATE_IDLE,
-	/*
-	 * Send PUSH_PROMISE    -> LWS_H2_STATE_RESERVED_LOCAL
-	 * Recv PUSH_PROMISE    -> LWS_H2_STATE_RESERVED_REMOTE
-	 * Send HEADERS         -> LWS_H2_STATE_OPEN
-	 * Recv HEADERS         -> LWS_H2_STATE_OPEN
-	 *
-	 *  - Only PUSH_PROMISE + HEADERS valid to send
-	 *  - Only HEADERS or PRIORITY valid to receive
-	 */
-	LWS_H2_STATE_RESERVED_LOCAL,
-	/*
-	 * Send RST_STREAM      -> LWS_H2_STATE_CLOSED
-	 * Recv RST_STREAM      -> LWS_H2_STATE_CLOSED
-	 * Send HEADERS         -> LWS_H2_STATE_HALF_CLOSED_REMOTE
-	 *
-	 * - Only HEADERS, RST_STREAM, or PRIORITY valid to send
-	 * - Only RST_STREAM, PRIORITY, or WINDOW_UPDATE valid to receive
-	 */
-	LWS_H2_STATE_RESERVED_REMOTE,
-	/*
-	 * Send RST_STREAM      -> LWS_H2_STATE_CLOSED
-	 * Recv RST_STREAM      -> LWS_H2_STATE_CLOSED
-	 * Recv HEADERS         -> LWS_H2_STATE_HALF_CLOSED_LOCAL
-	 *
-	 *  - Only RST_STREAM, WINDOW_UPDATE, or PRIORITY valid to send
-	 *  - Only HEADERS, RST_STREAM, or PRIORITY valid to receive
-	 */
-	LWS_H2_STATE_OPEN,
-	/*
-	 * Send RST_STREAM      -> LWS_H2_STATE_CLOSED
-	 * Recv RST_STREAM      -> LWS_H2_STATE_CLOSED
-	 * Send END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_LOCAL
-	 * Recv END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_REMOTE
-	 */
-	LWS_H2_STATE_HALF_CLOSED_REMOTE,
-	/*
-	 * Send RST_STREAM      -> LWS_H2_STATE_CLOSED
-	 * Recv RST_STREAM      -> LWS_H2_STATE_CLOSED
-	 * Send END_STREAM flag -> LWS_H2_STATE_CLOSED
-	 *
-	 *  - Any frame valid to send
-	 *  - Only WINDOW_UPDATE, PRIORITY, or RST_STREAM valid to receive
-	 */
-	LWS_H2_STATE_HALF_CLOSED_LOCAL,
-	/*
-	 * Send RST_STREAM      -> LWS_H2_STATE_CLOSED
-	 * Recv RST_STREAM      -> LWS_H2_STATE_CLOSED
-	 * Recv END_STREAM flag -> LWS_H2_STATE_CLOSED
-	 *
-	 *  - Only WINDOW_UPDATE, PRIORITY, and RST_STREAM valid to send
-	 *  - Any frame valid to receive
-	 */
-	LWS_H2_STATE_CLOSED,
-	/*
-	 *  - Only PRIORITY, WINDOW_UPDATE (IGNORE) and RST_STREAM (IGNORE)
-	 *     may be received
-	 *
-	 *  - Only PRIORITY valid to send
-	 */
-};
-
-#define LWS_H2_STREAM_ID_MASTER 0
-#define LWS_H2_SETTINGS_LEN 6
-
-enum http2_hpack_state {
-	HPKS_TYPE,
-
-	HPKS_IDX_EXT,
-
-	HPKS_HLEN,
-	HPKS_HLEN_EXT,
-
-	HPKS_DATA,
-};
-
-/*
- * lws general parsimonious header strategy is only store values from known
- * headers, and refer to them by index.
- *
- * That means if we can't map the peer header name to one that lws knows, we
- * will drop the content but track the indexing with associated_lws_hdr_idx =
- * LWS_HPACK_IGNORE_ENTRY.
- */
-
-enum http2_hpack_type {
-	HPKT_INDEXED_HDR_7,		/* 1xxxxxxx: just "header field" */
-	HPKT_INDEXED_HDR_6_VALUE_INCR,  /* 01xxxxxx: NEW indexed hdr with value */
-	HPKT_LITERAL_HDR_VALUE_INCR,	/* 01000000: NEW literal hdr with value */
-	HPKT_INDEXED_HDR_4_VALUE,	/* 0000xxxx: indexed hdr with value */
-	HPKT_INDEXED_HDR_4_VALUE_NEVER,	/* 0001xxxx: indexed hdr with value NEVER NEW */
-	HPKT_LITERAL_HDR_VALUE,		/* 00000000: literal hdr with value */
-	HPKT_LITERAL_HDR_VALUE_NEVER,	/* 00010000: literal hdr with value NEVER NEW */
-	HPKT_SIZE_5
-};
-
-#define LWS_HPACK_IGNORE_ENTRY 0xffff
-
-
-struct hpack_dt_entry {
-	char *value; /* malloc'd */
-	uint16_t value_len;
-	uint16_t hdr_len; /* virtual, for accounting */
-	uint16_t lws_hdr_idx; /* LWS_HPACK_IGNORE_ENTRY = IGNORE */
-};
-
-struct hpack_dynamic_table {
-	struct hpack_dt_entry *entries; /* malloc'd */
-	uint32_t virtual_payload_usage;
-	uint32_t virtual_payload_max;
-	uint16_t pos;
-	uint16_t used_entries;
-	uint16_t num_entries;
-};
-
-enum lws_h2_protocol_send_type {
-	LWS_PPS_NONE,
-	LWS_H2_PPS_MY_SETTINGS,
-	LWS_H2_PPS_ACK_SETTINGS,
-	LWS_H2_PPS_PONG,
-	LWS_H2_PPS_GOAWAY,
-	LWS_H2_PPS_RST_STREAM,
-	LWS_H2_PPS_UPDATE_WINDOW,
-};
-
-struct lws_h2_protocol_send {
-	struct lws_h2_protocol_send *next; /* linked list */
-	enum lws_h2_protocol_send_type type;
-
-	union uu {
-		struct {
-			char		str[32];
-			uint32_t	highest_sid;
-			uint32_t	err;
-		} ga;
-		struct {
-			uint32_t	sid;
-			uint32_t	err;
-		} rs;
-		struct {
-			uint8_t		ping_payload[8];
-		} ping;
-		struct {
-			uint32_t	sid;
-			uint32_t	credit;
-		} update_window;
-	} u;
-};
-
-struct lws_h2_ghost_sid {
-	struct lws_h2_ghost_sid *next;
-	uint32_t sid;
-};
-
-#define LWS_H2_RX_SCRATCH_SIZE 512
-
-/*
- * http/2 connection info that is only used by the root connection that has
- * the network connection.
- *
- * h2 tends to spawn many child connections from one network connection, so
- * it's necessary to make members only needed by the network connection
- * distinct and only malloc'd on network connections.
- *
- * There's only one HPACK parser per network connection.
- *
- * But there is an ah per logical child connection... the network connection
- * fills it but it belongs to the logical child.
- */
-struct lws_h2_netconn {
-	struct http2_settings set;
-	struct hpack_dynamic_table hpack_dyn_table;
-	uint8_t	ping_payload[8];
-	uint8_t one_setting[LWS_H2_SETTINGS_LEN];
-	char goaway_str[32]; /* for rx */
-	struct lws *swsi;
-	struct lws_h2_protocol_send *pps; /* linked list */
-	char *rx_scratch;
-
-	enum http2_hpack_state hpack;
-	enum http2_hpack_type hpack_type;
-
-	unsigned int huff:1;
-	unsigned int value:1;
-	unsigned int unknown_header:1;
-	unsigned int cont_exp:1;
-	unsigned int cont_exp_headers:1;
-	unsigned int we_told_goaway:1;
-	unsigned int pad_length:1;
-	unsigned int collected_priority:1;
-	unsigned int is_first_header_char:1;
-	unsigned int zero_huff_padding:1;
-	unsigned int last_action_dyntable_resize:1;
-
-	uint32_t hdr_idx;
-	uint32_t hpack_len;
-	uint32_t hpack_e_dep;
-	uint32_t count;
-	uint32_t preamble;
-	uint32_t length;
-	uint32_t sid;
-	uint32_t inside;
-	uint32_t highest_sid;
-	uint32_t highest_sid_opened;
-	uint32_t cont_exp_sid;
-	uint32_t dep;
-	uint32_t goaway_last_sid;
-	uint32_t goaway_err;
-	uint32_t hpack_hdr_len;
-
-	uint32_t rx_scratch_pos;
-	uint32_t rx_scratch_len;
-
-	uint16_t hpack_pos;
-
-	uint8_t frame_state;
-	uint8_t type;
-	uint8_t flags;
-	uint8_t padding;
-	uint8_t weight_temp;
-	uint8_t huff_pad;
-	char first_hdr_char;
-	uint8_t hpack_m;
-	uint8_t ext_count;
-};
-
-struct _lws_h2_related {
-	/*
-	 * having this first lets us also re-use all HTTP union code
-	 * and in turn, http_mode_related has allocated headers in right
-	 * place so we can use the header apis on the wsi directly still
-	 */
-	struct _lws_http_mode_related http; /* MUST BE FIRST IN STRUCT */
-
-	struct lws_h2_netconn *h2n; /* malloc'd for root net conn */
-	struct lws *parent_wsi;
-	struct lws *child_list;
-	struct lws *sibling_list;
-
-	char *pending_status_body;
-
-	int tx_cr;
-	int peer_tx_cr_est;
-	unsigned int my_sid;
-	unsigned int child_count;
-	int my_priority;
-	uint32_t dependent_on;
-
-	unsigned int END_STREAM:1;
-	unsigned int END_HEADERS:1;
-	unsigned int send_END_STREAM:1;
-	unsigned int GOING_AWAY;
-	unsigned int requested_POLLOUT:1;
-	unsigned int skint:1;
-
-	uint16_t round_robin_POLLOUT;
-	uint16_t count_POLLOUT_children;
-	uint8_t h2_state; /* the RFC7540 state of the connection */
-	uint8_t weight;
-
-	uint8_t initialized;
-};
-
-#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->u.h2.parent_wsi)
-
-#endif
-
-struct _lws_websocket_related {
-	/* cheapest way to deal with ah overlap with ws union transition */
-	struct _lws_header_related hdr;
-	char *rx_ubuf;
-	unsigned int rx_ubuf_alloc;
-	struct lws *rx_draining_ext_list;
-	struct lws *tx_draining_ext_list;
-	time_t time_next_ping_check;
-	size_t rx_packet_length;
-	unsigned int rx_ubuf_head;
-	unsigned char mask[4];
-	/* Also used for close content... control opcode == < 128 */
-	unsigned char ping_payload_buf[128 - 3 + LWS_PRE];
-
-	unsigned char ping_payload_len;
-	unsigned char mask_idx;
-	unsigned char opcode;
-	unsigned char rsv;
-	unsigned char rsv_first_msg;
-	/* zero if no info, or length including 2-byte close code */
-	unsigned char close_in_ping_buffer_len;
-	unsigned char utf8;
-	unsigned char stashed_write_type;
-	unsigned char tx_draining_stashed_wp;
-
-	unsigned int final:1;
-	unsigned int frame_is_binary:1;
-	unsigned int all_zero_nonce:1;
-	unsigned int this_frame_masked:1;
-	unsigned int inside_frame:1; /* next write will be more of frame */
-	unsigned int clean_buffer:1; /* buffer not rewritten by extension */
-	unsigned int payload_is_close:1; /* process as PONG, but it is close */
-	unsigned int ping_pending_flag:1;
-	unsigned int continuation_possible:1;
-	unsigned int owed_a_fin:1;
-	unsigned int check_utf8:1;
-	unsigned int defeat_check_utf8:1;
-	unsigned int pmce_compressed_message:1;
-	unsigned int stashed_write_pending:1;
-	unsigned int rx_draining_ext:1;
-	unsigned int tx_draining_ext:1;
-	unsigned int send_check_ping:1;
-	unsigned int first_fragment:1;
-};
-
-#ifdef LWS_WITH_CGI
-
-#define LWS_HTTP_CHUNK_HDR_SIZE 16
-
-enum {
-	SIGNIFICANT_HDR_CONTENT_LENGTH,
-	SIGNIFICANT_HDR_LOCATION,
-	SIGNIFICANT_HDR_STATUS,
-	SIGNIFICANT_HDR_TRANSFER_ENCODING,
-
-	SIGNIFICANT_HDR_COUNT
-};
-
-/* wsi who is master of the cgi points to an lws_cgi */
-
-struct lws_cgi {
-	struct lws_cgi *cgi_list;
-	struct lws *stdwsi[3]; /* points to the associated stdin/out/err wsis */
-	struct lws *wsi; /* owner */
-	unsigned char *headers_buf;
-	unsigned char *headers_start;
-	unsigned char *headers_pos;
-	unsigned char *headers_dumped;
-	unsigned char *headers_end;
-	lws_filepos_t content_length;
-	lws_filepos_t content_length_seen;
-	int pipe_fds[3][2];
-	int match[SIGNIFICANT_HDR_COUNT];
-	int pid;
-	int response_code;
-	int lp;
-	char l[12];
-
-	unsigned int being_closed:1;
-	unsigned int explicitly_chunked:1;
-
-	unsigned char chunked_grace;
-};
-#endif
-
-signed char char_to_hex(const char c);
-
-#ifndef LWS_NO_CLIENT
-enum lws_chunk_parser {
-	ELCP_HEX,
-	ELCP_CR,
-	ELCP_CONTENT,
-	ELCP_POST_CR,
-	ELCP_POST_LF,
-};
-#endif
-
-enum lws_parse_urldecode_results {
-	LPUR_CONTINUE,
-	LPUR_SWALLOW,
-	LPUR_FORBID,
-	LPUR_EXCESSIVE,
-};
-
-struct lws_rewrite;
-
-#ifdef LWS_WITH_ACCESS_LOG
-struct lws_access_log {
-	char *header_log;
-	char *user_agent;
-	char *referrer;
-	unsigned long sent;
-	int response;
-};
-#endif
-
-struct lws {
-
-	/* structs */
-	/* members with mutually exclusive lifetimes are unionized */
-
-	union u {
-		struct _lws_http_mode_related http;
-#ifdef LWS_WITH_HTTP2
-		struct _lws_h2_related h2;
-#endif
-		struct _lws_header_related hdr;
-		struct _lws_websocket_related ws;
-	} u;
-
-	/* lifetime members */
-
-#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
-	struct lws_io_watcher w_read;
-#endif
-#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBEVENT)
-	struct lws_io_watcher w_write;
-#endif
-#ifdef LWS_WITH_ACCESS_LOG
-	struct lws_access_log access_log;
-#endif
-	time_t pending_timeout_limit;
-
-	/* pointers */
-
-	struct lws_context *context;
-	struct lws_vhost *vhost;
-	struct lws *parent; /* points to parent, if any */
-	struct lws *child_list; /* points to first child */
-	struct lws *sibling_list; /* subsequent children at same level */
-#ifdef LWS_WITH_CGI
-	struct lws_cgi *cgi; /* wsi being cgi master have one of these */
-#endif
-	const struct lws_protocols *protocol;
-	struct lws **same_vh_protocol_prev, *same_vh_protocol_next;
-	struct lws *timeout_list;
-	struct lws **timeout_list_prev;
-#if defined(LWS_WITH_PEER_LIMITS)
-	struct lws_peer *peer;
-#endif
-
-	void *user_space;
-	void *opaque_parent_data;
-	/* rxflow handling */
-	unsigned char *rxflow_buffer;
-	/* truncated send handling */
-	unsigned char *trunc_alloc; /* non-NULL means buffering in progress */
-
-#if defined (LWS_WITH_ESP8266)
-	void *premature_rx;
-	unsigned short prem_rx_size, prem_rx_pos;
-#endif
-
-#ifndef LWS_NO_EXTENSIONS
-	const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];
-	void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];
-#endif
-#ifdef LWS_OPENSSL_SUPPORT
-	SSL *ssl;
-	BIO *client_bio;
-	struct lws *pending_read_list_prev, *pending_read_list_next;
-#if defined(LWS_WITH_STATS)
-	uint64_t accept_start_us;
-	char seen_rx;
-#endif
-#endif
-#ifdef LWS_WITH_HTTP_PROXY
-	struct lws_rewrite *rw;
-#endif
-#ifdef LWS_LATENCY
-	unsigned long action_start;
-	unsigned long latency_start;
-#endif
-	lws_sock_file_fd_type desc; /* .filefd / .sockfd */
-#if defined(LWS_WITH_STATS)
-	uint64_t active_writable_req_us;
-#endif
-	/* ints */
-	int position_in_fds_table;
-	uint32_t rxflow_len;
-	uint32_t rxflow_pos;
-	unsigned int trunc_alloc_len; /* size of malloc */
-	unsigned int trunc_offset; /* where we are in terms of spilling */
-	unsigned int trunc_len; /* how much is buffered */
-#ifndef LWS_NO_CLIENT
-	int chunk_remaining;
-#endif
-	unsigned int cache_secs;
-
-	unsigned int hdr_parsing_completed:1;
-	unsigned int http2_substream:1;
-	unsigned int upgraded_to_http2:1;
-	unsigned int seen_nonpseudoheader:1;
-	unsigned int listener:1;
-	unsigned int user_space_externally_allocated:1;
-	unsigned int socket_is_permanently_unusable:1;
-	unsigned int rxflow_change_to:2;
-	unsigned int more_rx_waiting:1; /* has to live here since ah may stick to end */
-	unsigned int conn_stat_done:1;
-	unsigned int cache_reuse:1;
-	unsigned int cache_revalidate:1;
-	unsigned int cache_intermediaries:1;
-	unsigned int favoured_pollin:1;
-	unsigned int sending_chunked:1;
-	unsigned int already_did_cce:1;
-	unsigned int told_user_closed:1;
-	unsigned int waiting_to_send_close_frame:1;
-	unsigned int ipv6:1;
-	unsigned int parent_carries_io:1;
-	unsigned int parent_pending_cb_on_writable:1;
-	unsigned int cgi_stdout_zero_length:1;
-	unsigned int seen_zero_length_recv:1;
-	unsigned int rxflow_will_be_applied:1;
-
-#if defined(LWS_WITH_ESP8266)
-	unsigned int pending_send_completion:3;
-	unsigned int close_is_pending_send_completion:1;
-#endif
-#ifdef LWS_WITH_ACCESS_LOG
-	unsigned int access_log_pending:1;
-#endif
-#ifndef LWS_NO_CLIENT
-	unsigned int do_ws:1; /* whether we are doing http or ws flow */
-	unsigned int chunked:1; /* if the clientside connection is chunked */
-	unsigned int client_rx_avail:1;
-	unsigned int client_http_body_pending:1;
-#endif
-#ifdef LWS_WITH_HTTP_PROXY
-	unsigned int perform_rewrite:1;
-#endif
-#ifndef LWS_NO_EXTENSIONS
-	unsigned int extension_data_pending:1;
-#endif
-#ifdef LWS_OPENSSL_SUPPORT
-	unsigned int use_ssl:4;
-#endif
-#ifdef _WIN32
-	unsigned int sock_send_blocking:1;
-#endif
-#ifdef LWS_OPENSSL_SUPPORT
-	unsigned int redirect_to_https:1;
-#endif
-
-	/* volatile to make sure code is aware other thread can change */
-	volatile unsigned int handling_pollout:1;
-	volatile unsigned int leave_pollout_active:1;
-
-#ifndef LWS_NO_CLIENT
-	unsigned short c_port;
-#endif
-
-	/* chars */
-#ifndef LWS_NO_EXTENSIONS
-	unsigned char count_act_ext;
-#endif
-	uint8_t ietf_spec_revision;
-	char mode; /* enum connection_mode */
-	char state; /* enum lws_connection_states */
-	char state_pre_close;
-	char lws_rx_parse_state; /* enum lws_rx_parse_state */
-	char rx_frame_type; /* enum lws_write_protocol */
-	char pending_timeout; /* enum pending_timeout */
-	char tsi; /* thread service index we belong to */
-	char protocol_interpret_idx;
-	char redirects;
-	uint8_t rxflow_bitmap;
-#ifdef LWS_WITH_CGI
-	char cgi_channel; /* which of stdin/out/err */
-	char hdr_state;
-#endif
-#ifndef LWS_NO_CLIENT
-	char chunk_parser; /* enum lws_chunk_parser */
-#endif
-#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT)
-	char reason_bf; /* internal writeable callback reason bitfield */
-#endif
-};
-
-#define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap))
-
-LWS_EXTERN int log_level;
-
-LWS_EXTERN int
-lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
-		const char *iface);
-
-#if defined(LWS_WITH_IPV6)
-LWS_EXTERN unsigned long
-lws_get_addr_scope(const char *ipaddr);
-#endif
-
-LWS_EXTERN void
-lws_close_free_wsi(struct lws *wsi, enum lws_close_status);
-
-LWS_EXTERN void
-lws_free_wsi(struct lws *wsi);
-
-LWS_EXTERN int
-remove_wsi_socket_from_fds(struct lws *wsi);
-LWS_EXTERN int
-lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len);
-
-#ifndef LWS_LATENCY
-static inline void
-lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
-	    int ret, int completion) {
-	do {
-		(void)context; (void)wsi; (void)action; (void)ret;
-		(void)completion;
-	} while (0);
-}
-static inline void
-lws_latency_pre(struct lws_context *context, struct lws *wsi) {
-	do { (void)context; (void)wsi; } while (0);
-}
-#else
-#define lws_latency_pre(_context, _wsi) lws_latency(_context, _wsi, NULL, 0, 0)
-extern void
-lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
-	    int ret, int completion);
-#endif
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_client_rx_sm(struct lws *wsi, unsigned char c);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_parse(struct lws *wsi, unsigned char c);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_parse_urldecode(struct lws *wsi, uint8_t *_c);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_http_action(struct lws *wsi);
-
-LWS_EXTERN int
-lws_b64_selftest(void);
-
-LWS_EXTERN int
-lws_service_flag_pending(struct lws_context *context, int tsi);
-
-#if defined(_WIN32) || defined(LWS_WITH_ESP8266)
-LWS_EXTERN struct lws *
-wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd);
-
-LWS_EXTERN int
-insert_wsi(struct lws_context *context, struct lws *wsi);
-
-LWS_EXTERN int
-delete_from_fd(struct lws_context *context, lws_sockfd_type fd);
-#else
-#define wsi_from_fd(A,B)  A->lws_lookup[B]
-#define insert_wsi(A,B)   assert(A->lws_lookup[B->desc.sockfd] == 0); A->lws_lookup[B->desc.sockfd]=B
-#define delete_from_fd(A,B) A->lws_lookup[B]=0
-#endif
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len);
-
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_service_timeout_check(struct lws *wsi, unsigned int sec);
-
-LWS_EXTERN void
-lws_remove_from_timeout_list(struct lws *wsi);
-
-LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
-lws_client_connect_2(struct lws *wsi);
-
-LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT
-lws_client_reset(struct lws **wsi, int ssl, const char *address, int port,
-		 const char *path, const char *host);
-
-LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
-lws_create_new_server_wsi(struct lws_vhost *vhost);
-
-LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
-lws_generate_client_handshake(struct lws *wsi, char *pkt);
-
-LWS_EXTERN int
-lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd);
-
-LWS_EXTERN struct lws *
-lws_client_connect_via_info2(struct lws *wsi);
-
-LWS_EXTERN int
-_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah);
-
-/*
- * EXTENSIONS
- */
-
-#ifndef LWS_NO_EXTENSIONS
-LWS_VISIBLE void
-lws_context_init_extensions(struct lws_context_creation_info *info,
-			    struct lws_context *context);
-LWS_EXTERN int
-lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
-			  void *v, size_t len);
-
-LWS_EXTERN int
-lws_ext_cb_active(struct lws *wsi, int reason, void *buf, int len);
-LWS_EXTERN int
-lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, int reason,
-		    void *arg, int len);
-
-#else
-#define lws_any_extension_handled(_a, _b, _c, _d) (0)
-#define lws_ext_cb_active(_a, _b, _c, _d) (0)
-#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0)
-#define lws_issue_raw_ext_access lws_issue_raw
-#define lws_context_init_extensions(_a, _b)
-#endif
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_client_interpret_server_handshake(struct lws *wsi);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_rx_sm(struct lws *wsi, unsigned char c);
-
-LWS_EXTERN int
-lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf, size_t *len);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len);
-
-LWS_EXTERN void
-lws_union_transition(struct lws *wsi, enum connection_mode mode);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-user_callback_handle_rxflow(lws_callback_function, struct lws *wsi,
-			    enum lws_callback_reasons reason, void *user,
-			    void *in, size_t len);
-#ifdef LWS_WITH_HTTP2
-struct lws * lws_h2_get_nth_child(struct lws *wsi, int n);
-LWS_EXTERN void lws_h2_init(struct lws *wsi);
-LWS_EXTERN int
-lws_h2_settings(struct lws *nwsi, struct http2_settings *settings,
-				     unsigned char *buf, int len);
-LWS_EXTERN int
-lws_h2_parser(struct lws *wsi, unsigned char c);
-LWS_EXTERN int lws_h2_do_pps_send(struct lws *wsi);
-LWS_EXTERN int lws_h2_frame_write(struct lws *wsi, int type, int flags,
-				     unsigned int sid, unsigned int len,
-				     unsigned char *buf);
-LWS_EXTERN struct lws *
-lws_h2_wsi_from_id(struct lws *wsi, unsigned int sid);
-LWS_EXTERN int lws_hpack_interpret(struct lws *wsi,
-				   unsigned char c);
-LWS_EXTERN 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);
-LWS_EXTERN 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);
-LWS_EXTERN int
-lws_add_http2_header_status(struct lws *wsi,
-			    unsigned int code, unsigned char **p,
-			    unsigned char *end);
-LWS_EXTERN int
-lws_h2_configure_if_upgraded(struct lws *wsi);
-LWS_EXTERN void
-lws_hpack_destroy_dynamic_header(struct lws *wsi);
-LWS_EXTERN int
-lws_hpack_dynamic_size(struct lws *wsi, int size);
-LWS_EXTERN int
-lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason);
-LWS_EXTERN int
-lws_h2_tx_cr_get(struct lws *wsi);
-LWS_EXTERN void
-lws_h2_tx_cr_consume(struct lws *wsi, int consumed);
-LWS_EXTERN int
-lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h);
-LWS_EXTERN void
-lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pss);
-
-LWS_EXTERN const struct http2_settings lws_h2_defaults;
-#else
-#define lws_h2_configure_if_upgraded(x)
-#endif
-
-LWS_EXTERN int
-lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd);
-
-LWS_EXTERN int
-lws_plat_check_connection_error(struct lws *wsi);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_header_table_attach(struct lws *wsi, int autoservice);
-
-LWS_EXTERN int
-lws_header_table_detach(struct lws *wsi, int autoservice);
-
-LWS_EXTERN void
-lws_header_table_reset(struct lws *wsi, int autoservice);
-void
-_lws_header_table_reset(struct allocated_headers *ah);
-
-void
-lws_header_table_force_to_detachable_state(struct lws *wsi);
-int
-lws_header_table_is_in_detachable_state(struct lws *wsi);
-
-LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
-lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ensure_user_space(struct lws *wsi);
-
-LWS_EXTERN int
-lws_change_pollfd(struct lws *wsi, int _and, int _or);
-
-#ifndef LWS_NO_SERVER
-int lws_context_init_server(struct lws_context_creation_info *info,
-			    struct lws_vhost *vhost);
-LWS_EXTERN struct lws_vhost *
-lws_select_vhost(struct lws_context *context, int port, const char *servername);
-LWS_EXTERN int
-handshake_0405(struct lws_context *context, struct lws *wsi);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len);
-LWS_EXTERN void
-lws_server_get_canonical_hostname(struct lws_context *context,
-				  struct lws_context_creation_info *info);
-#else
-#define lws_context_init_server(_a, _b) (0)
-#define lws_interpret_incoming_packet(_a, _b, _c) (0)
-#define lws_server_get_canonical_hostname(_a, _b)
-#endif
-
-#ifndef LWS_NO_DAEMONIZE
-LWS_EXTERN int get_daemonize_pid();
-#else
-#define get_daemonize_pid() (0)
-#endif
-
-#if !defined(LWS_WITH_ESP8266)
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-interface_to_sa(struct lws_vhost *vh, const char *ifname,
-		struct sockaddr_in *addr, size_t addrlen);
-#endif
-LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
-
-enum lws_ssl_capable_status {
-	LWS_SSL_CAPABLE_ERROR = -1,
-	LWS_SSL_CAPABLE_MORE_SERVICE = -2,
-};
-
-#ifndef LWS_OPENSSL_SUPPORT
-#define LWS_SSL_ENABLED(context) (0)
-#define lws_context_init_server_ssl(_a, _b) (0)
-#define lws_ssl_destroy(_a)
-#define lws_context_init_http2_ssl(_a)
-#define lws_ssl_capable_read lws_ssl_capable_read_no_ssl
-#define lws_ssl_capable_write lws_ssl_capable_write_no_ssl
-#define lws_ssl_pending lws_ssl_pending_no_ssl
-#define lws_server_socket_service_ssl(_b, _c) (0)
-#define lws_ssl_close(_a) (0)
-#define lws_ssl_context_destroy(_a)
-#define lws_ssl_SSL_CTX_destroy(_a)
-#define lws_ssl_remove_wsi_from_buffered_list(_a)
-#define lws_context_init_ssl_library(_a)
-#define lws_ssl_anybody_has_buffered_read_tsi(_a, _b) (0)
-#else
-#define LWS_SSL_ENABLED(context) (context->use_ssl)
-LWS_EXTERN int openssl_websocket_private_data_index;
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_pending(struct lws *wsi);
-LWS_EXTERN int
-lws_context_init_ssl_library(struct lws_context_creation_info *info);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd);
-LWS_EXTERN int
-lws_ssl_close(struct lws *wsi);
-LWS_EXTERN void
-lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost);
-LWS_EXTERN void
-lws_ssl_context_destroy(struct lws_context *context);
-LWS_VISIBLE void
-lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi);
-LWS_EXTERN int
-lws_ssl_client_bio_create(struct lws *wsi);
-LWS_EXTERN int
-lws_ssl_client_connect1(struct lws *wsi);
-LWS_EXTERN int
-lws_ssl_client_connect2(struct lws *wsi);
-LWS_EXTERN void
-lws_ssl_elaborate_error(void);
-LWS_EXTERN int
-lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi);
-#ifndef LWS_NO_SERVER
-LWS_EXTERN int
-lws_context_init_server_ssl(struct lws_context_creation_info *info,
-			    struct lws_vhost *vhost);
-#else
-#define lws_context_init_server_ssl(_a, _b) (0)
-#endif
-LWS_EXTERN void
-lws_ssl_destroy(struct lws_vhost *vhost);
-/* HTTP2-related */
-
-#ifdef LWS_WITH_HTTP2
-LWS_EXTERN void
-lws_context_init_http2_ssl(struct lws_vhost *vhost);
-#else
-#define lws_context_init_http2_ssl(_a)
-#endif
-#endif
-
-#if LWS_MAX_SMP > 1
-static LWS_INLINE void
-lws_pt_mutex_init(struct lws_context_per_thread *pt)
-{
-	pthread_mutex_init(&pt->lock, NULL);
-}
-
-static LWS_INLINE void
-lws_pt_mutex_destroy(struct lws_context_per_thread *pt)
-{
-	pthread_mutex_destroy(&pt->lock);
-}
-
-static LWS_INLINE void
-lws_pt_lock(struct lws_context_per_thread *pt)
-{
-	if (!pt->lock_depth++)
-		pthread_mutex_lock(&pt->lock);
-}
-
-static LWS_INLINE void
-lws_pt_unlock(struct lws_context_per_thread *pt)
-{
-	if (!(--pt->lock_depth))
-		pthread_mutex_unlock(&pt->lock);
-}
-static LWS_INLINE void
-lws_context_lock(struct lws_context *context)
-{
-	if (!context->lock_depth++)
-		pthread_mutex_lock(&context->lock);
-}
-
-static LWS_INLINE void
-lws_context_unlock(struct lws_context *context)
-{
-	if (!(--context->lock_depth))
-		pthread_mutex_unlock(&context->lock);
-}
-
-#else
-#define lws_pt_mutex_init(_a) (void)(_a)
-#define lws_pt_mutex_destroy(_a) (void)(_a)
-#define lws_pt_lock(_a) (void)(_a)
-#define lws_pt_unlock(_a) (void)(_a)
-#define lws_context_lock(_a) (void)(_a)
-#define lws_context_unlock(_a) (void)(_a)
-#endif
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_ssl_pending_no_ssl(struct lws *wsi);
-
-#ifdef LWS_WITH_HTTP_PROXY
-struct lws_rewrite {
-	hubbub_parser *parser;
-	hubbub_parser_optparams params;
-	const char *from, *to;
-	int from_len, to_len;
-	unsigned char *p, *end;
-	struct lws *wsi;
-};
-static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len)
-{
-	if (s->len != len)
-		return 1;
-
-	return strncmp((const char *)s->ptr, p, len);
-}
-typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw);
-LWS_EXTERN struct lws_rewrite *
-lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to);
-LWS_EXTERN void
-lws_rewrite_destroy(struct lws_rewrite *r);
-LWS_EXTERN int
-lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len);
-#endif
-
-#ifndef LWS_NO_CLIENT
-LWS_EXTERN int lws_client_socket_service(struct lws_context *context,
-					 struct lws *wsi,
-					 struct lws_pollfd *pollfd);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_http_transaction_completed_client(struct lws *wsi);
-#ifdef LWS_OPENSSL_SUPPORT
-LWS_EXTERN int
-lws_context_init_client_ssl(struct lws_context_creation_info *info,
-			    struct lws_vhost *vhost);
-
-LWS_EXTERN void
-lws_ssl_info_callback(const SSL *ssl, int where, int ret);
-
-#else
-	#define lws_context_init_client_ssl(_a, _b) (0)
-#endif
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len);
-LWS_EXTERN void
-lws_decode_ssl_error(void);
-#else
-#define lws_context_init_client_ssl(_a, _b) (0)
-#define lws_handshake_client(_a, _b, _c) (0)
-#endif
-
-LWS_EXTERN int
-_lws_rx_flow_control(struct lws *wsi);
-
-LWS_EXTERN int
-_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa);
-
-#ifndef LWS_NO_SERVER
-LWS_EXTERN int
-lws_server_socket_service(struct lws_context *context, struct lws *wsi,
-			  struct lws_pollfd *pollfd);
-LWS_EXTERN int
-lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len);
-#else
-#define lws_server_socket_service(_a, _b, _c) (0)
-#define lws_handshake_server(_a, _b, _c) (0)
-#endif
-
-#ifdef LWS_WITH_ACCESS_LOG
-LWS_EXTERN int
-lws_access_log(struct lws *wsi);
-LWS_EXTERN void
-lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth);
-#else
-#define lws_access_log(_a)
-#endif
-
-LWS_EXTERN int
-lws_cgi_kill_terminated(struct lws_context_per_thread *pt);
-
-LWS_EXTERN void
-lws_cgi_remove_and_kill(struct lws *wsi);
-
-int
-lws_protocol_init(struct lws_context *context);
-
-int
-lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p);
-
-const struct lws_http_mount *
-lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len);
-
-/*
- * custom allocator
- */
-LWS_EXTERN void *
-lws_realloc(void *ptr, size_t size, const char *reason);
-
-LWS_EXTERN void * LWS_WARN_UNUSED_RESULT
-lws_zalloc(size_t size, const char *reason);
-
-#ifdef LWS_PLAT_OPTEE
-void *lws_malloc(size_t size, const char *reason);
-void lws_free(void *p);
-#define lws_free_set_NULL(P)    do { lws_free(P); (P) = NULL; } while(0)
-#else
-#define lws_malloc(S, R)	lws_realloc(NULL, S, R)
-#define lws_free(P)	lws_realloc(P, 0, "lws_free")
-#define lws_free_set_NULL(P)	do { lws_realloc(P, 0, "free"); (P) = NULL; } while(0)
-#endif
-
-const struct lws_plat_file_ops *
-lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path,
-		    const char **vpath);
-
-/* lws_plat_ */
-LWS_EXTERN void
-lws_plat_delete_socket_from_fds(struct lws_context *context,
-				struct lws *wsi, int m);
-LWS_EXTERN void
-lws_plat_insert_socket_into_fds(struct lws_context *context,
-				struct lws *wsi);
-LWS_EXTERN void
-lws_plat_service_periodic(struct lws_context *context);
-
-LWS_EXTERN int
-lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi,
-		       struct lws_pollfd *pfd);
-LWS_EXTERN void
-lws_add_wsi_to_draining_ext_list(struct lws *wsi);
-LWS_EXTERN void
-lws_remove_wsi_from_draining_ext_list(struct lws *wsi);
-LWS_EXTERN int
-lws_plat_context_early_init(void);
-LWS_EXTERN void
-lws_plat_context_early_destroy(struct lws_context *context);
-LWS_EXTERN void
-lws_plat_context_late_destroy(struct lws_context *context);
-LWS_EXTERN int
-lws_poll_listen_fd(struct lws_pollfd *fd);
-LWS_EXTERN int
-lws_plat_service(struct lws_context *context, int timeout_ms);
-LWS_EXTERN LWS_VISIBLE int
-_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi);
-LWS_EXTERN int
-lws_plat_init(struct lws_context *context,
-	      struct lws_context_creation_info *info);
-LWS_EXTERN void
-lws_plat_drop_app_privileges(struct lws_context_creation_info *info);
-LWS_EXTERN unsigned long long
-time_in_microseconds(void);
-LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT
-lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt);
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_plat_inet_pton(int af, const char *src, void *dst);
-
-LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len);
-LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
-		                lws_filepos_t *amount);
-LWS_EXTERN int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf,
-	       lws_filepos_t *amount);
-
-LWS_EXTERN void
-lws_same_vh_protocol_remove(struct lws *wsi);
-LWS_EXTERN void
-lws_same_vh_protocol_insert(struct lws *wsi, int n);
-
-#if defined(LWS_WITH_STATS)
-void
-lws_stats_atomic_bump(struct lws_context * context,
-		struct lws_context_per_thread *pt, int index, uint64_t bump);
-void
-lws_stats_atomic_max(struct lws_context * context,
-		struct lws_context_per_thread *pt, int index, uint64_t val);
-#else
-static inline uint64_t lws_stats_atomic_bump(struct lws_context * context,
-		struct lws_context_per_thread *pt, int index, uint64_t bump) {
-	(void)context; (void)pt; (void)index; (void)bump; return 0; }
-static inline uint64_t lws_stats_atomic_max(struct lws_context * context,
-		struct lws_context_per_thread *pt, int index, uint64_t val) {
-	(void)context; (void)pt; (void)index; (void)val; return 0; }
-#endif
-
-/* socks */
-void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
-			ssize_t *msg_len);
-
-#if defined(LWS_WITH_PEER_LIMITS)
-void
-lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer);
-int
-lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer);
-void
-lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer);
-void
-lws_peer_cull_peer_wait_list(struct lws_context *context);
-struct lws_peer *
-lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd);
-void
-lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer,
-		 struct lws *wsi);
-#endif
-
-#ifdef __cplusplus
-};
-#endif

+ 687 - 0
thirdparty/libwebsockets/roles/h1/ops-h1.c

@@ -0,0 +1,687 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include <core/private.h>
+
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+
+/*
+ * We have to take care about parsing because the headers may be split
+ * into multiple fragments.  They may contain unknown headers with arbitrary
+ * argument lengths.  So, we parse using a single-character at a time state
+ * machine that is completely independent of packet size.
+ *
+ * Returns <0 for error or length of chars consumed from buf (up to len)
+ */
+
+int
+lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
+{
+	unsigned char *last_char, *oldbuf = buf;
+	lws_filepos_t body_chunk_len;
+	size_t n;
+
+	// lwsl_notice("%s: h1 path: wsi state 0x%x\n", __func__, lwsi_state(wsi));
+
+	switch (lwsi_state(wsi)) {
+
+	case LRS_ISSUING_FILE:
+		return 0;
+
+	case LRS_ESTABLISHED:
+
+		if (lwsi_role_ws(wsi))
+			goto ws_mode;
+
+		if (lwsi_role_client(wsi))
+			break;
+
+		wsi->hdr_parsing_completed = 0;
+
+		/* fallthru */
+
+	case LRS_HEADERS:
+		if (!wsi->http.ah) {
+			lwsl_err("%s: LRS_HEADERS: NULL ah\n", __func__);
+			assert(0);
+		}
+		lwsl_parser("issuing %d bytes to parser\n", (int)len);
+#if defined(LWS_ROLE_WS) && !defined(LWS_NO_CLIENT)
+		if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
+			goto bail;
+#endif
+		last_char = buf;
+		if (lws_handshake_server(wsi, &buf, (size_t)len))
+			/* Handshake indicates this session is done. */
+			goto bail;
+
+		/* we might have transitioned to RAW */
+		if (wsi->role_ops == &role_ops_raw_skt ||
+		    wsi->role_ops == &role_ops_raw_file)
+			 /* we gave the read buffer to RAW handler already */
+			goto read_ok;
+
+		/*
+		 * It's possible that we've exhausted our data already, or
+		 * rx flow control has stopped us dealing with this early,
+		 * but lws_handshake_server doesn't update len for us.
+		 * Figure out how much was read, so that we can proceed
+		 * appropriately:
+		 */
+		len -= (buf - last_char);
+//		lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
+
+		if (!wsi->hdr_parsing_completed)
+			/* More header content on the way */
+			goto read_ok;
+
+		switch (lwsi_state(wsi)) {
+			case LRS_ESTABLISHED:
+			case LRS_HEADERS:
+				goto read_ok;
+			case LRS_ISSUING_FILE:
+				goto read_ok;
+			case LRS_BODY:
+				wsi->http.rx_content_remain =
+						wsi->http.rx_content_length;
+				if (wsi->http.rx_content_remain)
+					goto http_postbody;
+
+				/* there is no POST content */
+				goto postbody_completion;
+			default:
+				break;
+		}
+		break;
+
+	case LRS_BODY:
+http_postbody:
+		lwsl_debug("%s: http post body: remain %d\n", __func__,
+			    (int)wsi->http.rx_content_remain);
+		while (len && wsi->http.rx_content_remain) {
+			/* Copy as much as possible, up to the limit of:
+			 * what we have in the read buffer (len)
+			 * remaining portion of the POST body (content_remain)
+			 */
+			body_chunk_len = min(wsi->http.rx_content_remain, len);
+			wsi->http.rx_content_remain -= body_chunk_len;
+			len -= body_chunk_len;
+#ifdef LWS_WITH_CGI
+			if (wsi->http.cgi) {
+				struct lws_cgi_args args;
+
+				args.ch = LWS_STDIN;
+				args.stdwsi = &wsi->http.cgi->stdwsi[0];
+				args.data = buf;
+				args.len = body_chunk_len;
+
+				/* returns how much used */
+				n = user_callback_handle_rxflow(
+					wsi->protocol->callback,
+					wsi, LWS_CALLBACK_CGI_STDIN_DATA,
+					wsi->user_space,
+					(void *)&args, 0);
+				if ((int)n < 0)
+					goto bail;
+			} else {
+#endif
+				n = wsi->protocol->callback(wsi,
+					LWS_CALLBACK_HTTP_BODY, wsi->user_space,
+					buf, (size_t)body_chunk_len);
+				if (n)
+					goto bail;
+				n = (size_t)body_chunk_len;
+#ifdef LWS_WITH_CGI
+			}
+#endif
+			buf += n;
+
+			if (wsi->http.rx_content_remain)  {
+				lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
+						wsi->context->timeout_secs);
+				break;
+			}
+			/* he sent all the content in time */
+postbody_completion:
+#ifdef LWS_WITH_CGI
+			/*
+			 * If we're running a cgi, we can't let him off the
+			 * hook just because he sent his POST data
+			 */
+			if (wsi->http.cgi)
+				lws_set_timeout(wsi, PENDING_TIMEOUT_CGI,
+						wsi->context->timeout_secs);
+			else
+#endif
+			lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+#ifdef LWS_WITH_CGI
+			if (!wsi->http.cgi)
+#endif
+			{
+				lwsl_info("HTTP_BODY_COMPLETION: %p (%s)\n",
+					  wsi, wsi->protocol->name);
+				n = wsi->protocol->callback(wsi,
+					LWS_CALLBACK_HTTP_BODY_COMPLETION,
+					wsi->user_space, NULL, 0);
+				if (n)
+					goto bail;
+
+				if (wsi->http2_substream)
+					lwsi_set_state(wsi, LRS_ESTABLISHED);
+			}
+
+			break;
+		}
+		break;
+
+	case LRS_AWAITING_CLOSE_ACK:
+	case LRS_WAITING_TO_SEND_CLOSE:
+	case LRS_SHUTDOWN:
+
+ws_mode:
+#if !defined(LWS_NO_CLIENT) && defined(LWS_ROLE_WS)
+		// lwsl_notice("%s: ws_mode\n", __func__);
+		if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
+			goto bail;
+#endif
+#if defined(LWS_ROLE_WS)
+		if (lwsi_role_ws(wsi) && lwsi_role_server(wsi) &&
+			/*
+			 * for h2 we are on the swsi
+			 */
+		    lws_parse_ws(wsi, &buf, (size_t)len) < 0) {
+			lwsl_info("%s: lws_parse_ws bailed\n", __func__);
+			goto bail;
+		}
+#endif
+		// lwsl_notice("%s: ws_mode: buf moved on by %d\n", __func__,
+		//	       lws_ptr_diff(buf, oldbuf));
+		break;
+
+	case LRS_DEFERRING_ACTION:
+		lwsl_debug("%s: LRS_DEFERRING_ACTION\n", __func__);
+		break;
+
+	case LRS_SSL_ACK_PENDING:
+		break;
+
+	case LRS_DEAD_SOCKET:
+		lwsl_err("%s: Unhandled state LRS_DEAD_SOCKET\n", __func__);
+		goto bail;
+		// assert(0);
+		/* fallthru */
+
+	default:
+		lwsl_err("%s: Unhandled state %d\n", __func__, lwsi_state(wsi));
+		assert(0);
+		goto bail;
+	}
+
+read_ok:
+	/* Nothing more to do for now */
+//	lwsl_info("%s: %p: read_ok, used %ld (len %d, state %d)\n", __func__,
+//		  wsi, (long)(buf - oldbuf), (int)len, wsi->state);
+
+	return lws_ptr_diff(buf, oldbuf);
+
+bail:
+	/*
+	 * h2 / h2-ws calls us recursively in
+	 *
+	 * lws_read_h1()->
+	 *   lws_h2_parser()->
+	 *     lws_read_h1()
+	 *
+	 * pattern, having stripped the h2 framing in the middle.
+	 *
+	 * When taking down the whole connection, make sure that only the
+	 * outer lws_read() does the wsi close.
+	 */
+	if (!wsi->outer_will_close)
+		lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
+				   "lws_read_h1 bail");
+
+	return -1;
+}
+#if !defined(LWS_NO_SERVER)
+static int
+lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
+{
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+	struct lws_tokens ebuf;
+	int n, buffered;
+
+	if (lwsi_state(wsi) == LRS_DEFERRING_ACTION)
+		goto try_pollout;
+
+	/* any incoming data ready? */
+
+	if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
+		goto try_pollout;
+
+	/*
+	 * If we previously just did POLLIN when IN and OUT were signaled
+	 * (because POLLIN processing may have used up the POLLOUT), don't let
+	 * that happen twice in a row... next time we see the situation favour
+	 * POLLOUT
+	 */
+
+	if (wsi->favoured_pollin &&
+	    (pollfd->revents & pollfd->events & LWS_POLLOUT)) {
+		// lwsl_notice("favouring pollout\n");
+		wsi->favoured_pollin = 0;
+		goto try_pollout;
+	}
+
+	/*
+	 * We haven't processed that the tunnel is set up yet, so
+	 * defer reading
+	 */
+
+	if (lwsi_state(wsi) == LRS_SSL_ACK_PENDING)
+		return LWS_HPI_RET_HANDLED;
+
+	/* these states imply we MUST have an ah attached */
+
+	if ((lwsi_state(wsi) == LRS_ESTABLISHED ||
+	     lwsi_state(wsi) == LRS_ISSUING_FILE ||
+	     lwsi_state(wsi) == LRS_HEADERS ||
+	     lwsi_state(wsi) == LRS_BODY)) {
+
+		if (!wsi->http.ah && lws_header_table_attach(wsi, 0)) {
+			lwsl_info("%s: wsi %p: ah not available\n", __func__, wsi);
+			goto try_pollout;
+		}
+
+		/*
+		 * We got here because there was specifically POLLIN...
+		 * regardless of our buflist state, we need to get it,
+		 * and either use it, or append to the buflist and use
+		 * buflist head material.
+		 *
+		 * We will not notice a connection close until the buflist is
+		 * exhausted and we tried to do a read of some kind.
+		 */
+
+		buffered = lws_buflist_aware_read(pt, wsi, &ebuf);
+		switch (ebuf.len) {
+		case 0:
+			lwsl_info("%s: read 0 len a\n", __func__);
+			wsi->seen_zero_length_recv = 1;
+			lws_change_pollfd(wsi, LWS_POLLIN, 0);
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+			/*
+			 * autobahn requires us to win the race between close
+			 * and draining the extensions
+			 */
+			if (wsi->ws &&
+			    (wsi->ws->rx_draining_ext || wsi->ws->tx_draining_ext))
+				goto try_pollout;
+#endif
+			/*
+			 * normally, we respond to close with logically closing
+			 * our side immediately
+			 */
+			goto fail;
+
+		case LWS_SSL_CAPABLE_ERROR:
+			goto fail;
+		case LWS_SSL_CAPABLE_MORE_SERVICE:
+			goto try_pollout;
+		}
+
+		/* just ignore incoming if waiting for close */
+		if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
+			lwsl_notice("%s: just ignoring\n", __func__);
+			goto try_pollout;
+		}
+
+		if (lwsi_state(wsi) == LRS_ISSUING_FILE) {
+			// lwsl_notice("stashing: wsi %p: bd %d\n", wsi, buffered);
+			if (lws_buflist_aware_consume(wsi, &ebuf, 0, buffered))
+				return LWS_HPI_RET_PLEASE_CLOSE_ME;
+
+			goto try_pollout;
+		}
+
+		/*
+		 * Otherwise give it to whoever wants it according to the
+		 * connection state
+		 */
+#if defined(LWS_ROLE_H2)
+		if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
+			n = lws_read_h2(wsi, (uint8_t *)ebuf.token, ebuf.len);
+		else
+#endif
+			n = lws_read_h1(wsi, (uint8_t *)ebuf.token, ebuf.len);
+		if (n < 0) /* we closed wsi */
+			return LWS_HPI_RET_WSI_ALREADY_DIED;
+
+		lwsl_debug("%s: consumed %d\n", __func__, n);
+
+		if (lws_buflist_aware_consume(wsi, &ebuf, n, buffered))
+			return LWS_HPI_RET_PLEASE_CLOSE_ME;
+
+		/*
+		 * during the parsing our role changed to something non-http,
+		 * so the ah has no further meaning
+		 */
+
+		if (wsi->http.ah &&
+		    !lwsi_role_h1(wsi) &&
+		    !lwsi_role_h2(wsi) &&
+		    !lwsi_role_cgi(wsi))
+			lws_header_table_detach(wsi, 0);
+
+		/*
+		 * He may have used up the writability above, if we will defer
+		 * POLLOUT processing in favour of POLLIN, note it
+		 */
+
+		if (pollfd->revents & LWS_POLLOUT)
+			wsi->favoured_pollin = 1;
+
+		return LWS_HPI_RET_HANDLED;
+	}
+
+	/*
+	 * He may have used up the writability above, if we will defer POLLOUT
+	 * processing in favour of POLLIN, note it
+	 */
+
+	if (pollfd->revents & LWS_POLLOUT)
+		wsi->favoured_pollin = 1;
+
+try_pollout:
+
+	/* this handles POLLOUT for http serving fragments */
+
+	if (!(pollfd->revents & LWS_POLLOUT))
+		return LWS_HPI_RET_HANDLED;
+
+	/* one shot */
+	if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+		lwsl_notice("%s a\n", __func__);
+		goto fail;
+	}
+
+	/* clear back-to-back write detection */
+	wsi->could_have_pending = 0;
+
+	if (lwsi_state(wsi) == LRS_DEFERRING_ACTION) {
+		lwsl_debug("%s: LRS_DEFERRING_ACTION now writable\n", __func__);
+
+		lwsi_set_state(wsi, LRS_ESTABLISHED);
+		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+			lwsl_info("failed at set pollfd\n");
+			goto fail;
+		}
+	}
+
+	if (!wsi->hdr_parsing_completed)
+		return LWS_HPI_RET_HANDLED;
+
+	if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
+
+		lws_stats_atomic_bump(wsi->context, pt,
+					LWSSTATS_C_WRITEABLE_CB, 1);
+#if defined(LWS_WITH_STATS)
+		if (wsi->active_writable_req_us) {
+			uint64_t ul = time_in_microseconds() -
+					wsi->active_writable_req_us;
+
+			lws_stats_atomic_bump(wsi->context, pt,
+					LWSSTATS_MS_WRITABLE_DELAY, ul);
+			lws_stats_atomic_max(wsi->context, pt,
+				  LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
+			wsi->active_writable_req_us = 0;
+		}
+#endif
+
+		n = user_callback_handle_rxflow(wsi->protocol->callback, wsi,
+						LWS_CALLBACK_HTTP_WRITEABLE,
+						wsi->user_space, NULL, 0);
+		if (n < 0) {
+			lwsl_info("writeable_fail\n");
+			goto fail;
+		}
+
+		return LWS_HPI_RET_HANDLED;
+	}
+
+	/* >0 == completion, <0 == error
+	 *
+	 * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
+	 * it's done.  That's the case even if we just completed the
+	 * send, so wait for that.
+	 */
+	n = lws_serve_http_file_fragment(wsi);
+	if (n < 0)
+		goto fail;
+
+	return LWS_HPI_RET_HANDLED;
+
+
+fail:
+	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
+			   "server socket svc fail");
+
+	return LWS_HPI_RET_WSI_ALREADY_DIED;
+}
+#endif
+
+static int
+rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
+		       struct lws_pollfd *pollfd)
+{
+
+//	lwsl_notice("%s: %p: wsistate 0x%x %s, revents 0x%x\n", __func__, wsi,
+//			wsi->wsistate, wsi->role_ops->name, pollfd->revents);
+
+#ifdef LWS_WITH_CGI
+	if (wsi->http.cgi && (pollfd->revents & LWS_POLLOUT)) {
+		if (lws_handle_POLLOUT_event(wsi, pollfd))
+			return LWS_HPI_RET_PLEASE_CLOSE_ME;
+
+		return LWS_HPI_RET_HANDLED;
+	}
+#endif
+
+        if (lws_is_flowcontrolled(wsi))
+                /* We cannot deal with any kind of new RX because we are
+                 * RX-flowcontrolled.
+                 */
+		return LWS_HPI_RET_HANDLED;
+
+#if !defined(LWS_NO_SERVER)
+	if (!lwsi_role_client(wsi)) {
+		int n;
+
+		lwsl_debug("%s: %p: wsistate 0x%x\n", __func__, wsi, wsi->wsistate);
+		n = lws_h1_server_socket_service(wsi, pollfd);
+		if (n != LWS_HPI_RET_HANDLED)
+			return n;
+		if (lwsi_state(wsi) != LRS_SSL_INIT)
+			if (lws_server_socket_service_ssl(wsi, LWS_SOCK_INVALID))
+				return LWS_HPI_RET_PLEASE_CLOSE_ME;
+
+		return LWS_HPI_RET_HANDLED;
+	}
+#endif
+
+#ifndef LWS_NO_CLIENT
+	if ((pollfd->revents & LWS_POLLIN) &&
+	     wsi->hdr_parsing_completed && !wsi->told_user_closed) {
+
+		/*
+		 * In SSL mode we get POLLIN notification about
+		 * encrypted data in.
+		 *
+		 * But that is not necessarily related to decrypted
+		 * data out becoming available; in may need to perform
+		 * other in or out before that happens.
+		 *
+		 * simply mark ourselves as having readable data
+		 * and turn off our POLLIN
+		 */
+		wsi->client_rx_avail = 1;
+		lws_change_pollfd(wsi, LWS_POLLIN, 0);
+
+		//lwsl_notice("calling back %s\n", wsi->protocol->name);
+
+		/* let user code know, he'll usually ask for writeable
+		 * callback and drain / re-enable it there
+		 */
+		if (user_callback_handle_rxflow(
+				wsi->protocol->callback,
+				wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
+				wsi->user_space, NULL, 0)) {
+			lwsl_info("RECEIVE_CLIENT_HTTP closed it\n");
+			return LWS_HPI_RET_PLEASE_CLOSE_ME;
+		}
+
+		return LWS_HPI_RET_HANDLED;
+	}
+#endif
+
+//	if (lwsi_state(wsi) == LRS_ESTABLISHED)
+//		return LWS_HPI_RET_HANDLED;
+
+#if !defined(LWS_NO_CLIENT)
+	if ((pollfd->revents & LWS_POLLOUT) &&
+	    lws_handle_POLLOUT_event(wsi, pollfd)) {
+		lwsl_debug("POLLOUT event closed it\n");
+		return LWS_HPI_RET_PLEASE_CLOSE_ME;
+	}
+
+	if (lws_client_socket_service(wsi, pollfd, NULL))
+		return LWS_HPI_RET_WSI_ALREADY_DIED;
+#endif
+
+	return LWS_HPI_RET_HANDLED;
+}
+
+int rops_handle_POLLOUT_h1(struct lws *wsi)
+{
+	if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY)
+		return LWS_HP_RET_USER_SERVICE;
+
+	if (lwsi_role_client(wsi))
+		return LWS_HP_RET_USER_SERVICE;
+
+	return LWS_HP_RET_BAIL_OK;
+}
+
+static int
+rops_write_role_protocol_h1(struct lws *wsi, unsigned char *buf, size_t len,
+			    enum lws_write_protocol *wp)
+{
+#if 0
+	/* if not in a state to send stuff, then just send nothing */
+
+	if ((lwsi_state(wsi) != LRS_RETURNED_CLOSE &&
+	     lwsi_state(wsi) != LRS_WAITING_TO_SEND_CLOSE &&
+	     lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK)) {
+		//assert(0);
+		lwsl_debug("binning %d %d\n", lwsi_state(wsi), *wp);
+		return 0;
+	}
+#endif
+
+	return lws_issue_raw(wsi, (unsigned char *)buf, len);
+}
+
+static int
+rops_alpn_negotiated_h1(struct lws *wsi, const char *alpn)
+{
+	lwsl_debug("%s: client %d\n", __func__, lwsi_role_client(wsi));
+#if !defined(LWS_NO_CLIENT)
+	if (lwsi_role_client(wsi)) {
+		/*
+		 * If alpn asserts it is http/1.1, server support for KA is
+		 * mandatory.
+		 *
+		 * Knowing this lets us proceed with sending pipelined headers
+		 * before we received the first response headers.
+		 */
+		wsi->keepalive_active = 1;
+	}
+#endif
+
+	return 0;
+}
+
+static int
+rops_destroy_role_h1(struct lws *wsi)
+{
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+	struct allocated_headers *ah;
+
+	/* we may not have an ah, but may be on the waiting list... */
+	lwsl_info("%s: ah det due to close\n", __func__);
+	__lws_header_table_detach(wsi, 0);
+
+	 ah = pt->http.ah_list;
+
+	while (ah) {
+		if (ah->in_use && ah->wsi == wsi) {
+			lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi);
+			ah->in_use = 0;
+			ah->wsi = NULL;
+			pt->http.ah_count_in_use--;
+			break;
+		}
+		ah = ah->next;
+	}
+
+	return 0;
+}
+
+struct lws_role_ops role_ops_h1 = {
+	/* role name */			"h1",
+	/* alpn id */			"http/1.1",
+	/* check_upgrades */		NULL,
+	/* init_context */		NULL,
+	/* init_vhost */		NULL,
+	/* destroy_vhost */		NULL,
+	/* periodic_checks */		NULL,
+	/* service_flag_pending */	NULL,
+	/* handle_POLLIN */		rops_handle_POLLIN_h1,
+	/* handle_POLLOUT */		rops_handle_POLLOUT_h1,
+	/* perform_user_POLLOUT */	NULL,
+	/* callback_on_writable */	NULL,
+	/* tx_credit */			NULL,
+	/* write_role_protocol */	rops_write_role_protocol_h1,
+	/* encapsulation_parent */	NULL,
+	/* alpn_negotiated */		rops_alpn_negotiated_h1,
+	/* close_via_role_protocol */	NULL,
+	/* close_role */		NULL,
+	/* close_kill_connection */	NULL,
+	/* destroy_role */		rops_destroy_role_h1,
+	/* writeable cb clnt, srv */	{ LWS_CALLBACK_CLIENT_HTTP_WRITEABLE,
+					  LWS_CALLBACK_HTTP_WRITEABLE },
+	/* close cb clnt, srv */	{ LWS_CALLBACK_CLOSED_CLIENT_HTTP,
+					  LWS_CALLBACK_CLOSED_HTTP },
+	/* file_handle */		0,
+};

+ 27 - 0
thirdparty/libwebsockets/roles/h1/private.h

@@ -0,0 +1,27 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ *
+ *  This is included from core/private.h if LWS_ROLE_H1
+ *
+ *  Most of the h1 business is defined in the h1 / h2 common roles/http dir
+ */
+
+extern struct lws_role_ops role_ops_h1;
+#define lwsi_role_h1(wsi) (wsi->role_ops == &role_ops_h1)

+ 417 - 184
thirdparty/libwebsockets/client/client-handshake.c → thirdparty/libwebsockets/roles/http/client/client-handshake.c

@@ -1,4 +1,4 @@
-#include "private-libwebsockets.h"
+#include "core/private.h"
 
 
 static int
 static int
 lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
 lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
@@ -20,7 +20,6 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
 	{
 	{
 		hints.ai_family = PF_UNSPEC;
 		hints.ai_family = PF_UNSPEC;
 		hints.ai_socktype = SOCK_STREAM;
 		hints.ai_socktype = SOCK_STREAM;
-		hints.ai_flags = AI_CANONNAME;
 	}
 	}
 
 
 	return getaddrinfo(ads, NULL, &hints, result);
 	return getaddrinfo(ads, NULL, &hints, result);
@@ -29,15 +28,20 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
 struct lws *
 struct lws *
 lws_client_connect_2(struct lws *wsi)
 lws_client_connect_2(struct lws *wsi)
 {
 {
-	sockaddr46 sa46;
-	struct addrinfo *result;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
 	struct lws_context *context = wsi->context;
 	struct lws_context *context = wsi->context;
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+	const char *adsin;
+	struct lws *wsi_piggyback = NULL;
 	struct lws_pollfd pfd;
 	struct lws_pollfd pfd;
-	const char *cce = "", *iface;
-	int n, port;
 	ssize_t plen = 0;
 	ssize_t plen = 0;
+#endif
+	struct addrinfo *result;
 	const char *ads;
 	const char *ads;
+	sockaddr46 sa46;
+	int n, port;
+	const char *cce = "", *iface;
+	const char *meth = NULL;
 #ifdef LWS_WITH_IPV6
 #ifdef LWS_WITH_IPV6
 	char ipv6only = lws_check_opt(wsi->vhost->options,
 	char ipv6only = lws_check_opt(wsi->vhost->options,
 			LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY |
 			LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY |
@@ -48,24 +52,148 @@ lws_client_connect_2(struct lws *wsi)
 #endif
 #endif
 #endif
 #endif
 
 
-	lwsl_client("%s\n", __func__);
+	lwsl_client("%s: %p\n", __func__, wsi);
 
 
-	if (!wsi->u.hdr.ah) {
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	if (!wsi->http.ah) {
 		cce = "ah was NULL at cc2";
 		cce = "ah was NULL at cc2";
 		lwsl_err("%s\n", cce);
 		lwsl_err("%s\n", cce);
 		goto oom4;
 		goto oom4;
 	}
 	}
 
 
+	/* we can only piggyback GET or POST */
+
+	meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
+	if (meth && strcmp(meth, "GET") && strcmp(meth, "POST"))
+		goto create_new_conn;
+
+	/* we only pipeline connections that said it was okay */
+
+	if (!wsi->client_pipeline)
+		goto create_new_conn;
+
+	/*
+	 * let's take a look first and see if there are any already-active
+	 * client connections we can piggy-back on.
+	 */
+
+	adsin = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
+
+	lws_vhost_lock(wsi->vhost); /* ----------------------------------- { */
+
+	lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+				   wsi->vhost->dll_active_client_conns.next) {
+		struct lws *w = lws_container_of(d, struct lws,
+						 dll_active_client_conns);
+
+		lwsl_debug("%s: check %s %s %d %d\n", __func__, adsin,
+			   w->client_hostname_copy, wsi->c_port, w->c_port);
+
+		if (w != wsi && w->client_hostname_copy &&
+		    !strcmp(adsin, w->client_hostname_copy) &&
+#if defined(LWS_WITH_TLS)
+		    (wsi->tls.use_ssl & LCCSCF_USE_SSL) ==
+		     (w->tls.use_ssl & LCCSCF_USE_SSL) &&
+#endif
+		    wsi->c_port == w->c_port) {
+
+			/* someone else is already connected to the right guy */
+
+			/* do we know for a fact pipelining won't fly? */
+			if (w->keepalive_rejected) {
+				lwsl_info("defeating pipelining due to no "
+					    "keepalive on server\n");
+				lws_vhost_unlock(wsi->vhost); /* } ---------- */
+				goto create_new_conn;
+			}
+#if defined (LWS_WITH_HTTP2)
+			/*
+			 * h2: in usable state already: just use it without
+			 *     going through the queue
+			 */
+			if (w->client_h2_alpn &&
+			    (lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS ||
+			     lwsi_state(w) == LRS_ESTABLISHED)) {
+
+				lwsl_info("%s: just join h2 directly\n",
+						__func__);
+
+				wsi->client_h2_alpn = 1;
+				lws_wsi_h2_adopt(w, wsi);
+				lws_vhost_unlock(wsi->vhost); /* } ---------- */
+
+				return wsi;
+			}
+#endif
+
+			lwsl_info("applying %p to txn queue on %p (wsistate 0x%x)\n",
+				wsi, w, w->wsistate);
+			/*
+			 * ...let's add ourselves to his transaction queue...
+			 * we are adding ourselves at the HEAD
+			 */
+			lws_dll_lws_add_front(&wsi->dll_client_transaction_queue,
+				&w->dll_client_transaction_queue_head);
+
+			/*
+			 * h1: pipeline our headers out on him,
+			 * and wait for our turn at client transaction_complete
+			 * to take over parsing the rx.
+			 */
+
+			wsi_piggyback = w;
+
+			lws_vhost_unlock(wsi->vhost); /* } ---------- */
+			goto send_hs;
+		}
+
+	} lws_end_foreach_dll_safe(d, d1);
+
+	lws_vhost_unlock(wsi->vhost); /* } ---------------------------------- */
+
+create_new_conn:
+#endif
+
+	/*
+	 * clients who will create their own fresh connection keep a copy of
+	 * the hostname they originally connected to, in case other connections
+	 * want to use it too
+	 */
+
+	if (!wsi->client_hostname_copy)
+		wsi->client_hostname_copy =
+			strdup(lws_hdr_simple_ptr(wsi,
+					_WSI_TOKEN_CLIENT_PEER_ADDRESS));
+
+	/*
+	 * If we made our own connection, and we're doing a method that can take
+	 * a pipeline, we are an "active client connection".
+	 *
+	 * Add ourselves to the vhost list of those so that others can
+	 * piggyback on our transaction queue
+	 */
+
+	if (meth && (!strcmp(meth, "GET") || !strcmp(meth, "POST")) &&
+	    lws_dll_is_null(&wsi->dll_client_transaction_queue) &&
+	    lws_dll_is_null(&wsi->dll_active_client_conns)) {
+		lws_vhost_lock(wsi->vhost);
+		lws_dll_lws_add_front(&wsi->dll_active_client_conns,
+				      &wsi->vhost->dll_active_client_conns);
+		lws_vhost_unlock(wsi->vhost);
+	}
+
 	/*
 	/*
 	 * start off allowing ipv6 on connection if vhost allows it
 	 * start off allowing ipv6 on connection if vhost allows it
 	 */
 	 */
 	wsi->ipv6 = LWS_IPV6_ENABLED(wsi->vhost);
 	wsi->ipv6 = LWS_IPV6_ENABLED(wsi->vhost);
 
 
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+
 	/* Decide what it is we need to connect to:
 	/* Decide what it is we need to connect to:
 	 *
 	 *
 	 * Priority 1: connect to http proxy */
 	 * Priority 1: connect to http proxy */
 
 
-	if (wsi->vhost->http_proxy_port) {
+	if (wsi->vhost->http.http_proxy_port) {
 		plen = sprintf((char *)pt->serv_buf,
 		plen = sprintf((char *)pt->serv_buf,
 			"CONNECT %s:%u HTTP/1.0\x0d\x0a"
 			"CONNECT %s:%u HTTP/1.0\x0d\x0a"
 			"User-agent: libwebsockets\x0d\x0a",
 			"User-agent: libwebsockets\x0d\x0a",
@@ -78,8 +206,11 @@ lws_client_connect_2(struct lws *wsi)
 					wsi->vhost->proxy_basic_auth_token);
 					wsi->vhost->proxy_basic_auth_token);
 
 
 		plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a");
 		plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a");
-		ads = wsi->vhost->http_proxy_address;
-		port = wsi->vhost->http_proxy_port;
+		ads = wsi->vhost->http.http_proxy_address;
+		port = wsi->vhost->http.http_proxy_port;
+#else
+		if (0) {
+#endif
 
 
 #if defined(LWS_WITH_SOCKS5)
 #if defined(LWS_WITH_SOCKS5)
 
 
@@ -104,12 +235,14 @@ lws_client_connect_2(struct lws *wsi)
 	 * to whatever we decided to connect to
 	 * to whatever we decided to connect to
 	 */
 	 */
 
 
-       lwsl_notice("%s: %p: address %s\n", __func__, wsi, ads);
+       lwsl_info("%s: %p: address %s\n", __func__, wsi, ads);
 
 
        n = lws_getaddrinfo46(wsi, ads, &result);
        n = lws_getaddrinfo46(wsi, ads, &result);
 
 
 #ifdef LWS_WITH_IPV6
 #ifdef LWS_WITH_IPV6
 	if (wsi->ipv6) {
 	if (wsi->ipv6) {
+		struct sockaddr_in6 *sa6 =
+				((struct sockaddr_in6 *)result->ai_addr);
 
 
 		if (n) {
 		if (n) {
 			/* lws_getaddrinfo46 failed, there is no usable result */
 			/* lws_getaddrinfo46 failed, there is no usable result */
@@ -138,11 +271,10 @@ lws_client_connect_2(struct lws *wsi)
 			break;
 			break;
 
 
 		case AF_INET6:
 		case AF_INET6:
-			memcpy(&sa46.sa6.sin6_addr,
-			  &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr,
+			memcpy(&sa46.sa6.sin6_addr, &sa6->sin6_addr,
 						sizeof(struct in6_addr));
 						sizeof(struct in6_addr));
-			sa46.sa6.sin6_scope_id = ((struct sockaddr_in6 *)result->ai_addr)->sin6_scope_id;
-			sa46.sa6.sin6_flowinfo = ((struct sockaddr_in6 *)result->ai_addr)->sin6_flowinfo;
+			sa46.sa6.sin6_scope_id = sa6->sin6_scope_id;
+			sa46.sa6.sin6_flowinfo = sa6->sin6_flowinfo;
 			break;
 			break;
 		default:
 		default:
 			lwsl_err("Unknown address family\n");
 			lwsl_err("Unknown address family\n");
@@ -211,14 +343,11 @@ lws_client_connect_2(struct lws *wsi)
 
 
 	if (!lws_socket_is_valid(wsi->desc.sockfd)) {
 	if (!lws_socket_is_valid(wsi->desc.sockfd)) {
 
 
-#if defined(LWS_WITH_LIBUV)
-		if (LWS_LIBUV_ENABLED(context))
-			if (lws_libuv_check_watcher_active(wsi)) {
-				lwsl_warn("Waiting for libuv watcher to close\n");
-				cce = "waiting for libuv watcher to close";
-				goto oom4;
-			}
-#endif
+		if (wsi->context->event_loop_ops->check_client_connect_ok &&
+		    wsi->context->event_loop_ops->check_client_connect_ok(wsi)) {
+			cce = "waiting for event loop watcher to close";
+			goto oom4;
+		}
 
 
 #ifdef LWS_WITH_IPV6
 #ifdef LWS_WITH_IPV6
 		if (wsi->ipv6)
 		if (wsi->ipv6)
@@ -240,13 +369,12 @@ lws_client_connect_2(struct lws *wsi)
 			goto oom4;
 			goto oom4;
 		}
 		}
 
 
-		wsi->mode = LWSCM_WSCL_WAITING_CONNECT;
+		lwsi_set_state(wsi, LRS_WAITING_CONNECT);
 
 
-		lws_libev_accept(wsi, wsi->desc);
-		lws_libuv_accept(wsi, wsi->desc);
-		lws_libevent_accept(wsi, wsi->desc);
+		if (wsi->context->event_loop_ops->accept)
+			wsi->context->event_loop_ops->accept(wsi);
 
 
-		if (insert_wsi_socket_into_fds(context, wsi)) {
+		if (__insert_wsi_socket_into_fds(wsi->context, wsi)) {
 			compatible_close(wsi->desc.sockfd);
 			compatible_close(wsi->desc.sockfd);
 			cce = "insert wsi failed";
 			cce = "insert wsi failed";
 			goto oom4;
 			goto oom4;
@@ -328,10 +456,11 @@ lws_client_connect_2(struct lws *wsi)
 
 
 	lwsl_client("connected\n");
 	lwsl_client("connected\n");
 
 
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
 	/* we are connected to server, or proxy */
 	/* we are connected to server, or proxy */
 
 
 	/* http proxy */
 	/* http proxy */
-	if (wsi->vhost->http_proxy_port) {
+	if (wsi->vhost->http.http_proxy_port) {
 
 
 		/*
 		/*
 		 * OK from now on we talk via the proxy, so connect to that
 		 * OK from now on we talk via the proxy, so connect to that
@@ -340,11 +469,11 @@ lws_client_connect_2(struct lws *wsi)
 		 * leaving old string/frag there but unreferenced)
 		 * leaving old string/frag there but unreferenced)
 		 */
 		 */
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
-					  wsi->vhost->http_proxy_address))
+					  wsi->vhost->http.http_proxy_address))
 			goto failed;
 			goto failed;
-		wsi->c_port = wsi->vhost->http_proxy_port;
+		wsi->c_port = wsi->vhost->http.http_proxy_port;
 
 
-		n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen,
+		n = send(wsi->desc.sockfd, (char *)pt->serv_buf, (int)plen,
 			 MSG_NOSIGNAL);
 			 MSG_NOSIGNAL);
 		if (n < 0) {
 		if (n < 0) {
 			lwsl_debug("ERROR writing to proxy socket\n");
 			lwsl_debug("ERROR writing to proxy socket\n");
@@ -355,10 +484,11 @@ lws_client_connect_2(struct lws *wsi)
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
 				AWAITING_TIMEOUT);
 				AWAITING_TIMEOUT);
 
 
-		wsi->mode = LWSCM_WSCL_WAITING_PROXY_REPLY;
+		lwsi_set_state(wsi, LRS_WAITING_PROXY_REPLY);
 
 
 		return wsi;
 		return wsi;
 	}
 	}
+#endif
 #if defined(LWS_WITH_SOCKS5)
 #if defined(LWS_WITH_SOCKS5)
 	/* socks proxy */
 	/* socks proxy */
 	else if (wsi->vhost->socks_proxy_port) {
 	else if (wsi->vhost->socks_proxy_port) {
@@ -373,72 +503,105 @@ lws_client_connect_2(struct lws *wsi)
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY,
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY,
 				AWAITING_TIMEOUT);
 				AWAITING_TIMEOUT);
 
 
-		wsi->mode = LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY;
+		lwsi_set_state(wsi, LRS_WAITING_SOCKS_GREETING_REPLY);
 
 
 		return wsi;
 		return wsi;
 	}
 	}
 #endif
 #endif
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+send_hs:
 
 
-	/*
-	 * provoke service to issue the handshake directly
-	 * we need to do it this way because in the proxy case, this is the
-	 * next state and executed only if and when we get a good proxy
-	 * response inside the state machine... but notice in SSL case this
-	 * may not have sent anything yet with 0 return, and won't until some
-	 * many retries from main loop.  To stop that becoming endless,
-	 * cover with a timeout.
-	 */
+	if (wsi_piggyback &&
+	    !lws_dll_is_null(&wsi->dll_client_transaction_queue)) {
+		/*
+		 * We are pipelining on an already-established connection...
+		 * we can skip tls establishment.
+		 */
+
+		lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
+
+		/*
+		 * we can't send our headers directly, because they have to
+		 * be sent when the parent is writeable.  The parent will check
+		 * for anybody on his client transaction queue that is in
+		 * LRS_H1C_ISSUE_HANDSHAKE2, and let them write.
+		 *
+		 * If we are trying to do this too early, before the master
+		 * connection has written his own headers, then it will just
+		 * wait in the queue until it's possible to send them.
+		 */
+		lws_callback_on_writable(wsi_piggyback);
+		lwsl_info("%s: wsi %p: waiting to send headers (parent state %x)\n",
+			    __func__, wsi, lwsi_state(wsi_piggyback));
+	} else {
+		lwsl_info("%s: wsi %p: client creating own connection\n",
+			    __func__, wsi);
 
 
-	lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE,
-			AWAITING_TIMEOUT);
+		/* we are making our own connection */
+		lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE);
+
+		/*
+		 * provoke service to issue the handshake directly.
+		 *
+		 * we need to do it this way because in the proxy case, this is
+		 * the next state and executed only if and when we get a good
+		 * proxy response inside the state machine... but notice in
+		 * SSL case this may not have sent anything yet with 0 return,
+		 * and won't until many retries from main loop.  To stop that
+		 * becoming endless, cover with a timeout.
+		 */
 
 
-	wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE;
-	pfd.fd = wsi->desc.sockfd;
-	pfd.events = LWS_POLLIN;
-	pfd.revents = LWS_POLLIN;
+		lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE,
+				AWAITING_TIMEOUT);
 
 
-	n = lws_service_fd(context, &pfd);
-	if (n < 0) {
-		cce = "first service failed";
-		goto failed;
-	}
-	if (n) /* returns 1 on failure after closing wsi */
-		return NULL;
+		pfd.fd = wsi->desc.sockfd;
+		pfd.events = LWS_POLLIN;
+		pfd.revents = LWS_POLLIN;
 
 
+		n = lws_service_fd(context, &pfd);
+		if (n < 0) {
+			cce = "first service failed";
+			goto failed;
+		}
+		if (n) /* returns 1 on failure after closing wsi */
+			return NULL;
+	}
+#endif
 	return wsi;
 	return wsi;
 
 
 oom4:
 oom4:
-	/* we're closing, losing some rx is OK */
-	lws_header_table_force_to_detachable_state(wsi);
-
-	if (wsi->mode == LWSCM_HTTP_CLIENT ||
-	    wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED ||
-	    wsi->mode == LWSCM_WSCL_WAITING_CONNECT) {
-		wsi->vhost->protocols[0].callback(wsi,
+	if (lwsi_role_client(wsi) && lwsi_state_est(wsi)) {
+		wsi->protocol->callback(wsi,
 			LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
 			LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
 			wsi->user_space, (void *)cce, strlen(cce));
 			wsi->user_space, (void *)cce, strlen(cce));
 		wsi->already_did_cce = 1;
 		wsi->already_did_cce = 1;
 	}
 	}
 	/* take care that we might be inserted in fds already */
 	/* take care that we might be inserted in fds already */
-	if (wsi->position_in_fds_table != -1)
+	if (wsi->position_in_fds_table != LWS_NO_FDS_POS)
 		goto failed1;
 		goto failed1;
 	lws_remove_from_timeout_list(wsi);
 	lws_remove_from_timeout_list(wsi);
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
 	lws_header_table_detach(wsi, 0);
 	lws_header_table_detach(wsi, 0);
+#endif
+	lws_client_stash_destroy(wsi);
+	lws_free_set_NULL(wsi->client_hostname_copy);
 	lws_free(wsi);
 	lws_free(wsi);
 
 
 	return NULL;
 	return NULL;
 
 
 failed:
 failed:
-	wsi->vhost->protocols[0].callback(wsi,
+	wsi->protocol->callback(wsi,
 		LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
 		LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
 		wsi->user_space, (void *)cce, strlen(cce));
 		wsi->user_space, (void *)cce, strlen(cce));
 	wsi->already_did_cce = 1;
 	wsi->already_did_cce = 1;
 failed1:
 failed1:
-	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2");
 
 
 	return NULL;
 	return NULL;
 }
 }
 
 
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+
 /**
 /**
  * lws_client_reset() - retarget a connected wsi to start over with a new connection (ie, redirect)
  * lws_client_reset() - retarget a connected wsi to start over with a new connection (ie, redirect)
  *			this only works if still in HTTP, ie, not upgraded yet
  *			this only works if still in HTTP, ie, not upgraded yet
@@ -452,7 +615,8 @@ LWS_VISIBLE struct lws *
 lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
 lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
 		 const char *path, const char *host)
 		 const char *path, const char *host)
 {
 {
-	char origin[300] = "", protocol[300] = "", method[32] = "", iface[16] = "", *p;
+	char origin[300] = "", protocol[300] = "", method[32] = "",
+	     iface[16] = "", alpn[32] = "", *p;
 	struct lws *wsi = *pwsi;
 	struct lws *wsi = *pwsi;
 
 
 	if (wsi->redirects == 3) {
 	if (wsi->redirects == 3) {
@@ -463,49 +627,42 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
 
 
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN);
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN);
 	if (p)
 	if (p)
-		strncpy(origin, p, sizeof(origin) - 1);
+		lws_strncpy(origin, p, sizeof(origin));
 
 
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
 	if (p)
 	if (p)
-		strncpy(protocol, p, sizeof(protocol) - 1);
+		lws_strncpy(protocol, p, sizeof(protocol));
 
 
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
 	if (p)
 	if (p)
-		strncpy(method, p, sizeof(method) - 1);
+		lws_strncpy(method, p, sizeof(method));
 
 
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
 	if (p)
 	if (p)
-		strncpy(method, p, sizeof(iface) - 1);
+		lws_strncpy(iface, p, sizeof(iface));
+
+	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ALPN);
+	if (p)
+		lws_strncpy(alpn, p, sizeof(alpn));
 
 
 	lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d\n",
 	lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d\n",
 		   address, port, path, ssl);
 		   address, port, path, ssl);
 
 
 	/* close the connection by hand */
 	/* close the connection by hand */
 
 
-#ifdef LWS_OPENSSL_SUPPORT
+#if defined(LWS_WITH_TLS)
 	lws_ssl_close(wsi);
 	lws_ssl_close(wsi);
 #endif
 #endif
 
 
-#ifdef LWS_WITH_LIBUV
-	if (LWS_LIBUV_ENABLED(wsi->context)) {
-		lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi);
-		/*
-		 * libuv has to do his own close handle processing asynchronously
-		 * but once it starts we can do everything else synchronously,
-		 * including trash wsi->desc.sockfd since it took a copy.
-		 *
-		 * When it completes it will call compatible_close()
-		 */
-		lws_libuv_closehandle_manually(wsi);
-	} else
-#else
-	compatible_close(wsi->desc.sockfd);
-#endif
+	if (wsi->context->event_loop_ops->close_handle_manually)
+		wsi->context->event_loop_ops->close_handle_manually(wsi);
+	else
+		compatible_close(wsi->desc.sockfd);
 
 
-	remove_wsi_socket_from_fds(wsi);
+	__remove_wsi_socket_from_fds(wsi);
 
 
-#ifdef LWS_OPENSSL_SUPPORT
-	wsi->use_ssl = ssl;
+#if defined(LWS_WITH_TLS)
+	wsi->tls.use_ssl = ssl;
 #else
 #else
 	if (ssl) {
 	if (ssl) {
 		lwsl_err("%s: not configured for ssl\n", __func__);
 		lwsl_err("%s: not configured for ssl\n", __func__);
@@ -514,12 +671,12 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
 #endif
 #endif
 
 
 	wsi->desc.sockfd = LWS_SOCK_INVALID;
 	wsi->desc.sockfd = LWS_SOCK_INVALID;
-	wsi->state = LWSS_CLIENT_UNCONNECTED;
+	lwsi_set_state(wsi, LRS_UNCONNECTED);
 	wsi->protocol = NULL;
 	wsi->protocol = NULL;
 	wsi->pending_timeout = NO_PENDING_TIMEOUT;
 	wsi->pending_timeout = NO_PENDING_TIMEOUT;
 	wsi->c_port = port;
 	wsi->c_port = port;
 	wsi->hdr_parsing_completed = 0;
 	wsi->hdr_parsing_completed = 0;
-	_lws_header_table_reset(wsi->u.hdr.ah);
+	_lws_header_table_reset(wsi->http.ah);
 
 
 	if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address))
 	if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address))
 		return NULL;
 		return NULL;
@@ -544,6 +701,10 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE,
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE,
 					  iface))
 					  iface))
 			return NULL;
 			return NULL;
+	if (alpn[0])
+		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ALPN,
+					  alpn))
+			return NULL;
 
 
 	origin[0] = '/';
 	origin[0] = '/';
 	strncpy(&origin[1], path, sizeof(origin) - 2);
 	strncpy(&origin[1], path, sizeof(origin) - 2);
@@ -683,26 +844,68 @@ html_parser_cb(const hubbub_token *token, void *pw)
 }
 }
 #endif
 #endif
 
 
+#endif
+
+static char *
+lws_strdup(const char *s)
+{
+	char *d = lws_malloc(strlen(s) + 1, "strdup");
+
+	if (d)
+		strcpy(d, s);
+
+	return d;
+}
+
+void
+lws_client_stash_destroy(struct lws *wsi)
+{
+	if (!wsi || !wsi->stash)
+		return;
+
+	lws_free_set_NULL(wsi->stash->address);
+	lws_free_set_NULL(wsi->stash->path);
+	lws_free_set_NULL(wsi->stash->host);
+	lws_free_set_NULL(wsi->stash->origin);
+	lws_free_set_NULL(wsi->stash->protocol);
+	lws_free_set_NULL(wsi->stash->method);
+	lws_free_set_NULL(wsi->stash->iface);
+	lws_free_set_NULL(wsi->stash->alpn);
+
+	lws_free_set_NULL(wsi->stash);
+}
+
 LWS_VISIBLE struct lws *
 LWS_VISIBLE struct lws *
 lws_client_connect_via_info(struct lws_client_connect_info *i)
 lws_client_connect_via_info(struct lws_client_connect_info *i)
 {
 {
 	struct lws *wsi;
 	struct lws *wsi;
-	int v = SPEC_LATEST_SUPPORTED;
 	const struct lws_protocols *p;
 	const struct lws_protocols *p;
+	const char *local = i->protocol;
 
 
 	if (i->context->requested_kill)
 	if (i->context->requested_kill)
 		return NULL;
 		return NULL;
 
 
 	if (!i->context->protocol_init_done)
 	if (!i->context->protocol_init_done)
 		lws_protocol_init(i->context);
 		lws_protocol_init(i->context);
+	/*
+	 * If we have .local_protocol_name, use it to select the
+	 * local protocol handler to bind to.  Otherwise use .protocol if
+	 * http[s].
+	 */
+	if (i->local_protocol_name)
+		local = i->local_protocol_name;
 
 
 	wsi = lws_zalloc(sizeof(struct lws), "client wsi");
 	wsi = lws_zalloc(sizeof(struct lws), "client wsi");
 	if (wsi == NULL)
 	if (wsi == NULL)
 		goto bail;
 		goto bail;
 
 
 	wsi->context = i->context;
 	wsi->context = i->context;
+#if defined(LWS_ROLE_H1)
 	/* assert the mode and union status (hdr) clearly */
 	/* assert the mode and union status (hdr) clearly */
-	lws_union_transition(wsi, LWSCM_HTTP_CLIENT);
+	lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1);
+#else
+	lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_raw_skt);
+#endif
 	wsi->desc.sockfd = LWS_SOCK_INVALID;
 	wsi->desc.sockfd = LWS_SOCK_INVALID;
 
 
 	/* 1) fill up the wsi with stuff from the connect_info as far as it
 	/* 1) fill up the wsi with stuff from the connect_info as far as it
@@ -710,26 +913,52 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
 	 * not even be able to get ahold of an ah at this point.
 	 * not even be able to get ahold of an ah at this point.
 	 */
 	 */
 
 
-	/* -1 means just use latest supported */
-	if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one)
-		v = i->ietf_version_or_minus_one;
+	if (!i->method) /* ie, ws */
+#if defined(LWS_ROLE_WS)
+		if (lws_create_client_ws_object(i, wsi))
+			return NULL;
+#else
+		return NULL;
+#endif
 
 
-	wsi->ietf_spec_revision = v;
 	wsi->user_space = NULL;
 	wsi->user_space = NULL;
-	wsi->state = LWSS_CLIENT_UNCONNECTED;
 	wsi->pending_timeout = NO_PENDING_TIMEOUT;
 	wsi->pending_timeout = NO_PENDING_TIMEOUT;
-	wsi->position_in_fds_table = -1;
+	wsi->position_in_fds_table = LWS_NO_FDS_POS;
 	wsi->c_port = i->port;
 	wsi->c_port = i->port;
 	wsi->vhost = i->vhost;
 	wsi->vhost = i->vhost;
 	if (!wsi->vhost)
 	if (!wsi->vhost)
 		wsi->vhost = i->context->vhost_list;
 		wsi->vhost = i->context->vhost_list;
 
 
+	if (!wsi->vhost) {
+		lwsl_err("At least one vhost in the context is required\n");
+
+		goto bail;
+	}
+
 	wsi->protocol = &wsi->vhost->protocols[0];
 	wsi->protocol = &wsi->vhost->protocols[0];
+	wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE);
 
 
-	/* for http[s] connection, allow protocol selection by name */
+	/* reasonable place to start */
+	lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
+#if defined(LWS_ROLE_H1)
+			&role_ops_h1);
+#else
+	&role_ops_raw_skt);
+#endif
 
 
-	if (i->method && i->vhost && i->protocol) {
-		p = lws_vhost_name_to_protocol(i->vhost, i->protocol);
+	/*
+	 * 1) for http[s] connection, allow protocol selection by name
+	 * 2) for ws[s], if local_protocol_name given also use it for
+	 *    local protocol binding... this defeats the server
+	 *    protocol negotiation if so
+	 *
+	 * Otherwise leave at protocols[0]... the server will tell us
+	 * which protocol we are associated with since we can give it a
+	 * list.
+	 */
+	if (/*(i->method || i->local_protocol_name) && */local) {
+		lwsl_info("binding to %s\n", local);
+		p = lws_vhost_name_to_protocol(wsi->vhost, local);
 		if (p)
 		if (p)
 			wsi->protocol = p;
 			wsi->protocol = p;
 	}
 	}
@@ -745,10 +974,10 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
 			if (lws_ensure_user_space(wsi))
 			if (lws_ensure_user_space(wsi))
 				goto bail;
 				goto bail;
 
 
-#ifdef LWS_OPENSSL_SUPPORT
-	wsi->use_ssl = i->ssl_connection;
+#if defined(LWS_WITH_TLS)
+	wsi->tls.use_ssl = i->ssl_connection;
 #else
 #else
-	if (i->ssl_connection) {
+	if (i->ssl_connection & LCCSCF_USE_SSL) {
 		lwsl_err("libwebsockets not configured for ssl\n");
 		lwsl_err("libwebsockets not configured for ssl\n");
 		goto bail;
 		goto bail;
 	}
 	}
@@ -760,47 +989,63 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
 	 * things pointed to have gone out of scope.
 	 * things pointed to have gone out of scope.
 	 */
 	 */
 
 
-	wsi->u.hdr.stash = lws_malloc(sizeof(*wsi->u.hdr.stash), "client stash");
-	if (!wsi->u.hdr.stash) {
+	wsi->stash = lws_zalloc(sizeof(*wsi->stash), "client stash");
+	if (!wsi->stash) {
 		lwsl_err("%s: OOM\n", __func__);
 		lwsl_err("%s: OOM\n", __func__);
-		goto bail;
+		goto bail1;
 	}
 	}
 
 
-	wsi->u.hdr.stash->origin[0] = '\0';
-	wsi->u.hdr.stash->protocol[0] = '\0';
-	wsi->u.hdr.stash->method[0] = '\0';
-	wsi->u.hdr.stash->iface[0] = '\0';
-
-	strncpy(wsi->u.hdr.stash->address, i->address,
-		sizeof(wsi->u.hdr.stash->address) - 1);
-	strncpy(wsi->u.hdr.stash->path, i->path,
-		sizeof(wsi->u.hdr.stash->path) - 1);
-	strncpy(wsi->u.hdr.stash->host, i->host,
-		sizeof(wsi->u.hdr.stash->host) - 1);
-	if (i->origin)
-		strncpy(wsi->u.hdr.stash->origin, i->origin,
-			sizeof(wsi->u.hdr.stash->origin) - 1);
-	if (i->protocol)
-		strncpy(wsi->u.hdr.stash->protocol, i->protocol,
-			sizeof(wsi->u.hdr.stash->protocol) - 1);
-	if (i->method)
-		strncpy(wsi->u.hdr.stash->method, i->method,
-			sizeof(wsi->u.hdr.stash->method) - 1);
-	if (i->iface)
-		strncpy(wsi->u.hdr.stash->iface, i->iface,
-			sizeof(wsi->u.hdr.stash->iface) - 1);
-
-	wsi->u.hdr.stash->address[sizeof(wsi->u.hdr.stash->address) - 1] = '\0';
-	wsi->u.hdr.stash->path[sizeof(wsi->u.hdr.stash->path) - 1] = '\0';
-	wsi->u.hdr.stash->host[sizeof(wsi->u.hdr.stash->host) - 1] = '\0';
-	wsi->u.hdr.stash->origin[sizeof(wsi->u.hdr.stash->origin) - 1] = '\0';
-	wsi->u.hdr.stash->protocol[sizeof(wsi->u.hdr.stash->protocol) - 1] = '\0';
-	wsi->u.hdr.stash->method[sizeof(wsi->u.hdr.stash->method) - 1] = '\0';
-	wsi->u.hdr.stash->iface[sizeof(wsi->u.hdr.stash->iface) - 1] = '\0';
+	wsi->stash->address = lws_strdup(i->address);
+	wsi->stash->path = lws_strdup(i->path);
+	wsi->stash->host = lws_strdup(i->host);
+
+	if (!wsi->stash->address || !wsi->stash->path || !wsi->stash->host)
+		goto bail1;
+
+	if (i->origin) {
+		wsi->stash->origin = lws_strdup(i->origin);
+		if (!wsi->stash->origin)
+			goto bail1;
+	}
+	if (i->protocol) {
+		wsi->stash->protocol = lws_strdup(i->protocol);
+		if (!wsi->stash->protocol)
+			goto bail1;
+	}
+	if (i->method) {
+		wsi->stash->method = lws_strdup(i->method);
+		if (!wsi->stash->method)
+			goto bail1;
+	}
+	if (i->iface) {
+		wsi->stash->iface = lws_strdup(i->iface);
+		if (!wsi->stash->iface)
+			goto bail1;
+	}
+	 /*
+	  * For ws, default to http/1.1 only.  If i->alpn is set, defer to
+	  * whatever he has set in there (eg, "h2").
+	  *
+	  * The problem is he has to commit to h2 before he can find out if the
+	  * server has the SETTINGS for ws-over-h2 enabled; if not then ws is
+	  * not possible on that connection.  So we only try it if he
+	  * assertively said to use h2 alpn.
+	  */
+	if (!i->method && !i->alpn) {
+		wsi->stash->alpn = lws_strdup("http/1.1");
+		if (!wsi->stash->alpn)
+			goto bail1;
+	} else
+		if (i->alpn) {
+			wsi->stash->alpn = lws_strdup(i->alpn);
+			if (!wsi->stash->alpn)
+				goto bail1;
+		}
 
 
 	if (i->pwsi)
 	if (i->pwsi)
 		*i->pwsi = wsi;
 		*i->pwsi = wsi;
 
 
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
 	/* if we went on the waiting list, no probs just return the wsi
 	/* if we went on the waiting list, no probs just return the wsi
 	 * when we get the ah, now or later, he will call
 	 * when we get the ah, now or later, he will call
 	 * lws_client_connect_via_info2() below.
 	 * lws_client_connect_via_info2() below.
@@ -810,9 +1055,11 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
 		 * if we failed here, the connection is already closed
 		 * if we failed here, the connection is already closed
 		 * and freed.
 		 * and freed.
 		 */
 		 */
-		goto bail1;
+		goto bail2;
 	}
 	}
 
 
+#endif
+
 	if (i->parent_wsi) {
 	if (i->parent_wsi) {
 		lwsl_info("%s: created child %p of parent %p\n", __func__,
 		lwsl_info("%s: created child %p of parent %p\n", __func__,
 				wsi, i->parent_wsi);
 				wsi, i->parent_wsi);
@@ -822,17 +1069,21 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
 	}
 	}
 #ifdef LWS_WITH_HTTP_PROXY
 #ifdef LWS_WITH_HTTP_PROXY
 	if (i->uri_replace_to)
 	if (i->uri_replace_to)
-		wsi->rw = lws_rewrite_create(wsi, html_parser_cb,
+		wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb,
 					     i->uri_replace_from,
 					     i->uri_replace_from,
 					     i->uri_replace_to);
 					     i->uri_replace_to);
 #endif
 #endif
 
 
 	return wsi;
 	return wsi;
 
 
+bail1:
+	lws_client_stash_destroy(wsi);
+
 bail:
 bail:
 	lws_free(wsi);
 	lws_free(wsi);
-
-bail1:
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+bail2:
+#endif
 	if (i->pwsi)
 	if (i->pwsi)
 		*i->pwsi = NULL;
 		*i->pwsi = NULL;
 
 
@@ -842,28 +1093,27 @@ bail1:
 struct lws *
 struct lws *
 lws_client_connect_via_info2(struct lws *wsi)
 lws_client_connect_via_info2(struct lws *wsi)
 {
 {
-	struct client_info_stash *stash = wsi->u.hdr.stash;
+	struct client_info_stash *stash = wsi->stash;
 
 
 	if (!stash)
 	if (!stash)
 		return wsi;
 		return wsi;
 
 
 	/*
 	/*
 	 * we're not necessarily in a position to action these right away,
 	 * we're not necessarily in a position to action these right away,
-	 * stash them... we only need during connect phase so u.hdr is fine
+	 * stash them... we only need during connect phase so into a temp
+	 * allocated stash
 	 */
 	 */
 	if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
 	if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
 				  stash->address))
 				  stash->address))
 		goto bail1;
 		goto bail1;
 
 
-	/* these only need u.hdr lifetime as well */
-
 	if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path))
 	if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path))
 		goto bail1;
 		goto bail1;
 
 
 	if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host))
 	if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host))
 		goto bail1;
 		goto bail1;
 
 
-	if (stash->origin[0])
+	if (stash->origin)
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN,
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN,
 					  stash->origin))
 					  stash->origin))
 			goto bail1;
 			goto bail1;
@@ -871,45 +1121,28 @@ lws_client_connect_via_info2(struct lws *wsi)
 	 * this is a list of protocols we tell the server we're okay with
 	 * this is a list of protocols we tell the server we're okay with
 	 * stash it for later when we compare server response with it
 	 * stash it for later when we compare server response with it
 	 */
 	 */
-	if (stash->protocol[0])
+	if (stash->protocol)
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
 					  stash->protocol))
 					  stash->protocol))
 			goto bail1;
 			goto bail1;
-	if (stash->method[0])
+	if (stash->method)
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD,
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD,
 					  stash->method))
 					  stash->method))
 			goto bail1;
 			goto bail1;
-	if (stash->iface[0])
+	if (stash->iface)
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE,
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE,
 					  stash->iface))
 					  stash->iface))
 			goto bail1;
 			goto bail1;
+	if (stash->alpn)
+		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ALPN,
+					  stash->alpn))
+			goto bail1;
 
 
 #if defined(LWS_WITH_SOCKS5)
 #if defined(LWS_WITH_SOCKS5)
 	if (!wsi->vhost->socks_proxy_port)
 	if (!wsi->vhost->socks_proxy_port)
-		lws_free_set_NULL(wsi->u.hdr.stash);
+		lws_client_stash_destroy(wsi);
 #endif
 #endif
 
 
-	/*
-	 * Check with each extension if it is able to route and proxy this
-	 * connection for us.  For example, an extension like x-google-mux
-	 * can handle this and then we don't need an actual socket for this
-	 * connection.
-	 */
-
-	if (lws_ext_cb_all_exts(wsi->context, wsi,
-				LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION,
-				(void *)stash->address,
-				wsi->c_port) > 0) {
-		lwsl_client("lws_client_connect: ext handling conn\n");
-
-		lws_set_timeout(wsi,
-			PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE,
-			        AWAITING_TIMEOUT);
-
-		wsi->mode = LWSCM_WSCL_WAITING_EXTENSION_CONNECT;
-		return wsi;
-	}
-	lwsl_client("lws_client_connect: direct conn\n");
 	wsi->context->count_wsi_allocated++;
 	wsi->context->count_wsi_allocated++;
 
 
 	return lws_client_connect_2(wsi);
 	return lws_client_connect_2(wsi);
@@ -917,7 +1150,7 @@ lws_client_connect_via_info2(struct lws *wsi)
 bail1:
 bail1:
 #if defined(LWS_WITH_SOCKS5)
 #if defined(LWS_WITH_SOCKS5)
 	if (!wsi->vhost->socks_proxy_port)
 	if (!wsi->vhost->socks_proxy_port)
-		lws_free_set_NULL(wsi->u.hdr.stash);
+		lws_free_set_NULL(wsi->stash);
 #endif
 #endif
 
 
 	return NULL;
 	return NULL;
@@ -1003,14 +1236,14 @@ void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
 		/* length of the user name */
 		/* length of the user name */
 		pt->serv_buf[len++] = n;
 		pt->serv_buf[len++] = n;
 		/* user name */
 		/* user name */
-		strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user,
-			context->pt_serv_buf_size - len);
+		lws_strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user,
+			context->pt_serv_buf_size - len + 1);
 		len += n;
 		len += n;
 		/* length of the password */
 		/* length of the password */
 		pt->serv_buf[len++] = passwd_len;
 		pt->serv_buf[len++] = passwd_len;
 		/* password */
 		/* password */
-		strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password,
-			context->pt_serv_buf_size - len);
+		lws_strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password,
+			context->pt_serv_buf_size - len + 1);
 		len += passwd_len;
 		len += passwd_len;
 		break;
 		break;
 
 
@@ -1029,9 +1262,9 @@ void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
 		n = len++;
 		n = len++;
 
 
 		/* the address we tell SOCKS proxy to connect to */
 		/* the address we tell SOCKS proxy to connect to */
-		strncpy((char *)&(pt->serv_buf[len]), wsi->u.hdr.stash->address,
-			context->pt_serv_buf_size - len);
-		len += strlen(wsi->u.hdr.stash->address);
+		lws_strncpy((char *)&(pt->serv_buf[len]), wsi->stash->address,
+			context->pt_serv_buf_size - len + 1);
+		len += strlen(wsi->stash->address);
 		net_num = htons(wsi->c_port);
 		net_num = htons(wsi->c_port);
 
 
 		/* the port we tell SOCKS proxy to connect to */
 		/* the port we tell SOCKS proxy to connect to */
@@ -1039,7 +1272,7 @@ void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
 		pt->serv_buf[len++] = p[1];
 		pt->serv_buf[len++] = p[1];
 
 
 		/* the length of the address, excluding port */
 		/* the length of the address, excluding port */
-		pt->serv_buf[n] = strlen(wsi->u.hdr.stash->address);
+		pt->serv_buf[n] = strlen(wsi->stash->address);
 		break;
 		break;
 		
 		
 	default:
 	default:

+ 1231 - 0
thirdparty/libwebsockets/roles/http/client/client.c

@@ -0,0 +1,1231 @@
+/*
+ * libwebsockets - lib/client/client.c
+ *
+ * Copyright (C) 2010-2017 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "core/private.h"
+
+LWS_VISIBLE LWS_EXTERN void
+lws_client_http_body_pending(struct lws *wsi, int something_left_to_send)
+{
+	wsi->client_http_body_pending = !!something_left_to_send;
+}
+
+/*
+ * return self, or queued client wsi we are acting on behalf of
+ *
+ * That is the TAIL of the queue (new queue elements are added at the HEAD)
+ */
+
+struct lws *
+lws_client_wsi_effective(struct lws *wsi)
+{
+	struct lws_dll_lws *tail = NULL;
+
+	if (!wsi->transaction_from_pipeline_queue ||
+	    !wsi->dll_client_transaction_queue_head.next)
+		return wsi;
+
+	lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+				   wsi->dll_client_transaction_queue_head.next) {
+		tail = d;
+	} lws_end_foreach_dll_safe(d, d1);
+
+	return lws_container_of(tail, struct lws,
+				  dll_client_transaction_queue);
+}
+
+/*
+ * return self or the guy we are queued under
+ *
+ * REQUIRES VHOST LOCK HELD
+ */
+
+static struct lws *
+_lws_client_wsi_master(struct lws *wsi)
+{
+	struct lws *wsi_eff = wsi;
+	struct lws_dll_lws *d;
+
+	d = wsi->dll_client_transaction_queue.prev;
+	while (d) {
+		wsi_eff = lws_container_of(d, struct lws,
+					dll_client_transaction_queue_head);
+
+		d = d->prev;
+	}
+
+	return wsi_eff;
+}
+
+int
+lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
+			  struct lws *wsi_conn)
+{
+	struct lws_context *context = wsi->context;
+	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+	char *p = (char *)&pt->serv_buf[0];
+	struct lws *w;
+#if defined(LWS_WITH_TLS)
+	char ebuf[128];
+#endif
+	const char *cce = NULL;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	ssize_t len = 0;
+	unsigned char c;
+#endif
+	char *sb = p;
+	int n = 0;
+#if defined(LWS_WITH_SOCKS5)
+	char conn_mode = 0, pending_timeout = 0;
+#endif
+
+	if ((pollfd->revents & LWS_POLLOUT) &&
+	     wsi->keepalive_active &&
+	     wsi->dll_client_transaction_queue_head.next) {
+		struct lws *wfound = NULL;
+
+		lwsl_debug("%s: pollout HANDSHAKE2\n", __func__);
+
+		/*
+		 * We have a transaction queued that wants to pipeline.
+		 *
+		 * We have to allow it to send headers strictly in the order
+		 * that it was queued, ie, tail-first.
+		 */
+		lws_vhost_lock(wsi->vhost);
+		lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+					   wsi->dll_client_transaction_queue_head.next) {
+			struct lws *w = lws_container_of(d, struct lws,
+						  dll_client_transaction_queue);
+
+			lwsl_debug("%s: %p states 0x%x\n", __func__, w, w->wsistate);
+			if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2)
+				wfound = w;
+		} lws_end_foreach_dll_safe(d, d1);
+
+		if (wfound) {
+			/*
+			 * pollfd has the master sockfd in it... we
+			 * need to use that in HANDSHAKE2 to understand
+			 * which wsi to actually write on
+			 */
+			lws_client_socket_service(wfound, pollfd, wsi);
+			lws_callback_on_writable(wsi);
+		} else
+			lwsl_debug("%s: didn't find anything in txn q in HS2\n",
+							   __func__);
+
+		lws_vhost_unlock(wsi->vhost);
+
+		return 0;
+	}
+
+	switch (lwsi_state(wsi)) {
+
+	case LRS_WAITING_CONNECT:
+
+		/*
+		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
+		 * timeout protection set in client-handshake.c
+		 */
+
+		if (!lws_client_connect_2(wsi)) {
+			/* closed */
+			lwsl_client("closed\n");
+			return -1;
+		}
+
+		/* either still pending connection, or changed mode */
+		return 0;
+
+#if defined(LWS_WITH_SOCKS5)
+	/* SOCKS Greeting Reply */
+	case LRS_WAITING_SOCKS_GREETING_REPLY:
+	case LRS_WAITING_SOCKS_AUTH_REPLY:
+	case LRS_WAITING_SOCKS_CONNECT_REPLY:
+
+		/* handle proxy hung up on us */
+
+		if (pollfd->revents & LWS_POLLHUP) {
+			lwsl_warn("SOCKS connection %p (fd=%d) dead\n",
+				  (void *)wsi, pollfd->fd);
+			goto bail3;
+		}
+
+		n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
+		if (n < 0) {
+			if (LWS_ERRNO == LWS_EAGAIN) {
+				lwsl_debug("SOCKS read EAGAIN, retrying\n");
+				return 0;
+			}
+			lwsl_err("ERROR reading from SOCKS socket\n");
+			goto bail3;
+		}
+
+		switch (lwsi_state(wsi)) {
+
+		case LRS_WAITING_SOCKS_GREETING_REPLY:
+			if (pt->serv_buf[0] != SOCKS_VERSION_5)
+				goto socks_reply_fail;
+
+			if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) {
+				lwsl_client("SOCKS GR: No Auth Method\n");
+				socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
+				conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
+				pending_timeout =
+				   PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
+				goto socks_send;
+			}
+
+			if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) {
+				lwsl_client("SOCKS GR: User/Pw Method\n");
+				socks_generate_msg(wsi,
+						   SOCKS_MSG_USERNAME_PASSWORD,
+						   &len);
+				conn_mode = LRS_WAITING_SOCKS_AUTH_REPLY;
+				pending_timeout =
+				      PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY;
+				goto socks_send;
+			}
+			goto socks_reply_fail;
+
+		case LRS_WAITING_SOCKS_AUTH_REPLY:
+			if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 ||
+			    pt->serv_buf[1] != SOCKS_SUBNEGOTIATION_STATUS_SUCCESS)
+				goto socks_reply_fail;
+
+			lwsl_client("SOCKS password OK, sending connect\n");
+			socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
+			conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
+			pending_timeout =
+				   PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
+socks_send:
+			n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len,
+				 MSG_NOSIGNAL);
+			if (n < 0) {
+				lwsl_debug("ERROR writing to socks proxy\n");
+				goto bail3;
+			}
+
+			lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT);
+			lwsi_set_state(wsi, conn_mode);
+			break;
+
+socks_reply_fail:
+			lwsl_notice("socks reply: v%d, err %d\n",
+				    pt->serv_buf[0], pt->serv_buf[1]);
+			goto bail3;
+
+		case LRS_WAITING_SOCKS_CONNECT_REPLY:
+			if (pt->serv_buf[0] != SOCKS_VERSION_5 ||
+			    pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS)
+				goto socks_reply_fail;
+
+			lwsl_client("socks connect OK\n");
+
+			/* free stash since we are done with it */
+			lws_client_stash_destroy(wsi);
+			if (lws_hdr_simple_create(wsi,
+						  _WSI_TOKEN_CLIENT_PEER_ADDRESS,
+						  wsi->vhost->socks_proxy_address))
+				goto bail3;
+
+			wsi->c_port = wsi->vhost->socks_proxy_port;
+
+			/* clear his proxy connection timeout */
+			lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+			goto start_ws_handshake;
+		}
+		break;
+#endif
+
+	case LRS_WAITING_PROXY_REPLY:
+
+		/* handle proxy hung up on us */
+
+		if (pollfd->revents & LWS_POLLHUP) {
+
+			lwsl_warn("Proxy connection %p (fd=%d) dead\n",
+				  (void *)wsi, pollfd->fd);
+
+			goto bail3;
+		}
+
+		n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
+		if (n < 0) {
+			if (LWS_ERRNO == LWS_EAGAIN) {
+				lwsl_debug("Proxy read EAGAIN... retrying\n");
+				return 0;
+			}
+			lwsl_err("ERROR reading from proxy socket\n");
+			goto bail3;
+		}
+
+		pt->serv_buf[13] = '\0';
+		if (strcmp(sb, "HTTP/1.0 200 ") &&
+		    strcmp(sb, "HTTP/1.1 200 ")) {
+			lwsl_err("ERROR proxy: %s\n", sb);
+			goto bail3;
+		}
+
+		/* clear his proxy connection timeout */
+
+		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+		/* fallthru */
+
+	case LRS_H1C_ISSUE_HANDSHAKE:
+
+		/*
+		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
+		 * timeout protection set in client-handshake.c
+		 *
+		 * take care of our lws_callback_on_writable
+		 * happening at a time when there's no real connection yet
+		 */
+#if defined(LWS_WITH_SOCKS5)
+start_ws_handshake:
+#endif
+		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
+			return -1;
+
+#if defined(LWS_WITH_TLS)
+		/* we can retry this... just cook the SSL BIO the first time */
+
+		if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && !wsi->tls.ssl &&
+		    lws_ssl_client_bio_create(wsi) < 0) {
+			cce = "bio_create failed";
+			goto bail3;
+		}
+
+		if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
+			n = lws_ssl_client_connect1(wsi);
+			if (!n)
+				return 0;
+			if (n < 0) {
+				cce = "lws_ssl_client_connect1 failed";
+				goto bail3;
+			}
+		} else
+			wsi->tls.ssl = NULL;
+
+		/* fallthru */
+
+	case LRS_WAITING_SSL:
+
+		if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
+			n = lws_ssl_client_connect2(wsi, ebuf, sizeof(ebuf));
+			if (!n)
+				return 0;
+			if (n < 0) {
+				cce = ebuf;
+				goto bail3;
+			}
+		} else
+			wsi->tls.ssl = NULL;
+#endif
+#if defined (LWS_WITH_HTTP2)
+		if (wsi->client_h2_alpn) {
+			/*
+			 * We connected to the server and set up tls, and
+			 * negotiated "h2".
+			 *
+			 * So this is it, we are an h2 master client connection
+			 * now, not an h1 client connection.
+			 */
+			lws_tls_server_conn_alpn(wsi);
+
+			/* send the H2 preface to legitimize the connection */
+			if (lws_h2_issue_preface(wsi)) {
+				cce = "error sending h2 preface";
+				goto bail3;
+			}
+
+			break;
+		}
+#endif
+		lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
+		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
+				context->timeout_secs);
+
+		/* fallthru */
+
+	case LRS_H1C_ISSUE_HANDSHAKE2:
+		p = lws_generate_client_handshake(wsi, p);
+		if (p == NULL) {
+			if (wsi->role_ops == &role_ops_raw_skt ||
+			    wsi->role_ops == &role_ops_raw_file)
+				return 0;
+
+			lwsl_err("Failed to generate handshake for client\n");
+			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "chs");
+			return 0;
+		}
+
+		/* send our request to the server */
+		lws_latency_pre(context, wsi);
+
+		w = _lws_client_wsi_master(wsi);
+		lwsl_info("%s: HANDSHAKE2: %p: sending headers on %p (wsistate 0x%x 0x%x)\n",
+				__func__, wsi, w, wsi->wsistate, w->wsistate);
+
+		n = lws_ssl_capable_write(w, (unsigned char *)sb, (int)(p - sb));
+		lws_latency(context, wsi, "send lws_issue_raw", n,
+			    n == p - sb);
+		switch (n) {
+		case LWS_SSL_CAPABLE_ERROR:
+			lwsl_debug("ERROR writing to client socket\n");
+			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cws");
+			return 0;
+		case LWS_SSL_CAPABLE_MORE_SERVICE:
+			lws_callback_on_writable(wsi);
+			break;
+		}
+
+		if (wsi->client_http_body_pending) {
+			lwsi_set_state(wsi, LRS_ISSUE_HTTP_BODY);
+			lws_set_timeout(wsi,
+					PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
+					context->timeout_secs);
+			/* user code must ask for writable callback */
+			break;
+		}
+
+		lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
+		wsi->hdr_parsing_completed = 0;
+
+		if (lwsi_state(w) == LRS_IDLING) {
+			lwsi_set_state(w, LRS_WAITING_SERVER_REPLY);
+			w->hdr_parsing_completed = 0;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+			w->http.ah->parser_state = WSI_TOKEN_NAME_PART;
+			w->http.ah->lextable_pos = 0;
+			/* If we're (re)starting on headers, need other implied init */
+			wsi->http.ah->ues = URIES_IDLE;
+#endif
+		}
+
+		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
+				wsi->context->timeout_secs);
+
+		lws_callback_on_writable(w);
+
+		goto client_http_body_sent;
+
+	case LRS_ISSUE_HTTP_BODY:
+		if (wsi->client_http_body_pending) {
+			//lws_set_timeout(wsi,
+			//		PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
+			//		context->timeout_secs);
+			/* user code must ask for writable callback */
+			break;
+		}
+client_http_body_sent:
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+		/* prepare ourselves to do the parsing */
+		wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
+		wsi->http.ah->lextable_pos = 0;
+#endif
+		lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
+		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
+				context->timeout_secs);
+		break;
+
+	case LRS_WAITING_SERVER_REPLY:
+		/*
+		 * handle server hanging up on us...
+		 * but if there is POLLIN waiting, handle that first
+		 */
+		if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) ==
+								LWS_POLLHUP) {
+
+			lwsl_debug("Server connection %p (fd=%d) dead\n",
+				(void *)wsi, pollfd->fd);
+			cce = "Peer hung up";
+			goto bail3;
+		}
+
+		if (!(pollfd->revents & LWS_POLLIN))
+			break;
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+		/* interpret the server response
+		 *
+		 *  HTTP/1.1 101 Switching Protocols
+		 *  Upgrade: websocket
+		 *  Connection: Upgrade
+		 *  Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
+		 *  Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
+		 *  Sec-WebSocket-Protocol: chat
+		 *
+		 * we have to take some care here to only take from the
+		 * socket bytewise.  The browser may (and has been seen to
+		 * in the case that onopen() performs websocket traffic)
+		 * coalesce both handshake response and websocket traffic
+		 * in one packet, since at that point the connection is
+		 * definitively ready from browser pov.
+		 */
+		len = 1;
+		while (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE &&
+		       len > 0) {
+			int plen = 1;
+
+			n = lws_ssl_capable_read(wsi, &c, 1);
+			lws_latency(context, wsi, "send lws_issue_raw", n,
+				    n == 1);
+			switch (n) {
+			case 0:
+			case LWS_SSL_CAPABLE_ERROR:
+				cce = "read failed";
+				goto bail3;
+			case LWS_SSL_CAPABLE_MORE_SERVICE:
+				return 0;
+			}
+
+			if (lws_parse(wsi, &c, &plen)) {
+				lwsl_warn("problems parsing header\n");
+				goto bail3;
+			}
+		}
+
+		/*
+		 * hs may also be coming in multiple packets, there is a 5-sec
+		 * libwebsocket timeout still active here too, so if parsing did
+		 * not complete just wait for next packet coming in this state
+		 */
+		if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
+			break;
+
+#endif
+
+		/*
+		 * otherwise deal with the handshake.  If there's any
+		 * packet traffic already arrived we'll trigger poll() again
+		 * right away and deal with it that way
+		 */
+		return lws_client_interpret_server_handshake(wsi);
+
+bail3:
+		lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n");
+		if (cce)
+			lwsl_info("reason: %s\n", cce);
+		wsi->protocol->callback(wsi,
+			LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+			wsi->user_space, (void *)cce, cce ? strlen(cce) : 0);
+		wsi->already_did_cce = 1;
+		lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cbail3");
+		return -1;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+
+int LWS_WARN_UNUSED_RESULT
+lws_http_transaction_completed_client(struct lws *wsi)
+{
+	struct lws *wsi_eff = lws_client_wsi_effective(wsi);
+
+	lwsl_info("%s: wsi: %p, wsi_eff: %p\n", __func__, wsi, wsi_eff);
+
+	if (user_callback_handle_rxflow(wsi_eff->protocol->callback,
+			wsi_eff, LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
+			wsi_eff->user_space, NULL, 0)) {
+		lwsl_debug("%s: Completed call returned nonzero (role 0x%x)\n",
+						__func__, lwsi_role(wsi_eff));
+		return -1;
+	}
+
+	/*
+	 * Are we constitutionally capable of having a queue, ie, we are on
+	 * the "active client connections" list?
+	 *
+	 * If not, that's it for us.
+	 */
+
+	if (lws_dll_is_null(&wsi->dll_active_client_conns))
+		return -1;
+
+	/* if this was a queued guy, close him and remove from queue */
+
+	if (wsi->transaction_from_pipeline_queue) {
+		lwsl_debug("closing queued wsi %p\n", wsi_eff);
+		/* so the close doesn't trigger a CCE */
+		wsi_eff->already_did_cce = 1;
+		__lws_close_free_wsi(wsi_eff,
+			LWS_CLOSE_STATUS_CLIENT_TRANSACTION_DONE,
+			"queued client done");
+	}
+
+	/* after the first one, they can only be coming from the queue */
+	wsi->transaction_from_pipeline_queue = 1;
+
+	wsi->http.rx_content_length = 0;
+	wsi->hdr_parsing_completed = 0;
+
+	/* is there a new tail after removing that one? */
+	wsi_eff = lws_client_wsi_effective(wsi);
+
+	/*
+	 * Do we have something pipelined waiting?
+	 * it's OK if he hasn't managed to send his headers yet... he's next
+	 * in line to do that...
+	 */
+	if (wsi_eff == wsi) {
+		/*
+		 * Nothing pipelined... we should hang around a bit
+		 * in case something turns up...
+		 */
+		lwsl_info("%s: nothing pipelined waiting\n", __func__);
+		lwsi_set_state(wsi, LRS_IDLING);
+
+		lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_CONN_IDLE, 5);
+
+		return 0;
+	}
+
+	/*
+	 * H1: we can serialize the queued guys into the same ah
+	 * H2: everybody needs their own ah until their own STREAM_END
+	 */
+
+	/* otherwise set ourselves up ready to go again */
+	lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
+
+	wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
+	wsi->http.ah->lextable_pos = 0;
+
+	lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
+			wsi->context->timeout_secs);
+
+	/* If we're (re)starting on headers, need other implied init */
+	wsi->http.ah->ues = URIES_IDLE;
+
+	lwsl_info("%s: %p: new queued transaction as %p\n", __func__, wsi, wsi_eff);
+	lws_callback_on_writable(wsi);
+
+	return 0;
+}
+
+LWS_VISIBLE LWS_EXTERN unsigned int
+lws_http_client_http_response(struct lws *wsi)
+{
+	if (!wsi->http.ah)
+		return 0;
+
+	return wsi->http.ah->http_response;
+}
+#endif
+#if defined(LWS_PLAT_OPTEE)
+char *
+strrchr(const char *s, int c)
+{
+	char *hit = NULL;
+
+	while (*s)
+		if (*(s++) == (char)c)
+		       hit = (char *)s - 1;
+
+	return hit;
+}
+
+#define atoll atoi
+#endif
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+int
+lws_client_interpret_server_handshake(struct lws *wsi)
+{
+	int n, port = 0, ssl = 0;
+	int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
+	const char *prot, *ads = NULL, *path, *cce = NULL;
+	struct allocated_headers *ah = NULL;
+	struct lws *w = lws_client_wsi_effective(wsi);
+	char *p, *q;
+	char new_path[300];
+
+	lws_client_stash_destroy(wsi);
+
+	ah = wsi->http.ah;
+	if (!wsi->do_ws) {
+		/* we are being an http client...
+		 */
+#if defined(LWS_ROLE_H2)
+		if (wsi->client_h2_alpn || wsi->client_h2_substream) {
+			lwsl_debug("%s: %p: transitioning to h2 client\n", __func__, wsi);
+			lws_role_transition(wsi, LWSIFR_CLIENT,
+					    LRS_ESTABLISHED, &role_ops_h2);
+		} else
+#endif
+		{
+#if defined(LWS_ROLE_H1)
+			{
+			lwsl_debug("%s: %p: transitioning to h1 client\n", __func__, wsi);
+			lws_role_transition(wsi, LWSIFR_CLIENT,
+					    LRS_ESTABLISHED, &role_ops_h1);
+			}
+#else
+			return -1;
+#endif
+		}
+
+		wsi->http.ah = ah;
+		ah->http_response = 0;
+	}
+
+	/*
+	 * well, what the server sent looked reasonable for syntax.
+	 * Now let's confirm it sent all the necessary headers
+	 *
+	 * http (non-ws) client will expect something like this
+	 *
+	 * HTTP/1.0.200
+	 * server:.libwebsockets
+	 * content-type:.text/html
+	 * content-length:.17703
+	 * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000
+	 */
+
+	wsi->http.connection_type = HTTP_CONNECTION_KEEP_ALIVE;
+	if (!wsi->client_h2_substream) {
+		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
+		if (wsi->do_ws && !p) {
+			lwsl_info("no URI\n");
+			cce = "HS: URI missing";
+			goto bail3;
+		}
+		if (!p) {
+			p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0);
+			wsi->http.connection_type = HTTP_CONNECTION_CLOSE;
+		}
+		if (!p) {
+			cce = "HS: URI missing";
+			lwsl_info("no URI\n");
+			goto bail3;
+		}
+	} else {
+		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_STATUS);
+		if (!p) {
+			cce = "HS: :status missing";
+			lwsl_info("no status\n");
+			goto bail3;
+		}
+	}
+	n = atoi(p);
+	if (ah)
+		ah->http_response = n;
+
+	if (n == 301 || n == 302 || n == 303 || n == 307 || n == 308) {
+		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION);
+		if (!p) {
+			cce = "HS: Redirect code but no Location";
+			goto bail3;
+		}
+
+		/* Relative reference absolute path */
+		if (p[0] == '/') {
+#if defined(LWS_WITH_TLS)
+			ssl = wsi->tls.use_ssl & LCCSCF_USE_SSL;
+#endif
+			ads = lws_hdr_simple_ptr(wsi,
+						 _WSI_TOKEN_CLIENT_PEER_ADDRESS);
+			port = wsi->c_port;
+			/* +1 as lws_client_reset expects leading / omitted */
+			path = p + 1;
+		}
+		/* Absolute (Full) URI */
+		else if (strchr(p, ':')) {
+			if (lws_parse_uri(p, &prot, &ads, &port, &path)) {
+				cce = "HS: URI did not parse";
+				goto bail3;
+			}
+
+			if (!strcmp(prot, "wss") || !strcmp(prot, "https"))
+				ssl = 1;
+		}
+		/* Relative reference relative path */
+		else {
+			/* This doesn't try to calculate an absolute path,
+			 * that will be left to the server */
+#if defined(LWS_WITH_TLS)
+			ssl = wsi->tls.use_ssl & LCCSCF_USE_SSL;
+#endif
+			ads = lws_hdr_simple_ptr(wsi,
+						 _WSI_TOKEN_CLIENT_PEER_ADDRESS);
+			port = wsi->c_port;
+			/* +1 as lws_client_reset expects leading / omitted */
+			path = new_path + 1;
+			lws_strncpy(new_path, lws_hdr_simple_ptr(wsi,
+				   _WSI_TOKEN_CLIENT_URI), sizeof(new_path));
+			q = strrchr(new_path, '/');
+			if (q)
+				lws_strncpy(q + 1, p, sizeof(new_path) -
+							(q - new_path));
+			else
+				path = p;
+		}
+
+#if defined(LWS_WITH_TLS)
+		if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && !ssl) {
+			cce = "HS: Redirect attempted SSL downgrade";
+			goto bail3;
+		}
+#endif
+
+		if (!lws_client_reset(&wsi, ssl, ads, port, path, ads)) {
+			/* there are two ways to fail out with NULL return...
+			 * simple, early problem where the wsi is intact, or
+			 * we went through with the reconnect attempt and the
+			 * wsi is already closed.  In the latter case, the wsi
+			 * has beet set to NULL additionally.
+			 */
+			lwsl_err("Redirect failed\n");
+			cce = "HS: Redirect failed";
+			if (wsi)
+				goto bail3;
+
+			return 1;
+		}
+		return 0;
+	}
+
+	if (!wsi->do_ws) {
+
+		/* if h1 KA is allowed, enable the queued pipeline guys */
+
+		if (!wsi->client_h2_alpn && !wsi->client_h2_substream && w == wsi) { /* ie, coming to this for the first time */
+			if (wsi->http.connection_type == HTTP_CONNECTION_KEEP_ALIVE)
+				wsi->keepalive_active = 1;
+			else {
+				/*
+				 * Ugh... now the main http connection has seen
+				 * both sides, we learn the server doesn't
+				 * support keepalive.
+				 *
+				 * That means any guys queued on us are going
+				 * to have to be restarted from connect2 with
+				 * their own connections.
+				 */
+
+				/*
+				 * stick around telling any new guys they can't
+				 * pipeline to this server
+				 */
+				wsi->keepalive_rejected = 1;
+
+				lws_vhost_lock(wsi->vhost);
+				lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+							   wsi->dll_client_transaction_queue_head.next) {
+					struct lws *ww = lws_container_of(d, struct lws,
+								  dll_client_transaction_queue);
+
+					/* remove him from our queue */
+					lws_dll_lws_remove(&ww->dll_client_transaction_queue);
+					/* give up on pipelining */
+					ww->client_pipeline = 0;
+
+					/* go back to "trying to connect" state */
+					lws_role_transition(ww, LWSIFR_CLIENT,
+							    LRS_UNCONNECTED,
+#if defined(LWS_ROLE_H1)
+							    &role_ops_h1);
+#else
+#if defined (LWS_ROLE_H2)
+							    &role_ops_h2);
+#else
+							    &role_ops_raw);
+#endif
+#endif
+					ww->user_space = NULL;
+				} lws_end_foreach_dll_safe(d, d1);
+				lws_vhost_unlock(wsi->vhost);
+			}
+		}
+
+#ifdef LWS_WITH_HTTP_PROXY
+		wsi->http.perform_rewrite = 0;
+		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
+			if (!strncmp(lws_hdr_simple_ptr(wsi,
+						WSI_TOKEN_HTTP_CONTENT_TYPE),
+						"text/html", 9))
+				wsi->http.perform_rewrite = 1;
+		}
+#endif
+
+		/* allocate the per-connection user memory (if any) */
+		if (lws_ensure_user_space(wsi)) {
+			lwsl_err("Problem allocating wsi user mem\n");
+			cce = "HS: OOM";
+			goto bail2;
+		}
+
+		/* he may choose to send us stuff in chunked transfer-coding */
+		wsi->chunked = 0;
+		wsi->chunk_remaining = 0; /* ie, next thing is chunk size */
+		if (lws_hdr_total_length(wsi,
+					WSI_TOKEN_HTTP_TRANSFER_ENCODING)) {
+			wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi,
+					       WSI_TOKEN_HTTP_TRANSFER_ENCODING),
+						"chunked");
+			/* first thing is hex, after payload there is crlf */
+			wsi->chunk_parser = ELCP_HEX;
+		}
+
+		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
+			wsi->http.rx_content_length =
+					atoll(lws_hdr_simple_ptr(wsi,
+						WSI_TOKEN_HTTP_CONTENT_LENGTH));
+			lwsl_info("%s: incoming content length %llu\n",
+				    __func__, (unsigned long long)
+					    wsi->http.rx_content_length);
+			wsi->http.rx_content_remain =
+					wsi->http.rx_content_length;
+		} else /* can't do 1.1 without a content length or chunked */
+			if (!wsi->chunked)
+				wsi->http.connection_type =
+							HTTP_CONNECTION_CLOSE;
+
+		/*
+		 * we seem to be good to go, give client last chance to check
+		 * headers and OK it
+		 */
+		if (wsi->protocol->callback(wsi,
+				LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
+					    wsi->user_space, NULL, 0)) {
+
+			cce = "HS: disallowed by client filter";
+			goto bail2;
+		}
+
+		/* clear his proxy connection timeout */
+		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+		wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
+
+		/* call him back to inform him he is up */
+		if (wsi->protocol->callback(wsi,
+					    LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
+					    wsi->user_space, NULL, 0)) {
+			cce = "HS: disallowed at ESTABLISHED";
+			goto bail3;
+		}
+
+		/*
+		 * for pipelining, master needs to keep his ah... guys who
+		 * queued on him can drop it now though.
+		 */
+
+		if (w != wsi)
+			/* free up parsing allocations for queued guy */
+			lws_header_table_detach(w, 0);
+
+		lwsl_info("%s: client connection up\n", __func__);
+
+		return 0;
+	}
+
+#if defined(LWS_ROLE_WS)
+	switch (lws_client_ws_upgrade(wsi, &cce)) {
+	case 2:
+		goto bail2;
+	case 3:
+		goto bail3;
+	}
+
+	return 0;
+#endif
+
+bail3:
+	close_reason = LWS_CLOSE_STATUS_NOSTATUS;
+
+bail2:
+	if (wsi->protocol) {
+		n = 0;
+		if (cce)
+			n = (int)strlen(cce);
+		wsi->protocol->callback(wsi,
+				LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+				wsi->user_space, (void *)cce,
+				(unsigned int)n);
+	}
+	wsi->already_did_cce = 1;
+
+	lwsl_info("closing connection due to bail2 connection error\n");
+
+	/* closing will free up his parsing allocations */
+	lws_close_free_wsi(wsi, close_reason, "c hs interp");
+
+	return 1;
+}
+#endif
+
+char *
+lws_generate_client_handshake(struct lws *wsi, char *pkt)
+{
+	char *p = pkt;
+	const char *meth;
+	const char *pp = lws_hdr_simple_ptr(wsi,
+				_WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
+
+	meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
+	if (!meth) {
+		meth = "GET";
+		wsi->do_ws = 1;
+	} else {
+		wsi->do_ws = 0;
+	}
+
+	if (!strcmp(meth, "RAW")) {
+		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+		lwsl_notice("client transition to raw\n");
+
+		if (pp) {
+			const struct lws_protocols *pr;
+
+			pr = lws_vhost_name_to_protocol(wsi->vhost, pp);
+
+			if (!pr) {
+				lwsl_err("protocol %s not enabled on vhost\n",
+					 pp);
+				return NULL;
+			}
+
+			lws_bind_protocol(wsi, pr);
+		}
+
+		if ((wsi->protocol->callback)(wsi, LWS_CALLBACK_RAW_ADOPT,
+					      wsi->user_space, NULL, 0))
+			return NULL;
+
+		lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_raw_skt);
+		lws_header_table_detach(wsi, 1);
+
+		return NULL;
+	}
+
+	/*
+	 * 04 example client handshake
+	 *
+	 * GET /chat HTTP/1.1
+	 * Host: server.example.com
+	 * Upgrade: websocket
+	 * Connection: Upgrade
+	 * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+	 * Sec-WebSocket-Origin: http://example.com
+	 * Sec-WebSocket-Protocol: chat, superchat
+	 * Sec-WebSocket-Version: 4
+	 */
+
+	p += sprintf(p, "%s %s HTTP/1.1\x0d\x0a", meth,
+		     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));
+
+	p += sprintf(p, "Pragma: no-cache\x0d\x0a"
+			"Cache-Control: no-cache\x0d\x0a");
+
+	p += sprintf(p, "Host: %s\x0d\x0a",
+		     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
+
+	if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) {
+		if (lws_check_opt(wsi->context->options,
+				  LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN))
+			p += sprintf(p, "Origin: %s\x0d\x0a",
+				     lws_hdr_simple_ptr(wsi,
+						     _WSI_TOKEN_CLIENT_ORIGIN));
+		else
+			p += sprintf(p, "Origin: http://%s\x0d\x0a",
+				     lws_hdr_simple_ptr(wsi,
+						     _WSI_TOKEN_CLIENT_ORIGIN));
+	}
+#if defined(LWS_ROLE_WS)
+	if (wsi->do_ws)
+		p = lws_generate_client_ws_handshake(wsi, p);
+#endif
+
+	/* give userland a chance to append, eg, cookies */
+
+	if (wsi->protocol->callback(wsi,
+				LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
+				wsi->user_space, &p,
+				(pkt + wsi->context->pt_serv_buf_size) - p - 12))
+		return NULL;
+
+	p += sprintf(p, "\x0d\x0a");
+
+	return p;
+}
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+
+LWS_VISIBLE int
+lws_http_client_read(struct lws *wsi, char **buf, int *len)
+{
+	int rlen, n;
+
+	rlen = lws_ssl_capable_read(wsi, (unsigned char *)*buf, *len);
+	*len = 0;
+
+	// lwsl_notice("%s: rlen %d\n", __func__, rlen);
+
+	/* allow the source to signal he has data again next time */
+	lws_change_pollfd(wsi, 0, LWS_POLLIN);
+
+	if (rlen == LWS_SSL_CAPABLE_ERROR) {
+		lwsl_notice("%s: SSL capable error\n", __func__);
+		return -1;
+	}
+
+	if (rlen == 0)
+		return -1;
+
+	if (rlen < 0)
+		return 0;
+
+	*len = rlen;
+	wsi->client_rx_avail = 0;
+
+	/*
+	 * server may insist on transfer-encoding: chunked,
+	 * so http client must deal with it
+	 */
+spin_chunks:
+	while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) {
+		switch (wsi->chunk_parser) {
+		case ELCP_HEX:
+			if ((*buf)[0] == '\x0d') {
+				wsi->chunk_parser = ELCP_CR;
+				break;
+			}
+			n = char_to_hex((*buf)[0]);
+			if (n < 0) {
+				lwsl_debug("chunking failure\n");
+				return -1;
+			}
+			wsi->chunk_remaining <<= 4;
+			wsi->chunk_remaining |= n;
+			break;
+		case ELCP_CR:
+			if ((*buf)[0] != '\x0a') {
+				lwsl_debug("chunking failure\n");
+				return -1;
+			}
+			wsi->chunk_parser = ELCP_CONTENT;
+			lwsl_info("chunk %d\n", wsi->chunk_remaining);
+			if (wsi->chunk_remaining)
+				break;
+			lwsl_info("final chunk\n");
+			goto completed;
+
+		case ELCP_CONTENT:
+			break;
+
+		case ELCP_POST_CR:
+			if ((*buf)[0] != '\x0d') {
+				lwsl_debug("chunking failure\n");
+
+				return -1;
+			}
+
+			wsi->chunk_parser = ELCP_POST_LF;
+			break;
+
+		case ELCP_POST_LF:
+			if ((*buf)[0] != '\x0a')
+				return -1;
+
+			wsi->chunk_parser = ELCP_HEX;
+			wsi->chunk_remaining = 0;
+			break;
+		}
+		(*buf)++;
+		(*len)--;
+	}
+
+	if (wsi->chunked && !wsi->chunk_remaining)
+		return 0;
+
+	if (wsi->http.rx_content_remain &&
+	    wsi->http.rx_content_remain < (unsigned int)*len)
+		n = (int)wsi->http.rx_content_remain;
+	else
+		n = *len;
+
+	if (wsi->chunked && wsi->chunk_remaining &&
+	    wsi->chunk_remaining < n)
+		n = wsi->chunk_remaining;
+
+#ifdef LWS_WITH_HTTP_PROXY
+	/* hubbub */
+	if (wsi->http.perform_rewrite)
+		lws_rewrite_parse(wsi->http.rw, (unsigned char *)*buf, n);
+	else
+#endif
+	{
+		struct lws *wsi_eff = lws_client_wsi_effective(wsi);
+
+		if (user_callback_handle_rxflow(wsi_eff->protocol->callback,
+				wsi_eff, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
+				wsi_eff->user_space, *buf, n)) {
+			lwsl_debug("%s: RECEIVE_CLIENT_HTTP_READ returned -1\n",
+				   __func__);
+
+			return -1;
+		}
+	}
+
+	if (wsi->chunked && wsi->chunk_remaining) {
+		(*buf) += n;
+		wsi->chunk_remaining -= n;
+		*len -= n;
+	}
+
+	if (wsi->chunked && !wsi->chunk_remaining)
+		wsi->chunk_parser = ELCP_POST_CR;
+
+	if (wsi->chunked && *len)
+		goto spin_chunks;
+
+	if (wsi->chunked)
+		return 0;
+
+	/* if we know the content length, decrement the content remaining */
+	if (wsi->http.rx_content_length > 0)
+		wsi->http.rx_content_remain -= n;
+
+	// lwsl_notice("rx_content_remain %lld, rx_content_length %lld\n",
+	//	wsi->http.rx_content_remain, wsi->http.rx_content_length);
+
+	if (wsi->http.rx_content_remain || !wsi->http.rx_content_length)
+		return 0;
+
+completed:
+
+	if (lws_http_transaction_completed_client(wsi)) {
+		lwsl_notice("%s: transaction completed says -1\n", __func__);
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif

+ 94 - 42
thirdparty/libwebsockets/header.c → thirdparty/libwebsockets/roles/http/header.c

@@ -19,12 +19,12 @@
  *  MA  02110-1301  USA
  *  MA  02110-1301  USA
  */
  */
 
 
-#include "private-libwebsockets.h"
-
+#include "core/private.h"
 #include "lextable-strings.h"
 #include "lextable-strings.h"
 
 
 
 
-const unsigned char *lws_token_to_string(enum lws_token_indexes token)
+const unsigned char *
+lws_token_to_string(enum lws_token_indexes token)
 {
 {
 	if ((unsigned int)token >= ARRAY_SIZE(set))
 	if ((unsigned int)token >= ARRAY_SIZE(set))
 		return NULL;
 		return NULL;
@@ -38,7 +38,7 @@ lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
 			    unsigned char **p, unsigned char *end)
 			    unsigned char **p, unsigned char *end)
 {
 {
 #ifdef LWS_WITH_HTTP2
 #ifdef LWS_WITH_HTTP2
-	if (wsi->mode == LWSCM_HTTP2_SERVING)
+	if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
 		return lws_add_http2_header_by_name(wsi, name,
 		return lws_add_http2_header_by_name(wsi, name,
 						    value, length, p, end);
 						    value, length, p, end);
 #else
 #else
@@ -66,7 +66,7 @@ int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
 			     unsigned char *end)
 			     unsigned char *end)
 {
 {
 #ifdef LWS_WITH_HTTP2
 #ifdef LWS_WITH_HTTP2
-	if (wsi->mode == LWSCM_HTTP2_SERVING)
+	if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
 		return 0;
 		return 0;
 #else
 #else
 	(void)wsi;
 	(void)wsi;
@@ -79,6 +79,25 @@ int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
 	return 0;
 	return 0;
 }
 }
 
 
+int
+lws_finalize_write_http_header(struct lws *wsi, unsigned char *start,
+			       unsigned char **pp, unsigned char *end)
+{
+	unsigned char *p;
+	int len;
+
+	if (lws_finalize_http_header(wsi, pp, end))
+		return 1;
+
+	p = *pp;
+	len = lws_ptr_diff(p, start);
+
+	if (lws_write(wsi, start, len, LWS_WRITE_HTTP_HEADERS) != len)
+		return 1;
+
+	return 0;
+}
+
 int
 int
 lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
 lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
 			     const unsigned char *value, int length,
 			     const unsigned char *value, int length,
@@ -86,13 +105,14 @@ lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
 {
 {
 	const unsigned char *name;
 	const unsigned char *name;
 #ifdef LWS_WITH_HTTP2
 #ifdef LWS_WITH_HTTP2
-	if (wsi->mode == LWSCM_HTTP2_SERVING)
+	if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
 		return lws_add_http2_header_by_token(wsi, token, value,
 		return lws_add_http2_header_by_token(wsi, token, value,
 						     length, p, end);
 						     length, p, end);
 #endif
 #endif
 	name = lws_token_to_string(token);
 	name = lws_token_to_string(token);
 	if (!name)
 	if (!name)
 		return 1;
 		return 1;
+
 	return lws_add_http_header_by_name(wsi, name, value, length, p, end);
 	return lws_add_http_header_by_name(wsi, name, value, length, p, end);
 }
 }
 
 
@@ -107,8 +127,31 @@ int lws_add_http_header_content_length(struct lws *wsi,
 	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
 	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
 					 (unsigned char *)b, n, p, end))
 					 (unsigned char *)b, n, p, end))
 		return 1;
 		return 1;
-	wsi->u.http.tx_content_length = content_length;
-	wsi->u.http.tx_content_remain = content_length;
+	wsi->http.tx_content_length = content_length;
+	wsi->http.tx_content_remain = content_length;
+
+	lwsl_info("%s: wsi %p: tx_content_length/remain %llu\n", __func__,
+			wsi, (unsigned long long)content_length);
+
+	return 0;
+}
+
+int
+lws_add_http_common_headers(struct lws *wsi, unsigned int code,
+			    const char *content_type, lws_filepos_t content_len,
+			    unsigned char **p, unsigned char *end)
+{
+	if (lws_add_http_header_status(wsi, code, p, end))
+		return 1;
+
+	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
+		    			(unsigned char *)content_type,
+		    			(int)strlen(content_type), p, end))
+		return 1;
+
+	if (content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN &&
+	    lws_add_http_header_content_length(wsi, content_len, p, end))
+		return 1;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -157,11 +200,11 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
 	int n;
 	int n;
 
 
 #ifdef LWS_WITH_ACCESS_LOG
 #ifdef LWS_WITH_ACCESS_LOG
-	wsi->access_log.response = code;
+	wsi->http.access_log.response = code;
 #endif
 #endif
 
 
 #ifdef LWS_WITH_HTTP2
 #ifdef LWS_WITH_HTTP2
-	if (wsi->mode == LWSCM_HTTP2_SERVING)
+	if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
 		return lws_add_http2_header_status(wsi, code, p, end);
 		return lws_add_http2_header_status(wsi, code, p, end);
 #endif
 #endif
 	if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
 	if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
@@ -171,18 +214,16 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
 
 
 	if (code == 100)
 	if (code == 100)
 		description = "Continue";
 		description = "Continue";
-
 	if (code == 200)
 	if (code == 200)
 		description = "OK";
 		description = "OK";
-
 	if (code == 304)
 	if (code == 304)
 		description = "Not Modified";
 		description = "Not Modified";
 	else
 	else
 		if (code >= 300 && code < 400)
 		if (code >= 300 && code < 400)
 			description = "Redirect";
 			description = "Redirect";
 
 
-	if (wsi->u.http.request_version < ARRAY_SIZE(hver))
-		p1 = hver[wsi->u.http.request_version];
+	if (wsi->http.request_version < ARRAY_SIZE(hver))
+		p1 = hver[wsi->http.request_version];
 	else
 	else
 		p1 = hver[0];
 		p1 = hver[0];
 
 
@@ -196,7 +237,7 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
 		if (lws_add_http_header_by_name(wsi,
 		if (lws_add_http_header_by_name(wsi,
 				(const unsigned char *)headers->name,
 				(const unsigned char *)headers->name,
 				(unsigned char *)headers->value,
 				(unsigned char *)headers->value,
-				strlen(headers->value), p, end))
+				(int)strlen(headers->value), p, end))
 			return 1;
 			return 1;
 
 
 		headers = headers->next;
 		headers = headers->next;
@@ -231,6 +272,26 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
 	int n = 0, m = 0, len;
 	int n = 0, m = 0, len;
 	char slen[20];
 	char slen[20];
 
 
+	if (!wsi->vhost) {
+		lwsl_err("%s: wsi not bound to vhost\n", __func__);
+
+		return 1;
+	}
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	if (!wsi->handling_404 &&
+	    wsi->vhost->http.error_document_404 &&
+	    code == HTTP_STATUS_NOT_FOUND)
+		/* we should do a redirect, and do the 404 there */
+		if (lws_http_redirect(wsi, HTTP_STATUS_FOUND,
+				       (uint8_t *)wsi->vhost->http.error_document_404,
+				       (int)strlen(wsi->vhost->http.error_document_404),
+				       &p, end) > 0)
+			return 0;
+#endif
+
+	/* if the redirect failed, just do a simple status */
+	p = start;
+
 	if (!html_body)
 	if (!html_body)
 		html_body = "";
 		html_body = "";
 
 
@@ -242,12 +303,11 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
 					 &p, end))
 					 &p, end))
 		return 1;
 		return 1;
 
 
-	len = 35 + strlen(html_body) + sprintf(slen, "%d", code);
+	len = 35 + (int)strlen(html_body) + sprintf(slen, "%d", code);
 	n = sprintf(slen, "%d", len);
 	n = sprintf(slen, "%d", len);
 
 
 	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
 	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
-					 (unsigned char *)slen, n,
-					 &p, end))
+					 (unsigned char *)slen, n, &p, end))
 		return 1;
 		return 1;
 
 
 	if (lws_finalize_http_header(wsi, &p, end))
 	if (lws_finalize_http_header(wsi, &p, end))
@@ -270,7 +330,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
 		 * Solve it by writing the headers now...
 		 * Solve it by writing the headers now...
 		 */
 		 */
 		m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
 		m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
-		if (m != (int)(p - start))
+		if (m != lws_ptr_diff(p, start))
 			return 1;
 			return 1;
 
 
 		/*
 		/*
@@ -281,15 +341,15 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
 		len = sprintf((char *)body,
 		len = sprintf((char *)body,
 			      "<html><body><h1>%u</h1>%s</body></html>",
 			      "<html><body><h1>%u</h1>%s</body></html>",
 			      code, html_body);
 			      code, html_body);
-		wsi->u.http.tx_content_length = len;
-		wsi->u.http.tx_content_remain = len;
+		wsi->http.tx_content_length = len;
+		wsi->http.tx_content_remain = len;
 
 
-		wsi->u.h2.pending_status_body = lws_malloc(len + LWS_PRE + 1,
+		wsi->h2.pending_status_body = lws_malloc(len + LWS_PRE + 1,
 							"pending status body");
 							"pending status body");
-		if (!wsi->u.h2.pending_status_body)
+		if (!wsi->h2.pending_status_body)
 			return -1;
 			return -1;
 
 
-		strcpy(wsi->u.h2.pending_status_body + LWS_PRE,
+		strcpy(wsi->h2.pending_status_body + LWS_PRE,
 		       (const char *)body);
 		       (const char *)body);
 		lws_callback_on_writable(wsi);
 		lws_callback_on_writable(wsi);
 
 
@@ -305,15 +365,12 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
 				  "<html><body><h1>%u</h1>%s</body></html>",
 				  "<html><body><h1>%u</h1>%s</body></html>",
 				  code, html_body);
 				  code, html_body);
 
 
-		n = (int)(p - start);
-
+		n = lws_ptr_diff(p, start);
 		m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
 		m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
 		if (m != n)
 		if (m != n)
 			return 1;
 			return 1;
 	}
 	}
 
 
-	lwsl_notice("%s: return\n", __func__);
-
 	return m != n;
 	return m != n;
 }
 }
 
 
@@ -322,34 +379,29 @@ lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
 		  unsigned char **p, unsigned char *end)
 		  unsigned char **p, unsigned char *end)
 {
 {
 	unsigned char *start = *p;
 	unsigned char *start = *p;
-	int n;
 
 
 	if (lws_add_http_header_status(wsi, code, p, end))
 	if (lws_add_http_header_status(wsi, code, p, end))
 		return -1;
 		return -1;
 
 
-	if (lws_add_http_header_by_token(wsi,
-			WSI_TOKEN_HTTP_LOCATION,
-			loc, len, p, end))
+	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION, loc, len,
+					 p, end))
 		return -1;
 		return -1;
 	/*
 	/*
 	 * if we're going with http/1.1 and keepalive, we have to give fake
 	 * 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
 	 * content metadata so the client knows we completed the transaction and
 	 * it can do the redirect...
 	 * it can do the redirect...
 	 */
 	 */
-	if (lws_add_http_header_by_token(wsi,
-			WSI_TOKEN_HTTP_CONTENT_TYPE,
-			(unsigned char *)"text/html", 9,
-			p, end))
+	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
+					 (unsigned char *)"text/html", 9, p,
+					 end))
 		return -1;
 		return -1;
-	if (lws_add_http_header_by_token(wsi,
-			WSI_TOKEN_HTTP_CONTENT_LENGTH,
-			(unsigned char *)"0", 1, p, end))
+	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
+					 (unsigned char *)"0", 1, p, end))
 		return -1;
 		return -1;
 
 
 	if (lws_finalize_http_header(wsi, p, end))
 	if (lws_finalize_http_header(wsi, p, end))
 		return -1;
 		return -1;
 
 
-	n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
-
-	return n;
+	return lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS |
+						 LWS_WRITE_H2_STREAM_END);
 }
 }

+ 4 - 0
thirdparty/libwebsockets/lextable-strings.h → thirdparty/libwebsockets/roles/http/lextable-strings.h

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

+ 838 - 0
thirdparty/libwebsockets/roles/http/lextable.h

@@ -0,0 +1,838 @@
+/* pos 0000:   0 */    0x67 /* 'g' */, 0x40, 0x00  /* (to 0x0040 state   1) */,
+                       0x70 /* 'p' */, 0x42, 0x00  /* (to 0x0045 state   5) */,
+                       0x6F /* 'o' */, 0x51, 0x00  /* (to 0x0057 state  10) */,
+                       0x68 /* 'h' */, 0x5D, 0x00  /* (to 0x0066 state  18) */,
+                       0x63 /* 'c' */, 0x69, 0x00  /* (to 0x0075 state  23) */,
+                       0x75 /* 'u' */, 0x8A, 0x00  /* (to 0x0099 state  34) */,
+                       0x73 /* 's' */, 0xA0, 0x00  /* (to 0x00B2 state  48) */,
+                       0x0D /* '.' */, 0xD9, 0x00  /* (to 0x00EE state  68) */,
+                       0x61 /* 'a' */, 0x31, 0x01  /* (to 0x0149 state 129) */,
+                       0x69 /* 'i' */, 0x70, 0x01  /* (to 0x018B state 163) */,
+                       0x64 /* 'd' */, 0x19, 0x02  /* (to 0x0237 state 265) */,
+                       0x72 /* 'r' */, 0x22, 0x02  /* (to 0x0243 state 270) */,
+                       0x3A /* ':' */, 0x56, 0x02  /* (to 0x027A state 299) */,
+                       0x65 /* 'e' */, 0xE8, 0x02  /* (to 0x030F state 409) */,
+                       0x66 /* 'f' */, 0x04, 0x03  /* (to 0x032E state 425) */,
+                       0x6C /* 'l' */, 0x26, 0x03  /* (to 0x0353 state 458) */,
+                       0x6D /* 'm' */, 0x49, 0x03  /* (to 0x0379 state 484) */,
+                       0x74 /* 't' */, 0xB8, 0x03  /* (to 0x03EB state 578) */,
+                       0x76 /* 'v' */, 0xD9, 0x03  /* (to 0x040F state 606) */,
+                       0x77 /* 'w' */, 0xE6, 0x03  /* (to 0x041F state 614) */,
+                       0x78 /* 'x' */, 0x0D, 0x04  /* (to 0x0449 state 650) */,
+                       0x08, /* fail */
+/* pos 0040:   1 */    0xE5 /* 'e' -> */,
+/* pos 0041:   2 */    0xF4 /* 't' -> */,
+/* pos 0042:   3 */    0xA0 /* ' ' -> */,
+/* pos 0043:   4 */    0x00, 0x00                  /* - terminal marker  0 - */,
+/* pos 0045:   5 */    0x6F /* 'o' */, 0x0D, 0x00  /* (to 0x0052 state   6) */,
+                       0x72 /* 'r' */, 0x95, 0x01  /* (to 0x01DD state 211) */,
+                       0x61 /* 'a' */, 0xE6, 0x03  /* (to 0x0431 state 631) */,
+                       0x75 /* 'u' */, 0xE8, 0x03  /* (to 0x0436 state 635) */,
+                       0x08, /* fail */
+/* pos 0052:   6 */    0xF3 /* 's' -> */,
+/* pos 0053:   7 */    0xF4 /* 't' -> */,
+/* pos 0054:   8 */    0xA0 /* ' ' -> */,
+/* pos 0055:   9 */    0x00, 0x01                  /* - terminal marker  1 - */,
+/* pos 0057:  10 */    0x70 /* 'p' */, 0x07, 0x00  /* (to 0x005E state  11) */,
+                       0x72 /* 'r' */, 0x51, 0x00  /* (to 0x00AB state  42) */,
+                       0x08, /* fail */
+/* pos 005e:  11 */    0xF4 /* 't' -> */,
+/* pos 005f:  12 */    0xE9 /* 'i' -> */,
+/* pos 0060:  13 */    0xEF /* 'o' -> */,
+/* pos 0061:  14 */    0xEE /* 'n' -> */,
+/* pos 0062:  15 */    0xF3 /* 's' -> */,
+/* pos 0063:  16 */    0xA0 /* ' ' -> */,
+/* pos 0064:  17 */    0x00, 0x02                  /* - terminal marker  2 - */,
+/* pos 0066:  18 */    0x6F /* 'o' */, 0x0A, 0x00  /* (to 0x0070 state  19) */,
+                       0x74 /* 't' */, 0xBF, 0x00  /* (to 0x0128 state 110) */,
+                       0x65 /* 'e' */, 0x04, 0x04  /* (to 0x0470 state 676) */,
+                       0x08, /* fail */
+/* pos 0070:  19 */    0xF3 /* 's' -> */,
+/* pos 0071:  20 */    0xF4 /* 't' -> */,
+/* pos 0072:  21 */    0xBA /* ':' -> */,
+/* pos 0073:  22 */    0x00, 0x03                  /* - terminal marker  3 - */,
+/* pos 0075:  23 */    0x6F /* 'o' */, 0x07, 0x00  /* (to 0x007C state  24) */,
+                       0x61 /* 'a' */, 0x72, 0x01  /* (to 0x01EA state 217) */,
+                       0x08, /* fail */
+/* pos 007c:  24 */    0x6E /* 'n' */, 0x07, 0x00  /* (to 0x0083 state  25) */,
+                       0x6F /* 'o' */, 0x87, 0x01  /* (to 0x0206 state 243) */,
+                       0x08, /* fail */
+/* pos 0083:  25 */    0x6E /* 'n' */, 0x07, 0x00  /* (to 0x008A state  26) */,
+                       0x74 /* 't' */, 0x86, 0x01  /* (to 0x020C state 248) */,
+                       0x08, /* fail */
+/* pos 008a:  26 */    0xE5 /* 'e' -> */,
+/* pos 008b:  27 */    0xE3 /* 'c' -> */,
+/* pos 008c:  28 */    0xF4 /* 't' -> */,
+/* pos 008d:  29 */    0x69 /* 'i' */, 0x07, 0x00  /* (to 0x0094 state  30) */,
+                       0x20 /* ' ' */, 0xDE, 0x03  /* (to 0x046E state 675) */,
+                       0x08, /* fail */
+/* pos 0094:  30 */    0xEF /* 'o' -> */,
+/* pos 0095:  31 */    0xEE /* 'n' -> */,
+/* pos 0096:  32 */    0xBA /* ':' -> */,
+/* pos 0097:  33 */    0x00, 0x04                  /* - terminal marker  4 - */,
+/* pos 0099:  34 */    0x70 /* 'p' */, 0x0A, 0x00  /* (to 0x00A3 state  35) */,
+                       0x73 /* 's' */, 0x68, 0x03  /* (to 0x0404 state 596) */,
+                       0x72 /* 'r' */, 0xA0, 0x03  /* (to 0x043F state 642) */,
+                       0x08, /* fail */
+/* pos 00a3:  35 */    0xE7 /* 'g' -> */,
+/* pos 00a4:  36 */    0xF2 /* 'r' -> */,
+/* pos 00a5:  37 */    0xE1 /* 'a' -> */,
+/* pos 00a6:  38 */    0xE4 /* 'd' -> */,
+/* pos 00a7:  39 */    0xE5 /* 'e' -> */,
+/* pos 00a8:  40 */    0xBA /* ':' -> */,
+/* pos 00a9:  41 */    0x00, 0x05                  /* - terminal marker  5 - */,
+/* pos 00ab:  42 */    0xE9 /* 'i' -> */,
+/* pos 00ac:  43 */    0xE7 /* 'g' -> */,
+/* pos 00ad:  44 */    0xE9 /* 'i' -> */,
+/* pos 00ae:  45 */    0xEE /* 'n' -> */,
+/* pos 00af:  46 */    0xBA /* ':' -> */,
+/* pos 00b0:  47 */    0x00, 0x06                  /* - terminal marker  6 - */,
+/* pos 00b2:  48 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x00B9 state  49) */,
+                       0x74 /* 't' */, 0x1C, 0x03  /* (to 0x03D1 state 553) */,
+                       0x08, /* fail */
+/* pos 00b9:  49 */    0x63 /* 'c' */, 0x0A, 0x00  /* (to 0x00C3 state  50) */,
+                       0x72 /* 'r' */, 0x05, 0x03  /* (to 0x03C1 state 539) */,
+                       0x74 /* 't' */, 0x08, 0x03  /* (to 0x03C7 state 544) */,
+                       0x08, /* fail */
+/* pos 00c3:  50 */    0xAD /* '-' -> */,
+/* pos 00c4:  51 */    0xF7 /* 'w' -> */,
+/* pos 00c5:  52 */    0xE5 /* 'e' -> */,
+/* pos 00c6:  53 */    0xE2 /* 'b' -> */,
+/* pos 00c7:  54 */    0xF3 /* 's' -> */,
+/* pos 00c8:  55 */    0xEF /* 'o' -> */,
+/* pos 00c9:  56 */    0xE3 /* 'c' -> */,
+/* pos 00ca:  57 */    0xEB /* 'k' -> */,
+/* pos 00cb:  58 */    0xE5 /* 'e' -> */,
+/* pos 00cc:  59 */    0xF4 /* 't' -> */,
+/* pos 00cd:  60 */    0xAD /* '-' -> */,
+/* pos 00ce:  61 */    0x64 /* 'd' */, 0x19, 0x00  /* (to 0x00E7 state  62) */,
+                       0x65 /* 'e' */, 0x20, 0x00  /* (to 0x00F1 state  70) */,
+                       0x6B /* 'k' */, 0x29, 0x00  /* (to 0x00FD state  81) */,
+                       0x70 /* 'p' */, 0x38, 0x00  /* (to 0x010F state  88) */,
+                       0x61 /* 'a' */, 0x3F, 0x00  /* (to 0x0119 state  97) */,
+                       0x6E /* 'n' */, 0x44, 0x00  /* (to 0x0121 state 104) */,
+                       0x76 /* 'v' */, 0x89, 0x01  /* (to 0x0269 state 284) */,
+                       0x6F /* 'o' */, 0x8F, 0x01  /* (to 0x0272 state 292) */,
+                       0x08, /* fail */
+/* pos 00e7:  62 */    0xF2 /* 'r' -> */,
+/* pos 00e8:  63 */    0xE1 /* 'a' -> */,
+/* pos 00e9:  64 */    0xE6 /* 'f' -> */,
+/* pos 00ea:  65 */    0xF4 /* 't' -> */,
+/* pos 00eb:  66 */    0xBA /* ':' -> */,
+/* pos 00ec:  67 */    0x00, 0x07                  /* - terminal marker  7 - */,
+/* pos 00ee:  68 */    0x8A /* '.' -> */,
+/* pos 00ef:  69 */    0x00, 0x08                  /* - terminal marker  8 - */,
+/* pos 00f1:  70 */    0xF8 /* 'x' -> */,
+/* pos 00f2:  71 */    0xF4 /* 't' -> */,
+/* pos 00f3:  72 */    0xE5 /* 'e' -> */,
+/* pos 00f4:  73 */    0xEE /* 'n' -> */,
+/* pos 00f5:  74 */    0xF3 /* 's' -> */,
+/* pos 00f6:  75 */    0xE9 /* 'i' -> */,
+/* pos 00f7:  76 */    0xEF /* 'o' -> */,
+/* pos 00f8:  77 */    0xEE /* 'n' -> */,
+/* pos 00f9:  78 */    0xF3 /* 's' -> */,
+/* pos 00fa:  79 */    0xBA /* ':' -> */,
+/* pos 00fb:  80 */    0x00, 0x09                  /* - terminal marker  9 - */,
+/* pos 00fd:  81 */    0xE5 /* 'e' -> */,
+/* pos 00fe:  82 */    0xF9 /* 'y' -> */,
+/* pos 00ff:  83 */    0x31 /* '1' */, 0x0A, 0x00  /* (to 0x0109 state  84) */,
+                       0x32 /* '2' */, 0x0A, 0x00  /* (to 0x010C state  86) */,
+                       0x3A /* ':' */, 0x62, 0x01  /* (to 0x0267 state 283) */,
+                       0x08, /* fail */
+/* pos 0109:  84 */    0xBA /* ':' -> */,
+/* pos 010a:  85 */    0x00, 0x0A                  /* - terminal marker 10 - */,
+/* pos 010c:  86 */    0xBA /* ':' -> */,
+/* pos 010d:  87 */    0x00, 0x0B                  /* - terminal marker 11 - */,
+/* pos 010f:  88 */    0xF2 /* 'r' -> */,
+/* pos 0110:  89 */    0xEF /* 'o' -> */,
+/* pos 0111:  90 */    0xF4 /* 't' -> */,
+/* pos 0112:  91 */    0xEF /* 'o' -> */,
+/* pos 0113:  92 */    0xE3 /* 'c' -> */,
+/* pos 0114:  93 */    0xEF /* 'o' -> */,
+/* pos 0115:  94 */    0xEC /* 'l' -> */,
+/* pos 0116:  95 */    0xBA /* ':' -> */,
+/* pos 0117:  96 */    0x00, 0x0C                  /* - terminal marker 12 - */,
+/* pos 0119:  97 */    0xE3 /* 'c' -> */,
+/* pos 011a:  98 */    0xE3 /* 'c' -> */,
+/* pos 011b:  99 */    0xE5 /* 'e' -> */,
+/* pos 011c: 100 */    0xF0 /* 'p' -> */,
+/* pos 011d: 101 */    0xF4 /* 't' -> */,
+/* pos 011e: 102 */    0xBA /* ':' -> */,
+/* pos 011f: 103 */    0x00, 0x0D                  /* - terminal marker 13 - */,
+/* pos 0121: 104 */    0xEF /* 'o' -> */,
+/* pos 0122: 105 */    0xEE /* 'n' -> */,
+/* pos 0123: 106 */    0xE3 /* 'c' -> */,
+/* pos 0124: 107 */    0xE5 /* 'e' -> */,
+/* pos 0125: 108 */    0xBA /* ':' -> */,
+/* pos 0126: 109 */    0x00, 0x0E                  /* - terminal marker 14 - */,
+/* pos 0128: 110 */    0xF4 /* 't' -> */,
+/* pos 0129: 111 */    0xF0 /* 'p' -> */,
+/* pos 012a: 112 */    0x2F /* '/' */, 0x07, 0x00  /* (to 0x0131 state 113) */,
+                       0x32 /* '2' */, 0x10, 0x00  /* (to 0x013D state 118) */,
+                       0x08, /* fail */
+/* pos 0131: 113 */    0xB1 /* '1' -> */,
+/* pos 0132: 114 */    0xAE /* '.' -> */,
+/* pos 0133: 115 */    0x31 /* '1' */, 0x07, 0x00  /* (to 0x013A state 116) */,
+                       0x30 /* '0' */, 0x27, 0x03  /* (to 0x045D state 660) */,
+                       0x08, /* fail */
+/* pos 013a: 116 */    0xA0 /* ' ' -> */,
+/* pos 013b: 117 */    0x00, 0x0F                  /* - terminal marker 15 - */,
+/* pos 013d: 118 */    0xAD /* '-' -> */,
+/* pos 013e: 119 */    0xF3 /* 's' -> */,
+/* pos 013f: 120 */    0xE5 /* 'e' -> */,
+/* pos 0140: 121 */    0xF4 /* 't' -> */,
+/* pos 0141: 122 */    0xF4 /* 't' -> */,
+/* pos 0142: 123 */    0xE9 /* 'i' -> */,
+/* pos 0143: 124 */    0xEE /* 'n' -> */,
+/* pos 0144: 125 */    0xE7 /* 'g' -> */,
+/* pos 0145: 126 */    0xF3 /* 's' -> */,
+/* pos 0146: 127 */    0xBA /* ':' -> */,
+/* pos 0147: 128 */    0x00, 0x10                  /* - terminal marker 16 - */,
+/* pos 0149: 129 */    0x63 /* 'c' */, 0x0D, 0x00  /* (to 0x0156 state 130) */,
+                       0x75 /* 'u' */, 0xAC, 0x00  /* (to 0x01F8 state 230) */,
+                       0x67 /* 'g' */, 0x86, 0x01  /* (to 0x02D5 state 358) */,
+                       0x6C /* 'l' */, 0x87, 0x01  /* (to 0x02D9 state 361) */,
+                       0x08, /* fail */
+/* pos 0156: 130 */    0xE3 /* 'c' -> */,
+/* pos 0157: 131 */    0xE5 /* 'e' -> */,
+/* pos 0158: 132 */    0x70 /* 'p' */, 0x07, 0x00  /* (to 0x015F state 133) */,
+                       0x73 /* 's' */, 0x0E, 0x00  /* (to 0x0169 state 136) */,
+                       0x08, /* fail */
+/* pos 015f: 133 */    0xF4 /* 't' -> */,
+/* pos 0160: 134 */    0x3A /* ':' */, 0x07, 0x00  /* (to 0x0167 state 135) */,
+                       0x2D /* '-' */, 0x59, 0x00  /* (to 0x01BC state 192) */,
+                       0x08, /* fail */
+/* pos 0167: 135 */    0x00, 0x11                  /* - terminal marker 17 - */,
+/* pos 0169: 136 */    0xF3 /* 's' -> */,
+/* pos 016a: 137 */    0xAD /* '-' -> */,
+/* pos 016b: 138 */    0xE3 /* 'c' -> */,
+/* pos 016c: 139 */    0xEF /* 'o' -> */,
+/* pos 016d: 140 */    0xEE /* 'n' -> */,
+/* pos 016e: 141 */    0xF4 /* 't' -> */,
+/* pos 016f: 142 */    0xF2 /* 'r' -> */,
+/* pos 0170: 143 */    0xEF /* 'o' -> */,
+/* pos 0171: 144 */    0xEC /* 'l' -> */,
+/* pos 0172: 145 */    0xAD /* '-' -> */,
+/* pos 0173: 146 */    0x72 /* 'r' */, 0x07, 0x00  /* (to 0x017A state 147) */,
+                       0x61 /* 'a' */, 0x51, 0x01  /* (to 0x02C7 state 345) */,
+                       0x08, /* fail */
+/* pos 017a: 147 */    0xE5 /* 'e' -> */,
+/* pos 017b: 148 */    0xF1 /* 'q' -> */,
+/* pos 017c: 149 */    0xF5 /* 'u' -> */,
+/* pos 017d: 150 */    0xE5 /* 'e' -> */,
+/* pos 017e: 151 */    0xF3 /* 's' -> */,
+/* pos 017f: 152 */    0xF4 /* 't' -> */,
+/* pos 0180: 153 */    0xAD /* '-' -> */,
+/* pos 0181: 154 */    0xE8 /* 'h' -> */,
+/* pos 0182: 155 */    0xE5 /* 'e' -> */,
+/* pos 0183: 156 */    0xE1 /* 'a' -> */,
+/* pos 0184: 157 */    0xE4 /* 'd' -> */,
+/* pos 0185: 158 */    0xE5 /* 'e' -> */,
+/* pos 0186: 159 */    0xF2 /* 'r' -> */,
+/* pos 0187: 160 */    0xF3 /* 's' -> */,
+/* pos 0188: 161 */    0xBA /* ':' -> */,
+/* pos 0189: 162 */    0x00, 0x12                  /* - terminal marker 18 - */,
+/* pos 018b: 163 */    0xE6 /* 'f' -> */,
+/* pos 018c: 164 */    0xAD /* '-' -> */,
+/* pos 018d: 165 */    0x6D /* 'm' */, 0x0D, 0x00  /* (to 0x019A state 166) */,
+                       0x6E /* 'n' */, 0x20, 0x00  /* (to 0x01B0 state 181) */,
+                       0x72 /* 'r' */, 0xA7, 0x01  /* (to 0x033A state 435) */,
+                       0x75 /* 'u' */, 0xAB, 0x01  /* (to 0x0341 state 441) */,
+                       0x08, /* fail */
+/* pos 019a: 166 */    0x6F /* 'o' */, 0x07, 0x00  /* (to 0x01A1 state 167) */,
+                       0x61 /* 'a' */, 0x97, 0x01  /* (to 0x0334 state 430) */,
+                       0x08, /* fail */
+/* pos 01a1: 167 */    0xE4 /* 'd' -> */,
+/* pos 01a2: 168 */    0xE9 /* 'i' -> */,
+/* pos 01a3: 169 */    0xE6 /* 'f' -> */,
+/* pos 01a4: 170 */    0xE9 /* 'i' -> */,
+/* pos 01a5: 171 */    0xE5 /* 'e' -> */,
+/* pos 01a6: 172 */    0xE4 /* 'd' -> */,
+/* pos 01a7: 173 */    0xAD /* '-' -> */,
+/* pos 01a8: 174 */    0xF3 /* 's' -> */,
+/* pos 01a9: 175 */    0xE9 /* 'i' -> */,
+/* pos 01aa: 176 */    0xEE /* 'n' -> */,
+/* pos 01ab: 177 */    0xE3 /* 'c' -> */,
+/* pos 01ac: 178 */    0xE5 /* 'e' -> */,
+/* pos 01ad: 179 */    0xBA /* ':' -> */,
+/* pos 01ae: 180 */    0x00, 0x13                  /* - terminal marker 19 - */,
+/* pos 01b0: 181 */    0xEF /* 'o' -> */,
+/* pos 01b1: 182 */    0xEE /* 'n' -> */,
+/* pos 01b2: 183 */    0xE5 /* 'e' -> */,
+/* pos 01b3: 184 */    0xAD /* '-' -> */,
+/* pos 01b4: 185 */    0xED /* 'm' -> */,
+/* pos 01b5: 186 */    0xE1 /* 'a' -> */,
+/* pos 01b6: 187 */    0xF4 /* 't' -> */,
+/* pos 01b7: 188 */    0xE3 /* 'c' -> */,
+/* pos 01b8: 189 */    0xE8 /* 'h' -> */,
+/* pos 01b9: 190 */    0xBA /* ':' -> */,
+/* pos 01ba: 191 */    0x00, 0x14                  /* - terminal marker 20 - */,
+/* pos 01bc: 192 */    0x65 /* 'e' */, 0x0D, 0x00  /* (to 0x01C9 state 193) */,
+                       0x6C /* 'l' */, 0x14, 0x00  /* (to 0x01D3 state 202) */,
+                       0x63 /* 'c' */, 0xF4, 0x00  /* (to 0x02B6 state 330) */,
+                       0x72 /* 'r' */, 0xFA, 0x00  /* (to 0x02BF state 338) */,
+                       0x08, /* fail */
+/* pos 01c9: 193 */    0xEE /* 'n' -> */,
+/* pos 01ca: 194 */    0xE3 /* 'c' -> */,
+/* pos 01cb: 195 */    0xEF /* 'o' -> */,
+/* pos 01cc: 196 */    0xE4 /* 'd' -> */,
+/* pos 01cd: 197 */    0xE9 /* 'i' -> */,
+/* pos 01ce: 198 */    0xEE /* 'n' -> */,
+/* pos 01cf: 199 */    0xE7 /* 'g' -> */,
+/* pos 01d0: 200 */    0xBA /* ':' -> */,
+/* pos 01d1: 201 */    0x00, 0x15                  /* - terminal marker 21 - */,
+/* pos 01d3: 202 */    0xE1 /* 'a' -> */,
+/* pos 01d4: 203 */    0xEE /* 'n' -> */,
+/* pos 01d5: 204 */    0xE7 /* 'g' -> */,
+/* pos 01d6: 205 */    0xF5 /* 'u' -> */,
+/* pos 01d7: 206 */    0xE1 /* 'a' -> */,
+/* pos 01d8: 207 */    0xE7 /* 'g' -> */,
+/* pos 01d9: 208 */    0xE5 /* 'e' -> */,
+/* pos 01da: 209 */    0xBA /* ':' -> */,
+/* pos 01db: 210 */    0x00, 0x16                  /* - terminal marker 22 - */,
+/* pos 01dd: 211 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x01E4 state 212) */,
+                       0x6F /* 'o' */, 0xA7, 0x01  /* (to 0x0387 state 497) */,
+                       0x08, /* fail */
+/* pos 01e4: 212 */    0xE7 /* 'g' -> */,
+/* pos 01e5: 213 */    0xED /* 'm' -> */,
+/* pos 01e6: 214 */    0xE1 /* 'a' -> */,
+/* pos 01e7: 215 */    0xBA /* ':' -> */,
+/* pos 01e8: 216 */    0x00, 0x17                  /* - terminal marker 23 - */,
+/* pos 01ea: 217 */    0xE3 /* 'c' -> */,
+/* pos 01eb: 218 */    0xE8 /* 'h' -> */,
+/* pos 01ec: 219 */    0xE5 /* 'e' -> */,
+/* pos 01ed: 220 */    0xAD /* '-' -> */,
+/* pos 01ee: 221 */    0xE3 /* 'c' -> */,
+/* pos 01ef: 222 */    0xEF /* 'o' -> */,
+/* pos 01f0: 223 */    0xEE /* 'n' -> */,
+/* pos 01f1: 224 */    0xF4 /* 't' -> */,
+/* pos 01f2: 225 */    0xF2 /* 'r' -> */,
+/* pos 01f3: 226 */    0xEF /* 'o' -> */,
+/* pos 01f4: 227 */    0xEC /* 'l' -> */,
+/* pos 01f5: 228 */    0xBA /* ':' -> */,
+/* pos 01f6: 229 */    0x00, 0x18                  /* - terminal marker 24 - */,
+/* pos 01f8: 230 */    0xF4 /* 't' -> */,
+/* pos 01f9: 231 */    0xE8 /* 'h' -> */,
+/* pos 01fa: 232 */    0xEF /* 'o' -> */,
+/* pos 01fb: 233 */    0xF2 /* 'r' -> */,
+/* pos 01fc: 234 */    0xE9 /* 'i' -> */,
+/* pos 01fd: 235 */    0xFA /* 'z' -> */,
+/* pos 01fe: 236 */    0xE1 /* 'a' -> */,
+/* pos 01ff: 237 */    0xF4 /* 't' -> */,
+/* pos 0200: 238 */    0xE9 /* 'i' -> */,
+/* pos 0201: 239 */    0xEF /* 'o' -> */,
+/* pos 0202: 240 */    0xEE /* 'n' -> */,
+/* pos 0203: 241 */    0xBA /* ':' -> */,
+/* pos 0204: 242 */    0x00, 0x19                  /* - terminal marker 25 - */,
+/* pos 0206: 243 */    0xEB /* 'k' -> */,
+/* pos 0207: 244 */    0xE9 /* 'i' -> */,
+/* pos 0208: 245 */    0xE5 /* 'e' -> */,
+/* pos 0209: 246 */    0xBA /* ':' -> */,
+/* pos 020a: 247 */    0x00, 0x1A                  /* - terminal marker 26 - */,
+/* pos 020c: 248 */    0xE5 /* 'e' -> */,
+/* pos 020d: 249 */    0xEE /* 'n' -> */,
+/* pos 020e: 250 */    0xF4 /* 't' -> */,
+/* pos 020f: 251 */    0xAD /* '-' -> */,
+/* pos 0210: 252 */    0x6C /* 'l' */, 0x10, 0x00  /* (to 0x0220 state 253) */,
+                       0x74 /* 't' */, 0x1E, 0x00  /* (to 0x0231 state 260) */,
+                       0x64 /* 'd' */, 0xC9, 0x00  /* (to 0x02DF state 366) */,
+                       0x65 /* 'e' */, 0xD3, 0x00  /* (to 0x02EC state 378) */,
+                       0x72 /* 'r' */, 0xEC, 0x00  /* (to 0x0308 state 403) */,
+                       0x08, /* fail */
+/* pos 0220: 253 */    0x65 /* 'e' */, 0x0A, 0x00  /* (to 0x022A state 254) */,
+                       0x61 /* 'a' */, 0xD3, 0x00  /* (to 0x02F6 state 387) */,
+                       0x6F /* 'o' */, 0xD9, 0x00  /* (to 0x02FF state 395) */,
+                       0x08, /* fail */
+/* pos 022a: 254 */    0xEE /* 'n' -> */,
+/* pos 022b: 255 */    0xE7 /* 'g' -> */,
+/* pos 022c: 256 */    0xF4 /* 't' -> */,
+/* pos 022d: 257 */    0xE8 /* 'h' -> */,
+/* pos 022e: 258 */    0xBA /* ':' -> */,
+/* pos 022f: 259 */    0x00, 0x1B                  /* - terminal marker 27 - */,
+/* pos 0231: 260 */    0xF9 /* 'y' -> */,
+/* pos 0232: 261 */    0xF0 /* 'p' -> */,
+/* pos 0233: 262 */    0xE5 /* 'e' -> */,
+/* pos 0234: 263 */    0xBA /* ':' -> */,
+/* pos 0235: 264 */    0x00, 0x1C                  /* - terminal marker 28 - */,
+/* pos 0237: 265 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x023E state 266) */,
+                       0x65 /* 'e' */, 0xFF, 0x01  /* (to 0x0439 state 637) */,
+                       0x08, /* fail */
+/* pos 023e: 266 */    0xF4 /* 't' -> */,
+/* pos 023f: 267 */    0xE5 /* 'e' -> */,
+/* pos 0240: 268 */    0xBA /* ':' -> */,
+/* pos 0241: 269 */    0x00, 0x1D                  /* - terminal marker 29 - */,
+/* pos 0243: 270 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x024A state 271) */,
+                       0x65 /* 'e' */, 0x0A, 0x00  /* (to 0x0250 state 276) */,
+                       0x08, /* fail */
+/* pos 024a: 271 */    0xEE /* 'n' -> */,
+/* pos 024b: 272 */    0xE7 /* 'g' -> */,
+/* pos 024c: 273 */    0xE5 /* 'e' -> */,
+/* pos 024d: 274 */    0xBA /* ':' -> */,
+/* pos 024e: 275 */    0x00, 0x1E                  /* - terminal marker 30 - */,
+/* pos 0250: 276 */    0x66 /* 'f' */, 0x0A, 0x00  /* (to 0x025A state 277) */,
+                       0x74 /* 't' */, 0x63, 0x01  /* (to 0x03B6 state 529) */,
+                       0x70 /* 'p' */, 0x22, 0x02  /* (to 0x0478 state 682) */,
+                       0x08, /* fail */
+/* pos 025a: 277 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x0261 state 278) */,
+                       0x72 /* 'r' */, 0x53, 0x01  /* (to 0x03B0 state 524) */,
+                       0x08, /* fail */
+/* pos 0261: 278 */    0xF2 /* 'r' -> */,
+/* pos 0262: 279 */    0xE5 /* 'e' -> */,
+/* pos 0263: 280 */    0xF2 /* 'r' -> */,
+/* pos 0264: 281 */    0xBA /* ':' -> */,
+/* pos 0265: 282 */    0x00, 0x1F                  /* - terminal marker 31 - */,
+/* pos 0267: 283 */    0x00, 0x20                  /* - terminal marker 32 - */,
+/* pos 0269: 284 */    0xE5 /* 'e' -> */,
+/* pos 026a: 285 */    0xF2 /* 'r' -> */,
+/* pos 026b: 286 */    0xF3 /* 's' -> */,
+/* pos 026c: 287 */    0xE9 /* 'i' -> */,
+/* pos 026d: 288 */    0xEF /* 'o' -> */,
+/* pos 026e: 289 */    0xEE /* 'n' -> */,
+/* pos 026f: 290 */    0xBA /* ':' -> */,
+/* pos 0270: 291 */    0x00, 0x21                  /* - terminal marker 33 - */,
+/* pos 0272: 292 */    0xF2 /* 'r' -> */,
+/* pos 0273: 293 */    0xE9 /* 'i' -> */,
+/* pos 0274: 294 */    0xE7 /* 'g' -> */,
+/* pos 0275: 295 */    0xE9 /* 'i' -> */,
+/* pos 0276: 296 */    0xEE /* 'n' -> */,
+/* pos 0277: 297 */    0xBA /* ':' -> */,
+/* pos 0278: 298 */    0x00, 0x22                  /* - terminal marker 34 - */,
+/* pos 027a: 299 */    0x61 /* 'a' */, 0x0D, 0x00  /* (to 0x0287 state 300) */,
+                       0x6D /* 'm' */, 0x14, 0x00  /* (to 0x0291 state 309) */,
+                       0x70 /* 'p' */, 0x18, 0x00  /* (to 0x0298 state 315) */,
+                       0x73 /* 's' */, 0x20, 0x00  /* (to 0x02A3 state 319) */,
+                       0x08, /* fail */
+/* pos 0287: 300 */    0xF5 /* 'u' -> */,
+/* pos 0288: 301 */    0xF4 /* 't' -> */,
+/* pos 0289: 302 */    0xE8 /* 'h' -> */,
+/* pos 028a: 303 */    0xEF /* 'o' -> */,
+/* pos 028b: 304 */    0xF2 /* 'r' -> */,
+/* pos 028c: 305 */    0xE9 /* 'i' -> */,
+/* pos 028d: 306 */    0xF4 /* 't' -> */,
+/* pos 028e: 307 */    0xF9 /* 'y' -> */,
+/* pos 028f: 308 */    0x00, 0x23                  /* - terminal marker 35 - */,
+/* pos 0291: 309 */    0xE5 /* 'e' -> */,
+/* pos 0292: 310 */    0xF4 /* 't' -> */,
+/* pos 0293: 311 */    0xE8 /* 'h' -> */,
+/* pos 0294: 312 */    0xEF /* 'o' -> */,
+/* pos 0295: 313 */    0xE4 /* 'd' -> */,
+/* pos 0296: 314 */    0x00, 0x24                  /* - terminal marker 36 - */,
+/* pos 0298: 315 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x029F state 316) */,
+                       0x72 /* 'r' */, 0xE9, 0x01  /* (to 0x0484 state 693) */,
+                       0x08, /* fail */
+/* pos 029f: 316 */    0xF4 /* 't' -> */,
+/* pos 02a0: 317 */    0xE8 /* 'h' -> */,
+/* pos 02a1: 318 */    0x00, 0x25                  /* - terminal marker 37 - */,
+/* pos 02a3: 319 */    0x63 /* 'c' */, 0x07, 0x00  /* (to 0x02AA state 320) */,
+                       0x74 /* 't' */, 0x0A, 0x00  /* (to 0x02B0 state 325) */,
+                       0x08, /* fail */
+/* pos 02aa: 320 */    0xE8 /* 'h' -> */,
+/* pos 02ab: 321 */    0xE5 /* 'e' -> */,
+/* pos 02ac: 322 */    0xED /* 'm' -> */,
+/* pos 02ad: 323 */    0xE5 /* 'e' -> */,
+/* pos 02ae: 324 */    0x00, 0x26                  /* - terminal marker 38 - */,
+/* pos 02b0: 325 */    0xE1 /* 'a' -> */,
+/* pos 02b1: 326 */    0xF4 /* 't' -> */,
+/* pos 02b2: 327 */    0xF5 /* 'u' -> */,
+/* pos 02b3: 328 */    0xF3 /* 's' -> */,
+/* pos 02b4: 329 */    0x00, 0x27                  /* - terminal marker 39 - */,
+/* pos 02b6: 330 */    0xE8 /* 'h' -> */,
+/* pos 02b7: 331 */    0xE1 /* 'a' -> */,
+/* pos 02b8: 332 */    0xF2 /* 'r' -> */,
+/* pos 02b9: 333 */    0xF3 /* 's' -> */,
+/* pos 02ba: 334 */    0xE5 /* 'e' -> */,
+/* pos 02bb: 335 */    0xF4 /* 't' -> */,
+/* pos 02bc: 336 */    0xBA /* ':' -> */,
+/* pos 02bd: 337 */    0x00, 0x28                  /* - terminal marker 40 - */,
+/* pos 02bf: 338 */    0xE1 /* 'a' -> */,
+/* pos 02c0: 339 */    0xEE /* 'n' -> */,
+/* pos 02c1: 340 */    0xE7 /* 'g' -> */,
+/* pos 02c2: 341 */    0xE5 /* 'e' -> */,
+/* pos 02c3: 342 */    0xF3 /* 's' -> */,
+/* pos 02c4: 343 */    0xBA /* ':' -> */,
+/* pos 02c5: 344 */    0x00, 0x29                  /* - terminal marker 41 - */,
+/* pos 02c7: 345 */    0xEC /* 'l' -> */,
+/* pos 02c8: 346 */    0xEC /* 'l' -> */,
+/* pos 02c9: 347 */    0xEF /* 'o' -> */,
+/* pos 02ca: 348 */    0xF7 /* 'w' -> */,
+/* pos 02cb: 349 */    0xAD /* '-' -> */,
+/* pos 02cc: 350 */    0xEF /* 'o' -> */,
+/* pos 02cd: 351 */    0xF2 /* 'r' -> */,
+/* pos 02ce: 352 */    0xE9 /* 'i' -> */,
+/* pos 02cf: 353 */    0xE7 /* 'g' -> */,
+/* pos 02d0: 354 */    0xE9 /* 'i' -> */,
+/* pos 02d1: 355 */    0xEE /* 'n' -> */,
+/* pos 02d2: 356 */    0xBA /* ':' -> */,
+/* pos 02d3: 357 */    0x00, 0x2A                  /* - terminal marker 42 - */,
+/* pos 02d5: 358 */    0xE5 /* 'e' -> */,
+/* pos 02d6: 359 */    0xBA /* ':' -> */,
+/* pos 02d7: 360 */    0x00, 0x2B                  /* - terminal marker 43 - */,
+/* pos 02d9: 361 */    0xEC /* 'l' -> */,
+/* pos 02da: 362 */    0xEF /* 'o' -> */,
+/* pos 02db: 363 */    0xF7 /* 'w' -> */,
+/* pos 02dc: 364 */    0xBA /* ':' -> */,
+/* pos 02dd: 365 */    0x00, 0x2C                  /* - terminal marker 44 - */,
+/* pos 02df: 366 */    0xE9 /* 'i' -> */,
+/* pos 02e0: 367 */    0xF3 /* 's' -> */,
+/* pos 02e1: 368 */    0xF0 /* 'p' -> */,
+/* pos 02e2: 369 */    0xEF /* 'o' -> */,
+/* pos 02e3: 370 */    0xF3 /* 's' -> */,
+/* pos 02e4: 371 */    0xE9 /* 'i' -> */,
+/* pos 02e5: 372 */    0xF4 /* 't' -> */,
+/* pos 02e6: 373 */    0xE9 /* 'i' -> */,
+/* pos 02e7: 374 */    0xEF /* 'o' -> */,
+/* pos 02e8: 375 */    0xEE /* 'n' -> */,
+/* pos 02e9: 376 */    0xBA /* ':' -> */,
+/* pos 02ea: 377 */    0x00, 0x2D                  /* - terminal marker 45 - */,
+/* pos 02ec: 378 */    0xEE /* 'n' -> */,
+/* pos 02ed: 379 */    0xE3 /* 'c' -> */,
+/* pos 02ee: 380 */    0xEF /* 'o' -> */,
+/* pos 02ef: 381 */    0xE4 /* 'd' -> */,
+/* pos 02f0: 382 */    0xE9 /* 'i' -> */,
+/* pos 02f1: 383 */    0xEE /* 'n' -> */,
+/* pos 02f2: 384 */    0xE7 /* 'g' -> */,
+/* pos 02f3: 385 */    0xBA /* ':' -> */,
+/* pos 02f4: 386 */    0x00, 0x2E                  /* - terminal marker 46 - */,
+/* pos 02f6: 387 */    0xEE /* 'n' -> */,
+/* pos 02f7: 388 */    0xE7 /* 'g' -> */,
+/* pos 02f8: 389 */    0xF5 /* 'u' -> */,
+/* pos 02f9: 390 */    0xE1 /* 'a' -> */,
+/* pos 02fa: 391 */    0xE7 /* 'g' -> */,
+/* pos 02fb: 392 */    0xE5 /* 'e' -> */,
+/* pos 02fc: 393 */    0xBA /* ':' -> */,
+/* pos 02fd: 394 */    0x00, 0x2F                  /* - terminal marker 47 - */,
+/* pos 02ff: 395 */    0xE3 /* 'c' -> */,
+/* pos 0300: 396 */    0xE1 /* 'a' -> */,
+/* pos 0301: 397 */    0xF4 /* 't' -> */,
+/* pos 0302: 398 */    0xE9 /* 'i' -> */,
+/* pos 0303: 399 */    0xEF /* 'o' -> */,
+/* pos 0304: 400 */    0xEE /* 'n' -> */,
+/* pos 0305: 401 */    0xBA /* ':' -> */,
+/* pos 0306: 402 */    0x00, 0x30                  /* - terminal marker 48 - */,
+/* pos 0308: 403 */    0xE1 /* 'a' -> */,
+/* pos 0309: 404 */    0xEE /* 'n' -> */,
+/* pos 030a: 405 */    0xE7 /* 'g' -> */,
+/* pos 030b: 406 */    0xE5 /* 'e' -> */,
+/* pos 030c: 407 */    0xBA /* ':' -> */,
+/* pos 030d: 408 */    0x00, 0x31                  /* - terminal marker 49 - */,
+/* pos 030f: 409 */    0x74 /* 't' */, 0x07, 0x00  /* (to 0x0316 state 410) */,
+                       0x78 /* 'x' */, 0x09, 0x00  /* (to 0x031B state 414) */,
+                       0x08, /* fail */
+/* pos 0316: 410 */    0xE1 /* 'a' -> */,
+/* pos 0317: 411 */    0xE7 /* 'g' -> */,
+/* pos 0318: 412 */    0xBA /* ':' -> */,
+/* pos 0319: 413 */    0x00, 0x32                  /* - terminal marker 50 - */,
+/* pos 031b: 414 */    0xF0 /* 'p' -> */,
+/* pos 031c: 415 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x0323 state 416) */,
+                       0x69 /* 'i' */, 0x09, 0x00  /* (to 0x0328 state 420) */,
+                       0x08, /* fail */
+/* pos 0323: 416 */    0xE3 /* 'c' -> */,
+/* pos 0324: 417 */    0xF4 /* 't' -> */,
+/* pos 0325: 418 */    0xBA /* ':' -> */,
+/* pos 0326: 419 */    0x00, 0x33                  /* - terminal marker 51 - */,
+/* pos 0328: 420 */    0xF2 /* 'r' -> */,
+/* pos 0329: 421 */    0xE5 /* 'e' -> */,
+/* pos 032a: 422 */    0xF3 /* 's' -> */,
+/* pos 032b: 423 */    0xBA /* ':' -> */,
+/* pos 032c: 424 */    0x00, 0x34                  /* - terminal marker 52 - */,
+/* pos 032e: 425 */    0xF2 /* 'r' -> */,
+/* pos 032f: 426 */    0xEF /* 'o' -> */,
+/* pos 0330: 427 */    0xED /* 'm' -> */,
+/* pos 0331: 428 */    0xBA /* ':' -> */,
+/* pos 0332: 429 */    0x00, 0x35                  /* - terminal marker 53 - */,
+/* pos 0334: 430 */    0xF4 /* 't' -> */,
+/* pos 0335: 431 */    0xE3 /* 'c' -> */,
+/* pos 0336: 432 */    0xE8 /* 'h' -> */,
+/* pos 0337: 433 */    0xBA /* ':' -> */,
+/* pos 0338: 434 */    0x00, 0x36                  /* - terminal marker 54 - */,
+/* pos 033a: 435 */    0xE1 /* 'a' -> */,
+/* pos 033b: 436 */    0xEE /* 'n' -> */,
+/* pos 033c: 437 */    0xE7 /* 'g' -> */,
+/* pos 033d: 438 */    0xE5 /* 'e' -> */,
+/* pos 033e: 439 */    0xBA /* ':' -> */,
+/* pos 033f: 440 */    0x00, 0x37                  /* - terminal marker 55 - */,
+/* pos 0341: 441 */    0xEE /* 'n' -> */,
+/* pos 0342: 442 */    0xED /* 'm' -> */,
+/* pos 0343: 443 */    0xEF /* 'o' -> */,
+/* pos 0344: 444 */    0xE4 /* 'd' -> */,
+/* pos 0345: 445 */    0xE9 /* 'i' -> */,
+/* pos 0346: 446 */    0xE6 /* 'f' -> */,
+/* pos 0347: 447 */    0xE9 /* 'i' -> */,
+/* pos 0348: 448 */    0xE5 /* 'e' -> */,
+/* pos 0349: 449 */    0xE4 /* 'd' -> */,
+/* pos 034a: 450 */    0xAD /* '-' -> */,
+/* pos 034b: 451 */    0xF3 /* 's' -> */,
+/* pos 034c: 452 */    0xE9 /* 'i' -> */,
+/* pos 034d: 453 */    0xEE /* 'n' -> */,
+/* pos 034e: 454 */    0xE3 /* 'c' -> */,
+/* pos 034f: 455 */    0xE5 /* 'e' -> */,
+/* pos 0350: 456 */    0xBA /* ':' -> */,
+/* pos 0351: 457 */    0x00, 0x38                  /* - terminal marker 56 - */,
+/* pos 0353: 458 */    0x61 /* 'a' */, 0x0A, 0x00  /* (to 0x035D state 459) */,
+                       0x69 /* 'i' */, 0x15, 0x00  /* (to 0x036B state 472) */,
+                       0x6F /* 'o' */, 0x17, 0x00  /* (to 0x0370 state 476) */,
+                       0x08, /* fail */
+/* pos 035d: 459 */    0xF3 /* 's' -> */,
+/* pos 035e: 460 */    0xF4 /* 't' -> */,
+/* pos 035f: 461 */    0xAD /* '-' -> */,
+/* pos 0360: 462 */    0xED /* 'm' -> */,
+/* pos 0361: 463 */    0xEF /* 'o' -> */,
+/* pos 0362: 464 */    0xE4 /* 'd' -> */,
+/* pos 0363: 465 */    0xE9 /* 'i' -> */,
+/* pos 0364: 466 */    0xE6 /* 'f' -> */,
+/* pos 0365: 467 */    0xE9 /* 'i' -> */,
+/* pos 0366: 468 */    0xE5 /* 'e' -> */,
+/* pos 0367: 469 */    0xE4 /* 'd' -> */,
+/* pos 0368: 470 */    0xBA /* ':' -> */,
+/* pos 0369: 471 */    0x00, 0x39                  /* - terminal marker 57 - */,
+/* pos 036b: 472 */    0xEE /* 'n' -> */,
+/* pos 036c: 473 */    0xEB /* 'k' -> */,
+/* pos 036d: 474 */    0xBA /* ':' -> */,
+/* pos 036e: 475 */    0x00, 0x3A                  /* - terminal marker 58 - */,
+/* pos 0370: 476 */    0xE3 /* 'c' -> */,
+/* pos 0371: 477 */    0xE1 /* 'a' -> */,
+/* pos 0372: 478 */    0xF4 /* 't' -> */,
+/* pos 0373: 479 */    0xE9 /* 'i' -> */,
+/* pos 0374: 480 */    0xEF /* 'o' -> */,
+/* pos 0375: 481 */    0xEE /* 'n' -> */,
+/* pos 0376: 482 */    0xBA /* ':' -> */,
+/* pos 0377: 483 */    0x00, 0x3B                  /* - terminal marker 59 - */,
+/* pos 0379: 484 */    0xE1 /* 'a' -> */,
+/* pos 037a: 485 */    0xF8 /* 'x' -> */,
+/* pos 037b: 486 */    0xAD /* '-' -> */,
+/* pos 037c: 487 */    0xE6 /* 'f' -> */,
+/* pos 037d: 488 */    0xEF /* 'o' -> */,
+/* pos 037e: 489 */    0xF2 /* 'r' -> */,
+/* pos 037f: 490 */    0xF7 /* 'w' -> */,
+/* pos 0380: 491 */    0xE1 /* 'a' -> */,
+/* pos 0381: 492 */    0xF2 /* 'r' -> */,
+/* pos 0382: 493 */    0xE4 /* 'd' -> */,
+/* pos 0383: 494 */    0xF3 /* 's' -> */,
+/* pos 0384: 495 */    0xBA /* ':' -> */,
+/* pos 0385: 496 */    0x00, 0x3C                  /* - terminal marker 60 - */,
+/* pos 0387: 497 */    0xF8 /* 'x' -> */,
+/* pos 0388: 498 */    0xF9 /* 'y' -> */,
+/* pos 0389: 499 */    0x2D /* '-' */, 0x07, 0x00  /* (to 0x0390 state 500) */,
+                       0x20 /* ' ' */, 0xBB, 0x00  /* (to 0x0447 state 649) */,
+                       0x08, /* fail */
+/* pos 0390: 500 */    0xE1 /* 'a' -> */,
+/* pos 0391: 501 */    0xF5 /* 'u' -> */,
+/* pos 0392: 502 */    0xF4 /* 't' -> */,
+/* pos 0393: 503 */    0xE8 /* 'h' -> */,
+/* pos 0394: 504 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x039B state 505) */,
+                       0x6F /* 'o' */, 0x0E, 0x00  /* (to 0x03A5 state 514) */,
+                       0x08, /* fail */
+/* pos 039b: 505 */    0xEE /* 'n' -> */,
+/* pos 039c: 506 */    0xF4 /* 't' -> */,
+/* pos 039d: 507 */    0xE9 /* 'i' -> */,
+/* pos 039e: 508 */    0xE3 /* 'c' -> */,
+/* pos 039f: 509 */    0xE1 /* 'a' -> */,
+/* pos 03a0: 510 */    0xF4 /* 't' -> */,
+/* pos 03a1: 511 */    0xE5 /* 'e' -> */,
+/* pos 03a2: 512 */    0xBA /* ':' -> */,
+/* pos 03a3: 513 */    0x00, 0x3D                  /* - terminal marker 61 - */,
+/* pos 03a5: 514 */    0xF2 /* 'r' -> */,
+/* pos 03a6: 515 */    0xE9 /* 'i' -> */,
+/* pos 03a7: 516 */    0xFA /* 'z' -> */,
+/* pos 03a8: 517 */    0xE1 /* 'a' -> */,
+/* pos 03a9: 518 */    0xF4 /* 't' -> */,
+/* pos 03aa: 519 */    0xE9 /* 'i' -> */,
+/* pos 03ab: 520 */    0xEF /* 'o' -> */,
+/* pos 03ac: 521 */    0xEE /* 'n' -> */,
+/* pos 03ad: 522 */    0xBA /* ':' -> */,
+/* pos 03ae: 523 */    0x00, 0x3E                  /* - terminal marker 62 - */,
+/* pos 03b0: 524 */    0xE5 /* 'e' -> */,
+/* pos 03b1: 525 */    0xF3 /* 's' -> */,
+/* pos 03b2: 526 */    0xE8 /* 'h' -> */,
+/* pos 03b3: 527 */    0xBA /* ':' -> */,
+/* pos 03b4: 528 */    0x00, 0x3F                  /* - terminal marker 63 - */,
+/* pos 03b6: 529 */    0xF2 /* 'r' -> */,
+/* pos 03b7: 530 */    0xF9 /* 'y' -> */,
+/* pos 03b8: 531 */    0xAD /* '-' -> */,
+/* pos 03b9: 532 */    0xE1 /* 'a' -> */,
+/* pos 03ba: 533 */    0xE6 /* 'f' -> */,
+/* pos 03bb: 534 */    0xF4 /* 't' -> */,
+/* pos 03bc: 535 */    0xE5 /* 'e' -> */,
+/* pos 03bd: 536 */    0xF2 /* 'r' -> */,
+/* pos 03be: 537 */    0xBA /* ':' -> */,
+/* pos 03bf: 538 */    0x00, 0x40                  /* - terminal marker 64 - */,
+/* pos 03c1: 539 */    0xF6 /* 'v' -> */,
+/* pos 03c2: 540 */    0xE5 /* 'e' -> */,
+/* pos 03c3: 541 */    0xF2 /* 'r' -> */,
+/* pos 03c4: 542 */    0xBA /* ':' -> */,
+/* pos 03c5: 543 */    0x00, 0x41                  /* - terminal marker 65 - */,
+/* pos 03c7: 544 */    0xAD /* '-' -> */,
+/* pos 03c8: 545 */    0xE3 /* 'c' -> */,
+/* pos 03c9: 546 */    0xEF /* 'o' -> */,
+/* pos 03ca: 547 */    0xEF /* 'o' -> */,
+/* pos 03cb: 548 */    0xEB /* 'k' -> */,
+/* pos 03cc: 549 */    0xE9 /* 'i' -> */,
+/* pos 03cd: 550 */    0xE5 /* 'e' -> */,
+/* pos 03ce: 551 */    0xBA /* ':' -> */,
+/* pos 03cf: 552 */    0x00, 0x42                  /* - terminal marker 66 - */,
+/* pos 03d1: 553 */    0xF2 /* 'r' -> */,
+/* pos 03d2: 554 */    0xE9 /* 'i' -> */,
+/* pos 03d3: 555 */    0xE3 /* 'c' -> */,
+/* pos 03d4: 556 */    0xF4 /* 't' -> */,
+/* pos 03d5: 557 */    0xAD /* '-' -> */,
+/* pos 03d6: 558 */    0xF4 /* 't' -> */,
+/* pos 03d7: 559 */    0xF2 /* 'r' -> */,
+/* pos 03d8: 560 */    0xE1 /* 'a' -> */,
+/* pos 03d9: 561 */    0xEE /* 'n' -> */,
+/* pos 03da: 562 */    0xF3 /* 's' -> */,
+/* pos 03db: 563 */    0xF0 /* 'p' -> */,
+/* pos 03dc: 564 */    0xEF /* 'o' -> */,
+/* pos 03dd: 565 */    0xF2 /* 'r' -> */,
+/* pos 03de: 566 */    0xF4 /* 't' -> */,
+/* pos 03df: 567 */    0xAD /* '-' -> */,
+/* pos 03e0: 568 */    0xF3 /* 's' -> */,
+/* pos 03e1: 569 */    0xE5 /* 'e' -> */,
+/* pos 03e2: 570 */    0xE3 /* 'c' -> */,
+/* pos 03e3: 571 */    0xF5 /* 'u' -> */,
+/* pos 03e4: 572 */    0xF2 /* 'r' -> */,
+/* pos 03e5: 573 */    0xE9 /* 'i' -> */,
+/* pos 03e6: 574 */    0xF4 /* 't' -> */,
+/* pos 03e7: 575 */    0xF9 /* 'y' -> */,
+/* pos 03e8: 576 */    0xBA /* ':' -> */,
+/* pos 03e9: 577 */    0x00, 0x43                  /* - terminal marker 67 - */,
+/* pos 03eb: 578 */    0x72 /* 'r' */, 0x07, 0x00  /* (to 0x03F2 state 579) */,
+                       0x65 /* 'e' */, 0x87, 0x00  /* (to 0x0475 state 680) */,
+                       0x08, /* fail */
+/* pos 03f2: 579 */    0xE1 /* 'a' -> */,
+/* pos 03f3: 580 */    0xEE /* 'n' -> */,
+/* pos 03f4: 581 */    0xF3 /* 's' -> */,
+/* pos 03f5: 582 */    0xE6 /* 'f' -> */,
+/* pos 03f6: 583 */    0xE5 /* 'e' -> */,
+/* pos 03f7: 584 */    0xF2 /* 'r' -> */,
+/* pos 03f8: 585 */    0xAD /* '-' -> */,
+/* pos 03f9: 586 */    0xE5 /* 'e' -> */,
+/* pos 03fa: 587 */    0xEE /* 'n' -> */,
+/* pos 03fb: 588 */    0xE3 /* 'c' -> */,
+/* pos 03fc: 589 */    0xEF /* 'o' -> */,
+/* pos 03fd: 590 */    0xE4 /* 'd' -> */,
+/* pos 03fe: 591 */    0xE9 /* 'i' -> */,
+/* pos 03ff: 592 */    0xEE /* 'n' -> */,
+/* pos 0400: 593 */    0xE7 /* 'g' -> */,
+/* pos 0401: 594 */    0xBA /* ':' -> */,
+/* pos 0402: 595 */    0x00, 0x44                  /* - terminal marker 68 - */,
+/* pos 0404: 596 */    0xE5 /* 'e' -> */,
+/* pos 0405: 597 */    0xF2 /* 'r' -> */,
+/* pos 0406: 598 */    0xAD /* '-' -> */,
+/* pos 0407: 599 */    0xE1 /* 'a' -> */,
+/* pos 0408: 600 */    0xE7 /* 'g' -> */,
+/* pos 0409: 601 */    0xE5 /* 'e' -> */,
+/* pos 040a: 602 */    0xEE /* 'n' -> */,
+/* pos 040b: 603 */    0xF4 /* 't' -> */,
+/* pos 040c: 604 */    0xBA /* ':' -> */,
+/* pos 040d: 605 */    0x00, 0x45                  /* - terminal marker 69 - */,
+/* pos 040f: 606 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x0416 state 607) */,
+                       0x69 /* 'i' */, 0x09, 0x00  /* (to 0x041B state 611) */,
+                       0x08, /* fail */
+/* pos 0416: 607 */    0xF2 /* 'r' -> */,
+/* pos 0417: 608 */    0xF9 /* 'y' -> */,
+/* pos 0418: 609 */    0xBA /* ':' -> */,
+/* pos 0419: 610 */    0x00, 0x46                  /* - terminal marker 70 - */,
+/* pos 041b: 611 */    0xE1 /* 'a' -> */,
+/* pos 041c: 612 */    0xBA /* ':' -> */,
+/* pos 041d: 613 */    0x00, 0x47                  /* - terminal marker 71 - */,
+/* pos 041f: 614 */    0xF7 /* 'w' -> */,
+/* pos 0420: 615 */    0xF7 /* 'w' -> */,
+/* pos 0421: 616 */    0xAD /* '-' -> */,
+/* pos 0422: 617 */    0xE1 /* 'a' -> */,
+/* pos 0423: 618 */    0xF5 /* 'u' -> */,
+/* pos 0424: 619 */    0xF4 /* 't' -> */,
+/* pos 0425: 620 */    0xE8 /* 'h' -> */,
+/* pos 0426: 621 */    0xE5 /* 'e' -> */,
+/* pos 0427: 622 */    0xEE /* 'n' -> */,
+/* pos 0428: 623 */    0xF4 /* 't' -> */,
+/* pos 0429: 624 */    0xE9 /* 'i' -> */,
+/* pos 042a: 625 */    0xE3 /* 'c' -> */,
+/* pos 042b: 626 */    0xE1 /* 'a' -> */,
+/* pos 042c: 627 */    0xF4 /* 't' -> */,
+/* pos 042d: 628 */    0xE5 /* 'e' -> */,
+/* pos 042e: 629 */    0xBA /* ':' -> */,
+/* pos 042f: 630 */    0x00, 0x48                  /* - terminal marker 72 - */,
+/* pos 0431: 631 */    0xF4 /* 't' -> */,
+/* pos 0432: 632 */    0xE3 /* 'c' -> */,
+/* pos 0433: 633 */    0xE8 /* 'h' -> */,
+/* pos 0434: 634 */    0x00, 0x49                  /* - terminal marker 73 - */,
+/* pos 0436: 635 */    0xF4 /* 't' -> */,
+/* pos 0437: 636 */    0x00, 0x4A                  /* - terminal marker 74 - */,
+/* pos 0439: 637 */    0xEC /* 'l' -> */,
+/* pos 043a: 638 */    0xE5 /* 'e' -> */,
+/* pos 043b: 639 */    0xF4 /* 't' -> */,
+/* pos 043c: 640 */    0xE5 /* 'e' -> */,
+/* pos 043d: 641 */    0x00, 0x4B                  /* - terminal marker 75 - */,
+/* pos 043f: 642 */    0xE9 /* 'i' -> */,
+/* pos 0440: 643 */    0xAD /* '-' -> */,
+/* pos 0441: 644 */    0xE1 /* 'a' -> */,
+/* pos 0442: 645 */    0xF2 /* 'r' -> */,
+/* pos 0443: 646 */    0xE7 /* 'g' -> */,
+/* pos 0444: 647 */    0xF3 /* 's' -> */,
+/* pos 0445: 648 */    0x00, 0x4C                  /* - terminal marker 76 - */,
+/* pos 0447: 649 */    0x00, 0x4D                  /* - terminal marker 77 - */,
+/* pos 0449: 650 */    0xAD /* '-' -> */,
+/* pos 044a: 651 */    0x72 /* 'r' */, 0x0A, 0x00  /* (to 0x0454 state 652) */,
+                       0x66 /* 'f' */, 0x13, 0x00  /* (to 0x0460 state 662) */,
+                       0x61 /* 'a' */, 0x3C, 0x00  /* (to 0x048C state 700) */,
+                       0x08, /* fail */
+/* pos 0454: 652 */    0xE5 /* 'e' -> */,
+/* pos 0455: 653 */    0xE1 /* 'a' -> */,
+/* pos 0456: 654 */    0xEC /* 'l' -> */,
+/* pos 0457: 655 */    0xAD /* '-' -> */,
+/* pos 0458: 656 */    0xE9 /* 'i' -> */,
+/* pos 0459: 657 */    0xF0 /* 'p' -> */,
+/* pos 045a: 658 */    0xBA /* ':' -> */,
+/* pos 045b: 659 */    0x00, 0x4E                  /* - terminal marker 78 - */,
+/* pos 045d: 660 */    0xA0 /* ' ' -> */,
+/* pos 045e: 661 */    0x00, 0x4F                  /* - terminal marker 79 - */,
+/* pos 0460: 662 */    0xEF /* 'o' -> */,
+/* pos 0461: 663 */    0xF2 /* 'r' -> */,
+/* pos 0462: 664 */    0xF7 /* 'w' -> */,
+/* pos 0463: 665 */    0xE1 /* 'a' -> */,
+/* pos 0464: 666 */    0xF2 /* 'r' -> */,
+/* pos 0465: 667 */    0xE4 /* 'd' -> */,
+/* pos 0466: 668 */    0xE5 /* 'e' -> */,
+/* pos 0467: 669 */    0xE4 /* 'd' -> */,
+/* pos 0468: 670 */    0xAD /* '-' -> */,
+/* pos 0469: 671 */    0xE6 /* 'f' -> */,
+/* pos 046a: 672 */    0xEF /* 'o' -> */,
+/* pos 046b: 673 */    0xF2 /* 'r' -> */,
+/* pos 046c: 674 */    0x00, 0x50                  /* - terminal marker 80 - */,
+/* pos 046e: 675 */    0x00, 0x51                  /* - terminal marker 81 - */,
+/* pos 0470: 676 */    0xE1 /* 'a' -> */,
+/* pos 0471: 677 */    0xE4 /* 'd' -> */,
+/* pos 0472: 678 */    0xA0 /* ' ' -> */,
+/* pos 0473: 679 */    0x00, 0x52                  /* - terminal marker 82 - */,
+/* pos 0475: 680 */    0xBA /* ':' -> */,
+/* pos 0476: 681 */    0x00, 0x53                  /* - terminal marker 83 - */,
+/* pos 0478: 682 */    0xEC /* 'l' -> */,
+/* pos 0479: 683 */    0xE1 /* 'a' -> */,
+/* pos 047a: 684 */    0xF9 /* 'y' -> */,
+/* pos 047b: 685 */    0xAD /* '-' -> */,
+/* pos 047c: 686 */    0xEE /* 'n' -> */,
+/* pos 047d: 687 */    0xEF /* 'o' -> */,
+/* pos 047e: 688 */    0xEE /* 'n' -> */,
+/* pos 047f: 689 */    0xE3 /* 'c' -> */,
+/* pos 0480: 690 */    0xE5 /* 'e' -> */,
+/* pos 0481: 691 */    0xBA /* ':' -> */,
+/* pos 0482: 692 */    0x00, 0x54                  /* - terminal marker 84 - */,
+/* pos 0484: 693 */    0xEF /* 'o' -> */,
+/* pos 0485: 694 */    0xF4 /* 't' -> */,
+/* pos 0486: 695 */    0xEF /* 'o' -> */,
+/* pos 0487: 696 */    0xE3 /* 'c' -> */,
+/* pos 0488: 697 */    0xEF /* 'o' -> */,
+/* pos 0489: 698 */    0xEC /* 'l' -> */,
+/* pos 048a: 699 */    0x00, 0x55                  /* - terminal marker 85 - */,
+/* pos 048c: 700 */    0xF5 /* 'u' -> */,
+/* pos 048d: 701 */    0xF4 /* 't' -> */,
+/* pos 048e: 702 */    0xE8 /* 'h' -> */,
+/* pos 048f: 703 */    0xAD /* '-' -> */,
+/* pos 0490: 704 */    0xF4 /* 't' -> */,
+/* pos 0491: 705 */    0xEF /* 'o' -> */,
+/* pos 0492: 706 */    0xEB /* 'k' -> */,
+/* pos 0493: 707 */    0xE5 /* 'e' -> */,
+/* pos 0494: 708 */    0xEE /* 'n' -> */,
+/* pos 0495: 709 */    0xBA /* ':' -> */,
+/* pos 0496: 710 */    0x00, 0x56                  /* - terminal marker 86 - */,
+/* total size 1176 bytes */

+ 257 - 0
thirdparty/libwebsockets/roles/http/private.h

@@ -0,0 +1,257 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ *
+ *  This is included from core/private.h if either H1 or H2 roles are
+ *  enabled
+ */
+
+#if defined(LWS_WITH_HTTP_PROXY)
+  #include <hubbub/hubbub.h>
+  #include <hubbub/parser.h>
+ #endif
+
+#define lwsi_role_http(wsi) (lwsi_role_h1(wsi) || lwsi_role_h2(wsi))
+
+enum http_version {
+	HTTP_VERSION_1_0,
+	HTTP_VERSION_1_1,
+	HTTP_VERSION_2
+};
+
+enum http_connection_type {
+	HTTP_CONNECTION_CLOSE,
+	HTTP_CONNECTION_KEEP_ALIVE
+};
+
+/*
+ * This is totally opaque to code using the library.  It's exported as a
+ * forward-reference pointer-only declaration; the user can use the pointer with
+ * other APIs to get information out of it.
+ */
+
+#if defined(LWS_WITH_ESP32)
+typedef uint16_t ah_data_idx_t;
+#else
+typedef uint32_t ah_data_idx_t;
+#endif
+
+struct lws_fragments {
+	ah_data_idx_t	offset;
+	uint16_t	len;
+	uint8_t		nfrag; /* which ah->frag[] continues this content, or 0 */
+	uint8_t		flags; /* only http2 cares */
+};
+
+#if defined(LWS_WITH_RANGES)
+enum range_states {
+	LWSRS_NO_ACTIVE_RANGE,
+	LWSRS_BYTES_EQ,
+	LWSRS_FIRST,
+	LWSRS_STARTING,
+	LWSRS_ENDING,
+	LWSRS_COMPLETED,
+	LWSRS_SYNTAX,
+};
+
+struct lws_range_parsing {
+	unsigned long long start, end, extent, agg, budget;
+	const char buf[128];
+	int pos;
+	enum range_states state;
+	char start_valid, end_valid, ctr, count_ranges, did_try, inside, send_ctr;
+};
+
+int
+lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp,
+		unsigned long long extent);
+int
+lws_ranges_next(struct lws_range_parsing *rp);
+void
+lws_ranges_reset(struct lws_range_parsing *rp);
+#endif
+
+/*
+ * these are assigned from a pool held in the context.
+ * Both client and server mode uses them for http header analysis
+ */
+
+struct allocated_headers {
+	struct allocated_headers *next; /* linked list */
+	struct lws *wsi; /* owner */
+	char *data; /* prepared by context init to point to dedicated storage */
+	ah_data_idx_t data_length;
+	/*
+	 * the randomly ordered fragments, indexed by frag_index and
+	 * lws_fragments->nfrag for continuation.
+	 */
+	struct lws_fragments frags[WSI_TOKEN_COUNT];
+	time_t assigned;
+	/*
+	 * for each recognized token, frag_index says which frag[] his data
+	 * starts in (0 means the token did not appear)
+	 * the actual header data gets dumped as it comes in, into data[]
+	 */
+	uint8_t frag_index[WSI_TOKEN_COUNT];
+
+#ifndef LWS_NO_CLIENT
+	char initial_handshake_hash_base64[30];
+#endif
+
+	uint32_t pos;
+	uint32_t http_response;
+	uint32_t current_token_limit;
+	int hdr_token_idx;
+
+	int16_t lextable_pos;
+
+	uint8_t in_use;
+	uint8_t nfrag;
+	char /*enum uri_path_states */ ups;
+	char /*enum uri_esc_states */ ues;
+
+	char esc_stash;
+	char post_literal_equal;
+	uint8_t /* enum lws_token_indexes */ parser_state;
+};
+
+
+
+#if defined(LWS_WITH_HTTP_PROXY)
+struct lws_rewrite {
+	hubbub_parser *parser;
+	hubbub_parser_optparams params;
+	const char *from, *to;
+	int from_len, to_len;
+	unsigned char *p, *end;
+	struct lws *wsi;
+};
+static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len)
+{
+	if ((int)s->len != len)
+		return 1;
+
+	return strncmp((const char *)s->ptr, p, len);
+}
+typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw);
+LWS_EXTERN struct lws_rewrite *
+lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to);
+LWS_EXTERN void
+lws_rewrite_destroy(struct lws_rewrite *r);
+LWS_EXTERN int
+lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len);
+#endif
+
+struct lws_pt_role_http {
+	struct allocated_headers *ah_list;
+	struct lws *ah_wait_list;
+#ifdef LWS_WITH_CGI
+	struct lws_cgi *cgi_list;
+#endif
+	int ah_wait_list_length;
+	uint32_t ah_pool_length;
+
+	int ah_count_in_use;
+};
+
+struct lws_peer_role_http {
+	uint32_t count_ah;
+	uint32_t total_ah;
+};
+
+struct lws_vhost_role_http {
+	char http_proxy_address[128];
+	const struct lws_http_mount *mount_list;
+	const char *error_document_404;
+	unsigned int http_proxy_port;
+};
+
+#ifdef LWS_WITH_ACCESS_LOG
+struct lws_access_log {
+	char *header_log;
+	char *user_agent;
+	char *referrer;
+	unsigned long sent;
+	int response;
+};
+#endif
+
+struct _lws_http_mode_related {
+	struct lws *new_wsi_list;
+
+#if defined(LWS_WITH_HTTP_PROXY)
+	struct lws_rewrite *rw;
+#endif
+	struct allocated_headers *ah;
+	struct lws *ah_wait_list;
+
+	lws_filepos_t filepos;
+	lws_filepos_t filelen;
+	lws_fop_fd_t fop_fd;
+
+#if defined(LWS_WITH_RANGES)
+	struct lws_range_parsing range;
+	char multipart_content_type[64];
+#endif
+
+#ifdef LWS_WITH_ACCESS_LOG
+	struct lws_access_log access_log;
+#endif
+#ifdef LWS_WITH_CGI
+	struct lws_cgi *cgi; /* wsi being cgi master have one of these */
+#endif
+
+	enum http_version request_version;
+	enum http_connection_type connection_type;
+	lws_filepos_t tx_content_length;
+	lws_filepos_t tx_content_remain;
+	lws_filepos_t rx_content_length;
+	lws_filepos_t rx_content_remain;
+
+#if defined(LWS_WITH_HTTP_PROXY)
+	unsigned int perform_rewrite:1;
+#endif
+};
+
+
+#ifndef LWS_NO_CLIENT
+enum lws_chunk_parser {
+	ELCP_HEX,
+	ELCP_CR,
+	ELCP_CONTENT,
+	ELCP_POST_CR,
+	ELCP_POST_LF,
+};
+#endif
+
+enum lws_parse_urldecode_results {
+	LPUR_CONTINUE,
+	LPUR_SWALLOW,
+	LPUR_FORBID,
+	LPUR_EXCESSIVE,
+};
+
+int
+lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
+
+void
+_lws_header_table_reset(struct allocated_headers *ah);
+
+LWS_EXTERN int
+_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah);

+ 182 - 0
thirdparty/libwebsockets/roles/http/server/access-log.c

@@ -0,0 +1,182 @@
+/*
+ * libwebsockets - server access log handling
+ *
+ * Copyright (C) 2010-2017 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "core/private.h"
+
+/*
+ * Produce Apache-compatible log string for wsi, like this:
+ *
+ * 2.31.234.19 - - [27/Mar/2016:03:22:44 +0800]
+ * "GET /aep-screen.png HTTP/1.1"
+ * 200 152987 "https://libwebsockets.org/index.html"
+ * "Mozilla/5.0 (Macint... Chrome/49.0.2623.87 Safari/537.36"
+ *
+ */
+
+extern const char * const method_names[];
+
+static const char * const hver[] = {
+	"HTTP/1.0", "HTTP/1.1", "HTTP/2"
+};
+
+void
+lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth)
+{
+#ifdef LWS_WITH_IPV6
+	char ads[INET6_ADDRSTRLEN];
+#else
+	char ads[INET_ADDRSTRLEN];
+#endif
+	char da[64];
+	const char *pa, *me;
+	struct tm *tmp;
+	time_t t = time(NULL);
+	int l = 256, m;
+
+	if (!wsi->vhost)
+		return;
+
+	/* only worry about preparing it if we store it */
+	if (wsi->vhost->log_fd == (int)LWS_INVALID_FILE)
+		return;
+
+	if (wsi->access_log_pending)
+		lws_access_log(wsi);
+
+	wsi->http.access_log.header_log = lws_malloc(l, "access log");
+	if (wsi->http.access_log.header_log) {
+
+		tmp = localtime(&t);
+		if (tmp)
+			strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", tmp);
+		else
+			strcpy(da, "01/Jan/1970:00:00:00 +0000");
+
+		pa = lws_get_peer_simple(wsi, ads, sizeof(ads));
+		if (!pa)
+			pa = "(unknown)";
+
+		if (wsi->http2_substream)
+			me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
+		else
+			me = method_names[meth];
+		if (!me)
+			me = "(null)";
+
+		lws_snprintf(wsi->http.access_log.header_log, l,
+			 "%s - - [%s] \"%s %s %s\"",
+			 pa, da, me, uri_ptr,
+			 hver[wsi->http.request_version]);
+
+		l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT);
+		if (l) {
+			wsi->http.access_log.user_agent = lws_malloc(l + 2, "access log");
+			if (!wsi->http.access_log.user_agent) {
+				lwsl_err("OOM getting user agent\n");
+				lws_free_set_NULL(wsi->http.access_log.header_log);
+				return;
+			}
+
+			lws_hdr_copy(wsi, wsi->http.access_log.user_agent,
+					l + 1, WSI_TOKEN_HTTP_USER_AGENT);
+
+			for (m = 0; m < l; m++)
+				if (wsi->http.access_log.user_agent[m] == '\"')
+					wsi->http.access_log.user_agent[m] = '\'';
+		}
+		l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER);
+		if (l) {
+			wsi->http.access_log.referrer = lws_malloc(l + 2, "referrer");
+			if (!wsi->http.access_log.referrer) {
+				lwsl_err("OOM getting user agent\n");
+				lws_free_set_NULL(wsi->http.access_log.user_agent);
+				lws_free_set_NULL(wsi->http.access_log.header_log);
+				return;
+			}
+			lws_hdr_copy(wsi, wsi->http.access_log.referrer,
+					l + 1, WSI_TOKEN_HTTP_REFERER);
+
+			for (m = 0; m < l; m++)
+				if (wsi->http.access_log.referrer[m] == '\"')
+					wsi->http.access_log.referrer[m] = '\'';
+		}
+		wsi->access_log_pending = 1;
+	}
+}
+
+
+int
+lws_access_log(struct lws *wsi)
+{
+	char *p = wsi->http.access_log.user_agent, ass[512],
+	     *p1 = wsi->http.access_log.referrer;
+	int l;
+
+	if (!wsi->vhost)
+		return 0;
+
+	if (wsi->vhost->log_fd == (int)LWS_INVALID_FILE)
+		return 0;
+
+	if (!wsi->access_log_pending)
+		return 0;
+
+	if (!wsi->http.access_log.header_log)
+		return 0;
+
+	if (!p)
+		p = "";
+
+	if (!p1)
+		p1 = "";
+
+	/*
+	 * We do this in two parts to restrict an oversize referrer such that
+	 * we will always have space left to append an empty useragent, while
+	 * maintaining the structure of the log text
+	 */
+	l = lws_snprintf(ass, sizeof(ass) - 7, "%s %d %lu \"%s",
+		     wsi->http.access_log.header_log,
+		     wsi->http.access_log.response, wsi->http.access_log.sent, p1);
+	if (strlen(p) > sizeof(ass) - 6 - l)
+		p[sizeof(ass) - 6 - l] = '\0';
+	l += lws_snprintf(ass + l, sizeof(ass) - 1 - l, "\" \"%s\"\n", p);
+
+	if (write(wsi->vhost->log_fd, ass, l) != l)
+		lwsl_err("Failed to write log\n");
+
+	if (wsi->http.access_log.header_log) {
+		lws_free(wsi->http.access_log.header_log);
+		wsi->http.access_log.header_log = NULL;
+	}
+	if (wsi->http.access_log.user_agent) {
+		lws_free(wsi->http.access_log.user_agent);
+		wsi->http.access_log.user_agent = NULL;
+	}
+	if (wsi->http.access_log.referrer) {
+		lws_free(wsi->http.access_log.referrer);
+		wsi->http.access_log.referrer = NULL;
+	}
+	wsi->access_log_pending = 0;
+
+	return 0;
+}
+

+ 7 - 8
thirdparty/libwebsockets/server/fops-zip.c → thirdparty/libwebsockets/roles/http/server/fops-zip.c

@@ -51,7 +51,7 @@
  *  MA  02110-1301  USA
  *  MA  02110-1301  USA
  */
  */
 
 
-#include "private-libwebsockets.h"
+#include "core/private.h"
 
 
 #include <zlib.h>
 #include <zlib.h>
 
 
@@ -241,13 +241,13 @@ lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
 		if (priv->hdr.filename_len != len)
 		if (priv->hdr.filename_len != len)
 			goto next;
 			goto next;
 
 
-		if (len >= sizeof(buf) - 1)
+		if (len >= (int)sizeof(buf) - 1)
 			return LWS_FZ_ERR_NAME_TOO_LONG;
 			return LWS_FZ_ERR_NAME_TOO_LONG;
 
 
 		if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
 		if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
 							&amount, buf, len))
 							&amount, buf, len))
 			return LWS_FZ_ERR_NAME_READ;
 			return LWS_FZ_ERR_NAME_READ;
-		if (amount != len)
+		if ((int)amount != len)
 			return LWS_FZ_ERR_NAME_READ;
 			return LWS_FZ_ERR_NAME_READ;
 
 
 		buf[len] = '\0';
 		buf[len] = '\0';
@@ -348,9 +348,8 @@ lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
 
 
 	m = sizeof(rp) - 1;
 	m = sizeof(rp) - 1;
 	if ((vpath - vfs_path - 1) < m)
 	if ((vpath - vfs_path - 1) < m)
-		m = vpath - vfs_path - 1;
-	strncpy(rp, vfs_path, m);
-	rp[m] = '\0';
+		m = lws_ptr_diff(vpath, vfs_path) - 1;
+	lws_strncpy(rp, vfs_path, m + 1);
 
 
 	/* open the zip file itself using the incoming fops, not fops_zip */
 	/* open the zip file itself using the incoming fops, not fops_zip */
 
 
@@ -363,7 +362,7 @@ lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
 	if (*vpath == '/')
 	if (*vpath == '/')
 		vpath++;
 		vpath++;
 
 
-	m = lws_fops_zip_scan(priv, vpath, strlen(vpath));
+	m = lws_fops_zip_scan(priv, vpath, (int)strlen(vpath));
 	if (m) {
 	if (m) {
 		lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
 		lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
 		goto bail2;
 		goto bail2;
@@ -565,7 +564,7 @@ spin:
 		switch (ret) {
 		switch (ret) {
 		case Z_NEED_DICT:
 		case Z_NEED_DICT:
 			ret = Z_DATA_ERROR;
 			ret = Z_DATA_ERROR;
-			/* and fall through */
+			/* fallthru */
 		case Z_DATA_ERROR:
 		case Z_DATA_ERROR:
 		case Z_MEM_ERROR:
 		case Z_MEM_ERROR:
 
 

+ 77 - 23
thirdparty/libwebsockets/server/lejp-conf.c → thirdparty/libwebsockets/roles/http/server/lejp-conf.c

@@ -19,8 +19,7 @@
  *  MA  02110-1301  USA
  *  MA  02110-1301  USA
  */
  */
 
 
-#include "private-libwebsockets.h"
-#include "../misc/lejp.h"
+#include "core/private.h"
 
 
 #ifndef _WIN32
 #ifndef _WIN32
 /* this is needed for Travis CI */
 /* this is needed for Travis CI */
@@ -40,6 +39,7 @@ static const char * const paths_global[] = {
 	"global.timeout-secs",
 	"global.timeout-secs",
 	"global.reject-service-keywords[].*",
 	"global.reject-service-keywords[].*",
 	"global.reject-service-keywords[]",
 	"global.reject-service-keywords[]",
+	"global.default-alpn",
 };
 };
 
 
 enum lejp_global_paths {
 enum lejp_global_paths {
@@ -52,7 +52,8 @@ enum lejp_global_paths {
 	LWJPGP_PINGPONG_SECS,
 	LWJPGP_PINGPONG_SECS,
 	LWJPGP_TIMEOUT_SECS,
 	LWJPGP_TIMEOUT_SECS,
 	LWJPGP_REJECT_SERVICE_KEYWORDS_NAME,
 	LWJPGP_REJECT_SERVICE_KEYWORDS_NAME,
-	LWJPGP_REJECT_SERVICE_KEYWORDS
+	LWJPGP_REJECT_SERVICE_KEYWORDS,
+	LWJPGP_DEFAULT_ALPN,
 };
 };
 
 
 static const char * const paths_vhosts[] = {
 static const char * const paths_vhosts[] = {
@@ -100,6 +101,10 @@ static const char * const paths_vhosts[] = {
 	"vhosts[].client-ssl-ca",
 	"vhosts[].client-ssl-ca",
 	"vhosts[].client-ssl-ciphers",
 	"vhosts[].client-ssl-ciphers",
 	"vhosts[].onlyraw",
 	"vhosts[].onlyraw",
+	"vhosts[].client-cert-required",
+	"vhosts[].ignore-missing-cert",
+	"vhosts[].error-document-404",
+	"vhosts[].alpn",
 };
 };
 
 
 enum lejp_vhost_paths {
 enum lejp_vhost_paths {
@@ -147,6 +152,10 @@ enum lejp_vhost_paths {
 	LEJPVP_CLIENT_SSL_CA,
 	LEJPVP_CLIENT_SSL_CA,
 	LEJPVP_CLIENT_CIPHERS,
 	LEJPVP_CLIENT_CIPHERS,
 	LEJPVP_FLAG_ONLYRAW,
 	LEJPVP_FLAG_ONLYRAW,
+	LEJPVP_FLAG_CLIENT_CERT_REQUIRED,
+	LEJPVP_IGNORE_MISSING_CERT,
+	LEJPVP_ERROR_DOCUMENT_404,
+	LEJPVP_ALPN,
 };
 };
 
 
 static const char * const parser_errs[] = {
 static const char * const parser_errs[] = {
@@ -216,7 +225,7 @@ arg_to_bool(const char *s)
 	if (n)
 	if (n)
 		return 1;
 		return 1;
 
 
-	for (n = 0; n < ARRAY_SIZE(on); n++)
+	for (n = 0; n < (int)ARRAY_SIZE(on); n++)
 		if (!strcasecmp(s, on[n]))
 		if (!strcasecmp(s, on[n]))
 			return 1;
 			return 1;
 
 
@@ -285,6 +294,10 @@ lejp_globals_cb(struct lejp_ctx *ctx, char reason)
 		a->info->timeout_secs = atoi(ctx->buf);
 		a->info->timeout_secs = atoi(ctx->buf);
 		return 0;
 		return 0;
 
 
+	case LWJPGP_DEFAULT_ALPN:
+		a->info->alpn = a->p;
+		break;
+
 	default:
 	default:
 		return 0;
 		return 0;
 	}
 	}
@@ -312,20 +325,39 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 #endif
 #endif
 
 
 	if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
 	if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
+		uint32_t i[4];
+		const char *ss;
+
 		/* set the defaults for this vhost */
 		/* set the defaults for this vhost */
 		a->valid = 1;
 		a->valid = 1;
 		a->head = NULL;
 		a->head = NULL;
 		a->last = NULL;
 		a->last = NULL;
-		a->info->port = 0;
-		a->info->iface = NULL;
+
+		i[0] = a->info->count_threads;
+		i[1] = a->info->options & (
+			LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME |
+			LWS_SERVER_OPTION_LIBUV |
+			LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
+			LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
+			LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN |
+			LWS_SERVER_OPTION_LIBEVENT |
+			LWS_SERVER_OPTION_LIBEV
+				);
+		ss = a->info->server_string;
+		i[2] = a->info->ws_ping_pong_interval;
+		i[3] = a->info->timeout_secs;
+
+		memset(a->info, 0, sizeof(*a->info));
+
+		a->info->count_threads = i[0];
+		a->info->options = i[1];
+		a->info->server_string = ss;
+		a->info->ws_ping_pong_interval = i[2];
+		a->info->timeout_secs = i[3];
+
 		a->info->protocols = a->protocols;
 		a->info->protocols = a->protocols;
 		a->info->extensions = a->extensions;
 		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;
+#if defined(LWS_WITH_TLS)
 		a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
 		a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
 			"ECDHE-RSA-AES256-GCM-SHA384:"
 			"ECDHE-RSA-AES256-GCM-SHA384:"
 			"DHE-RSA-AES256-GCM-SHA384:"
 			"DHE-RSA-AES256-GCM-SHA384:"
@@ -339,7 +371,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 			"!DHE-RSA-AES256-SHA256:"
 			"!DHE-RSA-AES256-SHA256:"
 			"!AES256-GCM-SHA384:"
 			"!AES256-GCM-SHA384:"
 			"!AES256-SHA256";
 			"!AES256-SHA256";
-		a->info->timeout_secs = 5;
+#endif
 		a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
 		a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
 				       "ECDHE-RSA-AES256-GCM-SHA384:"
 				       "ECDHE-RSA-AES256-GCM-SHA384:"
 				       "DHE-RSA-AES256-GCM-SHA384:"
 				       "DHE-RSA-AES256-GCM-SHA384:"
@@ -353,13 +385,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 				       "!DHE-RSA-AES256-SHA256:"
 				       "!DHE-RSA-AES256-SHA256:"
 				       "!AES256-GCM-SHA384:"
 				       "!AES256-GCM-SHA384:"
 				       "!AES256-SHA256";
 				       "!AES256-SHA256";
-		a->info->pvo = NULL;
-		a->info->headers = NULL;
 		a->info->keepalive_timeout = 5;
 		a->info->keepalive_timeout = 5;
-		a->info->log_filepath = NULL;
-		a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK |
-				      LWS_SERVER_OPTION_STS | LWS_SERVER_OPTION_ONLY_RAW);
-		a->enable_client_ssl = 0;
 	}
 	}
 
 
 	if (reason == LEJPCB_OBJECT_START &&
 	if (reason == LEJPCB_OBJECT_START &&
@@ -379,7 +405,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		a->pvo->next = a->info->pvo;
 		a->pvo->next = a->info->pvo;
 		a->info->pvo = a->pvo;
 		a->info->pvo = a->pvo;
 		a->pvo->name = a->p;
 		a->pvo->name = a->p;
-		lwsl_notice("  adding protocol %s\n", a->p);
+		lwsl_info("  adding protocol %s\n", a->p);
 		a->p += n;
 		a->p += n;
 		a->pvo->value = a->p;
 		a->pvo->value = a->p;
 		a->pvo->options = NULL;
 		a->pvo->options = NULL;
@@ -431,6 +457,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		}
 		}
 		a->any_vhosts = 1;
 		a->any_vhosts = 1;
 
 
+#if defined(LWS_WITH_TLS)
 		if (a->enable_client_ssl) {
 		if (a->enable_client_ssl) {
 			const char *cert_filepath = a->info->client_ssl_cert_filepath;
 			const char *cert_filepath = a->info->client_ssl_cert_filepath;
 			const char *private_key_filepath = a->info->client_ssl_private_key_filepath;
 			const char *private_key_filepath = a->info->client_ssl_private_key_filepath;
@@ -444,6 +471,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 			a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
 			a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
 			lws_init_vhost_client_ssl(a->info, vhost);
 			lws_init_vhost_client_ssl(a->info, vhost);
 		}
 		}
+#endif
 
 
 		return 0;
 		return 0;
 	}
 	}
@@ -474,7 +502,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		if (a->last)
 		if (a->last)
 			a->last->mount_next = m;
 			a->last->mount_next = m;
 
 
-		for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
+		for (n = 0; n < (int)ARRAY_SIZE(mount_protocols); n++)
 			if (!strncmp(a->m.origin, mount_protocols[n],
 			if (!strncmp(a->m.origin, mount_protocols[n],
 			     strlen(mount_protocols[n]))) {
 			     strlen(mount_protocols[n]))) {
 				lwsl_info("----%s\n", a->m.origin);
 				lwsl_info("----%s\n", a->m.origin);
@@ -484,7 +512,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 				break;
 				break;
 			}
 			}
 
 
-		if (n == ARRAY_SIZE(mount_protocols)) {
+		if (n == (int)ARRAY_SIZE(mount_protocols)) {
 			lwsl_err("unsupported protocol:// %s\n", a->m.origin);
 			lwsl_err("unsupported protocol:// %s\n", a->m.origin);
 			return 1;
 			return 1;
 		}
 		}
@@ -573,9 +601,11 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 	case LEJPVP_KEEPALIVE_TIMEOUT:
 	case LEJPVP_KEEPALIVE_TIMEOUT:
 		a->info->keepalive_timeout = atoi(ctx->buf);
 		a->info->keepalive_timeout = atoi(ctx->buf);
 		return 0;
 		return 0;
+#if defined(LWS_WITH_TLS)
 	case LEJPVP_CLIENT_CIPHERS:
 	case LEJPVP_CLIENT_CIPHERS:
 		a->info->client_ssl_cipher_list = a->p;
 		a->info->client_ssl_cipher_list = a->p;
 		break;
 		break;
+#endif
 	case LEJPVP_CIPHERS:
 	case LEJPVP_CIPHERS:
 		a->info->ssl_cipher_list = a->p;
 		a->info->ssl_cipher_list = a->p;
 		break;
 		break;
@@ -651,6 +681,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 	case LEJPVP_ENABLE_CLIENT_SSL:
 	case LEJPVP_ENABLE_CLIENT_SSL:
 		a->enable_client_ssl = arg_to_bool(ctx->buf);
 		a->enable_client_ssl = arg_to_bool(ctx->buf);
 		return 0;
 		return 0;
+#if defined(LWS_WITH_TLS)
 	case LEJPVP_CLIENT_SSL_KEY:
 	case LEJPVP_CLIENT_SSL_KEY:
 		a->info->client_ssl_private_key_filepath = a->p;
 		a->info->client_ssl_private_key_filepath = a->p;
 		break;
 		break;
@@ -660,6 +691,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 	case LEJPVP_CLIENT_SSL_CA:
 	case LEJPVP_CLIENT_SSL_CA:
 		a->info->client_ssl_ca_filepath = a->p;
 		a->info->client_ssl_ca_filepath = a->p;
 		break;
 		break;
+#endif
 
 
 	case LEJPVP_NOIPV6:
 	case LEJPVP_NOIPV6:
 		if (arg_to_bool(ctx->buf))
 		if (arg_to_bool(ctx->buf))
@@ -683,6 +715,24 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 			a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
 			a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
 		return 0;
 		return 0;
 
 
+	case LEJPVP_FLAG_CLIENT_CERT_REQUIRED:
+		if (arg_to_bool(ctx->buf))
+			a->info->options |=
+			    LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
+		return 0;
+
+	case LEJPVP_IGNORE_MISSING_CERT:
+		if (arg_to_bool(ctx->buf))
+			a->info->options |= LWS_SERVER_OPTION_IGNORE_MISSING_CERT;
+		else
+			a->info->options &= ~(LWS_SERVER_OPTION_IGNORE_MISSING_CERT);
+
+		return 0;
+
+	case LEJPVP_ERROR_DOCUMENT_404:
+		a->info->error_document_404 = a->p;
+		break;
+
 	case LEJPVP_SSL_OPTION_SET:
 	case LEJPVP_SSL_OPTION_SET:
 		a->info->ssl_options_set |= atol(ctx->buf);
 		a->info->ssl_options_set |= atol(ctx->buf);
 		return 0;
 		return 0;
@@ -690,6 +740,10 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		a->info->ssl_options_clear |= atol(ctx->buf);
 		a->info->ssl_options_clear |= atol(ctx->buf);
 		return 0;
 		return 0;
 
 
+	case LEJPVP_ALPN:
+		a->info->alpn = a->p;
+		break;
+
 	default:
 	default:
 		return 0;
 		return 0;
 	}
 	}
@@ -701,7 +755,7 @@ dostring:
 		n = p1 - p;
 		n = p1 - p;
 		if (n > a->end - a->p)
 		if (n > a->end - a->p)
 			n = a->end - a->p;
 			n = a->end - a->p;
-		strncpy(a->p, p, n);
+		lws_strncpy(a->p, p, n + 1);
 		a->p += n;
 		a->p += n;
 		a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
 		a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
 		p += n + strlen(ESC_INSTALL_DATADIR);
 		p += n + strlen(ESC_INSTALL_DATADIR);

+ 1139 - 0
thirdparty/libwebsockets/roles/http/server/parsers.c

@@ -0,0 +1,1139 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "core/private.h"
+
+static const unsigned char lextable[] = {
+	#include "../lextable.h"
+};
+
+#define FAIL_CHAR 0x08
+
+static struct allocated_headers *
+_lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size)
+{
+	struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct");
+
+	if (!ah)
+		return NULL;
+
+	ah->data = lws_malloc(data_size, "ah data");
+	if (!ah->data) {
+		lws_free(ah);
+
+		return NULL;
+	}
+	ah->next = pt->http.ah_list;
+	pt->http.ah_list = ah;
+	ah->data_length = data_size;
+	pt->http.ah_pool_length++;
+
+	lwsl_info("%s: created ah %p (size %d): pool length %d\n", __func__,
+		    ah, (int)data_size, pt->http.ah_pool_length);
+
+	return ah;
+}
+
+int
+_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah)
+{
+	lws_start_foreach_llp(struct allocated_headers **, a, pt->http.ah_list) {
+		if ((*a) == ah) {
+			*a = ah->next;
+			pt->http.ah_pool_length--;
+			lwsl_info("%s: freed ah %p : pool length %d\n",
+				    __func__, ah, pt->http.ah_pool_length);
+			if (ah->data)
+				lws_free(ah->data);
+			lws_free(ah);
+
+			return 0;
+		}
+	} lws_end_foreach_llp(a, next);
+
+	return 1;
+}
+
+void
+_lws_header_table_reset(struct allocated_headers *ah)
+{
+	/* init the ah to reflect no headers or data have appeared yet */
+	memset(ah->frag_index, 0, sizeof(ah->frag_index));
+	memset(ah->frags, 0, sizeof(ah->frags));
+	ah->nfrag = 0;
+	ah->pos = 0;
+	ah->http_response = 0;
+	ah->parser_state = WSI_TOKEN_NAME_PART;
+	ah->lextable_pos = 0;
+}
+
+// doesn't scrub the ah rxbuffer by default, parent must do if needed
+
+void
+__lws_header_table_reset(struct lws *wsi, int autoservice)
+{
+	struct allocated_headers *ah = wsi->http.ah;
+	struct lws_context_per_thread *pt;
+	struct lws_pollfd *pfd;
+
+	/* if we have the idea we're resetting 'our' ah, must be bound to one */
+	assert(ah);
+	/* ah also concurs with ownership */
+	assert(ah->wsi == wsi);
+
+	_lws_header_table_reset(ah);
+
+	/* since we will restart the ah, our new headers are not completed */
+	wsi->hdr_parsing_completed = 0;
+
+	/* while we hold the ah, keep a timeout on the wsi */
+	__lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
+			wsi->vhost->timeout_secs_ah_idle);
+
+	time(&ah->assigned);
+
+	if (wsi->position_in_fds_table != LWS_NO_FDS_POS &&
+	    lws_buflist_next_segment_len(&wsi->buflist, NULL) &&
+	    autoservice) {
+		lwsl_debug("%s: service on readbuf ah\n", __func__);
+
+		pt = &wsi->context->pt[(int)wsi->tsi];
+		/*
+		 * Unlike a normal connect, we have the headers already
+		 * (or the first part of them anyway)
+		 */
+		pfd = &pt->fds[wsi->position_in_fds_table];
+		pfd->revents |= LWS_POLLIN;
+		lwsl_err("%s: calling service\n", __func__);
+		lws_service_fd_tsi(wsi->context, pfd, wsi->tsi);
+	}
+}
+
+void
+lws_header_table_reset(struct lws *wsi, int autoservice)
+{
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+	lws_pt_lock(pt, __func__);
+
+	__lws_header_table_reset(wsi, autoservice);
+
+	lws_pt_unlock(pt);
+}
+
+static void
+_lws_header_ensure_we_are_on_waiting_list(struct lws *wsi)
+{
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+	struct lws_pollargs pa;
+	struct lws **pwsi = &pt->http.ah_wait_list;
+
+	while (*pwsi) {
+		if (*pwsi == wsi)
+			return;
+		pwsi = &(*pwsi)->http.ah_wait_list;
+	}
+
+	lwsl_info("%s: wsi: %p\n", __func__, wsi);
+	wsi->http.ah_wait_list = pt->http.ah_wait_list;
+	pt->http.ah_wait_list = wsi;
+	pt->http.ah_wait_list_length++;
+
+	/* we cannot accept input then */
+
+	_lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa);
+}
+
+static int
+__lws_remove_from_ah_waiting_list(struct lws *wsi)
+{
+        struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+	struct lws **pwsi =&pt->http.ah_wait_list;
+
+	while (*pwsi) {
+		if (*pwsi == wsi) {
+			lwsl_info("%s: wsi %p\n", __func__, wsi);
+			/* point prev guy to our next */
+			*pwsi = wsi->http.ah_wait_list;
+			/* we shouldn't point anywhere now */
+			wsi->http.ah_wait_list = NULL;
+			pt->http.ah_wait_list_length--;
+
+			return 1;
+		}
+		pwsi = &(*pwsi)->http.ah_wait_list;
+	}
+
+	return 0;
+}
+
+int LWS_WARN_UNUSED_RESULT
+lws_header_table_attach(struct lws *wsi, int autoservice)
+{
+	struct lws_context *context = wsi->context;
+	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+	struct lws_pollargs pa;
+	int n;
+
+	lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__,
+		  (void *)wsi, (void *)wsi->http.ah, wsi->tsi,
+		  pt->http.ah_count_in_use);
+
+	lws_pt_lock(pt, __func__);
+
+	/* if we are already bound to one, just clear it down */
+	if (wsi->http.ah) {
+		lwsl_info("%s: cleardown\n", __func__);
+		goto reset;
+	}
+
+	n = pt->http.ah_count_in_use == context->max_http_header_pool;
+#if defined(LWS_WITH_PEER_LIMITS)
+	if (!n) {
+		n = lws_peer_confirm_ah_attach_ok(context, wsi->peer);
+		if (n)
+			lws_stats_atomic_bump(wsi->context, pt,
+				LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
+	}
+#endif
+	if (n) {
+		/*
+		 * Pool is either all busy, or we don't want to give this
+		 * particular guy an ah right now...
+		 *
+		 * Make sure we are on the waiting list, and return that we
+		 * weren't able to provide the ah
+		 */
+		_lws_header_ensure_we_are_on_waiting_list(wsi);
+
+		goto bail;
+	}
+
+	__lws_remove_from_ah_waiting_list(wsi);
+
+	wsi->http.ah = _lws_create_ah(pt, context->max_http_header_data);
+	if (!wsi->http.ah) { /* we could not create an ah */
+		_lws_header_ensure_we_are_on_waiting_list(wsi);
+
+		goto bail;
+	}
+
+	wsi->http.ah->in_use = 1;
+	wsi->http.ah->wsi = wsi; /* mark our owner */
+	pt->http.ah_count_in_use++;
+
+#if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
+	lws_context_lock(context); /* <====================================== */
+	if (wsi->peer)
+		wsi->peer->http.count_ah++;
+	lws_context_unlock(context); /* ====================================> */
+#endif
+
+	_lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
+
+	lwsl_info("%s: did attach wsi %p: ah %p: count %d (on exit)\n", __func__,
+		  (void *)wsi, (void *)wsi->http.ah, pt->http.ah_count_in_use);
+
+reset:
+	__lws_header_table_reset(wsi, autoservice);
+
+	lws_pt_unlock(pt);
+
+#ifndef LWS_NO_CLIENT
+	if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED)
+		if (!lws_client_connect_via_info2(wsi))
+			/* our client connect has failed, the wsi
+			 * has been closed
+			 */
+			return -1;
+#endif
+
+	return 0;
+
+bail:
+	lws_pt_unlock(pt);
+
+	return 1;
+}
+
+int __lws_header_table_detach(struct lws *wsi, int autoservice)
+{
+	struct lws_context *context = wsi->context;
+	struct allocated_headers *ah = wsi->http.ah;
+	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+	struct lws_pollargs pa;
+	struct lws **pwsi, **pwsi_eligible;
+	time_t now;
+
+	__lws_remove_from_ah_waiting_list(wsi);
+
+	if (!ah)
+		return 0;
+
+	lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
+		  (void *)wsi, (void *)ah, wsi->tsi,
+		  pt->http.ah_count_in_use);
+
+	/* we did have an ah attached */
+	time(&now);
+	if (ah->assigned && now - ah->assigned > 3) {
+		/*
+		 * we're detaching the ah, but it was held an
+		 * unreasonably long time
+		 */
+		lwsl_debug("%s: wsi %p: ah held %ds, role/state 0x%x 0x%x,"
+			    "\n", __func__, wsi, (int)(now - ah->assigned),
+			    lwsi_role(wsi), lwsi_state(wsi));
+	}
+
+	ah->assigned = 0;
+
+	/* if we think we're detaching one, there should be one in use */
+	assert(pt->http.ah_count_in_use > 0);
+	/* and this specific one should have been in use */
+	assert(ah->in_use);
+	memset(&wsi->http.ah, 0, sizeof(wsi->http.ah));
+
+#if defined(LWS_WITH_PEER_LIMITS)
+	if (ah->wsi)
+		lws_peer_track_ah_detach(context, wsi->peer);
+#endif
+	ah->wsi = NULL; /* no owner */
+
+	pwsi = &pt->http.ah_wait_list;
+
+	/* oh there is nobody on the waiting list... leave the ah unattached */
+	if (!*pwsi)
+		goto nobody_usable_waiting;
+
+	/*
+	 * at least one wsi on the same tsi is waiting, give it to oldest guy
+	 * who is allowed to take it (if any)
+	 */
+	lwsl_info("pt wait list %p\n", *pwsi);
+	wsi = NULL;
+	pwsi_eligible = NULL;
+
+	while (*pwsi) {
+#if defined(LWS_WITH_PEER_LIMITS)
+		/* are we willing to give this guy an ah? */
+		if (!lws_peer_confirm_ah_attach_ok(context, (*pwsi)->peer))
+#endif
+		{
+			wsi = *pwsi;
+			pwsi_eligible = pwsi;
+		}
+#if defined(LWS_WITH_PEER_LIMITS)
+		else
+			if (!(*pwsi)->http.ah_wait_list)
+				lws_stats_atomic_bump(context, pt,
+					LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
+#endif
+		pwsi = &(*pwsi)->http.ah_wait_list;
+	}
+
+	if (!wsi) /* everybody waiting already has too many ah... */
+		goto nobody_usable_waiting;
+
+	lwsl_info("%s: transferring ah to last eligible wsi in wait list %p (wsistate 0x%x)\n", __func__, wsi, wsi->wsistate);
+
+	wsi->http.ah = ah;
+	ah->wsi = wsi; /* new owner */
+
+	__lws_header_table_reset(wsi, autoservice);
+#if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
+	lws_context_lock(context); /* <====================================== */
+	if (wsi->peer)
+		wsi->peer->http.count_ah++;
+	lws_context_unlock(context); /* ====================================> */
+#endif
+
+	/* clients acquire the ah and then insert themselves in fds table... */
+	if (wsi->position_in_fds_table != LWS_NO_FDS_POS) {
+		lwsl_info("%s: Enabling %p POLLIN\n", __func__, wsi);
+
+		/* he has been stuck waiting for an ah, but now his wait is
+		 * over, let him progress */
+
+		_lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
+	}
+
+	/* point prev guy to next guy in list instead */
+	*pwsi_eligible = wsi->http.ah_wait_list;
+	/* the guy who got one is out of the list */
+	wsi->http.ah_wait_list = NULL;
+	pt->http.ah_wait_list_length--;
+
+#ifndef LWS_NO_CLIENT
+	if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) {
+		lws_pt_unlock(pt);
+
+		if (!lws_client_connect_via_info2(wsi)) {
+			/* our client connect has failed, the wsi
+			 * has been closed
+			 */
+
+			return -1;
+		}
+		return 0;
+	}
+#endif
+
+	assert(!!pt->http.ah_wait_list_length == !!(lws_intptr_t)pt->http.ah_wait_list);
+bail:
+	lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
+		  (void *)wsi, (void *)ah, pt->tid, pt->http.ah_count_in_use);
+
+	return 0;
+
+nobody_usable_waiting:
+	lwsl_info("%s: nobody usable waiting\n", __func__);
+	_lws_destroy_ah(pt, ah);
+	pt->http.ah_count_in_use--;
+
+	goto bail;
+}
+
+int lws_header_table_detach(struct lws *wsi, int autoservice)
+{
+	struct lws_context *context = wsi->context;
+	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+	int n;
+
+	lws_pt_lock(pt, __func__);
+	n = __lws_header_table_detach(wsi, autoservice);
+	lws_pt_unlock(pt);
+
+	return n;
+}
+
+LWS_VISIBLE int
+lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx)
+{
+	int n;
+
+	if (!wsi->http.ah)
+		return 0;
+
+	n = wsi->http.ah->frag_index[h];
+	if (!n)
+		return 0;
+	do {
+		if (!frag_idx)
+			return wsi->http.ah->frags[n].len;
+		n = wsi->http.ah->frags[n].nfrag;
+	} while (frag_idx-- && n);
+
+	return 0;
+}
+
+LWS_VISIBLE int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h)
+{
+	int n;
+	int len = 0;
+
+	if (!wsi->http.ah)
+		return 0;
+
+	n = wsi->http.ah->frag_index[h];
+	if (!n)
+		return 0;
+	do {
+		len += wsi->http.ah->frags[n].len;
+		n = wsi->http.ah->frags[n].nfrag;
+	} while (n);
+
+	return len;
+}
+
+LWS_VISIBLE int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len,
+				      enum lws_token_indexes h, int frag_idx)
+{
+	int n = 0;
+	int f;
+
+	if (!wsi->http.ah)
+		return -1;
+
+	f = wsi->http.ah->frag_index[h];
+
+	if (!f)
+		return -1;
+
+	while (n < frag_idx) {
+		f = wsi->http.ah->frags[f].nfrag;
+		if (!f)
+			return -1;
+		n++;
+	}
+
+	if (wsi->http.ah->frags[f].len >= len)
+		return -1;
+
+	memcpy(dst, wsi->http.ah->data + wsi->http.ah->frags[f].offset,
+	       wsi->http.ah->frags[f].len);
+	dst[wsi->http.ah->frags[f].len] = '\0';
+
+	return wsi->http.ah->frags[f].len;
+}
+
+LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len,
+			     enum lws_token_indexes h)
+{
+	int toklen = lws_hdr_total_length(wsi, h);
+	int n;
+
+	if (toklen >= len)
+		return -1;
+
+	if (!wsi->http.ah)
+		return -1;
+
+	n = wsi->http.ah->frag_index[h];
+	if (!n)
+		return 0;
+
+	do {
+		if (wsi->http.ah->frags[n].len >= len)
+			return -1;
+		strncpy(dst, &wsi->http.ah->data[wsi->http.ah->frags[n].offset],
+		        wsi->http.ah->frags[n].len);
+		dst += wsi->http.ah->frags[n].len;
+		len -= wsi->http.ah->frags[n].len;
+		n = wsi->http.ah->frags[n].nfrag;
+	} while (n);
+	*dst = '\0';
+
+	return toklen;
+}
+
+char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)
+{
+	int n;
+
+	n = wsi->http.ah->frag_index[h];
+	if (!n)
+		return NULL;
+
+	return wsi->http.ah->data + wsi->http.ah->frags[n].offset;
+}
+
+static int LWS_WARN_UNUSED_RESULT
+lws_pos_in_bounds(struct lws *wsi)
+{
+	if (wsi->http.ah->pos <
+	    (unsigned int)wsi->context->max_http_header_data)
+		return 0;
+
+	if ((int)wsi->http.ah->pos == wsi->context->max_http_header_data) {
+		lwsl_err("Ran out of header data space\n");
+		return 1;
+	}
+
+	/*
+	 * with these tests everywhere, it should never be able to exceed
+	 * the limit, only meet it
+	 */
+	lwsl_err("%s: pos %d, limit %d\n", __func__, wsi->http.ah->pos,
+		 wsi->context->max_http_header_data);
+	assert(0);
+
+	return 1;
+}
+
+int LWS_WARN_UNUSED_RESULT
+lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
+{
+	wsi->http.ah->nfrag++;
+	if (wsi->http.ah->nfrag == ARRAY_SIZE(wsi->http.ah->frags)) {
+		lwsl_warn("More hdr frags than we can deal with, dropping\n");
+		return -1;
+	}
+
+	wsi->http.ah->frag_index[h] = wsi->http.ah->nfrag;
+
+	wsi->http.ah->frags[wsi->http.ah->nfrag].offset = wsi->http.ah->pos;
+	wsi->http.ah->frags[wsi->http.ah->nfrag].len = 0;
+	wsi->http.ah->frags[wsi->http.ah->nfrag].nfrag = 0;
+
+	do {
+		if (lws_pos_in_bounds(wsi))
+			return -1;
+
+		wsi->http.ah->data[wsi->http.ah->pos++] = *s;
+		if (*s)
+			wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
+	} while (*s++);
+
+	return 0;
+}
+
+static int LWS_WARN_UNUSED_RESULT
+issue_char(struct lws *wsi, unsigned char c)
+{
+	unsigned short frag_len;
+
+	if (lws_pos_in_bounds(wsi))
+		return -1;
+
+	frag_len = wsi->http.ah->frags[wsi->http.ah->nfrag].len;
+	/*
+	 * If we haven't hit the token limit, just copy the character into
+	 * the header
+	 */
+	if (frag_len < wsi->http.ah->current_token_limit) {
+		wsi->http.ah->data[wsi->http.ah->pos++] = c;
+		if (c)
+			wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
+		return 0;
+	}
+
+	/* Insert a null character when we *hit* the limit: */
+	if (frag_len == wsi->http.ah->current_token_limit) {
+		if (lws_pos_in_bounds(wsi))
+			return -1;
+
+		wsi->http.ah->data[wsi->http.ah->pos++] = '\0';
+		lwsl_warn("header %i exceeds limit %d\n",
+			  wsi->http.ah->parser_state,
+			  wsi->http.ah->current_token_limit);
+	}
+
+	return 1;
+}
+
+int
+lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
+{
+	struct allocated_headers *ah = wsi->http.ah;
+	unsigned int enc = 0;
+	uint8_t c = *_c;
+
+	// lwsl_notice("ah->ups %d\n", ah->ups);
+
+	/*
+	 * PRIORITY 1
+	 * special URI processing... convert %xx
+	 */
+	switch (ah->ues) {
+	case URIES_IDLE:
+		if (c == '%') {
+			ah->ues = URIES_SEEN_PERCENT;
+			goto swallow;
+		}
+		break;
+	case URIES_SEEN_PERCENT:
+		if (char_to_hex(c) < 0)
+			/* illegal post-% char */
+			goto forbid;
+
+		ah->esc_stash = c;
+		ah->ues = URIES_SEEN_PERCENT_H1;
+		goto swallow;
+
+	case URIES_SEEN_PERCENT_H1:
+		if (char_to_hex(c) < 0)
+			/* illegal post-% char */
+			goto forbid;
+
+		*_c = (char_to_hex(ah->esc_stash) << 4) |
+				char_to_hex(c);
+		c = *_c;
+		enc = 1;
+		ah->ues = URIES_IDLE;
+		break;
+	}
+
+	/*
+	 * PRIORITY 2
+	 * special URI processing...
+	 *  convert /.. or /... or /../ etc to /
+	 *  convert /./ to /
+	 *  convert // or /// etc to /
+	 *  leave /.dir or whatever alone
+	 */
+
+	switch (ah->ups) {
+	case URIPS_IDLE:
+		if (!c)
+			return -1;
+		/* genuine delimiter */
+		if ((c == '&' || c == ';') && !enc) {
+			if (issue_char(wsi, c) < 0)
+				return -1;
+			/* swallow the terminator */
+			ah->frags[ah->nfrag].len--;
+			/* link to next fragment */
+			ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
+			ah->nfrag++;
+			if (ah->nfrag >= ARRAY_SIZE(ah->frags))
+				goto excessive;
+			/* start next fragment after the & */
+			ah->post_literal_equal = 0;
+			ah->frags[ah->nfrag].offset = ah->pos;
+			ah->frags[ah->nfrag].len = 0;
+			ah->frags[ah->nfrag].nfrag = 0;
+			goto swallow;
+		}
+		/* uriencoded = in the name part, disallow */
+		if (c == '=' && enc &&
+		    ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
+		    !ah->post_literal_equal) {
+			c = '_';
+			*_c =c;
+		}
+
+		/* after the real =, we don't care how many = */
+		if (c == '=' && !enc)
+			ah->post_literal_equal = 1;
+
+		/* + to space */
+		if (c == '+' && !enc) {
+			c = ' ';
+			*_c = c;
+		}
+		/* issue the first / always */
+		if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
+			ah->ups = URIPS_SEEN_SLASH;
+		break;
+	case URIPS_SEEN_SLASH:
+		/* swallow subsequent slashes */
+		if (c == '/')
+			goto swallow;
+		/* track and swallow the first . after / */
+		if (c == '.') {
+			ah->ups = URIPS_SEEN_SLASH_DOT;
+			goto swallow;
+		}
+		ah->ups = URIPS_IDLE;
+		break;
+	case URIPS_SEEN_SLASH_DOT:
+		/* swallow second . */
+		if (c == '.') {
+			ah->ups = URIPS_SEEN_SLASH_DOT_DOT;
+			goto swallow;
+		}
+		/* change /./ to / */
+		if (c == '/') {
+			ah->ups = URIPS_SEEN_SLASH;
+			goto swallow;
+		}
+		/* it was like /.dir ... regurgitate the . */
+		ah->ups = URIPS_IDLE;
+		if (issue_char(wsi, '.') < 0)
+			return -1;
+		break;
+
+	case URIPS_SEEN_SLASH_DOT_DOT:
+
+		/* /../ or /..[End of URI] --> backup to last / */
+		if (c == '/' || c == '?') {
+			/*
+			 * back up one dir level if possible
+			 * safe against header fragmentation because
+			 * the method URI can only be in 1 fragment
+			 */
+			if (ah->frags[ah->nfrag].len > 2) {
+				ah->pos--;
+				ah->frags[ah->nfrag].len--;
+				do {
+					ah->pos--;
+					ah->frags[ah->nfrag].len--;
+				} while (ah->frags[ah->nfrag].len > 1 &&
+					 ah->data[ah->pos] != '/');
+			}
+			ah->ups = URIPS_SEEN_SLASH;
+			if (ah->frags[ah->nfrag].len > 1)
+				break;
+			goto swallow;
+		}
+
+		/*  /..[^/] ... regurgitate and allow */
+
+		if (issue_char(wsi, '.') < 0)
+			return -1;
+		if (issue_char(wsi, '.') < 0)
+			return -1;
+		ah->ups = URIPS_IDLE;
+		break;
+	}
+
+	if (c == '?' && !enc &&
+	    !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI args */
+		if (ah->ues != URIES_IDLE)
+			goto forbid;
+
+		/* seal off uri header */
+		if (issue_char(wsi, '\0') < 0)
+			return -1;
+
+		/* move to using WSI_TOKEN_HTTP_URI_ARGS */
+		ah->nfrag++;
+		if (ah->nfrag >= ARRAY_SIZE(ah->frags))
+			goto excessive;
+		ah->frags[ah->nfrag].offset = ah->pos;
+		ah->frags[ah->nfrag].len = 0;
+		ah->frags[ah->nfrag].nfrag = 0;
+
+		ah->post_literal_equal = 0;
+		ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
+		ah->ups = URIPS_IDLE;
+		goto swallow;
+	}
+
+	return LPUR_CONTINUE;
+
+swallow:
+	return LPUR_SWALLOW;
+
+forbid:
+	return LPUR_FORBID;
+
+excessive:
+	return LPUR_EXCESSIVE;
+}
+
+static const unsigned char methods[] = {
+	WSI_TOKEN_GET_URI,
+	WSI_TOKEN_POST_URI,
+	WSI_TOKEN_OPTIONS_URI,
+	WSI_TOKEN_PUT_URI,
+	WSI_TOKEN_PATCH_URI,
+	WSI_TOKEN_DELETE_URI,
+	WSI_TOKEN_CONNECT,
+	WSI_TOKEN_HEAD_URI,
+};
+
+/*
+ * possible returns:, -1 fail, 0 ok or 2, transition to raw
+ */
+
+int LWS_WARN_UNUSED_RESULT
+lws_parse(struct lws *wsi, unsigned char *buf, int *len)
+{
+	struct allocated_headers *ah = wsi->http.ah;
+	struct lws_context *context = wsi->context;
+	unsigned int n, m;
+	unsigned char c;
+	int r, pos;
+
+	assert(wsi->http.ah);
+
+	do {
+		(*len)--;
+		c = *buf++;
+
+		switch (ah->parser_state) {
+		default:
+
+			lwsl_parser("WSI_TOK_(%d) '%c'\n", ah->parser_state, c);
+
+			/* collect into malloc'd buffers */
+			/* optional initial space swallow */
+			if (!ah->frags[ah->frag_index[ah->parser_state]].len &&
+			    c == ' ')
+				break;
+
+			for (m = 0; m < ARRAY_SIZE(methods); m++)
+				if (ah->parser_state == methods[m])
+					break;
+			if (m == ARRAY_SIZE(methods))
+				/* it was not any of the methods */
+				goto check_eol;
+
+			/* special URI processing... end at space */
+
+			if (c == ' ') {
+				/* enforce starting with / */
+				if (!ah->frags[ah->nfrag].len)
+					if (issue_char(wsi, '/') < 0)
+						return -1;
+
+				if (ah->ups == URIPS_SEEN_SLASH_DOT_DOT) {
+					/*
+					 * back up one dir level if possible
+					 * safe against header fragmentation because
+					 * the method URI can only be in 1 fragment
+					 */
+					if (ah->frags[ah->nfrag].len > 2) {
+						ah->pos--;
+						ah->frags[ah->nfrag].len--;
+						do {
+							ah->pos--;
+							ah->frags[ah->nfrag].len--;
+						} while (ah->frags[ah->nfrag].len > 1 &&
+							 ah->data[ah->pos] != '/');
+					}
+				}
+
+				/* begin parsing HTTP version: */
+				if (issue_char(wsi, '\0') < 0)
+					return -1;
+				ah->parser_state = WSI_TOKEN_HTTP;
+				goto start_fragment;
+			}
+
+			r = lws_parse_urldecode(wsi, &c);
+			switch (r) {
+			case LPUR_CONTINUE:
+				break;
+			case LPUR_SWALLOW:
+				goto swallow;
+			case LPUR_FORBID:
+				goto forbid;
+			case LPUR_EXCESSIVE:
+				goto excessive;
+			default:
+				return -1;
+			}
+check_eol:
+			/* bail at EOL */
+			if (ah->parser_state != WSI_TOKEN_CHALLENGE &&
+			    c == '\x0d') {
+				if (ah->ues != URIES_IDLE)
+					goto forbid;
+
+				c = '\0';
+				ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
+				lwsl_parser("*\n");
+			}
+
+			n = issue_char(wsi, c);
+			if ((int)n < 0)
+				return -1;
+			if (n > 0)
+				ah->parser_state = WSI_TOKEN_SKIPPING;
+
+swallow:
+			/* per-protocol end of headers management */
+
+			if (ah->parser_state == WSI_TOKEN_CHALLENGE)
+				goto set_parsing_complete;
+			break;
+
+			/* collecting and checking a name part */
+		case WSI_TOKEN_NAME_PART:
+			lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X (role=0x%x) "
+				    "wsi->lextable_pos=%d\n", c, c, lwsi_role(wsi),
+				    ah->lextable_pos);
+
+			if (c >= 'A' && c <= 'Z')
+				c += 'a' - 'A';
+
+			pos = ah->lextable_pos;
+
+			while (1) {
+				if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */
+					if ((lextable[pos] & 0x7f) != c) {
+nope:
+						ah->lextable_pos = -1;
+						break;
+					}
+					/* fall thru */
+					pos++;
+					if (lextable[pos] == FAIL_CHAR)
+						goto nope;
+
+					ah->lextable_pos = pos;
+					break;
+				}
+
+				if (lextable[pos] == FAIL_CHAR)
+					goto nope;
+
+				/* b7 = 0, end or 3-byte */
+				if (lextable[pos] < FAIL_CHAR) { /* terminal marker */
+					ah->lextable_pos = pos;
+					break;
+				}
+
+				if (lextable[pos] == c) { /* goto */
+					ah->lextable_pos = pos + (lextable[pos + 1]) +
+							(lextable[pos + 2] << 8);
+					break;
+				}
+
+				/* fall thru goto */
+				pos += 3;
+				/* continue */
+			}
+
+			/*
+			 * If it's h1, server needs to look out for unknown
+			 * methods...
+			 */
+			if (ah->lextable_pos < 0 && lwsi_role_h1(wsi) &&
+			    lwsi_role_server(wsi)) {
+				/* this is not a header we know about */
+				for (m = 0; m < ARRAY_SIZE(methods); m++)
+					if (ah->frag_index[methods[m]]) {
+						/*
+						 * already had the method, no idea what
+						 * this crap from the client is, ignore
+						 */
+						ah->parser_state = WSI_TOKEN_SKIPPING;
+						break;
+					}
+				/*
+				 * hm it's an unknown http method from a client in fact,
+				 * it cannot be valid http
+				 */
+				if (m == ARRAY_SIZE(methods)) {
+					/*
+					 * are we set up to accept raw in these cases?
+					 */
+					if (lws_check_opt(wsi->vhost->options,
+						   LWS_SERVER_OPTION_FALLBACK_TO_RAW))
+						return 2; /* transition to raw */
+
+					lwsl_info("Unknown method - dropping\n");
+					goto forbid;
+				}
+				break;
+			}
+			/*
+			 * ...otherwise for a client, let him ignore unknown headers
+			 * coming from the server
+			 */
+			if (ah->lextable_pos < 0) {
+				ah->parser_state = WSI_TOKEN_SKIPPING;
+				break;
+			}
+
+			if (lextable[ah->lextable_pos] < FAIL_CHAR) {
+				/* terminal state */
+
+				n = ((unsigned int)lextable[ah->lextable_pos] << 8) |
+						lextable[ah->lextable_pos + 1];
+
+				lwsl_parser("known hdr %d\n", n);
+				for (m = 0; m < ARRAY_SIZE(methods); m++)
+					if (n == methods[m] &&
+					    ah->frag_index[methods[m]]) {
+						lwsl_warn("Duplicated method\n");
+						return -1;
+					}
+
+				/*
+				 * WSORIGIN is protocol equiv to ORIGIN,
+				 * JWebSocket likes to send it, map to ORIGIN
+				 */
+				if (n == WSI_TOKEN_SWORIGIN)
+					n = WSI_TOKEN_ORIGIN;
+
+				ah->parser_state = (enum lws_token_indexes)
+								(WSI_TOKEN_GET_URI + n);
+				ah->ups = URIPS_IDLE;
+
+				if (context->token_limits)
+					ah->current_token_limit = context->
+							token_limits->token_limit[
+								      ah->parser_state];
+				else
+					ah->current_token_limit =
+						wsi->context->max_http_header_data;
+
+				if (ah->parser_state == WSI_TOKEN_CHALLENGE)
+					goto set_parsing_complete;
+
+				goto start_fragment;
+			}
+			break;
+
+start_fragment:
+			ah->nfrag++;
+excessive:
+			if (ah->nfrag == ARRAY_SIZE(ah->frags)) {
+				lwsl_warn("More hdr frags than we can deal with\n");
+				return -1;
+			}
+
+			ah->frags[ah->nfrag].offset = ah->pos;
+			ah->frags[ah->nfrag].len = 0;
+			ah->frags[ah->nfrag].nfrag = 0;
+			ah->frags[ah->nfrag].flags = 2;
+
+			n = ah->frag_index[ah->parser_state];
+			if (!n) { /* first fragment */
+				ah->frag_index[ah->parser_state] = ah->nfrag;
+				ah->hdr_token_idx = ah->parser_state;
+				break;
+			}
+			/* continuation */
+			while (ah->frags[n].nfrag)
+				n = ah->frags[n].nfrag;
+			ah->frags[n].nfrag = ah->nfrag;
+
+			if (issue_char(wsi, ' ') < 0)
+				return -1;
+			break;
+
+			/* skipping arg part of a name we didn't recognize */
+		case WSI_TOKEN_SKIPPING:
+			lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c);
+
+			if (c == '\x0d')
+				ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
+			break;
+
+		case WSI_TOKEN_SKIPPING_SAW_CR:
+			lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
+			if (ah->ues != URIES_IDLE)
+				goto forbid;
+			if (c == '\x0a') {
+				ah->parser_state = WSI_TOKEN_NAME_PART;
+				ah->lextable_pos = 0;
+			} else
+				ah->parser_state = WSI_TOKEN_SKIPPING;
+			break;
+			/* we're done, ignore anything else */
+
+		case WSI_PARSING_COMPLETE:
+			lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c);
+			break;
+		}
+
+	} while (*len);
+
+	return 0;
+
+set_parsing_complete:
+	if (ah->ues != URIES_IDLE)
+		goto forbid;
+	if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
+		if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
+			wsi->rx_frame_type = /* temp for ws version index */
+			       atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
+
+		lwsl_parser("v%02d hdrs done\n", wsi->rx_frame_type);
+	}
+	ah->parser_state = WSI_PARSING_COMPLETE;
+	wsi->hdr_parsing_completed = 1;
+
+	return 0;
+
+forbid:
+	lwsl_notice(" forbidding on uri sanitation\n");
+	lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
+
+	return -1;
+}
+

File diff suppressed because it is too large
+ 306 - 265
thirdparty/libwebsockets/roles/http/server/server.c


+ 177 - 0
thirdparty/libwebsockets/roles/listen/ops-listen.c

@@ -0,0 +1,177 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include <core/private.h>
+
+static int
+rops_handle_POLLIN_listen(struct lws_context_per_thread *pt, struct lws *wsi,
+			  struct lws_pollfd *pollfd)
+{
+	struct lws_context *context = wsi->context;
+	lws_sockfd_type accept_fd = LWS_SOCK_INVALID;
+	lws_sock_file_fd_type fd;
+	int opts = LWS_ADOPT_SOCKET | LWS_ADOPT_ALLOW_SSL;
+	struct sockaddr_storage cli_addr;
+	socklen_t clilen;
+
+	/* pollin means a client has connected to us then
+	 *
+	 * pollout is a hack on esp32 for background accepts signalling
+	 * they completed
+	 */
+
+	do {
+		struct lws *cwsi;
+
+		if (!(pollfd->revents & (LWS_POLLIN | LWS_POLLOUT)) ||
+		    !(pollfd->events & LWS_POLLIN))
+			break;
+
+#if defined(LWS_WITH_TLS)
+		/*
+		 * can we really accept it, with regards to SSL limit?
+		 * another vhost may also have had POLLIN on his
+		 * listener this round and used it up already
+		 */
+		if (wsi->vhost->tls.use_ssl &&
+		    context->simultaneous_ssl_restriction &&
+		    context->simultaneous_ssl ==
+				  context->simultaneous_ssl_restriction)
+			/*
+			 * no... ignore it, he won't come again until
+			 * we are below the simultaneous_ssl_restriction
+			 * limit and POLLIN is enabled on him again
+			 */
+			break;
+#endif
+		/* listen socket got an unencrypted connection... */
+
+		clilen = sizeof(cli_addr);
+		lws_latency_pre(context, wsi);
+
+		/*
+		 * We cannot identify the peer who is in the listen
+		 * socket connect queue before we accept it; even if
+		 * we could, not accepting it due to PEER_LIMITS would
+		 * block the connect queue for other legit peers.
+		 */
+
+		accept_fd  = accept((int)pollfd->fd,
+				    (struct sockaddr *)&cli_addr, &clilen);
+		lws_latency(context, wsi, "listener accept",
+			    (int)accept_fd, accept_fd != LWS_SOCK_INVALID);
+		if (accept_fd == LWS_SOCK_INVALID) {
+			if (LWS_ERRNO == LWS_EAGAIN ||
+			    LWS_ERRNO == LWS_EWOULDBLOCK) {
+				break;
+			}
+			lwsl_err("ERROR on accept: %s\n",
+				 strerror(LWS_ERRNO));
+			break;
+		}
+
+		lws_plat_set_socket_options(wsi->vhost, accept_fd);
+
+#if defined(LWS_WITH_IPV6)
+		lwsl_debug("accepted new conn port %u on fd=%d\n",
+			((cli_addr.ss_family == AF_INET6) ?
+			ntohs(((struct sockaddr_in6 *) &cli_addr)->sin6_port) :
+			ntohs(((struct sockaddr_in *) &cli_addr)->sin_port)),
+			accept_fd);
+#else
+		lwsl_debug("accepted new conn port %u on fd=%d\n",
+			   ntohs(((struct sockaddr_in *) &cli_addr)->sin_port),
+			   accept_fd);
+#endif
+
+		/*
+		 * look at who we connected to and give user code a
+		 * chance to reject based on client IP.  There's no
+		 * protocol selected yet so we issue this to
+		 * protocols[0]
+		 */
+		if ((wsi->vhost->protocols[0].callback)(wsi,
+				LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
+				NULL,
+				(void *)(lws_intptr_t)accept_fd, 0)) {
+			lwsl_debug("Callback denied net connection\n");
+			compatible_close(accept_fd);
+			break;
+		}
+
+		if (!(wsi->vhost->options & LWS_SERVER_OPTION_ONLY_RAW))
+			opts |= LWS_ADOPT_HTTP;
+		else
+			opts = LWS_ADOPT_SOCKET;
+
+		fd.sockfd = accept_fd;
+		cwsi = lws_adopt_descriptor_vhost(wsi->vhost, opts, fd,
+						  NULL, NULL);
+		if (!cwsi)
+			/* already closed cleanly as necessary */
+			return LWS_HPI_RET_WSI_ALREADY_DIED;
+
+		if (lws_server_socket_service_ssl(cwsi, accept_fd)) {
+			lws_close_free_wsi(cwsi, LWS_CLOSE_STATUS_NOSTATUS,
+					   "listen svc fail");
+			return LWS_HPI_RET_WSI_ALREADY_DIED;
+		}
+
+		lwsl_info("%s: new wsi %p: wsistate 0x%x, role_ops %s\n",
+			    __func__, cwsi, cwsi->wsistate, cwsi->role_ops->name);
+
+	} while (pt->fds_count < context->fd_limit_per_thread - 1 &&
+		 wsi->position_in_fds_table != LWS_NO_FDS_POS &&
+		 lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0);
+
+	return LWS_HPI_RET_HANDLED;
+}
+
+int rops_handle_POLLOUT_listen(struct lws *wsi)
+{
+	return LWS_HP_RET_USER_SERVICE;
+}
+
+struct lws_role_ops role_ops_listen = {
+	/* role name */			"listen",
+	/* alpn id */			NULL,
+	/* check_upgrades */		NULL,
+	/* init_context */		NULL,
+	/* init_vhost */		NULL,
+	/* destroy_vhost */		NULL,
+	/* periodic_checks */		NULL,
+	/* service_flag_pending */	NULL,
+	/* handle_POLLIN */		rops_handle_POLLIN_listen,
+	/* handle_POLLOUT */		rops_handle_POLLOUT_listen,
+	/* perform_user_POLLOUT */	NULL,
+	/* callback_on_writable */	NULL,
+	/* tx_credit */			NULL,
+	/* write_role_protocol */	NULL,
+	/* encapsulation_parent */	NULL,
+	/* alpn_negotiated */		NULL,
+	/* close_via_role_protocol */	NULL,
+	/* close_role */		NULL,
+	/* close_kill_connection */	NULL,
+	/* destroy_role */		NULL,
+	/* writeable cb clnt, srv */	{ 0, 0 },
+	/* close cb clnt, srv */	{ 0, 0 },
+	/* file_handle */		0,
+};

+ 81 - 0
thirdparty/libwebsockets/roles/pipe/ops-pipe.c

@@ -0,0 +1,81 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include <core/private.h>
+
+static int
+rops_handle_POLLIN_pipe(struct lws_context_per_thread *pt, struct lws *wsi,
+			struct lws_pollfd *pollfd)
+{
+#if !defined(WIN32) && !defined(_WIN32)
+	char s[100];
+	int n;
+
+	/*
+	 * discard the byte(s) that signaled us
+	 * We really don't care about the number of bytes, but coverity
+	 * thinks we should.
+	 */
+	n = read(wsi->desc.sockfd, s, sizeof(s));
+	(void)n;
+	if (n < 0)
+		return LWS_HPI_RET_PLEASE_CLOSE_ME;
+#endif
+	/*
+	 * the poll() wait, or the event loop for libuv etc is a
+	 * process-wide resource that we interrupted.  So let every
+	 * protocol that may be interested in the pipe event know that
+	 * it happened.
+	 */
+	if (lws_broadcast(wsi->context, LWS_CALLBACK_EVENT_WAIT_CANCELLED,
+			  NULL, 0)) {
+		lwsl_info("closed in event cancel\n");
+		return LWS_HPI_RET_PLEASE_CLOSE_ME;
+	}
+
+	return LWS_HPI_RET_HANDLED;
+}
+
+struct lws_role_ops role_ops_pipe = {
+	/* role name */			"pipe",
+	/* alpn id */			NULL,
+	/* check_upgrades */		NULL,
+	/* init_context */		NULL,
+	/* init_vhost */		NULL,
+	/* destroy_vhost */		NULL,
+	/* periodic_checks */		NULL,
+	/* service_flag_pending */	NULL,
+	/* handle_POLLIN */		rops_handle_POLLIN_pipe,
+	/* handle_POLLOUT */		NULL,
+	/* perform_user_POLLOUT */	NULL,
+	/* callback_on_writable */	NULL,
+	/* tx_credit */			NULL,
+	/* write_role_protocol */	NULL,
+	/* encapsulation_parent */	NULL,
+	/* alpn_negotiated */		NULL,
+	/* close_via_role_protocol */	NULL,
+	/* close_role */		NULL,
+	/* close_kill_connection */	NULL,
+	/* destroy_role */		NULL,
+	/* writeable cb clnt, srv */	{ 0, 0 },
+	/* close cb clnt, srv */	{ 0, 0 },
+	/* file_handle */		1,
+};

+ 282 - 0
thirdparty/libwebsockets/roles/private.h

@@ -0,0 +1,282 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ *
+ *  This is included from core/private.h
+ */
+
+typedef uint32_t lws_wsi_state_t;
+
+/*
+ * The wsi->role_ops pointer decides almost everything about what role the wsi
+ * will play, h2, raw, ws, etc.
+ *
+ * However there are a few additional flags needed that vary, such as if the
+ * role is a client or server side, if it has that concept.  And the connection
+ * fulfilling the role, has a separate dynamic state.
+ *
+ *   31           16 15      0
+ *   [  role flags ] [ state ]
+ *
+ * The role flags part is generally invariant for the lifetime of the wsi,
+ * although it can change if the connection role itself does, eg, if the
+ * connection upgrades from H1 -> WS1 the role flags may be changed at that
+ * point.
+ *
+ * The state part reflects the dynamic connection state, and the states are
+ * reused between roles.
+ *
+ * None of the internal role or state representations are made available outside
+ * of lws internals.  Even for lws internals, if you add stuff here, please keep
+ * the constants inside this header only by adding necessary helpers here and
+ * use the helpers in the actual code.  This is to ease any future refactors.
+ *
+ * Notice LWSIFR_ENCAP means we have a parent wsi that actually carries our
+ * data as a stream inside a different protocol.
+ */
+
+#define _RS 16
+
+#define LWSIFR_CLIENT		(0x1000 << _RS) /* client side */
+#define LWSIFR_SERVER		(0x2000 << _RS) /* server side */
+
+#define LWSIFR_P_ENCAP_H2	(0x0100 << _RS) /* we are encapsulated by h2 */
+
+enum lwsi_role {
+	LWSI_ROLE_MASK		=			     (0xffff << _RS),
+	LWSI_ROLE_ENCAP_MASK	=			     (0x0f00 << _RS),
+};
+
+#define lwsi_role(wsi) (wsi->wsistate & LWSI_ROLE_MASK)
+#if !defined (_DEBUG)
+#define lwsi_set_role(wsi, role) wsi->wsistate = \
+				(wsi->wsistate & (~LWSI_ROLE_MASK)) | role
+#else
+void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role);
+#endif
+
+#define lwsi_role_client(wsi) (!!(wsi->wsistate & LWSIFR_CLIENT))
+#define lwsi_role_server(wsi) (!!(wsi->wsistate & LWSIFR_SERVER))
+#define lwsi_role_h2_ENCAPSULATION(wsi) \
+		((wsi->wsistate & LWSI_ROLE_ENCAP_MASK) == LWSIFR_P_ENCAP_H2)
+
+/* Pollout wants a callback in this state */
+#define LWSIFS_POCB		(0x100)
+/* Before any protocol connection was established */
+#define LWSIFS_NOT_EST		(0x200)
+
+enum lwsi_state {
+
+	/* Phase 1: pre-transport */
+
+	LRS_UNCONNECTED				= LWSIFS_NOT_EST | 0,
+	LRS_WAITING_CONNECT			= LWSIFS_NOT_EST | 1,
+
+	/* Phase 2: establishing intermediaries on top of transport */
+
+	LRS_WAITING_PROXY_REPLY			= LWSIFS_NOT_EST | 2,
+	LRS_WAITING_SSL				= LWSIFS_NOT_EST | 3,
+	LRS_WAITING_SOCKS_GREETING_REPLY	= LWSIFS_NOT_EST | 4,
+	LRS_WAITING_SOCKS_CONNECT_REPLY		= LWSIFS_NOT_EST | 5,
+	LRS_WAITING_SOCKS_AUTH_REPLY		= LWSIFS_NOT_EST | 6,
+
+	/* Phase 3: establishing tls tunnel */
+
+	LRS_SSL_INIT				= LWSIFS_NOT_EST | 7,
+	LRS_SSL_ACK_PENDING			= LWSIFS_NOT_EST | 8,
+	LRS_PRE_WS_SERVING_ACCEPT		= LWSIFS_NOT_EST | 9,
+
+	/* Phase 4: connected */
+
+	LRS_WAITING_SERVER_REPLY		= LWSIFS_NOT_EST | 10,
+	LRS_H2_AWAIT_PREFACE			= LWSIFS_NOT_EST | 11,
+	LRS_H2_AWAIT_SETTINGS			= LWSIFS_NOT_EST |
+						  LWSIFS_POCB | 12,
+
+	/* Phase 5: protocol logically established */
+
+	LRS_H2_CLIENT_SEND_SETTINGS		= LWSIFS_POCB | 13,
+	LRS_H2_WAITING_TO_SEND_HEADERS		= LWSIFS_POCB | 14,
+	LRS_DEFERRING_ACTION			= LWSIFS_POCB | 15,
+	LRS_IDLING				= 16,
+	LRS_H1C_ISSUE_HANDSHAKE			= 17,
+	LRS_H1C_ISSUE_HANDSHAKE2		= 18,
+	LRS_ISSUE_HTTP_BODY			= 19,
+	LRS_ISSUING_FILE			= 20,
+	LRS_HEADERS				= 21,
+	LRS_BODY				= 22,
+	LRS_ESTABLISHED				= LWSIFS_POCB | 23,
+	/* we are established, but we have embarked on serving a single
+	 * transaction.  Other transaction input may be pending, but we will
+	 * not service it while we are busy dealing with the current
+	 * transaction.
+	 *
+	 * When we complete the current transaction, we would reset our state
+	 * back to ESTABLISHED and start to process the next transaction.
+	 */
+	LRS_DOING_TRANSACTION			= LWSIFS_POCB | 24,
+
+	/* Phase 6: finishing */
+
+	LRS_WAITING_TO_SEND_CLOSE		= LWSIFS_POCB | 25,
+	LRS_RETURNED_CLOSE			= LWSIFS_POCB | 26,
+	LRS_AWAITING_CLOSE_ACK			= LWSIFS_POCB | 27,
+	LRS_FLUSHING_BEFORE_CLOSE		= LWSIFS_POCB | 28,
+	LRS_SHUTDOWN				= 29,
+
+	/* Phase 7: dead */
+
+	LRS_DEAD_SOCKET				= 30,
+
+	LRS_MASK				= 0xffff
+};
+
+#define lwsi_state(wsi) ((enum lwsi_state)(wsi->wsistate & LRS_MASK))
+#define lwsi_state_PRE_CLOSE(wsi) ((enum lwsi_state)(wsi->wsistate_pre_close & LRS_MASK))
+#define lwsi_state_est(wsi) (!(wsi->wsistate & LWSIFS_NOT_EST))
+#define lwsi_state_est_PRE_CLOSE(wsi) (!(wsi->wsistate_pre_close & LWSIFS_NOT_EST))
+#define lwsi_state_can_handle_POLLOUT(wsi) (wsi->wsistate & LWSIFS_POCB)
+#if !defined (_DEBUG)
+#define lwsi_set_state(wsi, lrs) wsi->wsistate = \
+			  (wsi->wsistate & (~LRS_MASK)) | lrs
+#else
+void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs);
+#endif
+
+/*
+ * internal role-specific ops
+ */
+struct lws_context_per_thread;
+struct lws_role_ops {
+	const char *name;
+	const char *alpn;
+	/*
+	 * After http headers have parsed, this is the last chance for a role
+	 * to upgrade the connection to something else using the headers.
+	 * ws-over-h2 is upgraded from h2 like this.
+	 */
+	int (*check_upgrades)(struct lws *wsi);
+	/* role-specific context init during context creation */
+	int (*init_context)(struct lws_context *context,
+			    const struct lws_context_creation_info *info);
+	/* role-specific per-vhost init during vhost creation */
+	int (*init_vhost)(struct lws_vhost *vh,
+			  const struct lws_context_creation_info *info);
+	/* role-specific per-vhost destructor during vhost destroy */
+	int (*destroy_vhost)(struct lws_vhost *vh);
+	/* generic 1Hz callback for the role itself */
+	int (*periodic_checks)(struct lws_context *context, int tsi,
+			       time_t now);
+	/* chance for the role to force POLLIN without network activity */
+	int (*service_flag_pending)(struct lws_context *context, int tsi);
+	/* an fd using this role has POLLIN signalled */
+	int (*handle_POLLIN)(struct lws_context_per_thread *pt, struct lws *wsi,
+			     struct lws_pollfd *pollfd);
+	/* an fd using the role wanted a POLLOUT callback and now has it */
+	int (*handle_POLLOUT)(struct lws *wsi);
+	/* perform user pollout */
+	int (*perform_user_POLLOUT)(struct lws *wsi);
+	/* do effective callback on writeable */
+	int (*callback_on_writable)(struct lws *wsi);
+	/* connection-specific tx credit in bytes */
+	lws_fileofs_t (*tx_credit)(struct lws *wsi);
+	/* role-specific write formatting */
+	int (*write_role_protocol)(struct lws *wsi, unsigned char *buf,
+				   size_t len, enum lws_write_protocol *wp);
+
+	/* get encapsulation parent */
+	struct lws * (*encapsulation_parent)(struct lws *wsi);
+
+	/* role-specific destructor */
+	int (*alpn_negotiated)(struct lws *wsi, const char *alpn);
+
+	/* chance for the role to handle close in the protocol */
+	int (*close_via_role_protocol)(struct lws *wsi,
+				       enum lws_close_status reason);
+	/* role-specific close processing */
+	int (*close_role)(struct lws_context_per_thread *pt, struct lws *wsi);
+	/* role-specific connection close processing */
+	int (*close_kill_connection)(struct lws *wsi,
+				     enum lws_close_status reason);
+	/* role-specific destructor */
+	int (*destroy_role)(struct lws *wsi);
+
+	/*
+	 * the callback reasons for WRITEABLE for client, server
+	 * (just client applies if no concept of client or server)
+	 */
+	uint16_t writeable_cb[2];
+	/*
+	 * the callback reasons for CLOSE for client, server
+	 * (just client applies if no concept of client or server)
+	 */
+	uint16_t close_cb[2];
+
+	unsigned int file_handle:1; /* role operates on files not sockets */
+};
+
+/* core roles */
+extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen,
+			   role_ops_pipe;
+
+/* bring in role private declarations */
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+ #include "roles/http/private.h"
+#else
+ #define lwsi_role_http(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_H1)
+ #include "roles/h1/private.h"
+#else
+ #define lwsi_role_h1(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_H2)
+ #include "roles/h2/private.h"
+#else
+ #define lwsi_role_h2(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_WS)
+ #include "roles/ws/private.h"
+#else
+ #define lwsi_role_ws(wsi) (0)
+#endif
+
+#if defined(LWS_ROLE_CGI)
+ #include "roles/cgi/private.h"
+#else
+ #define lwsi_role_cgi(wsi) (0)
+#endif
+
+enum {
+	LWS_HP_RET_BAIL_OK,
+	LWS_HP_RET_BAIL_DIE,
+	LWS_HP_RET_USER_SERVICE,
+
+	LWS_HPI_RET_WSI_ALREADY_DIED,	/* we closed it */
+	LWS_HPI_RET_HANDLED,		/* no probs */
+	LWS_HPI_RET_PLEASE_CLOSE_ME,	/* close it for us */
+
+	LWS_UPG_RET_DONE,
+	LWS_UPG_RET_CONTINUE,
+	LWS_UPG_RET_BAIL
+};

+ 223 - 0
thirdparty/libwebsockets/roles/raw/ops-raw.c

@@ -0,0 +1,223 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include <core/private.h>
+
+static int
+rops_handle_POLLIN_raw_skt(struct lws_context_per_thread *pt, struct lws *wsi,
+			   struct lws_pollfd *pollfd)
+{
+	struct lws_tokens ebuf;
+	int n, buffered;
+
+	/* pending truncated sends have uber priority */
+
+	if (wsi->trunc_len) {
+		if (!(pollfd->revents & LWS_POLLOUT))
+			return LWS_HPI_RET_HANDLED;
+
+		if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
+				  wsi->trunc_len) < 0)
+			goto fail;
+		/*
+		 * we can't afford to allow input processing to send
+		 * something new, so spin around he event loop until
+		 * he doesn't have any partials
+		 */
+		return LWS_HPI_RET_HANDLED;
+	}
+
+	if ((pollfd->revents & pollfd->events & LWS_POLLIN) &&
+	    /* any tunnel has to have been established... */
+	    lwsi_state(wsi) != LRS_SSL_ACK_PENDING &&
+	    !(wsi->favoured_pollin &&
+	      (pollfd->revents & pollfd->events & LWS_POLLOUT))) {
+
+		buffered = lws_buflist_aware_read(pt, wsi, &ebuf);
+		switch (ebuf.len) {
+		case 0:
+			lwsl_info("%s: read 0 len\n", __func__);
+			wsi->seen_zero_length_recv = 1;
+			lws_change_pollfd(wsi, LWS_POLLIN, 0);
+
+			/*
+			 * we need to go to fail here, since it's the only
+			 * chance we get to understand that the socket has
+			 * closed
+			 */
+			// goto try_pollout;
+			goto fail;
+
+		case LWS_SSL_CAPABLE_ERROR:
+			goto fail;
+		case LWS_SSL_CAPABLE_MORE_SERVICE:
+			goto try_pollout;
+		}
+
+		n = user_callback_handle_rxflow(wsi->protocol->callback,
+						wsi, LWS_CALLBACK_RAW_RX,
+						wsi->user_space, ebuf.token,
+						ebuf.len);
+		if (n < 0) {
+			lwsl_info("LWS_CALLBACK_RAW_RX_fail\n");
+			goto fail;
+		}
+
+		if (lws_buflist_aware_consume(wsi, &ebuf, ebuf.len, buffered))
+			return LWS_HPI_RET_PLEASE_CLOSE_ME;
+	} else
+		if (wsi->favoured_pollin &&
+		    (pollfd->revents & pollfd->events & LWS_POLLOUT))
+			/* we balanced the last favouring of pollin */
+			wsi->favoured_pollin = 0;
+
+try_pollout:
+
+	/* this handles POLLOUT for http serving fragments */
+
+	if (!(pollfd->revents & LWS_POLLOUT))
+		return LWS_HPI_RET_HANDLED;
+
+	/* one shot */
+	if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+		lwsl_notice("%s a\n", __func__);
+		goto fail;
+	}
+
+	/* clear back-to-back write detection */
+	wsi->could_have_pending = 0;
+
+	lws_stats_atomic_bump(wsi->context, pt,
+				LWSSTATS_C_WRITEABLE_CB, 1);
+#if defined(LWS_WITH_STATS)
+	if (wsi->active_writable_req_us) {
+		uint64_t ul = time_in_microseconds() -
+				wsi->active_writable_req_us;
+
+		lws_stats_atomic_bump(wsi->context, pt,
+				LWSSTATS_MS_WRITABLE_DELAY, ul);
+		lws_stats_atomic_max(wsi->context, pt,
+			  LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
+		wsi->active_writable_req_us = 0;
+	}
+#endif
+	n = user_callback_handle_rxflow(wsi->protocol->callback,
+			wsi, LWS_CALLBACK_RAW_WRITEABLE,
+			wsi->user_space, NULL, 0);
+	if (n < 0) {
+		lwsl_info("writeable_fail\n");
+		goto fail;
+	}
+
+	return LWS_HPI_RET_HANDLED;
+
+fail:
+	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "raw svc fail");
+
+	return LWS_HPI_RET_WSI_ALREADY_DIED;
+}
+
+
+static int
+rops_handle_POLLIN_raw_file(struct lws_context_per_thread *pt, struct lws *wsi,
+			    struct lws_pollfd *pollfd)
+{
+	int n;
+
+	if (pollfd->revents & LWS_POLLOUT) {
+		n = lws_callback_as_writeable(wsi);
+		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+			lwsl_info("failed at set pollfd\n");
+			return LWS_HPI_RET_WSI_ALREADY_DIED;
+		}
+		if (n)
+			return LWS_HPI_RET_PLEASE_CLOSE_ME;
+	}
+
+	if (pollfd->revents & LWS_POLLIN) {
+		if (user_callback_handle_rxflow(wsi->protocol->callback,
+						wsi, LWS_CALLBACK_RAW_RX_FILE,
+						wsi->user_space, NULL, 0)) {
+			lwsl_debug("raw rx callback closed it\n");
+			return LWS_HPI_RET_PLEASE_CLOSE_ME;
+		}
+	}
+
+	if (pollfd->revents & LWS_POLLHUP)
+		return LWS_HPI_RET_PLEASE_CLOSE_ME;
+
+	return LWS_HPI_RET_HANDLED;
+}
+
+
+struct lws_role_ops role_ops_raw_skt = {
+	/* role name */			"raw-skt",
+	/* alpn id */			NULL,
+	/* check_upgrades */		NULL,
+	/* init_context */		NULL,
+	/* init_vhost */		NULL,
+	/* destroy_vhost */		NULL,
+	/* periodic_checks */		NULL,
+	/* service_flag_pending */	NULL,
+	/* handle_POLLIN */		rops_handle_POLLIN_raw_skt,
+	/* handle_POLLOUT */		NULL,
+	/* perform_user_POLLOUT */	NULL,
+	/* callback_on_writable */	NULL,
+	/* tx_credit */			NULL,
+	/* write_role_protocol */	NULL,
+	/* encapsulation_parent */	NULL,
+	/* alpn_negotiated */		NULL,
+	/* close_via_role_protocol */	NULL,
+	/* close_role */		NULL,
+	/* close_kill_connection */	NULL,
+	/* destroy_role */		NULL,
+	/* writeable cb clnt, srv */	{ LWS_CALLBACK_RAW_WRITEABLE, 0 },
+	/* close cb clnt, srv */	{ LWS_CALLBACK_RAW_CLOSE, 0 },
+	/* file_handle */		0,
+};
+
+
+
+struct lws_role_ops role_ops_raw_file = {
+	/* role name */			"raw-file",
+	/* alpn id */			NULL,
+	/* check_upgrades */		NULL,
+	/* init_context */		NULL,
+	/* init_vhost */		NULL,
+	/* destroy_vhost */		NULL,
+	/* periodic_checks */		NULL,
+	/* service_flag_pending */	NULL,
+	/* handle_POLLIN */		rops_handle_POLLIN_raw_file,
+	/* handle_POLLOUT */		NULL,
+	/* perform_user_POLLOUT */	NULL,
+	/* callback_on_writable */	NULL,
+	/* tx_credit */			NULL,
+	/* write_role_protocol */	NULL,
+	/* encapsulation_parent */	NULL,
+	/* alpn_negotiated */		NULL,
+	/* close_via_role_protocol */	NULL,
+	/* close_role */		NULL,
+	/* close_kill_connection */	NULL,
+	/* destroy_role */		NULL,
+	/* writeable cb clnt, srv */	{ LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 },
+	/* close cb clnt, srv */	{ LWS_CALLBACK_RAW_CLOSE_FILE, 0 },
+	/* file_handle */		1,
+};

+ 187 - 175
thirdparty/libwebsockets/client/client-parser.c → thirdparty/libwebsockets/roles/ws/client-parser-ws.c

@@ -19,31 +19,38 @@
  *  MA  02110-1301  USA
  *  MA  02110-1301  USA
  */
  */
 
 
-#include "private-libwebsockets.h"
+#include "core/private.h"
 
 
 /*
 /*
- * parsers.c: lws_rx_sm() needs to be roughly kept in
+ * parsers.c: lws_ws_rx_sm() needs to be roughly kept in
  *   sync with changes here, esp related to ext draining
  *   sync with changes here, esp related to ext draining
  */
  */
 
 
-int lws_client_rx_sm(struct lws *wsi, unsigned char c)
+int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
 {
 {
 	int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
 	int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
-	int handled, n, m, rx_draining_ext = 0;
+	int handled, m;
 	unsigned short close_code;
 	unsigned short close_code;
-	struct lws_tokens eff_buf;
+	struct lws_tokens ebuf;
 	unsigned char *pp;
 	unsigned char *pp;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	int rx_draining_ext = 0, n;
+#endif
+
+	ebuf.token = NULL;
+	ebuf.len = 0;
 
 
-	if (wsi->u.ws.rx_draining_ext) {
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	if (wsi->ws->rx_draining_ext) {
 		assert(!c);
 		assert(!c);
-		eff_buf.token = NULL;
-		eff_buf.token_len = 0;
+
 		lws_remove_wsi_from_draining_ext_list(wsi);
 		lws_remove_wsi_from_draining_ext_list(wsi);
 		rx_draining_ext = 1;
 		rx_draining_ext = 1;
 		lwsl_debug("%s: doing draining flow\n", __func__);
 		lwsl_debug("%s: doing draining flow\n", __func__);
 
 
 		goto drain_extension;
 		goto drain_extension;
 	}
 	}
+#endif
 
 
 	if (wsi->socket_is_permanently_unusable)
 	if (wsi->socket_is_permanently_unusable)
 		return -1;
 		return -1;
@@ -51,35 +58,38 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
 	switch (wsi->lws_rx_parse_state) {
 	switch (wsi->lws_rx_parse_state) {
 	case LWS_RXPS_NEW:
 	case LWS_RXPS_NEW:
 		/* control frames (PING) may interrupt checkable sequences */
 		/* control frames (PING) may interrupt checkable sequences */
-		wsi->u.ws.defeat_check_utf8 = 0;
+		wsi->ws->defeat_check_utf8 = 0;
 
 
-		switch (wsi->ietf_spec_revision) {
+		switch (wsi->ws->ietf_spec_revision) {
 		case 13:
 		case 13:
-			wsi->u.ws.opcode = c & 0xf;
+			wsi->ws->opcode = c & 0xf;
 			/* revisit if an extension wants them... */
 			/* revisit if an extension wants them... */
-			switch (wsi->u.ws.opcode) {
+			switch (wsi->ws->opcode) {
 			case LWSWSOPC_TEXT_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->ws->rsv_first_msg = (c & 0x70);
+				wsi->ws->continuation_possible = 1;
+				wsi->ws->check_utf8 = lws_check_opt(
 					wsi->context->options,
 					wsi->context->options,
 					LWS_SERVER_OPTION_VALIDATE_UTF8);
 					LWS_SERVER_OPTION_VALIDATE_UTF8);
-				wsi->u.ws.utf8 = 0;
+				wsi->ws->utf8 = 0;
+				wsi->ws->first_fragment = 1;
 				break;
 				break;
 			case LWSWSOPC_BINARY_FRAME:
 			case LWSWSOPC_BINARY_FRAME:
-				wsi->u.ws.rsv_first_msg = (c & 0x70);
-				wsi->u.ws.check_utf8 = 0;
-				wsi->u.ws.continuation_possible = 1;
+				wsi->ws->rsv_first_msg = (c & 0x70);
+				wsi->ws->check_utf8 = 0;
+				wsi->ws->continuation_possible = 1;
+				wsi->ws->first_fragment = 1;
 				break;
 				break;
 			case LWSWSOPC_CONTINUATION:
 			case LWSWSOPC_CONTINUATION:
-				if (!wsi->u.ws.continuation_possible) {
+				if (!wsi->ws->continuation_possible) {
 					lwsl_info("disordered continuation\n");
 					lwsl_info("disordered continuation\n");
 					return -1;
 					return -1;
 				}
 				}
+				wsi->ws->first_fragment = 0;
 				break;
 				break;
 			case LWSWSOPC_CLOSE:
 			case LWSWSOPC_CLOSE:
-				wsi->u.ws.check_utf8 = 0;
-				wsi->u.ws.utf8 = 0;
+				wsi->ws->check_utf8 = 0;
+				wsi->ws->utf8 = 0;
 				break;
 				break;
 			case 3:
 			case 3:
 			case 4:
 			case 4:
@@ -94,45 +104,45 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
 				lwsl_info("illegal opcode\n");
 				lwsl_info("illegal opcode\n");
 				return -1;
 				return -1;
 			default:
 			default:
-				wsi->u.ws.defeat_check_utf8 = 1;
+				wsi->ws->defeat_check_utf8 = 1;
 				break;
 				break;
 			}
 			}
-			wsi->u.ws.rsv = (c & 0x70);
+			wsi->ws->rsv = (c & 0x70);
 			/* revisit if an extension wants them... */
 			/* revisit if an extension wants them... */
 			if (
 			if (
-#ifndef LWS_NO_EXTENSIONS
-				!wsi->count_act_ext &&
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+				!wsi->ws->count_act_ext &&
 #endif
 #endif
-				wsi->u.ws.rsv) {
+				wsi->ws->rsv) {
 				lwsl_info("illegal rsv bits set\n");
 				lwsl_info("illegal rsv bits set\n");
 				return -1;
 				return -1;
 			}
 			}
-			wsi->u.ws.final = !!((c >> 7) & 1);
+			wsi->ws->final = !!((c >> 7) & 1);
 			lwsl_ext("%s:    This RX frame Final %d\n", __func__,
 			lwsl_ext("%s:    This RX frame Final %d\n", __func__,
-				 wsi->u.ws.final);
+				 wsi->ws->final);
 
 
-			if (wsi->u.ws.owed_a_fin &&
-			    (wsi->u.ws.opcode == LWSWSOPC_TEXT_FRAME ||
-			     wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME)) {
+			if (wsi->ws->owed_a_fin &&
+			    (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
+			     wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
 				lwsl_info("hey you owed us a FIN\n");
 				lwsl_info("hey you owed us a FIN\n");
 				return -1;
 				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->ws->opcode & 8)) && wsi->ws->final) {
+				wsi->ws->continuation_possible = 0;
+				wsi->ws->owed_a_fin = 0;
 			}
 			}
 
 
-			if ((wsi->u.ws.opcode & 8) && !wsi->u.ws.final) {
+			if ((wsi->ws->opcode & 8) && !wsi->ws->final) {
 				lwsl_info("control msg can't be fragmented\n");
 				lwsl_info("control msg can't be fragmented\n");
 				return -1;
 				return -1;
 			}
 			}
-			if (!wsi->u.ws.final)
-				wsi->u.ws.owed_a_fin = 1;
+			if (!wsi->ws->final)
+				wsi->ws->owed_a_fin = 1;
 
 
-			switch (wsi->u.ws.opcode) {
+			switch (wsi->ws->opcode) {
 			case LWSWSOPC_TEXT_FRAME:
 			case LWSWSOPC_TEXT_FRAME:
 			case LWSWSOPC_BINARY_FRAME:
 			case LWSWSOPC_BINARY_FRAME:
-				wsi->u.ws.frame_is_binary = wsi->u.ws.opcode ==
+				wsi->ws->frame_is_binary = wsi->ws->opcode ==
 						 LWSWSOPC_BINARY_FRAME;
 						 LWSWSOPC_BINARY_FRAME;
 				break;
 				break;
 			}
 			}
@@ -141,38 +151,38 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
 
 
 		default:
 		default:
 			lwsl_err("unknown spec version %02d\n",
 			lwsl_err("unknown spec version %02d\n",
-				 wsi->ietf_spec_revision);
+				 wsi->ws->ietf_spec_revision);
 			break;
 			break;
 		}
 		}
 		break;
 		break;
 
 
 	case LWS_RXPS_04_FRAME_HDR_LEN:
 	case LWS_RXPS_04_FRAME_HDR_LEN:
 
 
-		wsi->u.ws.this_frame_masked = !!(c & 0x80);
+		wsi->ws->this_frame_masked = !!(c & 0x80);
 
 
 		switch (c & 0x7f) {
 		switch (c & 0x7f) {
 		case 126:
 		case 126:
 			/* control frames are not allowed to have big lengths */
 			/* control frames are not allowed to have big lengths */
-			if (wsi->u.ws.opcode & 8)
+			if (wsi->ws->opcode & 8)
 				goto illegal_ctl_length;
 				goto illegal_ctl_length;
 			wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
 			wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
 			break;
 			break;
 		case 127:
 		case 127:
 			/* control frames are not allowed to have big lengths */
 			/* control frames are not allowed to have big lengths */
-			if (wsi->u.ws.opcode & 8)
+			if (wsi->ws->opcode & 8)
 				goto illegal_ctl_length;
 				goto illegal_ctl_length;
 			wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
 			wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
 			break;
 			break;
 		default:
 		default:
-			wsi->u.ws.rx_packet_length = c;
-			if (wsi->u.ws.this_frame_masked)
+			wsi->ws->rx_packet_length = c & 0x7f;
+			if (wsi->ws->this_frame_masked)
 				wsi->lws_rx_parse_state =
 				wsi->lws_rx_parse_state =
 						LWS_RXPS_07_COLLECT_FRAME_KEY_1;
 						LWS_RXPS_07_COLLECT_FRAME_KEY_1;
 			else {
 			else {
-				if (c)
+				if (wsi->ws->rx_packet_length) {
 					wsi->lws_rx_parse_state =
 					wsi->lws_rx_parse_state =
-					LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
-				else {
+					LWS_RXPS_WS_FRAME_PAYLOAD;
+				} else {
 					wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 					wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 					goto spill;
 					goto spill;
 				}
 				}
@@ -182,18 +192,18 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
 		break;
 		break;
 
 
 	case LWS_RXPS_04_FRAME_HDR_LEN16_2:
 	case LWS_RXPS_04_FRAME_HDR_LEN16_2:
-		wsi->u.ws.rx_packet_length = c << 8;
+		wsi->ws->rx_packet_length = c << 8;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
 		break;
 		break;
 
 
 	case LWS_RXPS_04_FRAME_HDR_LEN16_1:
 	case LWS_RXPS_04_FRAME_HDR_LEN16_1:
-		wsi->u.ws.rx_packet_length |= c;
-		if (wsi->u.ws.this_frame_masked)
+		wsi->ws->rx_packet_length |= c;
+		if (wsi->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 {
 		else {
-			if (wsi->u.ws.rx_packet_length)
+			if (wsi->ws->rx_packet_length)
 				wsi->lws_rx_parse_state =
 				wsi->lws_rx_parse_state =
-					LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
+					LWS_RXPS_WS_FRAME_PAYLOAD;
 			else {
 			else {
 				wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 				wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 				goto spill;
 				goto spill;
@@ -208,58 +218,58 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
 			return -1;
 			return -1;
 		}
 		}
 #if defined __LP64__
 #if defined __LP64__
-		wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
+		wsi->ws->rx_packet_length = ((size_t)c) << 56;
 #else
 #else
-		wsi->u.ws.rx_packet_length = 0;
+		wsi->ws->rx_packet_length = 0;
 #endif
 #endif
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
 		break;
 		break;
 
 
 	case LWS_RXPS_04_FRAME_HDR_LEN64_7:
 	case LWS_RXPS_04_FRAME_HDR_LEN64_7:
 #if defined __LP64__
 #if defined __LP64__
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
+		wsi->ws->rx_packet_length |= ((size_t)c) << 48;
 #endif
 #endif
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
 		break;
 		break;
 
 
 	case LWS_RXPS_04_FRAME_HDR_LEN64_6:
 	case LWS_RXPS_04_FRAME_HDR_LEN64_6:
 #if defined __LP64__
 #if defined __LP64__
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
+		wsi->ws->rx_packet_length |= ((size_t)c) << 40;
 #endif
 #endif
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
 		break;
 		break;
 
 
 	case LWS_RXPS_04_FRAME_HDR_LEN64_5:
 	case LWS_RXPS_04_FRAME_HDR_LEN64_5:
 #if defined __LP64__
 #if defined __LP64__
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
+		wsi->ws->rx_packet_length |= ((size_t)c) << 32;
 #endif
 #endif
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
 		break;
 		break;
 
 
 	case LWS_RXPS_04_FRAME_HDR_LEN64_4:
 	case LWS_RXPS_04_FRAME_HDR_LEN64_4:
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
+		wsi->ws->rx_packet_length |= ((size_t)c) << 24;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
 		break;
 		break;
 
 
 	case LWS_RXPS_04_FRAME_HDR_LEN64_3:
 	case LWS_RXPS_04_FRAME_HDR_LEN64_3:
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
+		wsi->ws->rx_packet_length |= ((size_t)c) << 16;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
 		break;
 		break;
 
 
 	case LWS_RXPS_04_FRAME_HDR_LEN64_2:
 	case LWS_RXPS_04_FRAME_HDR_LEN64_2:
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
+		wsi->ws->rx_packet_length |= ((size_t)c) << 8;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
 		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
 		break;
 		break;
 
 
 	case LWS_RXPS_04_FRAME_HDR_LEN64_1:
 	case LWS_RXPS_04_FRAME_HDR_LEN64_1:
-		wsi->u.ws.rx_packet_length |= (size_t)c;
-		if (wsi->u.ws.this_frame_masked)
+		wsi->ws->rx_packet_length |= (size_t)c;
+		if (wsi->ws->this_frame_masked)
 			wsi->lws_rx_parse_state =
 			wsi->lws_rx_parse_state =
 					LWS_RXPS_07_COLLECT_FRAME_KEY_1;
 					LWS_RXPS_07_COLLECT_FRAME_KEY_1;
 		else {
 		else {
-			if (wsi->u.ws.rx_packet_length)
+			if (wsi->ws->rx_packet_length)
 				wsi->lws_rx_parse_state =
 				wsi->lws_rx_parse_state =
-					LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
+					LWS_RXPS_WS_FRAME_PAYLOAD;
 			else {
 			else {
 				wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 				wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 				goto spill;
 				goto spill;
@@ -268,53 +278,53 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
 		break;
 		break;
 
 
 	case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
 	case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
-		wsi->u.ws.mask[0] = c;
+		wsi->ws->mask[0] = c;
 		if (c)
 		if (c)
-			wsi->u.ws.all_zero_nonce = 0;
+			wsi->ws->all_zero_nonce = 0;
 		wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
 		wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
 		break;
 		break;
 
 
 	case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
 	case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
-		wsi->u.ws.mask[1] = c;
+		wsi->ws->mask[1] = c;
 		if (c)
 		if (c)
-			wsi->u.ws.all_zero_nonce = 0;
+			wsi->ws->all_zero_nonce = 0;
 		wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
 		wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
 		break;
 		break;
 
 
 	case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
 	case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
-		wsi->u.ws.mask[2] = c;
+		wsi->ws->mask[2] = c;
 		if (c)
 		if (c)
-			wsi->u.ws.all_zero_nonce = 0;
+			wsi->ws->all_zero_nonce = 0;
 		wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
 		wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
 		break;
 		break;
 
 
 	case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
 	case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
-		wsi->u.ws.mask[3] = c;
+		wsi->ws->mask[3] = c;
 		if (c)
 		if (c)
-			wsi->u.ws.all_zero_nonce = 0;
+			wsi->ws->all_zero_nonce = 0;
 
 
-		if (wsi->u.ws.rx_packet_length)
+		if (wsi->ws->rx_packet_length)
 			wsi->lws_rx_parse_state =
 			wsi->lws_rx_parse_state =
-					LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
+					LWS_RXPS_WS_FRAME_PAYLOAD;
 		else {
 		else {
 			wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 			wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 			goto spill;
 			goto spill;
 		}
 		}
 		break;
 		break;
 
 
-	case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
-
-		assert(wsi->u.ws.rx_ubuf);
+	case LWS_RXPS_WS_FRAME_PAYLOAD:
 
 
-		if (wsi->u.ws.rx_draining_ext)
+		assert(wsi->ws->rx_ubuf);
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		if (wsi->ws->rx_draining_ext)
 			goto drain_extension;
 			goto drain_extension;
+#endif
+		if (wsi->ws->this_frame_masked && !wsi->ws->all_zero_nonce)
+			c ^= wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
 
 
-		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;
+		wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] = c;
 
 
-		if (--wsi->u.ws.rx_packet_length == 0) {
+		if (--wsi->ws->rx_packet_length == 0) {
 			/* spill because we have the whole frame */
 			/* spill because we have the whole frame */
 			wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 			wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 			goto spill;
 			goto spill;
@@ -325,11 +335,11 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
 		 * supposed to default to context->pt_serv_buf_size
 		 * supposed to default to context->pt_serv_buf_size
 		 */
 		 */
 		if (!wsi->protocol->rx_buffer_size &&
 		if (!wsi->protocol->rx_buffer_size &&
-		    wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size)
+		    wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size)
 			break;
 			break;
 
 
 		if (wsi->protocol->rx_buffer_size &&
 		if (wsi->protocol->rx_buffer_size &&
-		    wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size)
+		    wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size)
 			break;
 			break;
 
 
 		/* spill because we filled our rx buffer */
 		/* spill because we filled our rx buffer */
@@ -342,18 +352,18 @@ spill:
 		 * layer?  If so service it and hide it from the user callback
 		 * layer?  If so service it and hide it from the user callback
 		 */
 		 */
 
 
-		switch (wsi->u.ws.opcode) {
+		switch (wsi->ws->opcode) {
 		case LWSWSOPC_CLOSE:
 		case LWSWSOPC_CLOSE:
-			pp = (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE];
+			pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE];
 			if (lws_check_opt(wsi->context->options,
 			if (lws_check_opt(wsi->context->options,
 					  LWS_SERVER_OPTION_VALIDATE_UTF8) &&
 					  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))
+			    wsi->ws->rx_ubuf_head > 2 &&
+			    lws_check_utf8(&wsi->ws->utf8, pp + 2,
+					   wsi->ws->rx_ubuf_head - 2))
 				goto utf8_fail;
 				goto utf8_fail;
 
 
-			/* is this an acknowledgement of our close? */
-			if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
+			/* is this an acknowledgment of our close? */
+			if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
 				/*
 				/*
 				 * fine he has told us he is closing too, let's
 				 * fine he has told us he is closing too, let's
 				 * finish our close
 				 * finish our close
@@ -363,8 +373,8 @@ spill:
 			}
 			}
 
 
 			lwsl_parser("client sees server close len = %d\n",
 			lwsl_parser("client sees server close len = %d\n",
-						 wsi->u.ws.rx_ubuf_head);
-			if (wsi->u.ws.rx_ubuf_head >= 2) {
+						 wsi->ws->rx_ubuf_head);
+			if (wsi->ws->rx_ubuf_head >= 2) {
 				close_code = (pp[0] << 8) | pp[1];
 				close_code = (pp[0] << 8) | pp[1];
 				if (close_code < 1000 ||
 				if (close_code < 1000 ||
 				    close_code == 1004 ||
 				    close_code == 1004 ||
@@ -384,39 +394,32 @@ spill:
 					wsi->protocol->callback, wsi,
 					wsi->protocol->callback, wsi,
 					LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
 					LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
 					wsi->user_space, pp,
 					wsi->user_space, pp,
-					wsi->u.ws.rx_ubuf_head))
+					wsi->ws->rx_ubuf_head))
 				return -1;
 				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;
+			memcpy(wsi->ws->ping_payload_buf + LWS_PRE, pp,
+			       wsi->ws->rx_ubuf_head);
+			wsi->ws->close_in_ping_buffer_len = wsi->ws->rx_ubuf_head;
+
+			lwsl_info("%s: scheduling return close as ack\n", __func__);
+			__lws_change_pollfd(wsi, LWS_POLLIN, 0);
+			lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 3);
+			wsi->waiting_to_send_close_frame = 1;
+			wsi->close_needs_ack = 0;
+			lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
+			lws_callback_on_writable(wsi);
+			handled = 1;
+			break;
 
 
 		case LWSWSOPC_PING:
 		case LWSWSOPC_PING:
 			lwsl_info("received %d byte ping, sending pong\n",
 			lwsl_info("received %d byte ping, sending pong\n",
-				  wsi->u.ws.rx_ubuf_head);
+				  wsi->ws->rx_ubuf_head);
 
 
 			/* he set a close reason on this guy, ignore PING */
 			/* he set a close reason on this guy, ignore PING */
-			if (wsi->u.ws.close_in_ping_buffer_len)
+			if (wsi->ws->close_in_ping_buffer_len)
 				goto ping_drop;
 				goto ping_drop;
 
 
-			if (wsi->u.ws.ping_pending_flag) {
+			if (wsi->ws->ping_pending_flag) {
 				/*
 				/*
 				 * there is already a pending ping payload
 				 * there is already a pending ping payload
 				 * we should just log and drop
 				 * we should just log and drop
@@ -426,30 +429,30 @@ spill:
 			}
 			}
 
 
 			/* control packets can only be < 128 bytes long */
 			/* control packets can only be < 128 bytes long */
-			if (wsi->u.ws.rx_ubuf_head > 128 - 3) {
+			if (wsi->ws->rx_ubuf_head > 128 - 3) {
 				lwsl_parser("DROP PING payload too large\n");
 				lwsl_parser("DROP PING payload too large\n");
 				goto ping_drop;
 				goto ping_drop;
 			}
 			}
 
 
 			/* stash the pong payload */
 			/* stash the pong payload */
-			memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE,
-			       &wsi->u.ws.rx_ubuf[LWS_PRE],
-				wsi->u.ws.rx_ubuf_head);
+			memcpy(wsi->ws->ping_payload_buf + LWS_PRE,
+			       &wsi->ws->rx_ubuf[LWS_PRE],
+				wsi->ws->rx_ubuf_head);
 
 
-			wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head;
-			wsi->u.ws.ping_pending_flag = 1;
+			wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head;
+			wsi->ws->ping_pending_flag = 1;
 
 
 			/* get it sent as soon as possible */
 			/* get it sent as soon as possible */
 			lws_callback_on_writable(wsi);
 			lws_callback_on_writable(wsi);
 ping_drop:
 ping_drop:
-			wsi->u.ws.rx_ubuf_head = 0;
+			wsi->ws->rx_ubuf_head = 0;
 			handled = 1;
 			handled = 1;
 			break;
 			break;
 
 
 		case LWSWSOPC_PONG:
 		case LWSWSOPC_PONG:
 			lwsl_info("client receied pong\n");
 			lwsl_info("client receied pong\n");
-			lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE],
-				     wsi->u.ws.rx_ubuf_head);
+			lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
+				     wsi->ws->rx_ubuf_head);
 
 
 			if (wsi->pending_timeout ==
 			if (wsi->pending_timeout ==
 				       PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
 				       PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
@@ -467,30 +470,11 @@ ping_drop:
 			break;
 			break;
 
 
 		default:
 		default:
+			/* not handled or failed */
+			lwsl_ext("Unhandled ext opc 0x%x\n", wsi->ws->opcode);
+			wsi->ws->rx_ubuf_head = 0;
 
 
-			lwsl_parser("Reserved opc 0x%2X\n", wsi->u.ws.opcode);
-
-			/*
-			 * It's something special we can't understand here.
-			 * Pass the payload up to the extension's parsing
-			 * state machine.
-			 */
-
-			eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
-			eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
-
-			if (lws_ext_cb_active(wsi,
-				LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
-					&eff_buf, 0) <= 0) {
-				/* not handled or failed */
-				lwsl_ext("Unhandled ext opc 0x%x\n",
-					 wsi->u.ws.opcode);
-				wsi->u.ws.rx_ubuf_head = 0;
-
-				return 0;
-			}
-			handled = 1;
-			break;
+			return -1;
 		}
 		}
 
 
 		/*
 		/*
@@ -501,53 +485,71 @@ ping_drop:
 		if (handled)
 		if (handled)
 			goto already_done;
 			goto already_done;
 
 
-		eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
-		eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
+		ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
+		ebuf.len = wsi->ws->rx_ubuf_head;
 
 
-		if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len)
+		if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len)
 			goto already_done;
 			goto already_done;
 
 
+#if !defined(LWS_WITHOUT_EXTENSIONS)
 drain_extension:
 drain_extension:
-		lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len);
+		lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len);
 
 
-		n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
+		n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
 		lwsl_ext("Ext RX returned %d\n", n);
 		lwsl_ext("Ext RX returned %d\n", n);
 		if (n < 0) {
 		if (n < 0) {
 			wsi->socket_is_permanently_unusable = 1;
 			wsi->socket_is_permanently_unusable = 1;
 			return -1;
 			return -1;
 		}
 		}
+#endif
+		lwsl_debug("post inflate ebuf len %d\n", ebuf.len);
 
 
-		lwsl_ext("post inflate eff_buf len %d\n", eff_buf.token_len);
-
-		if (rx_draining_ext && !eff_buf.token_len) {
+		if (
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		    rx_draining_ext &&
+#endif
+		    !ebuf.len) {
 			lwsl_debug("   --- ending drain on 0 read result\n");
 			lwsl_debug("   --- ending drain on 0 read result\n");
 			goto already_done;
 			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))
+		if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
+			if (lws_check_utf8(&wsi->ws->utf8,
+					   (unsigned char *)ebuf.token,
+					   ebuf.len)) {
+				lws_close_reason(wsi,
+					LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+					(uint8_t *)"bad utf8", 8);
 				goto utf8_fail;
 				goto utf8_fail;
+			}
 
 
 			/* we are ending partway through utf-8 character? */
 			/* we are ending partway through utf-8 character? */
-			if (!wsi->u.ws.rx_packet_length && wsi->u.ws.final &&
-			    wsi->u.ws.utf8 && !n) {
+			if (!wsi->ws->rx_packet_length && wsi->ws->final &&
+			    wsi->ws->utf8
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+			    && !n
+#endif
+			    ) {
 				lwsl_info("FINAL utf8 error\n");
 				lwsl_info("FINAL utf8 error\n");
+				lws_close_reason(wsi,
+					LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+					(uint8_t *)"partial utf8", 12);
 utf8_fail:
 utf8_fail:
 				lwsl_info("utf8 error\n");
 				lwsl_info("utf8 error\n");
+				lwsl_hexdump_info(ebuf.token, ebuf.len);
+
 				return -1;
 				return -1;
 			}
 			}
 		}
 		}
 
 
-		if (eff_buf.token_len < 0 &&
+		if (ebuf.len < 0 &&
 		    callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
 		    callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
 			goto already_done;
 			goto already_done;
 
 
-		if (!eff_buf.token)
+		if (!ebuf.token)
 			goto already_done;
 			goto already_done;
 
 
-		eff_buf.token[eff_buf.token_len] = '\0';
+		ebuf.token[ebuf.len] = '\0';
 
 
 		if (!wsi->protocol->callback)
 		if (!wsi->protocol->callback)
 			goto already_done;
 			goto already_done;
@@ -555,7 +557,12 @@ utf8_fail:
 		if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
 		if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
 			lwsl_info("Client doing pong callback\n");
 			lwsl_info("Client doing pong callback\n");
 
 
-		if (n && eff_buf.token_len)
+		if (
+				/* coverity says dead code otherwise */
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+				n &&
+#endif
+				ebuf.len)
 			/* extension had more... main loop will come back
 			/* extension had more... main loop will come back
 			 * we want callback to be done with this set, if so,
 			 * we want callback to be done with this set, if so,
 			 * because lws_is_final() hides it was final until the
 			 * because lws_is_final() hides it was final until the
@@ -565,21 +572,26 @@ utf8_fail:
 		else
 		else
 			lws_remove_wsi_from_draining_ext_list(wsi);
 			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)
+		if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
+		    lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
+		    lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
 			goto already_done;
 			goto already_done;
 
 
 		m = wsi->protocol->callback(wsi,
 		m = wsi->protocol->callback(wsi,
 			(enum lws_callback_reasons)callback_action,
 			(enum lws_callback_reasons)callback_action,
-			wsi->user_space, eff_buf.token, eff_buf.token_len);
+			wsi->user_space, ebuf.token, ebuf.len);
+
+		wsi->ws->first_fragment = 0;
+
+		// lwsl_notice("%s: bulk ws rx: input used %d, output %d\n",
+		//	__func__, wsi->ws->rx_ubuf_head, ebuf.len);
 
 
 		/* if user code wants to close, let caller know */
 		/* if user code wants to close, let caller know */
 		if (m)
 		if (m)
 			return 1;
 			return 1;
 
 
 already_done:
 already_done:
-		wsi->u.ws.rx_ubuf_head = 0;
+		wsi->ws->rx_ubuf_head = 0;
 		break;
 		break;
 	default:
 	default:
 		lwsl_err("client rx illegal state\n");
 		lwsl_err("client rx illegal state\n");

+ 629 - 0
thirdparty/libwebsockets/roles/ws/client-ws.c

@@ -0,0 +1,629 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include <core/private.h>
+
+/*
+ * In-place str to lower case
+ */
+
+static void
+strtolower(char *s)
+{
+	while (*s) {
+#ifdef LWS_PLAT_OPTEE
+		int tolower_optee(int c);
+		*s = tolower_optee((int)*s);
+#else
+		*s = tolower((int)*s);
+#endif
+		s++;
+	}
+}
+
+int
+lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi)
+{
+	int v = SPEC_LATEST_SUPPORTED;
+
+	/* allocate the ws struct for the wsi */
+	wsi->ws = lws_zalloc(sizeof(*wsi->ws), "client ws struct");
+	if (!wsi->ws) {
+		lwsl_notice("OOM\n");
+		return 1;
+	}
+
+	/* -1 means just use latest supported */
+	if (i->ietf_version_or_minus_one != -1 &&
+	    i->ietf_version_or_minus_one)
+		v = i->ietf_version_or_minus_one;
+
+	wsi->ws->ietf_spec_revision = v;
+
+	return 0;
+}
+
+#if !defined(LWS_NO_CLIENT)
+int
+lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
+{
+	if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
+	    (lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
+	    (lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
+	    !lwsi_role_client(wsi))
+		return 0;
+
+	// lwsl_notice("%s: hs client gets %d in\n", __func__, (int)len);
+
+	while (len) {
+		/*
+		 * we were accepting input but now we stopped doing so
+		 */
+		if (lws_is_flowcontrolled(wsi)) {
+			//lwsl_notice("%s: caching %ld\n", __func__, (long)len);
+			lws_rxflow_cache(wsi, *buf, 0, (int)len);
+			*buf += len;
+			return 0;
+		}
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		if (wsi->ws->rx_draining_ext) {
+			int m;
+
+			//lwsl_notice("%s: draining ext\n", __func__);
+			if (lwsi_role_client(wsi))
+				m = lws_ws_client_rx_sm(wsi, 0);
+			else
+				m = lws_ws_rx_sm(wsi, 0, 0);
+			if (m < 0)
+				return -1;
+			continue;
+		}
+#endif
+		/* caller will account for buflist usage */
+
+		if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
+			lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n",
+				    __func__, (int)len);
+			return -1;
+		}
+		len--;
+	}
+	// lwsl_notice("%s: finished with %ld\n", __func__, (long)len);
+
+	return 0;
+}
+#endif
+
+char *
+lws_generate_client_ws_handshake(struct lws *wsi, char *p)
+{
+	char buf[128], hash[20], key_b64[40];
+	int n;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	const struct lws_extension *ext;
+	int ext_count = 0;
+#endif
+
+	/*
+	 * create the random key
+	 */
+	n = lws_get_random(wsi->context, hash, 16);
+	if (n != 16) {
+		lwsl_err("Unable to read from random dev %s\n",
+			 SYSTEM_RANDOM_FILEPATH);
+		return NULL;
+	}
+
+	lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
+
+	p += sprintf(p, "Upgrade: websocket\x0d\x0a"
+			"Connection: Upgrade\x0d\x0a"
+			"Sec-WebSocket-Key: ");
+	strcpy(p, key_b64);
+	p += strlen(key_b64);
+	p += sprintf(p, "\x0d\x0a");
+	if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
+		p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
+		     lws_hdr_simple_ptr(wsi,
+				     _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));
+
+	/* tell the server what extensions we could support */
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	ext = wsi->vhost->ws.extensions;
+	while (ext && ext->callback) {
+
+		n = wsi->vhost->protocols[0].callback(wsi,
+			LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
+				wsi->user_space, (char *)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) {
+			ext++;
+			continue;
+		}
+
+		/* apply it */
+
+		if (ext_count)
+			*p++ = ',';
+		else
+			p += sprintf(p, "Sec-WebSocket-Extensions: ");
+		p += sprintf(p, "%s", ext->client_offer);
+		ext_count++;
+
+		ext++;
+	}
+	if (ext_count)
+		p += sprintf(p, "\x0d\x0a");
+#endif
+
+	if (wsi->ws->ietf_spec_revision)
+		p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
+			     wsi->ws->ietf_spec_revision);
+
+	/* prepare the expected server accept response */
+
+	key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
+	n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
+			  key_b64);
+
+	lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);
+
+	lws_b64_encode_string(hash, 20,
+		  wsi->http.ah->initial_handshake_hash_base64,
+		  sizeof(wsi->http.ah->initial_handshake_hash_base64));
+
+	return p;
+}
+
+int
+lws_client_ws_upgrade(struct lws *wsi, const char **cce)
+{
+	int n, len, okay = 0;
+	struct lws_context *context = wsi->context;
+	const char *pc;
+	char *p;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+	char *sb = (char *)&pt->serv_buf[0];
+	const struct lws_ext_options *opts;
+	const struct lws_extension *ext;
+	char ext_name[128];
+	const char *c, *a;
+	char ignore;
+	int more = 1;
+#endif
+
+	if (wsi->client_h2_substream) {/* !!! client ws-over-h2 not there yet */
+		lwsl_warn("%s: client ws-over-h2 upgrade not supported yet\n",
+			  __func__);
+		*cce = "HS: h2 / ws upgrade unsupported";
+		goto bail3;
+	}
+
+	if (wsi->http.ah->http_response == 401) {
+		lwsl_warn(
+		       "lws_client_handshake: got bad HTTP response '%d'\n",
+		       wsi->http.ah->http_response);
+		*cce = "HS: ws upgrade unauthorized";
+		goto bail3;
+	}
+
+	if (wsi->http.ah->http_response != 101) {
+		lwsl_warn(
+		       "lws_client_handshake: got bad HTTP response '%d'\n",
+		       wsi->http.ah->http_response);
+		*cce = "HS: ws upgrade response not 101";
+		goto bail3;
+	}
+
+	if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
+		lwsl_info("no ACCEPT\n");
+		*cce = "HS: ACCEPT missing";
+		goto bail3;
+	}
+
+	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
+	if (!p) {
+		lwsl_info("no UPGRADE\n");
+		*cce = "HS: UPGRADE missing";
+		goto bail3;
+	}
+	strtolower(p);
+	if (strcmp(p, "websocket")) {
+		lwsl_warn(
+		      "lws_client_handshake: got bad Upgrade header '%s'\n", p);
+		*cce = "HS: Upgrade to something other than websocket";
+		goto bail3;
+	}
+
+	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION);
+	if (!p) {
+		lwsl_info("no Connection hdr\n");
+		*cce = "HS: CONNECTION missing";
+		goto bail3;
+	}
+	strtolower(p);
+	if (strcmp(p, "upgrade")) {
+		lwsl_warn("lws_client_int_s_hs: bad header %s\n", p);
+		*cce = "HS: UPGRADE malformed";
+		goto bail3;
+	}
+
+	pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
+	if (!pc) {
+		lwsl_parser("lws_client_int_s_hs: no protocol list\n");
+	} else
+		lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc);
+
+	/*
+	 * confirm the protocol the server wants to talk was in the list
+	 * of protocols we offered
+	 */
+
+	len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
+	if (!len) {
+		lwsl_info("%s: WSI_TOKEN_PROTOCOL is null\n", __func__);
+		/*
+		 * no protocol name to work from,
+		 * default to first protocol
+		 */
+		n = 0;
+		wsi->protocol = &wsi->vhost->protocols[0];
+		goto check_extensions;
+	}
+
+	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
+	len = (int)strlen(p);
+
+	while (pc && *pc && !okay) {
+		if (!strncmp(pc, p, len) &&
+		    (pc[len] == ',' || pc[len] == '\0')) {
+			okay = 1;
+			continue;
+		}
+		while (*pc && *pc++ != ',')
+			;
+		while (*pc && *pc == ' ')
+			pc++;
+	}
+
+	if (!okay) {
+		lwsl_info("%s: got bad protocol %s\n", __func__, p);
+		*cce = "HS: PROTOCOL malformed";
+		goto bail2;
+	}
+
+	/*
+	 * identify the selected protocol struct and set it
+	 */
+	n = 0;
+	/* keep client connection pre-bound protocol */
+	if (!lwsi_role_client(wsi))
+		wsi->protocol = NULL;
+
+	while (wsi->vhost->protocols[n].callback) {
+		if (!wsi->protocol &&
+		    strcmp(p, wsi->vhost->protocols[n].name) == 0) {
+			wsi->protocol = &wsi->vhost->protocols[n];
+			break;
+		}
+		n++;
+	}
+
+	if (!wsi->vhost->protocols[n].callback) { /* no match */
+		/* if server, that's already fatal */
+		if (!lwsi_role_client(wsi)) {
+			lwsl_info("%s: fail protocol %s\n", __func__, p);
+			*cce = "HS: Cannot match protocol";
+			goto bail2;
+		}
+
+		/* for client, find the index of our pre-bound protocol */
+
+		n = 0;
+		while (wsi->vhost->protocols[n].callback) {
+			if (wsi->protocol && strcmp(wsi->protocol->name,
+				   wsi->vhost->protocols[n].name) == 0) {
+				wsi->protocol = &wsi->vhost->protocols[n];
+				break;
+			}
+			n++;
+		}
+
+		if (!wsi->vhost->protocols[n].callback) {
+			if (wsi->protocol)
+				lwsl_err("Failed to match protocol %s\n",
+						wsi->protocol->name);
+			else
+				lwsl_err("No protocol on client\n");
+			goto bail2;
+		}
+	}
+
+	lwsl_debug("Selected protocol %s\n", wsi->protocol->name);
+
+check_extensions:
+	/*
+	 * stitch protocol choice into the vh protocol linked list
+	 * We always insert ourselves at the start of the list
+	 *
+	 * X <-> B
+	 * X <-> pAn <-> pB
+	 */
+
+	lws_vhost_lock(wsi->vhost);
+
+	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;
+	wsi->on_same_vh_list = 1;
+
+	lws_vhost_unlock(wsi->vhost);
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	/* instantiate the accepted extensions */
+
+	if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
+		lwsl_ext("no client extensions allowed by server\n");
+		goto check_accept;
+	}
+
+	/*
+	 * break down the list of server accepted extensions
+	 * and go through matching them or identifying bogons
+	 */
+
+	if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size,
+			 WSI_TOKEN_EXTENSIONS) < 0) {
+		lwsl_warn("ext list from server failed to copy\n");
+		*cce = "HS: EXT: list too big";
+		goto bail2;
+	}
+
+	c = sb;
+	n = 0;
+	ignore = 0;
+	a = NULL;
+	while (more) {
+
+		if (*c && (*c != ',' && *c != '\t')) {
+			if (*c == ';') {
+				ignore = 1;
+				if (!a)
+					a = c + 1;
+			}
+			if (ignore || *c == ' ') {
+				c++;
+				continue;
+			}
+
+			ext_name[n] = *c++;
+			if (n < (int)sizeof(ext_name) - 1)
+				n++;
+			continue;
+		}
+		ext_name[n] = '\0';
+		ignore = 0;
+		if (!*c)
+			more = 0;
+		else {
+			c++;
+			if (!n)
+				continue;
+		}
+
+		/* check we actually support it */
+
+		lwsl_notice("checking client ext %s\n", ext_name);
+
+		n = 0;
+		ext = wsi->vhost->ws.extensions;
+		while (ext && ext->callback) {
+			if (strcmp(ext_name, ext->name)) {
+				ext++;
+				continue;
+			}
+
+			n = 1;
+			lwsl_notice("instantiating client ext %s\n", ext_name);
+
+			/* instantiate the extension on this conn */
+
+			wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext;
+
+			/* allow him to construct his ext instance */
+
+			if (ext->callback(lws_get_context(wsi), ext, wsi,
+				   LWS_EXT_CB_CLIENT_CONSTRUCT,
+				   (void *)&wsi->ws->act_ext_user[wsi->ws->count_act_ext],
+				   (void *)&opts, 0)) {
+				lwsl_info(" ext %s failed construction\n",
+					  ext_name);
+				ext++;
+				continue;
+			}
+
+			/*
+			 * allow the user code to override ext defaults if it
+			 * wants to
+			 */
+			ext_name[0] = '\0';
+			if (user_callback_handle_rxflow(wsi->protocol->callback,
+					wsi, LWS_CALLBACK_WS_EXT_DEFAULTS,
+					(char *)ext->name, ext_name,
+					sizeof(ext_name))) {
+				*cce = "HS: EXT: failed setting defaults";
+				goto bail2;
+			}
+
+			if (ext_name[0] &&
+			    lws_ext_parse_options(ext, wsi, wsi->ws->act_ext_user[
+						  wsi->ws->count_act_ext], opts, ext_name,
+						  (int)strlen(ext_name))) {
+				lwsl_err("%s: unable to parse user defaults '%s'",
+					 __func__, ext_name);
+				*cce = "HS: EXT: failed parsing defaults";
+				goto bail2;
+			}
+
+			/*
+			 * give the extension the server options
+			 */
+			if (a && lws_ext_parse_options(ext, wsi,
+					wsi->ws->act_ext_user[wsi->ws->count_act_ext],
+					opts, a, lws_ptr_diff(c, a))) {
+				lwsl_err("%s: unable to parse remote def '%s'",
+					 __func__, a);
+				*cce = "HS: EXT: failed parsing options";
+				goto bail2;
+			}
+
+			if (ext->callback(lws_get_context(wsi), ext, wsi,
+					LWS_EXT_CB_OPTION_CONFIRM,
+				      wsi->ws->act_ext_user[wsi->ws->count_act_ext],
+				      NULL, 0)) {
+				lwsl_err("%s: ext %s rejects server options %s",
+					 __func__, ext->name, a);
+				*cce = "HS: EXT: Rejects server options";
+				goto bail2;
+			}
+
+			wsi->ws->count_act_ext++;
+
+			ext++;
+		}
+
+		if (n == 0) {
+			lwsl_warn("Unknown ext '%s'!\n", ext_name);
+			*cce = "HS: EXT: unknown ext";
+			goto bail2;
+		}
+
+		a = NULL;
+		n = 0;
+	}
+
+check_accept:
+#endif
+
+	/*
+	 * Confirm his accept token is the one we precomputed
+	 */
+
+	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT);
+	if (strcmp(p, wsi->http.ah->initial_handshake_hash_base64)) {
+		lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p,
+				  wsi->http.ah->initial_handshake_hash_base64);
+		*cce = "HS: Accept hash wrong";
+		goto bail2;
+	}
+
+	/* allocate the per-connection user memory (if any) */
+	if (lws_ensure_user_space(wsi)) {
+		lwsl_err("Problem allocating wsi user mem\n");
+		*cce = "HS: OOM";
+		goto bail2;
+	}
+
+	/*
+	 * we seem to be good to go, give client last chance to check
+	 * headers and OK it
+	 */
+	if (wsi->protocol->callback(wsi,
+				    LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
+				    wsi->user_space, NULL, 0)) {
+		*cce = "HS: Rejected by filter cb";
+		goto bail2;
+	}
+
+	/* clear his proxy connection timeout */
+	lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+	/* free up his parsing allocations */
+	lws_header_table_detach(wsi, 0);
+
+	lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED,
+			    &role_ops_ws);
+	lws_restart_ws_ping_pong_timer(wsi);
+
+	wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
+
+	/*
+	 * create the frame buffer for this connection according to the
+	 * size mentioned in the protocol definition.  If 0 there, then
+	 * use a big default for compatibility
+	 */
+	n = (int)wsi->protocol->rx_buffer_size;
+	if (!n)
+		n = context->pt_serv_buf_size;
+	n += LWS_PRE;
+	wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */,
+				"client frame buffer");
+	if (!wsi->ws->rx_ubuf) {
+		lwsl_err("Out of Mem allocating rx buffer %d\n", n);
+		*cce = "HS: OOM";
+		goto bail2;
+	}
+	wsi->ws->rx_ubuf_alloc = n;
+	lwsl_info("Allocating client RX buffer %d\n", n);
+
+#if !defined(LWS_WITH_ESP32)
+	if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
+		       (const char *)&n, sizeof n)) {
+		lwsl_warn("Failed to set SNDBUF to %d", n);
+		*cce = "HS: SO_SNDBUF failed";
+		goto bail3;
+	}
+#endif
+
+	lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);
+
+	/* call him back to inform him he is up */
+
+	if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED,
+				    wsi->user_space, NULL, 0)) {
+		*cce = "HS: Rejected at CLIENT_ESTABLISHED";
+		goto bail3;
+	}
+
+	return 0;
+
+bail3:
+	return 3;
+
+bail2:
+	return 2;
+}

+ 1992 - 0
thirdparty/libwebsockets/roles/ws/ops-ws.c

@@ -0,0 +1,1992 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include <core/private.h>
+
+#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
+
+/*
+ * client-parser.c: lws_ws_client_rx_sm() needs to be roughly kept in
+ *   sync with changes here, esp related to ext draining
+ */
+
+int
+lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)
+{
+	int callback_action = LWS_CALLBACK_RECEIVE;
+	int ret = 0;
+	unsigned short close_code;
+	struct lws_tokens ebuf;
+	unsigned char *pp;
+	int n = 0;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	int rx_draining_ext = 0;
+	int lin;
+#endif
+
+	ebuf.token = NULL;
+	ebuf.len = 0;
+	if (wsi->socket_is_permanently_unusable)
+		return -1;
+
+	switch (wsi->lws_rx_parse_state) {
+	case LWS_RXPS_NEW:
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		if (wsi->ws->rx_draining_ext) {
+			ebuf.token = NULL;
+			ebuf.len = 0;
+			lws_remove_wsi_from_draining_ext_list(wsi);
+			rx_draining_ext = 1;
+			lwsl_debug("%s: doing draining flow\n", __func__);
+
+			goto drain_extension;
+		}
+#endif
+		switch (wsi->ws->ietf_spec_revision) {
+		case 13:
+			/*
+			 * no prepended frame key any more
+			 */
+			wsi->ws->all_zero_nonce = 1;
+			goto handle_first;
+
+		default:
+			lwsl_warn("lws_ws_rx_sm: unknown spec version %d\n",
+				  wsi->ws->ietf_spec_revision);
+			break;
+		}
+		break;
+	case LWS_RXPS_04_mask_1:
+		wsi->ws->mask[1] = c;
+		if (c)
+			wsi->ws->all_zero_nonce = 0;
+		wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2;
+		break;
+	case LWS_RXPS_04_mask_2:
+		wsi->ws->mask[2] = c;
+		if (c)
+			wsi->ws->all_zero_nonce = 0;
+		wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3;
+		break;
+	case LWS_RXPS_04_mask_3:
+		wsi->ws->mask[3] = c;
+		if (c)
+			wsi->ws->all_zero_nonce = 0;
+
+		/*
+		 * start from the zero'th byte in the XOR key buffer since
+		 * this is the start of a frame with a new key
+		 */
+
+		wsi->ws->mask_idx = 0;
+
+		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1;
+		break;
+
+	/*
+	 *  04 logical framing from the spec (all this is masked when incoming
+	 *  and has to be unmasked)
+	 *
+	 * We ignore the possibility of extension data because we don't
+	 * negotiate any extensions at the moment.
+	 *
+	 *    0                   1                   2                   3
+	 *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+	 *   +-+-+-+-+-------+-+-------------+-------------------------------+
+	 *   |F|R|R|R| opcode|R| Payload len |    Extended payload length    |
+	 *   |I|S|S|S|  (4)  |S|     (7)     |             (16/63)           |
+	 *   |N|V|V|V|       |V|             |   (if payload len==126/127)   |
+	 *   | |1|2|3|       |4|             |                               |
+	 *   +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+	 *   |     Extended payload length continued, if payload len == 127  |
+	 *   + - - - - - - - - - - - - - - - +-------------------------------+
+	 *   |                               |         Extension data        |
+	 *   +-------------------------------+ - - - - - - - - - - - - - - - +
+	 *   :                                                               :
+	 *   +---------------------------------------------------------------+
+	 *   :                       Application data                        :
+	 *   +---------------------------------------------------------------+
+	 *
+	 *  We pass payload through to userland as soon as we get it, ignoring
+	 *  FIN.  It's up to userland to buffer it up if it wants to see a
+	 *  whole unfragmented block of the original size (which may be up to
+	 *  2^63 long!)
+	 */
+
+	case LWS_RXPS_04_FRAME_HDR_1:
+handle_first:
+
+		wsi->ws->opcode = c & 0xf;
+		wsi->ws->rsv = c & 0x70;
+		wsi->ws->final = !!((c >> 7) & 1);
+		wsi->ws->defeat_check_utf8 = 0;
+
+		if (((wsi->ws->opcode) & 8) && !wsi->ws->final) {
+			lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
+					(uint8_t *)"frag ctl", 8);
+			return -1;
+		}
+
+		switch (wsi->ws->opcode) {
+		case LWSWSOPC_TEXT_FRAME:
+			wsi->ws->check_utf8 = lws_check_opt(
+				wsi->context->options,
+				LWS_SERVER_OPTION_VALIDATE_UTF8);
+			/* fallthru */
+		case LWSWSOPC_BINARY_FRAME:
+			if (wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)
+				wsi->ws->check_utf8 = 0;
+			if (wsi->ws->continuation_possible) {
+				lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad cont", 8);
+				return -1;
+			}
+			wsi->ws->rsv_first_msg = (c & 0x70);
+			wsi->ws->frame_is_binary =
+			     wsi->ws->opcode == LWSWSOPC_BINARY_FRAME;
+			wsi->ws->first_fragment = 1;
+			wsi->ws->continuation_possible = !wsi->ws->final;
+			break;
+		case LWSWSOPC_CONTINUATION:
+			if (!wsi->ws->continuation_possible) {
+				lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad cont", 8);
+				return -1;
+			}
+			break;
+		case LWSWSOPC_CLOSE:
+			wsi->ws->check_utf8 = 0;
+			wsi->ws->utf8 = 0;
+			break;
+		case 3:
+		case 4:
+		case 5:
+		case 6:
+		case 7:
+		case 0xb:
+		case 0xc:
+		case 0xd:
+		case 0xe:
+		case 0xf:
+			lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad opc", 7);
+			lwsl_info("illegal opcode\n");
+			return -1;
+		}
+
+		if (wsi->ws->owed_a_fin &&
+		    (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
+		     wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
+			lwsl_info("hey you owed us a FIN\n");
+			lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad fin", 7);
+			return -1;
+		}
+		if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) {
+			wsi->ws->continuation_possible = 0;
+			wsi->ws->owed_a_fin = 0;
+		}
+
+		if (!wsi->ws->final)
+			wsi->ws->owed_a_fin = 1;
+
+		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
+		if (wsi->ws->rsv &&
+		    (
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+				    !wsi->ws->count_act_ext ||
+#endif
+				    (wsi->ws->rsv & ~0x40))) {
+			lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
+					 (uint8_t *)"rsv bits", 8);
+			return -1;
+		}
+		break;
+
+	case LWS_RXPS_04_FRAME_HDR_LEN:
+
+		wsi->ws->this_frame_masked = !!(c & 0x80);
+
+		switch (c & 0x7f) {
+		case 126:
+			/* control frames are not allowed to have big lengths */
+			if (wsi->ws->opcode & 8)
+				goto illegal_ctl_length;
+
+			wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
+			break;
+		case 127:
+			/* control frames are not allowed to have big lengths */
+			if (wsi->ws->opcode & 8)
+				goto illegal_ctl_length;
+
+			wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
+			break;
+		default:
+			wsi->ws->rx_packet_length = c & 0x7f;
+
+
+			if (wsi->ws->this_frame_masked)
+				wsi->lws_rx_parse_state =
+						LWS_RXPS_07_COLLECT_FRAME_KEY_1;
+			else
+				if (wsi->ws->rx_packet_length) {
+					wsi->lws_rx_parse_state =
+					LWS_RXPS_WS_FRAME_PAYLOAD;
+				} else {
+					wsi->lws_rx_parse_state = LWS_RXPS_NEW;
+					goto spill;
+				}
+			break;
+		}
+		break;
+
+	case LWS_RXPS_04_FRAME_HDR_LEN16_2:
+		wsi->ws->rx_packet_length = c << 8;
+		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
+		break;
+
+	case LWS_RXPS_04_FRAME_HDR_LEN16_1:
+		wsi->ws->rx_packet_length |= c;
+		if (wsi->ws->this_frame_masked)
+			wsi->lws_rx_parse_state =
+					LWS_RXPS_07_COLLECT_FRAME_KEY_1;
+		else {
+			wsi->lws_rx_parse_state =
+				LWS_RXPS_WS_FRAME_PAYLOAD;
+		}
+		break;
+
+	case LWS_RXPS_04_FRAME_HDR_LEN64_8:
+		if (c & 0x80) {
+			lwsl_warn("b63 of length must be zero\n");
+			/* kill the connection */
+			return -1;
+		}
+#if defined __LP64__
+		wsi->ws->rx_packet_length = ((size_t)c) << 56;
+#else
+		wsi->ws->rx_packet_length = 0;
+#endif
+		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
+		break;
+
+	case LWS_RXPS_04_FRAME_HDR_LEN64_7:
+#if defined __LP64__
+		wsi->ws->rx_packet_length |= ((size_t)c) << 48;
+#endif
+		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
+		break;
+
+	case LWS_RXPS_04_FRAME_HDR_LEN64_6:
+#if defined __LP64__
+		wsi->ws->rx_packet_length |= ((size_t)c) << 40;
+#endif
+		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
+		break;
+
+	case LWS_RXPS_04_FRAME_HDR_LEN64_5:
+#if defined __LP64__
+		wsi->ws->rx_packet_length |= ((size_t)c) << 32;
+#endif
+		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
+		break;
+
+	case LWS_RXPS_04_FRAME_HDR_LEN64_4:
+		wsi->ws->rx_packet_length |= ((size_t)c) << 24;
+		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
+		break;
+
+	case LWS_RXPS_04_FRAME_HDR_LEN64_3:
+		wsi->ws->rx_packet_length |= ((size_t)c) << 16;
+		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
+		break;
+
+	case LWS_RXPS_04_FRAME_HDR_LEN64_2:
+		wsi->ws->rx_packet_length |= ((size_t)c) << 8;
+		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
+		break;
+
+	case LWS_RXPS_04_FRAME_HDR_LEN64_1:
+		wsi->ws->rx_packet_length |= ((size_t)c);
+		if (wsi->ws->this_frame_masked)
+			wsi->lws_rx_parse_state =
+					LWS_RXPS_07_COLLECT_FRAME_KEY_1;
+		else
+			wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD;
+		break;
+
+	case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
+		wsi->ws->mask[0] = c;
+		if (c)
+			wsi->ws->all_zero_nonce = 0;
+		wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
+		break;
+
+	case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
+		wsi->ws->mask[1] = c;
+		if (c)
+			wsi->ws->all_zero_nonce = 0;
+		wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
+		break;
+
+	case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
+		wsi->ws->mask[2] = c;
+		if (c)
+			wsi->ws->all_zero_nonce = 0;
+		wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
+		break;
+
+	case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
+		wsi->ws->mask[3] = c;
+		if (c)
+			wsi->ws->all_zero_nonce = 0;
+		wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD;
+		wsi->ws->mask_idx = 0;
+		if (wsi->ws->rx_packet_length == 0) {
+			wsi->lws_rx_parse_state = LWS_RXPS_NEW;
+			goto spill;
+		}
+		break;
+
+
+	case LWS_RXPS_WS_FRAME_PAYLOAD:
+		assert(wsi->ws->rx_ubuf);
+
+		if (wsi->ws->rx_ubuf_head + LWS_PRE >= wsi->ws->rx_ubuf_alloc) {
+			lwsl_err("Attempted overflow \n");
+			return -1;
+		}
+		if (!(already_processed & ALREADY_PROCESSED_IGNORE_CHAR)) {
+			if (wsi->ws->all_zero_nonce)
+				wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] =
+				   c;
+			else
+				wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] =
+				   c ^ wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
+
+			--wsi->ws->rx_packet_length;
+		}
+
+		if (!wsi->ws->rx_packet_length) {
+			lwsl_debug("%s: ws fragment length exhausted\n", __func__);
+			/* spill because we have the whole frame */
+			wsi->lws_rx_parse_state = LWS_RXPS_NEW;
+			goto spill;
+		}
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		if (wsi->ws->rx_draining_ext) {
+			lwsl_debug("%s: UNTIL_EXHAUSTED draining\n", __func__);
+			goto drain_extension;
+		}
+#endif
+		/*
+		 * if there's no protocol max frame size given, we are
+		 * supposed to default to context->pt_serv_buf_size
+		 */
+		if (!wsi->protocol->rx_buffer_size &&
+		    wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size)
+			break;
+
+		if (wsi->protocol->rx_buffer_size &&
+		    wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size)
+			break;
+
+		/* spill because we filled our rx buffer */
+spill:
+		/*
+		 * is this frame a control packet we should take care of at this
+		 * layer?  If so service it and hide it from the user callback
+		 */
+
+		lwsl_parser("spill on %s\n", wsi->protocol->name);
+
+		switch (wsi->ws->opcode) {
+		case LWSWSOPC_CLOSE:
+
+			if (wsi->ws->peer_has_sent_close)
+				break;
+
+			wsi->ws->peer_has_sent_close = 1;
+
+			pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE];
+			if (lws_check_opt(wsi->context->options,
+					  LWS_SERVER_OPTION_VALIDATE_UTF8) &&
+			    wsi->ws->rx_ubuf_head > 2 &&
+			    lws_check_utf8(&wsi->ws->utf8, pp + 2,
+					   wsi->ws->rx_ubuf_head - 2))
+				goto utf8_fail;
+
+			/* is this an acknowledgment of our close? */
+			if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
+				/*
+				 * fine he has told us he is closing too, let's
+				 * finish our close
+				 */
+				lwsl_parser("seen client close ack\n");
+				return -1;
+			}
+			if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
+				/* if he sends us 2 CLOSE, kill him */
+				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;
+				lwsl_parser("Closing on peer close due to Pending tx\n");
+				return -1;
+			}
+
+			if (wsi->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,
+					&wsi->ws->rx_ubuf[LWS_PRE],
+					wsi->ws->rx_ubuf_head))
+				return -1;
+
+			lwsl_parser("server sees client close packet\n");
+			lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
+			/* deal with the close packet contents as a PONG */
+			wsi->ws->payload_is_close = 1;
+			goto process_as_ping;
+
+		case LWSWSOPC_PING:
+			lwsl_info("received %d byte ping, sending pong\n",
+						 wsi->ws->rx_ubuf_head);
+
+			if (wsi->ws->ping_pending_flag) {
+				/*
+				 * there is already a pending ping payload
+				 * we should just log and drop
+				 */
+				lwsl_parser("DROP PING since one pending\n");
+				goto ping_drop;
+			}
+process_as_ping:
+			/* control packets can only be < 128 bytes long */
+			if (wsi->ws->rx_ubuf_head > 128 - 3) {
+				lwsl_parser("DROP PING payload too large\n");
+				goto ping_drop;
+			}
+
+			/* stash the pong payload */
+			memcpy(wsi->ws->ping_payload_buf + LWS_PRE,
+			       &wsi->ws->rx_ubuf[LWS_PRE],
+				wsi->ws->rx_ubuf_head);
+
+			wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head;
+			wsi->ws->ping_pending_flag = 1;
+
+			/* get it sent as soon as possible */
+			lws_callback_on_writable(wsi);
+ping_drop:
+			wsi->ws->rx_ubuf_head = 0;
+			return 0;
+
+		case LWSWSOPC_PONG:
+			lwsl_info("received pong\n");
+			lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
+			             wsi->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_RECEIVE_PONG;
+			break;
+
+		case LWSWSOPC_TEXT_FRAME:
+		case LWSWSOPC_BINARY_FRAME:
+		case LWSWSOPC_CONTINUATION:
+			break;
+
+		default:
+			lwsl_parser("unknown opc %x\n", wsi->ws->opcode);
+
+			return -1;
+		}
+
+		/*
+		 * No it's real payload, pass it up to the user callback.
+		 * It's nicely buffered with the pre-padding taken care of
+		 * so it can be sent straight out again using lws_write
+		 */
+
+		ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
+		ebuf.len = wsi->ws->rx_ubuf_head;
+
+		if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len)
+			goto already_done;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+drain_extension:
+#endif
+		// lwsl_notice("%s: passing %d to ext\n", __func__, ebuf.len);
+
+		if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
+		    lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
+			goto already_done;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		lin = ebuf.len;
+		//if (lin)
+		//	lwsl_hexdump_notice(ebuf.token, ebuf.len);
+		n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
+		lwsl_debug("%s: ext says %d / ebuf.len %d\n", __func__,  n, ebuf.len);
+		if (wsi->ws->rx_draining_ext)
+			already_processed &= ~ALREADY_PROCESSED_NO_CB;
+#endif
+		/*
+		 * ebuf may be pointing somewhere completely different now,
+		 * it's the output
+		 */
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		if (n < 0) {
+			/*
+			 * we may rely on this to get RX, just drop connection
+			 */
+			wsi->socket_is_permanently_unusable = 1;
+			return -1;
+		}
+#endif
+		if (
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		    rx_draining_ext &&
+#endif
+		    ebuf.len == 0)
+			goto already_done;
+
+		if (
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		    n &&
+#endif
+		    ebuf.len)
+			/* extension had more... main loop will come back */
+			lws_add_wsi_to_draining_ext_list(wsi);
+		else
+			lws_remove_wsi_from_draining_ext_list(wsi);
+
+		if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
+			if (lws_check_utf8(&wsi->ws->utf8,
+					   (unsigned char *)ebuf.token,
+					   ebuf.len)) {
+				lws_close_reason(wsi,
+					LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+					(uint8_t *)"bad utf8", 8);
+				goto utf8_fail;
+			}
+
+			/* we are ending partway through utf-8 character? */
+			if (!wsi->ws->rx_packet_length && wsi->ws->final &&
+			    wsi->ws->utf8 && !n) {
+				lwsl_info("FINAL utf8 error\n");
+				lws_close_reason(wsi,
+					LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+					(uint8_t *)"partial utf8", 12);
+utf8_fail:
+				lwsl_notice("utf8 error\n");
+				lwsl_hexdump_notice(ebuf.token, ebuf.len);
+
+				return -1;
+			}
+		}
+
+		if (!wsi->wsistate_pre_close && (ebuf.len >= 0 ||
+		    callback_action == LWS_CALLBACK_RECEIVE_PONG)) {
+			if (ebuf.len)
+				ebuf.token[ebuf.len] = '\0';
+
+			if (wsi->protocol->callback &&
+			    !(already_processed & ALREADY_PROCESSED_NO_CB)) {
+				if (callback_action == LWS_CALLBACK_RECEIVE_PONG)
+					lwsl_info("Doing pong callback\n");
+
+				ret = user_callback_handle_rxflow(
+						wsi->protocol->callback,
+						wsi, (enum lws_callback_reasons)
+						     callback_action,
+						wsi->user_space,
+						ebuf.token,
+						ebuf.len);
+			}
+			wsi->ws->first_fragment = 0;
+		}
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		if (!lin)
+			break;
+#endif
+
+already_done:
+		wsi->ws->rx_ubuf_head = 0;
+		break;
+	}
+
+	return ret;
+
+illegal_ctl_length:
+
+	lwsl_warn("Control frame with xtended length is illegal\n");
+	/* kill the connection */
+	return -1;
+}
+
+
+LWS_VISIBLE size_t
+lws_remaining_packet_payload(struct lws *wsi)
+{
+	return wsi->ws->rx_packet_length;
+}
+
+LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi)
+{
+	return wsi->ws->frame_is_binary;
+}
+
+void
+lws_add_wsi_to_draining_ext_list(struct lws *wsi)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+	if (wsi->ws->rx_draining_ext)
+		return;
+
+	lwsl_debug("%s: RX EXT DRAINING: Adding to list\n", __func__);
+
+	wsi->ws->rx_draining_ext = 1;
+	wsi->ws->rx_draining_ext_list = pt->ws.rx_draining_ext_list;
+	pt->ws.rx_draining_ext_list = wsi;
+#endif
+}
+
+void
+lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+	struct lws **w = &pt->ws.rx_draining_ext_list;
+
+	if (!wsi->ws->rx_draining_ext)
+		return;
+
+	lwsl_debug("%s: RX EXT DRAINING: Removing from list\n", __func__);
+
+	wsi->ws->rx_draining_ext = 0;
+
+	/* remove us from context draining ext list */
+	while (*w) {
+		if (*w == wsi) {
+			/* if us, point it instead to who we were pointing to */
+			*w = wsi->ws->rx_draining_ext_list;
+			break;
+		}
+		w = &((*w)->ws->rx_draining_ext_list);
+	}
+	wsi->ws->rx_draining_ext_list = NULL;
+#endif
+}
+
+LWS_EXTERN void
+lws_restart_ws_ping_pong_timer(struct lws *wsi)
+{
+	if (!wsi->context->ws_ping_pong_interval ||
+	    !lwsi_role_ws(wsi))
+		return;
+
+	wsi->ws->time_next_ping_check = (time_t)lws_now_secs();
+}
+
+static int
+lws_0405_frame_mask_generate(struct lws *wsi)
+{
+	int n;
+	/* fetch the per-frame nonce */
+
+	n = lws_get_random(lws_get_context(wsi), wsi->ws->mask, 4);
+	if (n != 4) {
+		lwsl_parser("Unable to read from random device %s %d\n",
+			    SYSTEM_RANDOM_FILEPATH, n);
+		return 1;
+	}
+
+	/* start masking from first byte of masking key buffer */
+	wsi->ws->mask_idx = 0;
+
+	return 0;
+}
+
+int
+lws_server_init_wsi_for_ws(struct lws *wsi)
+{
+	int n;
+
+	lwsi_set_state(wsi, LRS_ESTABLISHED);
+	lws_restart_ws_ping_pong_timer(wsi);
+
+	/*
+	 * create the frame buffer for this connection according to the
+	 * size mentioned in the protocol definition.  If 0 there, use
+	 * a big default for compatibility
+	 */
+
+	n = (int)wsi->protocol->rx_buffer_size;
+	if (!n)
+		n = wsi->context->pt_serv_buf_size;
+	n += LWS_PRE;
+	wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "rx_ubuf");
+	if (!wsi->ws->rx_ubuf) {
+		lwsl_err("Out of Mem allocating rx buffer %d\n", n);
+		return 1;
+	}
+	wsi->ws->rx_ubuf_alloc = n;
+	lwsl_debug("Allocating RX buffer %d\n", n);
+
+#if !defined(LWS_WITH_ESP32)
+	if (!wsi->parent_carries_io &&
+	    !wsi->h2_stream_carries_ws)
+		if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
+		       (const char *)&n, sizeof n)) {
+			lwsl_warn("Failed to set SNDBUF to %d", n);
+			return 1;
+		}
+#endif
+
+	/* notify user code that we're ready to roll */
+
+	if (wsi->protocol->callback)
+		if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,
+					    wsi->user_space,
+#ifdef LWS_WITH_TLS
+					    wsi->tls.ssl,
+#else
+					    NULL,
+#endif
+					    wsi->h2_stream_carries_ws))
+			return 1;
+
+	lwsl_debug("ws established\n");
+
+	return 0;
+}
+
+
+
+LWS_VISIBLE int
+lws_is_final_fragment(struct lws *wsi)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+       lwsl_debug("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
+			wsi->ws->final, (long)wsi->ws->rx_packet_length,
+			(long)wsi->ws->rx_draining_ext);
+	return wsi->ws->final && !wsi->ws->rx_packet_length &&
+	       !wsi->ws->rx_draining_ext;
+#else
+	return wsi->ws->final && !wsi->ws->rx_packet_length;
+#endif
+}
+
+LWS_VISIBLE int
+lws_is_first_fragment(struct lws *wsi)
+{
+	return wsi->ws->first_fragment;
+}
+
+LWS_VISIBLE unsigned char
+lws_get_reserved_bits(struct lws *wsi)
+{
+	return wsi->ws->rsv;
+}
+
+LWS_VISIBLE LWS_EXTERN int
+lws_get_close_length(struct lws *wsi)
+{
+	return wsi->ws->close_in_ping_buffer_len;
+}
+
+LWS_VISIBLE LWS_EXTERN unsigned char *
+lws_get_close_payload(struct lws *wsi)
+{
+	return &wsi->ws->ping_payload_buf[LWS_PRE];
+}
+
+LWS_VISIBLE LWS_EXTERN void
+lws_close_reason(struct lws *wsi, enum lws_close_status status,
+		 unsigned char *buf, size_t len)
+{
+	unsigned char *p, *start;
+	int budget = sizeof(wsi->ws->ping_payload_buf) - LWS_PRE;
+
+	assert(lwsi_role_ws(wsi));
+
+	start = p = &wsi->ws->ping_payload_buf[LWS_PRE];
+
+	*p++ = (((int)status) >> 8) & 0xff;
+	*p++ = ((int)status) & 0xff;
+
+	if (buf)
+		while (len-- && p < start + budget)
+			*p++ = *buf++;
+
+	wsi->ws->close_in_ping_buffer_len = lws_ptr_diff(p, start);
+}
+
+static int
+lws_is_ws_with_ext(struct lws *wsi)
+{
+#if defined(LWS_WITHOUT_EXTENSIONS)
+	return 0;
+#else
+	return lwsi_role_ws(wsi) && !!wsi->ws->count_act_ext;
+#endif
+}
+
+static int
+rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
+		       struct lws_pollfd *pollfd)
+{
+	struct lws_tokens ebuf;
+	unsigned int pending = 0;
+	char buffered = 0;
+	int n = 0, m;
+#if defined(LWS_WITH_HTTP2)
+	struct lws *wsi1;
+#endif
+
+	if (!wsi->ws) {
+		lwsl_err("ws role wsi with no ws\n");
+		return 1;
+	}
+
+	// lwsl_notice("%s: %s\n", __func__, wsi->protocol->name);
+
+	//lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__,
+	//	   wsi->wsistate, pollfd->revents & LWS_POLLOUT);
+
+	/*
+	 * something went wrong with parsing the handshake, and
+	 * we ended up back in the event loop without completing it
+	 */
+	if (lwsi_state(wsi) == LRS_PRE_WS_SERVING_ACCEPT) {
+		wsi->socket_is_permanently_unusable = 1;
+		return LWS_HPI_RET_PLEASE_CLOSE_ME;
+	}
+
+	ebuf.token = NULL;
+	ebuf.len = 0;
+
+	if (lwsi_state(wsi) == LRS_WAITING_CONNECT) {
+#if !defined(LWS_NO_CLIENT)
+		if ((pollfd->revents & LWS_POLLOUT) &&
+		    lws_handle_POLLOUT_event(wsi, pollfd)) {
+			lwsl_debug("POLLOUT event closed it\n");
+			return LWS_HPI_RET_PLEASE_CLOSE_ME;
+		}
+
+		n = lws_client_socket_service(wsi, pollfd, NULL);
+		if (n)
+			return LWS_HPI_RET_WSI_ALREADY_DIED;
+#endif
+		return LWS_HPI_RET_HANDLED;
+	}
+
+	//lwsl_notice("%s:  wsi->ws->tx_draining_ext %d revents 0x%x 0x%x %d\n", __func__,  wsi->ws->tx_draining_ext, pollfd->revents, wsi->wsistate, lwsi_state_can_handle_POLLOUT(wsi));
+
+	/* 1: something requested a callback when it was OK to write */
+
+	if ((pollfd->revents & LWS_POLLOUT) &&
+	    lwsi_state_can_handle_POLLOUT(wsi) &&
+	    lws_handle_POLLOUT_event(wsi, pollfd)) {
+		if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
+			lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
+
+		return LWS_HPI_RET_PLEASE_CLOSE_ME;
+	}
+
+	if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
+	    lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) {
+		/*
+		 * we stopped caring about anything except control
+		 * packets.  Force flow control off, defeat tx
+		 * draining.
+		 */
+		lws_rx_flow_control(wsi, 1);
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		if (wsi->ws)
+			wsi->ws->tx_draining_ext = 0;
+#endif
+	}
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	if (wsi->ws->tx_draining_ext)
+		/*
+		 * We cannot deal with new RX until the TX ext path has
+		 * been drained.  It's because new rx will, eg, crap on
+		 * the wsi rx buf that may be needed to retain state.
+		 *
+		 * TX ext drain path MUST go through event loop to avoid
+		 * blocking.
+		 */
+		return LWS_HPI_RET_HANDLED;
+#endif
+	if (lws_is_flowcontrolled(wsi)) {
+		/* We cannot deal with any kind of new RX because we are
+		 * RX-flowcontrolled.
+		 */
+		lwsl_info("flowcontrolled\n");
+		return LWS_HPI_RET_HANDLED;
+	}
+
+#if defined(LWS_WITH_HTTP2)
+	if (wsi->http2_substream || wsi->upgraded_to_http2) {
+		wsi1 = lws_get_network_wsi(wsi);
+		if (wsi1 && wsi1->trunc_len)
+			/* We cannot deal with any kind of new RX
+			 * because we are dealing with a partial send
+			 * (new RX may trigger new http_action() that
+			 * expect to be able to send)
+			 */
+			return LWS_HPI_RET_HANDLED;
+	}
+#endif
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	/* 2: RX Extension needs to be drained
+	 */
+
+	if (wsi->ws->rx_draining_ext) {
+
+		lwsl_debug("%s: RX EXT DRAINING: Service\n", __func__);
+#ifndef LWS_NO_CLIENT
+		if (lwsi_role_client(wsi)) {
+			n = lws_ws_client_rx_sm(wsi, 0);
+			if (n < 0)
+				/* we closed wsi */
+				return LWS_HPI_RET_PLEASE_CLOSE_ME;
+		} else
+#endif
+			n = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
+
+		return LWS_HPI_RET_HANDLED;
+	}
+
+	if (wsi->ws->rx_draining_ext)
+		/*
+		 * We have RX EXT content to drain, but can't do it
+		 * right now.  That means we cannot do anything lower
+		 * priority either.
+		 */
+		return LWS_HPI_RET_HANDLED;
+#endif
+
+	/* 3: buflist needs to be drained
+	 */
+read:
+	//lws_buflist_describe(&wsi->buflist, wsi);
+	ebuf.len = (int)lws_buflist_next_segment_len(&wsi->buflist,
+						     (uint8_t **)&ebuf.token);
+	if (ebuf.len) {
+		lwsl_info("draining buflist (len %d)\n", ebuf.len);
+		buffered = 1;
+		goto drain;
+	}
+
+	if (!(pollfd->revents & pollfd->events & LWS_POLLIN) && !wsi->http.ah)
+		return LWS_HPI_RET_HANDLED;
+
+	if (lws_is_flowcontrolled(wsi)) {
+		lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n",
+			    __func__, wsi, wsi->rxflow_bitmap);
+		return LWS_HPI_RET_HANDLED;
+	}
+
+	if (!(lwsi_role_client(wsi) &&
+	      (lwsi_state(wsi) != LRS_ESTABLISHED &&
+	       lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK &&
+	       lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS))) {
+		/*
+		 * In case we are going to react to this rx by scheduling
+		 * writes, we need to restrict the amount of rx to the size
+		 * the protocol reported for rx buffer.
+		 *
+		 * Otherwise we get a situation we have to absorb possibly a
+		 * lot of reads before we get a chance to drain them by writing
+		 * them, eg, with echo type tests in autobahn.
+		 */
+
+		buffered = 0;
+		ebuf.token = (char *)pt->serv_buf;
+		if (lwsi_role_ws(wsi))
+			ebuf.len = wsi->ws->rx_ubuf_alloc;
+		else
+			ebuf.len = wsi->context->pt_serv_buf_size;
+
+		if ((unsigned int)ebuf.len > wsi->context->pt_serv_buf_size)
+			ebuf.len = wsi->context->pt_serv_buf_size;
+
+		if ((int)pending > ebuf.len)
+			pending = ebuf.len;
+
+		ebuf.len = lws_ssl_capable_read(wsi, (uint8_t *)ebuf.token,
+						pending ? (int)pending :
+						ebuf.len);
+		switch (ebuf.len) {
+		case 0:
+			lwsl_info("%s: zero length read\n",
+				  __func__);
+			return LWS_HPI_RET_PLEASE_CLOSE_ME;
+		case LWS_SSL_CAPABLE_MORE_SERVICE:
+			lwsl_info("SSL Capable more service\n");
+			return LWS_HPI_RET_HANDLED;
+		case LWS_SSL_CAPABLE_ERROR:
+			lwsl_info("%s: LWS_SSL_CAPABLE_ERROR\n",
+					__func__);
+			return LWS_HPI_RET_PLEASE_CLOSE_ME;
+		}
+		// lwsl_notice("Actual RX %d\n", ebuf.len);
+
+		lws_restart_ws_ping_pong_timer(wsi);
+
+		/*
+		 * coverity thinks ssl_capable_read() may read over
+		 * 2GB.  Dissuade it...
+		 */
+		ebuf.len &= 0x7fffffff;
+	}
+
+drain:
+
+	/*
+	 * give any active extensions a chance to munge the buffer
+	 * before parse.  We pass in a pointer to an lws_tokens struct
+	 * prepared with the default buffer and content length that's in
+	 * there.  Rather than rewrite the default buffer, extensions
+	 * that expect to grow the buffer can adapt .token to
+	 * point to their own per-connection buffer in the extension
+	 * user allocation.  By default with no extensions or no
+	 * extension callback handling, just the normal input buffer is
+	 * used then so it is efficient.
+	 */
+	m = 0;
+	do {
+
+		/* service incoming data */
+		//lws_buflist_describe(&wsi->buflist, wsi);
+		if (ebuf.len) {
+#if defined(LWS_ROLE_H2)
+			if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
+				n = lws_read_h2(wsi, (unsigned char *)ebuf.token,
+					     ebuf.len);
+			else
+#endif
+				n = lws_read_h1(wsi, (unsigned char *)ebuf.token,
+					     ebuf.len);
+
+			if (n < 0) {
+				/* we closed wsi */
+				n = 0;
+				return LWS_HPI_RET_WSI_ALREADY_DIED;
+			}
+			//lws_buflist_describe(&wsi->buflist, wsi);
+			//lwsl_notice("%s: consuming %d / %d\n", __func__, n, ebuf.len);
+			if (lws_buflist_aware_consume(wsi, &ebuf, n, buffered))
+				return LWS_HPI_RET_PLEASE_CLOSE_ME;
+		}
+
+		ebuf.token = NULL;
+		ebuf.len = 0;
+	} while (m);
+
+	if (wsi->http.ah
+#if !defined(LWS_NO_CLIENT)
+			&& !wsi->client_h2_alpn
+#endif
+			) {
+		lwsl_info("%s: %p: detaching ah\n", __func__, wsi);
+		lws_header_table_detach(wsi, 0);
+	}
+
+	pending = lws_ssl_pending(wsi);
+	if (pending) {
+		if (lws_is_ws_with_ext(wsi))
+			pending = pending > wsi->ws->rx_ubuf_alloc ?
+				wsi->ws->rx_ubuf_alloc : pending;
+		else
+			pending = pending > wsi->context->pt_serv_buf_size ?
+				wsi->context->pt_serv_buf_size : pending;
+		goto read;
+	}
+
+	if (buffered && /* were draining, now nothing left */
+	    !lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
+		lwsl_info("%s: %p flow buf: drained\n", __func__, wsi);
+		/* having drained the rxflow buffer, can rearm POLLIN */
+#ifdef LWS_NO_SERVER
+		n =
+#endif
+		__lws_rx_flow_control(wsi);
+		/* n ignored, needed for NO_SERVER case */
+	}
+
+	/* n = 0 */
+	return LWS_HPI_RET_HANDLED;
+}
+
+
+int rops_handle_POLLOUT_ws(struct lws *wsi)
+{
+	int write_type = LWS_WRITE_PONG;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	struct lws_tokens ebuf;
+	int ret, m;
+#endif
+	int n;
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	lwsl_debug("%s: %s: wsi->ws->tx_draining_ext %d\n", __func__,
+			wsi->protocol->name, wsi->ws->tx_draining_ext);
+#endif
+
+	/* Priority 3: pending control packets (pong or close)
+	 *
+	 * 3a: close notification packet requested from close api
+	 */
+
+	if (lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) {
+		lwsl_debug("sending close packet\n");
+		lwsl_hexdump_debug(&wsi->ws->ping_payload_buf[LWS_PRE],
+				   wsi->ws->close_in_ping_buffer_len);
+		wsi->waiting_to_send_close_frame = 0;
+		n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
+			      wsi->ws->close_in_ping_buffer_len,
+			      LWS_WRITE_CLOSE);
+		if (n >= 0) {
+			if (wsi->close_needs_ack) {
+				lwsi_set_state(wsi, LRS_AWAITING_CLOSE_ACK);
+				lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 5);
+				lwsl_debug("sent close indication, awaiting ack\n");
+
+				return LWS_HP_RET_BAIL_OK;
+			}
+			wsi->close_needs_ack = 0;
+			lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
+		}
+
+		return LWS_HP_RET_BAIL_DIE;
+	}
+
+	/* else, the send failed and we should just hang up */
+
+	if ((lwsi_role_ws(wsi) && wsi->ws->ping_pending_flag) ||
+	    (lwsi_state(wsi) == LRS_RETURNED_CLOSE &&
+	     wsi->ws->payload_is_close)) {
+
+		if (wsi->ws->payload_is_close)
+			write_type = LWS_WRITE_CLOSE;
+		else {
+			if (wsi->wsistate_pre_close) {
+				/* we started close flow, forget pong */
+				wsi->ws->ping_pending_flag = 0;
+				return LWS_HP_RET_BAIL_OK;
+			}
+			lwsl_info("issuing pong %d on wsi %p\n", wsi->ws->ping_payload_len, wsi);
+		}
+
+		n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
+			      wsi->ws->ping_payload_len, write_type);
+		if (n < 0)
+			return LWS_HP_RET_BAIL_DIE;
+
+		/* well he is sent, mark him done */
+		wsi->ws->ping_pending_flag = 0;
+		if (wsi->ws->payload_is_close) {
+			// assert(0);
+			/* oh... a close frame was it... then we are done */
+			return LWS_HP_RET_BAIL_DIE;
+		}
+
+		/* otherwise for PING, leave POLLOUT active either way */
+		return LWS_HP_RET_BAIL_OK;
+	}
+
+	if (lwsi_role_client(wsi) && !wsi->socket_is_permanently_unusable &&
+	    wsi->ws->send_check_ping) {
+
+		lwsl_info("issuing ping on wsi %p\n", wsi);
+		wsi->ws->send_check_ping = 0;
+		n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
+			      0, LWS_WRITE_PING);
+		if (n < 0)
+			return LWS_HP_RET_BAIL_DIE;
+
+		/*
+		 * we apparently were able to send the PING in a reasonable time
+		 * now reset the clock on our peer to be able to send the
+		 * PONG in a reasonable time.
+		 */
+
+		lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG,
+				wsi->context->timeout_secs);
+
+		return LWS_HP_RET_BAIL_OK;
+	}
+
+	/* Priority 4: if we are closing, not allowed to send more data frags
+	 *	       which means user callback or tx ext flush banned now
+	 */
+	if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
+		return LWS_HP_RET_USER_SERVICE;
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	/* Priority 5: Tx path extension with more to send
+	 *
+	 *	       These are handled as new fragments each time around
+	 *	       So while we must block new writeable callback to enforce
+	 *	       payload ordering, but since they are always complete
+	 *	       fragments control packets can interleave OK.
+	 */
+	if (lwsi_role_client(wsi) && wsi->ws->tx_draining_ext) {
+		lwsl_ext("SERVICING TX EXT DRAINING\n");
+		if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0)
+			return LWS_HP_RET_BAIL_DIE;
+		/* leave POLLOUT active */
+		return LWS_HP_RET_BAIL_OK;
+	}
+
+	/* Priority 6: extensions
+	 */
+	if (!wsi->ws->extension_data_pending)
+		return LWS_HP_RET_USER_SERVICE;
+
+	/*
+	 * check in on the active extensions, see if they
+	 * had pending stuff to spill... they need to get the
+	 * first look-in otherwise sequence will be disordered
+	 *
+	 * NULL, zero-length ebuf means just spill pending
+	 */
+
+	ret = 1;
+	if (wsi->role_ops == &role_ops_raw_skt ||
+	    wsi->role_ops == &role_ops_raw_file)
+		ret = 0;
+
+	while (ret == 1) {
+
+		/* default to nobody has more to spill */
+
+		ret = 0;
+		ebuf.token = NULL;
+		ebuf.len = 0;
+
+		/* give every extension a chance to spill */
+
+		m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND,
+				      &ebuf, 0);
+		if (m < 0) {
+			lwsl_err("ext reports fatal error\n");
+			return LWS_HP_RET_BAIL_DIE;
+		}
+		if (m)
+			/*
+			 * at least one extension told us he has more
+			 * to spill, so we will go around again after
+			 */
+			ret = 1;
+
+		/* assuming they gave us something to send, send it */
+
+		if (ebuf.len) {
+			n = lws_issue_raw(wsi, (unsigned char *)ebuf.token,
+					  ebuf.len);
+			if (n < 0) {
+				lwsl_info("closing from POLLOUT spill\n");
+				return LWS_HP_RET_BAIL_DIE;
+			}
+			/*
+			 * Keep amount spilled small to minimize chance of this
+			 */
+			if (n != ebuf.len) {
+				lwsl_err("Unable to spill ext %d vs %d\n",
+							  ebuf.len, n);
+				return LWS_HP_RET_BAIL_DIE;
+			}
+		} else
+			continue;
+
+		/* no extension has more to spill */
+
+		if (!ret)
+			continue;
+
+		/*
+		 * There's more to spill from an extension, but we just sent
+		 * something... did that leave the pipe choked?
+		 */
+
+		if (!lws_send_pipe_choked(wsi))
+			/* no we could add more */
+			continue;
+
+		lwsl_info("choked in POLLOUT service\n");
+
+		/*
+		 * Yes, he's choked.  Leave the POLLOUT masked on so we will
+		 * come back here when he is unchoked.  Don't call the user
+		 * callback to enforce ordering of spilling, he'll get called
+		 * when we come back here and there's nothing more to spill.
+		 */
+
+		return LWS_HP_RET_BAIL_OK;
+	}
+
+	wsi->ws->extension_data_pending = 0;
+#endif
+
+	return LWS_HP_RET_USER_SERVICE;
+}
+
+static int
+rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now)
+{
+	struct lws_vhost *vh;
+
+	if (!context->ws_ping_pong_interval ||
+	    context->last_ws_ping_pong_check_s >= now + 10)
+		return 0;
+
+	vh = context->vhost_list;
+	context->last_ws_ping_pong_check_s = now;
+
+	while (vh) {
+		int n;
+
+		lws_vhost_lock(vh);
+
+		for (n = 0; n < vh->count_protocols; n++) {
+			struct lws *wsi = vh->same_vh_protocol_list[n];
+
+			while (wsi) {
+				if (lwsi_role_ws(wsi) &&
+				    !wsi->socket_is_permanently_unusable &&
+				    !wsi->ws->send_check_ping &&
+				    wsi->ws->time_next_ping_check &&
+				    lws_compare_time_t(context, now,
+					wsi->ws->time_next_ping_check) >
+				       context->ws_ping_pong_interval) {
+
+					lwsl_info("req pp on wsi %p\n",
+						  wsi);
+					wsi->ws->send_check_ping = 1;
+					lws_set_timeout(wsi,
+					PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING,
+						context->timeout_secs);
+					lws_callback_on_writable(wsi);
+					wsi->ws->time_next_ping_check =
+						now;
+				}
+				wsi = wsi->same_vh_protocol_next;
+			}
+		}
+
+		lws_vhost_unlock(vh);
+		vh = vh->vhost_next;
+	}
+
+	return 0;
+}
+
+static int
+rops_service_flag_pending_ws(struct lws_context *context, int tsi)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	struct lws_context_per_thread *pt = &context->pt[tsi];
+	struct lws *wsi;
+	int forced = 0;
+
+	/* POLLIN faking (the pt lock is taken by the parent) */
+
+	/*
+	 * 1) For all guys with already-available ext data to drain, if they are
+	 * not flowcontrolled, fake their POLLIN status
+	 */
+	wsi = pt->ws.rx_draining_ext_list;
+	while (wsi && wsi->position_in_fds_table != LWS_NO_FDS_POS) {
+		pt->fds[wsi->position_in_fds_table].revents |=
+			pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
+		if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN)
+			forced = 1;
+
+		wsi = wsi->ws->rx_draining_ext_list;
+	}
+
+	return forced;
+#else
+	return 0;
+#endif
+}
+
+static int
+rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason)
+{
+	if (!wsi->ws->close_in_ping_buffer_len && /* already a reason */
+	     (reason == LWS_CLOSE_STATUS_NOSTATUS ||
+	      reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY))
+		return 0;
+
+	lwsl_debug("%s: sending close indication...\n", __func__);
+
+	/* if no prepared close reason, use 1000 and no aux data */
+
+	if (!wsi->ws->close_in_ping_buffer_len) {
+		wsi->ws->close_in_ping_buffer_len = 2;
+		wsi->ws->ping_payload_buf[LWS_PRE] = (reason >> 8) & 0xff;
+		wsi->ws->ping_payload_buf[LWS_PRE + 1] = reason & 0xff;
+	}
+
+	wsi->waiting_to_send_close_frame = 1;
+	wsi->close_needs_ack = 1;
+	lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
+	__lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 5);
+
+	lws_callback_on_writable(wsi);
+
+	return 1;
+}
+
+static int
+rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	if (wsi->ws->rx_draining_ext) {
+		struct lws **w = &pt->ws.rx_draining_ext_list;
+
+		wsi->ws->rx_draining_ext = 0;
+		/* remove us from context draining ext list */
+		while (*w) {
+			if (*w == wsi) {
+				*w = wsi->ws->rx_draining_ext_list;
+				break;
+			}
+			w = &((*w)->ws->rx_draining_ext_list);
+		}
+		wsi->ws->rx_draining_ext_list = NULL;
+	}
+
+	if (wsi->ws->tx_draining_ext) {
+		struct lws **w = &pt->ws.tx_draining_ext_list;
+		lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
+		wsi->ws->tx_draining_ext = 0;
+		/* remove us from context draining ext list */
+		while (*w) {
+			if (*w == wsi) {
+				*w = wsi->ws->tx_draining_ext_list;
+				break;
+			}
+			w = &((*w)->ws->tx_draining_ext_list);
+		}
+		wsi->ws->tx_draining_ext_list = NULL;
+	}
+#endif
+	lws_free_set_NULL(wsi->ws->rx_ubuf);
+
+	if (wsi->trunc_alloc)
+		/* not going to be completed... nuke it */
+		lws_free_set_NULL(wsi->trunc_alloc);
+
+	wsi->ws->ping_payload_len = 0;
+	wsi->ws->ping_pending_flag = 0;
+
+	/* deallocate any active extension contexts */
+
+	if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
+		lwsl_warn("extension destruction failed\n");
+
+	return 0;
+}
+
+static int
+rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
+			    enum lws_write_protocol *wp)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+	enum lws_write_protocol wpt;
+#endif
+	int masked7 = lwsi_role_client(wsi);
+	unsigned char is_masked_bit = 0;
+	unsigned char *dropmask = NULL;
+	struct lws_tokens ebuf;
+	size_t orig_len = len;
+	int pre = 0, n = 0;
+
+	// lwsl_err("%s: wp 0x%x len %d\n", __func__, *wp, (int)len);
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	if (wsi->ws->tx_draining_ext) {
+		/* remove us from the list */
+		struct lws **w = &pt->ws.tx_draining_ext_list;
+
+		lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
+		wsi->ws->tx_draining_ext = 0;
+		/* remove us from context draining ext list */
+		while (*w) {
+			if (*w == wsi) {
+				*w = wsi->ws->tx_draining_ext_list;
+				break;
+			}
+			w = &((*w)->ws->tx_draining_ext_list);
+		}
+		wsi->ws->tx_draining_ext_list = NULL;
+
+		wpt = *wp;
+		*wp = (wsi->ws->tx_draining_stashed_wp & 0xc0)|
+				LWS_WRITE_CONTINUATION;
+
+		/*
+		 * When we are just flushing (len == 0), we can trust the
+		 * stashed wp info completely.  Otherwise adjust it to the
+		 * FIN status of the incoming packet.
+		 */
+
+		if (!(wpt & LWS_WRITE_NO_FIN) && len)
+			*wp &= ~LWS_WRITE_NO_FIN;
+
+		lwsl_notice("FORCED draining wp to 0x%02X (stashed 0x%02X, incoming 0x%02X)\n", *wp,
+				wsi->ws->tx_draining_stashed_wp, wpt);
+		// assert(0);
+	}
+#endif
+	lws_restart_ws_ping_pong_timer(wsi);
+
+	if (((*wp) & 0x1f) == LWS_WRITE_HTTP ||
+	    ((*wp) & 0x1f) == LWS_WRITE_HTTP_FINAL ||
+	    ((*wp) & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION ||
+	    ((*wp) & 0x1f) == LWS_WRITE_HTTP_HEADERS)
+		goto send_raw;
+
+
+
+	/* if we are continuing a frame that already had its header done */
+
+	if (wsi->ws->inside_frame) {
+		lwsl_debug("INSIDE FRAME\n");
+		goto do_more_inside_frame;
+	}
+
+	wsi->ws->clean_buffer = 1;
+
+	/*
+	 * give a chance to the extensions to modify payload
+	 * the extension may decide to produce unlimited payload erratically
+	 * (eg, compression extension), so we require only that if he produces
+	 * something, it will be a complete fragment of the length known at
+	 * the time (just the fragment length known), and if he has
+	 * more we will come back next time he is writeable and allow him to
+	 * produce more fragments until he's drained.
+	 *
+	 * This allows what is sent each time it is writeable to be limited to
+	 * a size that can be sent without partial sends or blocking, allows
+	 * interleaving of control frames and other connection service.
+	 */
+	ebuf.token = (char *)buf;
+	ebuf.len = (int)len;
+
+	switch ((int)*wp) {
+	case LWS_WRITE_PING:
+	case LWS_WRITE_PONG:
+	case LWS_WRITE_CLOSE:
+		break;
+	default:
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		// lwsl_notice("LWS_EXT_CB_PAYLOAD_TX\n");
+		// m = (int)ebuf.len;
+		/* returns 0 if no more tx pending, 1 if more pending */
+		n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &ebuf, *wp);
+		if (n < 0)
+			return -1;
+		// lwsl_notice("ext processed %d plaintext into %d compressed (wp 0x%x)\n", m, (int)ebuf.len, *wp);
+
+		if (n && ebuf.len) {
+			lwsl_notice("write drain len %d (wp 0x%x) SETTING tx_draining_ext\n", (int)ebuf.len, *wp);
+			/* extension requires further draining */
+			wsi->ws->tx_draining_ext = 1;
+			wsi->ws->tx_draining_ext_list = pt->ws.tx_draining_ext_list;
+			pt->ws.tx_draining_ext_list = wsi;
+			/* we must come back to do more */
+			lws_callback_on_writable(wsi);
+			/*
+			 * keep a copy of the write type for the overall
+			 * action that has provoked generation of these
+			 * fragments, so the last guy can use its FIN state.
+			 */
+			wsi->ws->tx_draining_stashed_wp = *wp;
+			/* this is definitely not actually the last fragment
+			 * because the extension asserted he has more coming
+			 * So make sure this intermediate one doesn't go out
+			 * with a FIN.
+			 */
+			*wp |= LWS_WRITE_NO_FIN;
+		}
+#endif
+		if (ebuf.len && wsi->ws->stashed_write_pending) {
+			wsi->ws->stashed_write_pending = 0;
+			*wp = ((*wp) & 0xc0) | (int)wsi->ws->stashed_write_type;
+		}
+	}
+
+	/*
+	 * an extension did something we need to keep... for example, if
+	 * compression extension, it has already updated its state according
+	 * to this being issued
+	 */
+	if ((char *)buf != ebuf.token) {
+		/*
+		 * ext might eat it, but not have anything to issue yet.
+		 * In that case we have to follow his lead, but stash and
+		 * replace the write type that was lost here the first time.
+		 */
+		if (len && !ebuf.len) {
+			if (!wsi->ws->stashed_write_pending)
+				wsi->ws->stashed_write_type = (char)(*wp) & 0x3f;
+			wsi->ws->stashed_write_pending = 1;
+			return (int)len;
+		}
+		/*
+		 * extension recreated it:
+		 * need to buffer this if not all sent
+		 */
+		wsi->ws->clean_buffer = 0;
+	}
+
+	buf = (unsigned char *)ebuf.token;
+	len = ebuf.len;
+
+	if (!buf) {
+		lwsl_err("null buf (%d)\n", (int)len);
+		return -1;
+	}
+
+	switch (wsi->ws->ietf_spec_revision) {
+	case 13:
+		if (masked7) {
+			pre += 4;
+			dropmask = &buf[0 - pre];
+			is_masked_bit = 0x80;
+		}
+
+		switch ((*wp) & 0xf) {
+		case LWS_WRITE_TEXT:
+			n = LWSWSOPC_TEXT_FRAME;
+			break;
+		case LWS_WRITE_BINARY:
+			n = LWSWSOPC_BINARY_FRAME;
+			break;
+		case LWS_WRITE_CONTINUATION:
+			n = LWSWSOPC_CONTINUATION;
+			break;
+
+		case LWS_WRITE_CLOSE:
+			n = LWSWSOPC_CLOSE;
+			break;
+		case LWS_WRITE_PING:
+			n = LWSWSOPC_PING;
+			break;
+		case LWS_WRITE_PONG:
+			n = LWSWSOPC_PONG;
+			break;
+		default:
+			lwsl_warn("lws_write: unknown write opc / wp\n");
+			return -1;
+		}
+
+		if (!((*wp) & LWS_WRITE_NO_FIN))
+			n |= 1 << 7;
+
+		if (len < 126) {
+			pre += 2;
+			buf[-pre] = n;
+			buf[-pre + 1] = (unsigned char)(len | is_masked_bit);
+		} else {
+			if (len < 65536) {
+				pre += 4;
+				buf[-pre] = n;
+				buf[-pre + 1] = 126 | is_masked_bit;
+				buf[-pre + 2] = (unsigned char)(len >> 8);
+				buf[-pre + 3] = (unsigned char)len;
+			} else {
+				pre += 10;
+				buf[-pre] = n;
+				buf[-pre + 1] = 127 | is_masked_bit;
+#if defined __LP64__
+					buf[-pre + 2] = (len >> 56) & 0x7f;
+					buf[-pre + 3] = len >> 48;
+					buf[-pre + 4] = len >> 40;
+					buf[-pre + 5] = len >> 32;
+#else
+					buf[-pre + 2] = 0;
+					buf[-pre + 3] = 0;
+					buf[-pre + 4] = 0;
+					buf[-pre + 5] = 0;
+#endif
+				buf[-pre + 6] = (unsigned char)(len >> 24);
+				buf[-pre + 7] = (unsigned char)(len >> 16);
+				buf[-pre + 8] = (unsigned char)(len >> 8);
+				buf[-pre + 9] = (unsigned char)len;
+			}
+		}
+		break;
+	}
+
+do_more_inside_frame:
+
+	/*
+	 * Deal with masking if we are in client -> server direction and
+	 * the wp demands it
+	 */
+
+	if (masked7) {
+		if (!wsi->ws->inside_frame)
+			if (lws_0405_frame_mask_generate(wsi)) {
+				lwsl_err("frame mask generation failed\n");
+				return -1;
+			}
+
+		/*
+		 * in v7, just mask the payload
+		 */
+		if (dropmask) { /* never set if already inside frame */
+			for (n = 4; n < (int)len + 4; n++)
+				dropmask[n] = dropmask[n] ^ wsi->ws->mask[
+					(wsi->ws->mask_idx++) & 3];
+
+			/* copy the frame nonce into place */
+			memcpy(dropmask, wsi->ws->mask, 4);
+		}
+	}
+
+	if (lwsi_role_h2_ENCAPSULATION(wsi)) {
+		struct lws *encap = lws_get_network_wsi(wsi);
+
+		assert(encap != wsi);
+		return encap->role_ops->write_role_protocol(wsi, buf - pre,
+							len + pre, wp);
+	}
+
+	switch ((*wp) & 0x1f) {
+	case LWS_WRITE_TEXT:
+	case LWS_WRITE_BINARY:
+	case LWS_WRITE_CONTINUATION:
+		if (!wsi->h2_stream_carries_ws) {
+
+			/*
+			 * give any active extensions a chance to munge the
+			 * buffer before send.  We pass in a pointer to an
+			 * lws_tokens struct prepared with the default buffer
+			 * and content length that's in there.  Rather than
+			 * rewrite the default buffer, extensions that expect
+			 * to grow the buffer can adapt .token to point to their
+			 * own per-connection buffer in the extension user
+			 * allocation.  By default with no extensions or no
+			 * extension callback handling, just the normal input
+			 * buffer is used then so it is efficient.
+			 *
+			 * callback returns 1 in case it wants to spill more
+			 * buffers
+			 *
+			 * This takes care of holding the buffer if send is
+			 * incomplete, ie, if wsi->ws->clean_buffer is 0
+			 * (meaning an extension meddled with the buffer).  If
+			 * wsi->ws->clean_buffer is 1, it will instead return
+			 * to the user code how much OF THE USER BUFFER was
+			 * consumed.
+			 */
+
+			n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre);
+			wsi->ws->inside_frame = 1;
+			if (n <= 0)
+				return n;
+
+			if (n == (int)len + pre) {
+				/* everything in the buffer was handled
+				 * (or rebuffered...) */
+				wsi->ws->inside_frame = 0;
+				return (int)orig_len;
+			}
+
+			/*
+			 * it is how many bytes of user buffer got sent... may
+			 * be < orig_len in which case callback when writable
+			 * has already been arranged and user code can call
+			 * lws_write() again with the rest later.
+			 */
+
+			return n - pre;
+		}
+		break;
+	default:
+		break;
+	}
+
+send_raw:
+	return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre);
+}
+
+static int
+rops_close_kill_connection_ws(struct lws *wsi, enum lws_close_status reason)
+{
+	/* deal with ws encapsulation in h2 */
+#if defined(LWS_WITH_HTTP2)
+	if (wsi->http2_substream && wsi->h2_stream_carries_ws)
+		return role_ops_h2.close_kill_connection(wsi, reason);
+
+	return 0;
+#else
+	return 0;
+#endif
+}
+
+static int
+rops_callback_on_writable_ws(struct lws *wsi)
+{
+#if defined(LWS_WITH_HTTP2)
+	if (lwsi_role_h2_ENCAPSULATION(wsi)) {
+		/* we know then that it has an h2 parent */
+		struct lws *enc = role_ops_h2.encapsulation_parent(wsi);
+
+		assert(enc);
+		if (enc->role_ops->callback_on_writable(wsi))
+			return 1;
+	}
+#endif
+	return 0;
+}
+
+static int
+rops_init_vhost_ws(struct lws_vhost *vh,
+		   const struct lws_context_creation_info *info)
+{
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+#ifdef LWS_WITH_PLUGINS
+	struct lws_plugin *plugin = vh->context->plugin_list;
+	int m;
+
+	if (vh->context->plugin_extension_count) {
+
+		m = 0;
+		while (info->extensions && info->extensions[m].callback)
+			m++;
+
+		/*
+		 * give the vhost a unified list of extensions including the
+		 * ones that came from plugins
+		 */
+		vh->ws.extensions = lws_zalloc(sizeof(struct lws_extension) *
+				     (m + vh->context->plugin_extension_count + 1),
+				     "extensions");
+		if (!vh->ws.extensions)
+			return 1;
+
+		memcpy((struct lws_extension *)vh->ws.extensions, info->extensions,
+		       sizeof(struct lws_extension) * m);
+		plugin = vh->context->plugin_list;
+		while (plugin) {
+			memcpy((struct lws_extension *)&vh->ws.extensions[m],
+				plugin->caps.extensions,
+			       sizeof(struct lws_extension) *
+			       plugin->caps.count_extensions);
+			m += plugin->caps.count_extensions;
+			plugin = plugin->list;
+		}
+	} else
+#endif
+		vh->ws.extensions = info->extensions;
+#endif
+
+	return 0;
+}
+
+static int
+rops_destroy_vhost_ws(struct lws_vhost *vh)
+{
+#ifdef LWS_WITH_PLUGINS
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	if (vh->context->plugin_extension_count)
+		lws_free((void *)vh->ws.extensions);
+#endif
+#endif
+
+	return 0;
+}
+
+static int
+rops_destroy_role_ws(struct lws *wsi)
+{
+	lws_free_set_NULL(wsi->ws);
+
+	return 0;
+}
+
+struct lws_role_ops role_ops_ws = {
+	/* role name */			"ws",
+	/* alpn id */			NULL,
+	/* check_upgrades */		NULL,
+	/* init_context */		NULL,
+	/* init_vhost */		rops_init_vhost_ws,
+	/* destroy_vhost */		rops_destroy_vhost_ws,
+	/* periodic_checks */		rops_periodic_checks_ws,
+	/* service_flag_pending */	rops_service_flag_pending_ws,
+	/* handle_POLLIN */		rops_handle_POLLIN_ws,
+	/* handle_POLLOUT */		rops_handle_POLLOUT_ws,
+	/* perform_user_POLLOUT */	NULL,
+	/* callback_on_writable */	rops_callback_on_writable_ws,
+	/* tx_credit */			NULL,
+	/* write_role_protocol */	rops_write_role_protocol_ws,
+	/* encapsulation_parent */	NULL,
+	/* alpn_negotiated */		NULL,
+	/* close_via_role_protocol */	rops_close_via_role_protocol_ws,
+	/* close_role */		rops_close_role_ws,
+	/* close_kill_connection */	rops_close_kill_connection_ws,
+	/* destroy_role */		rops_destroy_role_ws,
+	/* writeable cb clnt, srv */	{ LWS_CALLBACK_CLIENT_WRITEABLE,
+					  LWS_CALLBACK_SERVER_WRITEABLE },
+	/* close cb clnt, srv */	{ LWS_CALLBACK_CLIENT_CLOSED,
+					  LWS_CALLBACK_CLOSED },
+	/* file handles */		0
+};

+ 164 - 0
thirdparty/libwebsockets/roles/ws/private.h

@@ -0,0 +1,164 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ *
+ *  This is included from core/private.h if LWS_ROLE_WS
+ */
+
+extern struct lws_role_ops role_ops_ws;
+
+#define lwsi_role_ws(wsi) (wsi->role_ops == &role_ops_ws)
+
+enum lws_rx_parse_state {
+	LWS_RXPS_NEW,
+
+	LWS_RXPS_04_mask_1,
+	LWS_RXPS_04_mask_2,
+	LWS_RXPS_04_mask_3,
+
+	LWS_RXPS_04_FRAME_HDR_1,
+	LWS_RXPS_04_FRAME_HDR_LEN,
+	LWS_RXPS_04_FRAME_HDR_LEN16_2,
+	LWS_RXPS_04_FRAME_HDR_LEN16_1,
+	LWS_RXPS_04_FRAME_HDR_LEN64_8,
+	LWS_RXPS_04_FRAME_HDR_LEN64_7,
+	LWS_RXPS_04_FRAME_HDR_LEN64_6,
+	LWS_RXPS_04_FRAME_HDR_LEN64_5,
+	LWS_RXPS_04_FRAME_HDR_LEN64_4,
+	LWS_RXPS_04_FRAME_HDR_LEN64_3,
+	LWS_RXPS_04_FRAME_HDR_LEN64_2,
+	LWS_RXPS_04_FRAME_HDR_LEN64_1,
+
+	LWS_RXPS_07_COLLECT_FRAME_KEY_1,
+	LWS_RXPS_07_COLLECT_FRAME_KEY_2,
+	LWS_RXPS_07_COLLECT_FRAME_KEY_3,
+	LWS_RXPS_07_COLLECT_FRAME_KEY_4,
+
+	LWS_RXPS_WS_FRAME_PAYLOAD
+};
+
+enum lws_websocket_opcodes_07 {
+	LWSWSOPC_CONTINUATION = 0,
+	LWSWSOPC_TEXT_FRAME = 1,
+	LWSWSOPC_BINARY_FRAME = 2,
+
+	LWSWSOPC_NOSPEC__MUX = 7,
+
+	/* control extensions 8+ */
+
+	LWSWSOPC_CLOSE = 8,
+	LWSWSOPC_PING = 9,
+	LWSWSOPC_PONG = 0xa,
+};
+
+/* this is not usable directly by user code any more, lws_close_reason() */
+#define LWS_WRITE_CLOSE 4
+
+#define ALREADY_PROCESSED_IGNORE_CHAR 1
+#define ALREADY_PROCESSED_NO_CB 2
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+struct lws_vhost_role_ws {
+	const struct lws_extension *extensions;
+};
+
+struct lws_pt_role_ws {
+	struct lws *rx_draining_ext_list;
+	struct lws *tx_draining_ext_list;
+};
+#endif
+
+struct _lws_websocket_related {
+	char *rx_ubuf;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];
+	void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];
+	struct lws *rx_draining_ext_list;
+	struct lws *tx_draining_ext_list;
+#endif
+	/* Also used for close content... control opcode == < 128 */
+	uint8_t ping_payload_buf[128 - 3 + LWS_PRE];
+	uint8_t mask[4];
+
+	time_t time_next_ping_check;
+	size_t rx_packet_length;
+	uint32_t rx_ubuf_head;
+	uint32_t rx_ubuf_alloc;
+
+	uint8_t ping_payload_len;
+	uint8_t mask_idx;
+	uint8_t opcode;
+	uint8_t rsv;
+	uint8_t rsv_first_msg;
+	/* zero if no info, or length including 2-byte close code */
+	uint8_t close_in_ping_buffer_len;
+	uint8_t utf8;
+	uint8_t stashed_write_type;
+	uint8_t tx_draining_stashed_wp;
+	uint8_t ietf_spec_revision;
+
+	unsigned int final:1;
+	unsigned int frame_is_binary:1;
+	unsigned int all_zero_nonce:1;
+	unsigned int this_frame_masked:1;
+	unsigned int inside_frame:1; /* next write will be more of frame */
+	unsigned int clean_buffer:1; /* buffer not rewritten by extension */
+	unsigned int payload_is_close:1; /* process as PONG, but it is close */
+	unsigned int ping_pending_flag:1;
+	unsigned int continuation_possible:1;
+	unsigned int owed_a_fin:1;
+	unsigned int check_utf8:1;
+	unsigned int defeat_check_utf8:1;
+	unsigned int stashed_write_pending:1;
+	unsigned int send_check_ping:1;
+	unsigned int first_fragment:1;
+	unsigned int peer_has_sent_close:1;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	unsigned int extension_data_pending:1;
+	unsigned int rx_draining_ext:1;
+	unsigned int tx_draining_ext:1;
+
+	uint8_t count_act_ext;
+#endif
+};
+
+int
+lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len);
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+LWS_VISIBLE void
+lws_context_init_extensions(const struct lws_context_creation_info *info,
+			    struct lws_context *context);
+LWS_EXTERN int
+lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
+			  void *v, size_t len);
+
+LWS_EXTERN int
+lws_ext_cb_active(struct lws *wsi, int reason, void *buf, int len);
+LWS_EXTERN int
+lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, int reason,
+		    void *arg, int len);
+#endif
+
+int
+handshake_0405(struct lws_context *context, struct lws *wsi);
+int
+lws_process_ws_upgrade(struct lws *wsi);
+int
+lws_server_init_wsi_for_ws(struct lws *wsi);

+ 836 - 0
thirdparty/libwebsockets/roles/ws/server-ws.c

@@ -0,0 +1,836 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include <core/private.h>
+
+#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+static int
+lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
+{
+	struct lws_context *context = wsi->context;
+	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+	char ext_name[64], *args, *end = (*p) + budget - 1;
+	const struct lws_ext_options *opts, *po;
+	const struct lws_extension *ext;
+	struct lws_ext_option_arg oa;
+	int n, m, more = 1;
+	int ext_count = 0;
+	char ignore;
+	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;
+
+	/*
+	 * break down the list of client extensions
+	 * and go through them
+	 */
+
+	if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size,
+			 WSI_TOKEN_EXTENSIONS) < 0)
+		return 1;
+
+	c = (char *)pt->serv_buf;
+	lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
+	wsi->ws->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 >= (char *)pt->serv_buf + 255)
+			return -1;
+
+		if (*c && (*c != ',' && *c != '\t')) {
+			if (*c == ';') {
+				ignore = 1;
+				if (!args)
+					args = c + 1;
+			}
+			if (ignore || *c == ' ') {
+				c++;
+				continue;
+			}
+			ext_name[n] = *c++;
+			if (n < (int)sizeof(ext_name) - 1)
+				n++;
+			continue;
+		}
+		ext_name[n] = '\0';
+
+		ignore = 0;
+		if (!*c)
+			more = 0;
+		else {
+			c++;
+			if (!n)
+				continue;
+		}
+
+		while (args && *args && *args == ' ')
+			args++;
+
+		/* check a client's extension against our support */
+
+		ext = wsi->vhost->ws.extensions;
+
+		while (ext && ext->callback) {
+
+			if (strcmp(ext_name, ext->name)) {
+				ext++;
+				continue;
+			}
+
+			/*
+			 * oh, we do support this one he asked for... but let's
+			 * confirm he only gave it once
+			 */
+			for (m = 0; m < wsi->ws->count_act_ext; m++)
+				if (wsi->ws->active_extensions[m] == ext) {
+					lwsl_info("extension mentioned twice\n");
+					return 1; /* shenanigans */
+				}
+
+			/*
+			 * ask user code if it's OK to apply it on this
+			 * 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 (m) {
+				ext++;
+				continue;
+			}
+
+			/* apply it */
+
+			ext_count++;
+
+			/* instantiate the extension on this conn */
+
+			wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext;
+
+			/* allow him to construct his context */
+
+			if (ext->callback(lws_get_context(wsi), ext, wsi,
+					  LWS_EXT_CB_CONSTRUCT,
+					  (void *)&wsi->ws->act_ext_user[
+					                wsi->ws->count_act_ext],
+					  (void *)&opts, 0)) {
+				lwsl_info("ext %s failed construction\n",
+					    ext_name);
+				ext_count--;
+				ext++;
+
+				continue;
+			}
+
+			if (ext_count > 1)
+				*(*p)++ = ',';
+			else
+				LWS_CPYAPP(*p,
+					  "\x0d\x0aSec-WebSocket-Extensions: ");
+			*p += lws_snprintf(*p, (end - *p), "%s", ext_name);
+
+			/*
+			 * The client may send a bunch of different option
+			 * sets for the same extension, we are supposed to
+			 * pick one we like the look of.  The option sets are
+			 * separated by comma.
+			 *
+			 * Actually we just either accept the first one or
+			 * nothing.
+			 *
+			 * Go through the options trying to apply the
+			 * recognized ones
+			 */
+
+			lwsl_info("ext args %s\n", args);
+
+			while (args && *args && *args != ',') {
+				while (*args == ' ')
+					args++;
+				po = opts;
+				while (po->name) {
+					/* only support arg-less options... */
+					if (po->type != EXTARG_NONE ||
+					    strncmp(args, po->name,
+						    strlen(po->name))) {
+						po++;
+						continue;
+					}
+					oa.option_name = NULL;
+					oa.option_index = (int)(po - opts);
+					oa.start = NULL;
+					oa.len = 0;
+					lwsl_info("setting '%s'\n", po->name);
+					if (!ext->callback(lws_get_context(wsi),
+							  ext, wsi,
+							  LWS_EXT_CB_OPTION_SET,
+							  wsi->ws->act_ext_user[
+								 wsi->ws->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++;
+
+				if (*args == ';')
+					args++;
+			}
+
+			wsi->ws->count_act_ext++;
+			lwsl_parser("cnt_act_ext <- %d\n", wsi->ws->count_act_ext);
+
+			if (args && *args == ',')
+				more = 0;
+
+			ext++;
+		}
+
+		n = 0;
+		args = NULL;
+	}
+
+	return 0;
+}
+#endif
+
+
+
+int
+lws_process_ws_upgrade(struct lws *wsi)
+{
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+	char protocol_list[128], protocol_name[64], *p;
+	int protocol_len, hit, n = 0, non_space_char_found = 0;
+
+	if (!wsi->protocol)
+		lwsl_err("NULL protocol at lws_read\n");
+
+	/*
+	 * It's either websocket or h2->websocket
+	 *
+	 * Select the first protocol we support from the list
+	 * the client sent us.
+	 *
+	 * Copy it to remove header fragmentation
+	 */
+
+	if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
+			 WSI_TOKEN_PROTOCOL) < 0) {
+		lwsl_err("protocol list too long");
+		return 1;
+	}
+
+	protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
+	protocol_list[protocol_len] = '\0';
+	p = protocol_list;
+	hit = 0;
+
+	while (*p && !hit) {
+		n = 0;
+		non_space_char_found = 0;
+		while (n < (int)sizeof(protocol_name) - 1 &&
+		       *p && *p != ',') {
+			/* ignore leading spaces */
+			if (!non_space_char_found && *p == ' ') {
+				n++;
+				continue;
+			}
+			non_space_char_found = 1;
+			protocol_name[n++] = *p++;
+		}
+		protocol_name[n] = '\0';
+		if (*p)
+			p++;
+
+		lwsl_debug("checking %s\n", protocol_name);
+
+		n = 0;
+		while (wsi->vhost->protocols[n].callback) {
+			lwsl_debug("try %s\n",
+				  wsi->vhost->protocols[n].name);
+
+			if (wsi->vhost->protocols[n].name &&
+			    !strcmp(wsi->vhost->protocols[n].name,
+				    protocol_name)) {
+				wsi->protocol = &wsi->vhost->protocols[n];
+				hit = 1;
+				break;
+			}
+
+			n++;
+		}
+	}
+
+	/* we didn't find a protocol he wanted? */
+
+	if (!hit) {
+		if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
+			lwsl_notice("No protocol from \"%s\" supported\n",
+				 protocol_list);
+			return 1;
+		}
+		/*
+		 * some clients only have one protocol and
+		 * do not send the protocol list header...
+		 * allow it and match to the vhost's default
+		 * protocol (which itself defaults to zero)
+		 */
+		lwsl_info("defaulting to prot handler %d\n",
+			wsi->vhost->default_protocol_index);
+		n = wsi->vhost->default_protocol_index;
+		wsi->protocol = &wsi->vhost->protocols[
+			      (int)wsi->vhost->default_protocol_index];
+	}
+
+	/* allocate the ws struct for the wsi */
+	wsi->ws = lws_zalloc(sizeof(*wsi->ws), "ws struct");
+	if (!wsi->ws) {
+		lwsl_notice("OOM\n");
+		return 1;
+	}
+
+	if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
+		wsi->ws->ietf_spec_revision =
+			       atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
+
+	/* allocate wsi->user storage */
+	if (lws_ensure_user_space(wsi)) {
+		lwsl_notice("problem with user space\n");
+		return 1;
+	}
+
+	/*
+	 * Give the user code a chance to study the request and
+	 * have the opportunity to deny it
+	 */
+	if ((wsi->protocol->callback)(wsi,
+			LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
+			wsi->user_space,
+		      lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
+		lwsl_warn("User code denied connection\n");
+		return 1;
+	}
+
+	/*
+	 * Perform the handshake according to the protocol version the
+	 * client announced
+	 */
+
+	switch (wsi->ws->ietf_spec_revision) {
+	default:
+		lwsl_notice("Unknown client spec version %d\n",
+			  wsi->ws->ietf_spec_revision);
+		wsi->ws->ietf_spec_revision = 13;
+		//return 1;
+		/* fallthru */
+	case 13:
+#if defined(LWS_WITH_HTTP2)
+		if (wsi->h2_stream_carries_ws) {
+			if (lws_h2_ws_handshake(wsi)) {
+				lwsl_notice("h2 ws handshake failed\n");
+				return 1;
+			}
+		} else
+#endif
+		{
+			lwsl_parser("lws_parse calling handshake_04\n");
+			if (handshake_0405(wsi->context, wsi)) {
+				lwsl_notice("hs0405 has failed the connection\n");
+				return 1;
+			}
+		}
+		break;
+	}
+
+	lws_same_vh_protocol_insert(wsi, n);
+
+	/*
+	 * We are upgrading to ws, so http/1.1 + h2 and keepalive + pipelined
+	 * header considerations about keeping the ah around no longer apply.
+	 *
+	 * However it's common for the first ws protocol data to have been
+	 * coalesced with the browser upgrade request and to already be in the
+	 * ah rx buffer.
+	 */
+
+	lws_pt_lock(pt, __func__);
+
+	if (wsi->h2_stream_carries_ws)
+		lws_role_transition(wsi, LWSIFR_SERVER | LWSIFR_P_ENCAP_H2,
+				    LRS_ESTABLISHED, &role_ops_ws);
+	else
+		lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED,
+				    &role_ops_ws);
+
+	lws_pt_unlock(pt);
+
+	lws_server_init_wsi_for_ws(wsi);
+	lwsl_parser("accepted v%02d connection\n", wsi->ws->ietf_spec_revision);
+
+	lwsl_info("%s: %p: dropping ah on ws upgrade\n", __func__, wsi);
+	lws_header_table_detach(wsi, 1);
+
+	return 0;
+}
+
+int
+handshake_0405(struct lws_context *context, struct lws *wsi)
+{
+	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+	struct lws_process_html_args args;
+	unsigned char hash[20];
+	int n, accept_len;
+	char *response;
+	char *p;
+
+	if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
+	    !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
+		lwsl_info("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) {
+		lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
+		goto bail;
+	}
+
+	/*
+	 * since key length is restricted above (currently 128), cannot
+	 * overflow
+	 */
+	n = sprintf((char *)pt->serv_buf,
+		    "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
+		    lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
+
+	lws_SHA1(pt->serv_buf, n, hash);
+
+	accept_len = lws_b64_encode_string((char *)hash, 20,
+			(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 (lws_ensure_user_space(wsi))
+		goto bail;
+
+	/* create the response packet */
+
+	/* make a buffer big enough for everything */
+
+	response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + 256 + 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 *)pt->serv_buf);
+	p += accept_len;
+
+	/* we can only return the protocol header if:
+	 *  - one came in, and ... */
+	if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
+	    /*  - it is not an empty string */
+	    wsi->protocol->name &&
+	    wsi->protocol->name[0]) {
+		LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
+		p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
+	}
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	/*
+	 * Figure out which extensions the client has that we want to
+	 * enable on this connection, and give him back the list.
+	 *
+	 * Give him a limited write bugdet
+	 */
+	if (lws_extension_server_handshake(wsi, &p, 192))
+		goto bail;
+#endif
+	LWS_CPYAPP(p, "\x0d\x0a");
+
+	args.p = p;
+	args.max_len = lws_ptr_diff((char *)pt->serv_buf +
+				    context->pt_serv_buf_size, p);
+	if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
+					LWS_CALLBACK_ADD_HEADERS,
+					wsi->user_space, &args, 0))
+		goto bail;
+
+	p = args.p;
+
+	/* end of response packet */
+
+	LWS_CPYAPP(p, "\x0d\x0a");
+
+	/* okay send the handshake response accepting the connection */
+
+	lwsl_parser("issuing resp pkt %d len\n",
+		    lws_ptr_diff(p, response));
+#if defined(DEBUG)
+	fwrite(response, 1,  p - response, stderr);
+#endif
+	n = lws_write(wsi, (unsigned char *)response, p - response,
+		      LWS_WRITE_HTTP_HEADERS);
+	if (n != (p - response)) {
+		lwsl_info("%s: ERROR writing to socket %d\n", __func__, n);
+		goto bail;
+	}
+
+	/* alright clean up and set ourselves into established state */
+
+	lwsi_set_state(wsi, LRS_ESTABLISHED);
+	wsi->lws_rx_parse_state = LWS_RXPS_NEW;
+
+	{
+		const char * uri_ptr =
+			lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
+		int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
+		const struct lws_http_mount *hit =
+			lws_find_mount(wsi, uri_ptr, uri_len);
+		if (hit && hit->cgienv &&
+		    wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
+			wsi->user_space, (void *)hit->cgienv, 0))
+			return 1;
+	}
+
+	return 0;
+
+bail:
+	/* caller will free up his parsing allocations */
+	return -1;
+}
+
+
+
+/*
+ * Once we reach LWS_RXPS_WS_FRAME_PAYLOAD, we know how much
+ * to expect in that state and can deal with it in bulk more efficiently.
+ */
+
+static int
+lws_ws_frame_rest_is_payload(struct lws *wsi, uint8_t **buf, size_t len)
+{
+	uint8_t *buffer = *buf, mask[4];
+	struct lws_tokens ebuf;
+	unsigned int avail = (unsigned int)len;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	unsigned int old_packet_length = (int)wsi->ws->rx_packet_length;
+#endif
+	int n = 0;
+
+	/*
+	 * With zlib, we can give it as much input as we like.  The pmd
+	 * extension will draw it down in chunks (default 1024).
+	 *
+	 * If we try to restrict how much we give it, because we must go
+	 * back to the event loop each time, we will drop the remainder...
+	 */
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	if (!wsi->ws->count_act_ext)
+#endif
+	{
+		if (wsi->protocol->rx_buffer_size)
+			avail = (int)wsi->protocol->rx_buffer_size;
+		else
+			avail = wsi->context->pt_serv_buf_size;
+	}
+
+	/* do not consume more than we should */
+	if (avail > wsi->ws->rx_packet_length)
+		avail = (unsigned int)wsi->ws->rx_packet_length;
+
+	/* do not consume more than what is in the buffer */
+	if (avail > len)
+		avail = (unsigned int)len;
+
+	if (avail <= 0)
+		return 0;
+
+	ebuf.token = (char *)buffer;
+	ebuf.len = avail;
+
+	//lwsl_hexdump_notice(ebuf.token, ebuf.len);
+
+	if (!wsi->ws->all_zero_nonce) {
+
+		for (n = 0; n < 4; n++)
+			mask[n] = wsi->ws->mask[(wsi->ws->mask_idx + n) & 3];
+
+		/* deal with 4-byte chunks using unwrapped loop */
+		n = avail >> 2;
+		while (n--) {
+			*(buffer) = *(buffer) ^ mask[0];
+			buffer++;
+			*(buffer) = *(buffer) ^ mask[1];
+			buffer++;
+			*(buffer) = *(buffer) ^ mask[2];
+			buffer++;
+			*(buffer) = *(buffer) ^ mask[3];
+			buffer++;
+		}
+		/* and the remaining bytes bytewise */
+		for (n = 0; n < (int)(avail & 3); n++) {
+			*(buffer) = *(buffer) ^ mask[n];
+			buffer++;
+		}
+
+		wsi->ws->mask_idx = (wsi->ws->mask_idx + avail) & 3;
+	}
+
+	lwsl_info("%s: using %d of raw input (total %d on offer)\n", __func__,
+		    avail, (int)len);
+
+	(*buf) += avail;
+	len -= avail;
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
+	lwsl_info("%s: ext says %d / ebuf.len %d\n", __func__,  n, ebuf.len);
+#endif
+	/*
+	 * ebuf may be pointing somewhere completely different now,
+	 * it's the output
+	 */
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	if (n < 0) {
+		/*
+		 * we may rely on this to get RX, just drop connection
+		 */
+		lwsl_notice("%s: LWS_EXT_CB_PAYLOAD_RX blew out\n", __func__);
+		wsi->socket_is_permanently_unusable = 1;
+		return -1;
+	}
+#endif
+
+	wsi->ws->rx_packet_length -= avail;
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	/*
+	 * if we had an rx fragment right at the last compressed byte of the
+	 * message, we can get a zero length inflated output, where no prior
+	 * rx inflated output marked themselves with FIN, since there was
+	 * raw ws payload still to drain at that time.
+	 *
+	 * Then we need to generate a zero length ws rx that can be understood
+	 * as the message completion.
+	 */
+
+	if (!ebuf.len &&		      /* zero-length inflation output */
+	    !n &&		   /* nothing left to drain from the inflator */
+	    wsi->ws->count_act_ext &&			  /* we are using pmd */
+	    old_packet_length &&	    /* we gave the inflator new input */
+	    !wsi->ws->rx_packet_length &&   /* raw ws packet payload all gone */
+	    wsi->ws->final &&		    /* the raw ws packet is a FIN guy */
+	    wsi->protocol->callback &&
+	    !wsi->wsistate_pre_close) {
+
+		if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
+						LWS_CALLBACK_RECEIVE,
+						wsi->user_space, NULL, 0))
+			return -1;
+
+		return avail;
+	}
+#endif
+
+	if (!ebuf.len)
+		return avail;
+
+	if (
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	    n &&
+#endif
+	    ebuf.len)
+		/* extension had more... main loop will come back */
+		lws_add_wsi_to_draining_ext_list(wsi);
+	else
+		lws_remove_wsi_from_draining_ext_list(wsi);
+
+	if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
+		if (lws_check_utf8(&wsi->ws->utf8,
+				   (unsigned char *)ebuf.token, ebuf.len)) {
+			lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+					 (uint8_t *)"bad utf8", 8);
+			goto utf8_fail;
+		}
+
+		/* we are ending partway through utf-8 character? */
+		if (!wsi->ws->rx_packet_length && wsi->ws->final &&
+		    wsi->ws->utf8 && !n) {
+			lwsl_info("FINAL utf8 error\n");
+			lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
+					 (uint8_t *)"partial utf8", 12);
+
+utf8_fail:
+			lwsl_info("utf8 error\n");
+			lwsl_hexdump_info(ebuf.token, ebuf.len);
+
+			return -1;
+		}
+	}
+
+	if (wsi->protocol->callback && !wsi->wsistate_pre_close)
+		if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
+						LWS_CALLBACK_RECEIVE,
+						wsi->user_space,
+						ebuf.token, ebuf.len))
+			return -1;
+
+	wsi->ws->first_fragment = 0;
+
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+	lwsl_info("%s: input used %d, output %d, rem len %d, rx_draining_ext %d\n",
+		  __func__, avail, ebuf.len, (int)len, wsi->ws->rx_draining_ext);
+#endif
+
+	return avail; /* how much we used from the input */
+}
+
+
+int
+lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
+{
+	int m, bulk = 0;
+
+	lwsl_debug("%s: received %d byte packet\n", __func__, (int)len);
+
+	//lwsl_hexdump_notice(*buf, len);
+
+	/* let the rx protocol state machine have as much as it needs */
+
+	while (len) {
+		/*
+		 * we were accepting input but now we stopped doing so
+		 */
+		if (wsi->rxflow_bitmap) {
+			lwsl_info("%s: doing rxflow\n", __func__);
+			lws_rxflow_cache(wsi, *buf, 0, (int)len);
+			lwsl_parser("%s: cached %ld\n", __func__, (long)len);
+			*buf += len; /* stashing it is taking care of it */
+			return 1;
+		}
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+		if (wsi->ws->rx_draining_ext) {
+			lwsl_debug("%s: draining rx ext\n", __func__);
+			m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
+			if (m < 0)
+				return -1;
+			continue;
+		}
+#endif
+
+		/* consume payload bytes efficiently */
+		while (wsi->lws_rx_parse_state == LWS_RXPS_WS_FRAME_PAYLOAD &&
+				(wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
+				 wsi->ws->opcode == LWSWSOPC_BINARY_FRAME ||
+				 wsi->ws->opcode == LWSWSOPC_CONTINUATION) &&
+		       len) {
+			uint8_t *bin = *buf;
+
+			bulk = 1;
+			m = lws_ws_frame_rest_is_payload(wsi, buf, len);
+			assert((int)lws_ptr_diff(*buf, bin) <= (int)len);
+			len -= lws_ptr_diff(*buf, bin);
+
+			if (!m) {
+
+				break;
+			}
+			if (m < 0) {
+				lwsl_info("%s: rest_is_payload bailed\n",
+					  __func__);
+				return -1;
+			}
+		}
+
+		if (!bulk) {
+			/* process the byte */
+			m = lws_ws_rx_sm(wsi, 0, *(*buf)++);
+			len--;
+		} else {
+			/*
+			 * We already handled this byte in bulk, just deal
+			 * with the ramifications
+			 */
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+			lwsl_debug("%s: coming out of bulk with len %d, "
+				   "wsi->ws->rx_draining_ext %d\n",
+				   __func__, (int)len,
+				   wsi->ws->rx_draining_ext);
+#endif
+			m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR |
+					 ALREADY_PROCESSED_NO_CB, 0);
+		}
+
+		if (m < 0) {
+			lwsl_info("%s: lws_ws_rx_sm bailed %d\n", __func__,
+				  bulk);
+
+			return -1;
+		}
+
+		bulk = 0;
+	}
+
+	lwsl_debug("%s: exit with %d unused\n", __func__, (int)len);
+
+	return 0;
+}

+ 0 - 1783
thirdparty/libwebsockets/server/parsers.c

@@ -1,1783 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <[email protected]>
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public
- *  License as published by the Free Software Foundation:
- *  version 2.1 of the License.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- *  MA  02110-1301  USA
- */
-
-#include "private-libwebsockets.h"
-
-const unsigned char lextable[] = {
-	#include "lextable.h"
-};
-
-#define FAIL_CHAR 0x08
-
-int LWS_WARN_UNUSED_RESULT
-lextable_decode(int pos, char c)
-{
-	if (c >= 'A' && c <= 'Z')
-		c += 'a' - 'A';
-
-	while (1) {
-		if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */
-			if ((lextable[pos] & 0x7f) != c)
-				return -1;
-			/* fall thru */
-			pos++;
-			if (lextable[pos] == FAIL_CHAR)
-				return -1;
-			return pos;
-		}
-
-		if (lextable[pos] == FAIL_CHAR)
-			return -1;
-
-		/* b7 = 0, end or 3-byte */
-		if (lextable[pos] < FAIL_CHAR) /* terminal marker */
-			return pos;
-
-		if (lextable[pos] == c) /* goto */
-			return pos + (lextable[pos + 1]) +
-						(lextable[pos + 2] << 8);
-		/* fall thru goto */
-		pos += 3;
-		/* continue */
-	}
-}
-
-static struct allocated_headers *
-_lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size)
-{
-	struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct");
-
-	if (!ah)
-		return NULL;
-
-	ah->data = lws_malloc(data_size, "ah data");
-	if (!ah->data) {
-		lws_free(ah);
-
-		return NULL;
-	}
-	ah->next = pt->ah_list;
-	pt->ah_list = ah;
-	ah->data_length = data_size;
-	pt->ah_pool_length++;
-
-	lwsl_info("%s: created ah %p (size %d): pool length %d\n", __func__,
-		    ah, (int)data_size, pt->ah_pool_length);
-
-	return ah;
-}
-
-int
-_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah)
-{
-	lws_start_foreach_llp(struct allocated_headers **, a, pt->ah_list) {
-		if ((*a) == ah) {
-			*a = ah->next;
-			pt->ah_pool_length--;
-			lwsl_info("%s: freed ah %p : pool length %d\n",
-				    __func__, ah, pt->ah_pool_length);
-			if (ah->data)
-				lws_free(ah->data);
-			lws_free(ah);
-
-			return 0;
-		}
-	} lws_end_foreach_llp(a, next);
-
-	return 1;
-}
-
-void
-_lws_header_table_reset(struct allocated_headers *ah)
-{
-	/* init the ah to reflect no headers or data have appeared yet */
-	memset(ah->frag_index, 0, sizeof(ah->frag_index));
-	memset(ah->frags, 0, sizeof(ah->frags));
-	ah->nfrag = 0;
-	ah->pos = 0;
-	ah->http_response = 0;
-}
-
-// doesn't scrub the ah rxbuffer by default, parent must do if needed
-
-void
-lws_header_table_reset(struct lws *wsi, int autoservice)
-{
-	struct allocated_headers *ah = wsi->u.hdr.ah;
-	struct lws_context_per_thread *pt;
-	struct lws_pollfd *pfd;
-
-	/* if we have the idea we're resetting 'our' ah, must be bound to one */
-	assert(ah);
-	/* ah also concurs with ownership */
-	assert(ah->wsi == wsi);
-
-	_lws_header_table_reset(ah);
-
-        wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
-        wsi->u.hdr.lextable_pos = 0;
-
-	/* since we will restart the ah, our new headers are not completed */
-	wsi->hdr_parsing_completed = 0;
-
-	/* while we hold the ah, keep a timeout on the wsi */
-	lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
-			wsi->vhost->timeout_secs_ah_idle);
-
-	time(&ah->assigned);
-
-	/*
-	 * if we inherited pending rx (from socket adoption deferred
-	 * processing), apply and free it.
-	 */
-	if (wsi->u.hdr.preamble_rx) {
-		memcpy(ah->rx, wsi->u.hdr.preamble_rx,
-		       wsi->u.hdr.preamble_rx_len);
-		ah->rxlen = wsi->u.hdr.preamble_rx_len;
-		lws_free_set_NULL(wsi->u.hdr.preamble_rx);
-
-		if (autoservice) {
-			lwsl_debug("%s: service on readbuf ah\n", __func__);
-
-			pt = &wsi->context->pt[(int)wsi->tsi];
-			/*
-			 * Unlike a normal connect, we have the headers already
-			 * (or the first part of them anyway)
-			 */
-			pfd = &pt->fds[wsi->position_in_fds_table];
-			pfd->revents |= LWS_POLLIN;
-			lwsl_err("%s: calling service\n", __func__);
-			lws_service_fd_tsi(wsi->context, pfd, wsi->tsi);
-		}
-	}
-}
-
-static void
-_lws_header_ensure_we_are_on_waiting_list(struct lws *wsi)
-{
-	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-	struct lws_pollargs pa;
-	struct lws **pwsi = &pt->ah_wait_list;
-
-	while (*pwsi) {
-		if (*pwsi == wsi)
-			return;
-		pwsi = &(*pwsi)->u.hdr.ah_wait_list;
-	}
-
-	lwsl_info("%s: wsi: %p\n", __func__, wsi);
-	wsi->u.hdr.ah_wait_list = pt->ah_wait_list;
-	pt->ah_wait_list = wsi;
-	pt->ah_wait_list_length++;
-
-	/* we cannot accept input then */
-
-	_lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa);
-}
-
-static int
-__lws_remove_from_ah_waiting_list(struct lws *wsi)
-{
-        struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-	struct lws **pwsi =&pt->ah_wait_list;
-
-	while (*pwsi) {
-		if (*pwsi == wsi) {
-			lwsl_info("%s: wsi %p\n", __func__, wsi);
-			/* point prev guy to our next */
-			*pwsi = wsi->u.hdr.ah_wait_list;
-			/* we shouldn't point anywhere now */
-			wsi->u.hdr.ah_wait_list = NULL;
-			pt->ah_wait_list_length--;
-
-			return 1;
-		}
-		pwsi = &(*pwsi)->u.hdr.ah_wait_list;
-	}
-
-	return 0;
-}
-
-int LWS_WARN_UNUSED_RESULT
-lws_header_table_attach(struct lws *wsi, int autoservice)
-{
-	struct lws_context *context = wsi->context;
-	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-	struct lws_pollargs pa;
-	int n;
-
-	lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__,
-		  (void *)wsi, (void *)wsi->u.hdr.ah, wsi->tsi,
-		  pt->ah_count_in_use);
-
-	/* if we are already bound to one, just clear it down */
-	if (wsi->u.hdr.ah) {
-		lwsl_info("%s: cleardown\n", __func__);
-		goto reset;
-	}
-
-	lws_pt_lock(pt);
-
-	n = pt->ah_count_in_use == context->max_http_header_pool;
-#if defined(LWS_WITH_PEER_LIMITS)
-	if (!n) {
-		n = lws_peer_confirm_ah_attach_ok(context, wsi->peer);
-		if (n)
-			lws_stats_atomic_bump(wsi->context, pt,
-				LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
-	}
-#endif
-	if (n) {
-		/*
-		 * Pool is either all busy, or we don't want to give this
-		 * particular guy an ah right now...
-		 *
-		 * Make sure we are on the waiting list, and return that we
-		 * weren't able to provide the ah
-		 */
-		_lws_header_ensure_we_are_on_waiting_list(wsi);
-
-		goto bail;
-	}
-
-	__lws_remove_from_ah_waiting_list(wsi);
-
-	wsi->u.hdr.ah = _lws_create_ah(pt, context->max_http_header_data);
-	if (!wsi->u.hdr.ah) { /* we could not create an ah */
-		_lws_header_ensure_we_are_on_waiting_list(wsi);
-
-		goto bail;
-	}
-
-	wsi->u.hdr.ah->in_use = 1;
-	wsi->u.hdr.ah->wsi = wsi; /* mark our owner */
-	pt->ah_count_in_use++;
-
-#if defined(LWS_WITH_PEER_LIMITS)
-	if (wsi->peer)
-		wsi->peer->count_ah++;
-#endif
-
-	_lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
-
-	lwsl_info("%s: did attach wsi %p: ah %p: count %d (on exit)\n", __func__,
-		  (void *)wsi, (void *)wsi->u.hdr.ah, pt->ah_count_in_use);
-
-	lws_pt_unlock(pt);
-
-reset:
-
-	/* and reset the rx state */
-	wsi->u.hdr.ah->rxpos = 0;
-	wsi->u.hdr.ah->rxlen = 0;
-
-	lws_header_table_reset(wsi, autoservice);
-
-#ifndef LWS_NO_CLIENT
-	if (wsi->state == LWSS_CLIENT_UNCONNECTED)
-		if (!lws_client_connect_via_info2(wsi))
-			/* our client connect has failed, the wsi
-			 * has been closed
-			 */
-			return -1;
-#endif
-
-	return 0;
-
-bail:
-	lws_pt_unlock(pt);
-
-	return 1;
-}
-
-void
-lws_header_table_force_to_detachable_state(struct lws *wsi)
-{
-	if (wsi->u.hdr.ah) {
-		wsi->u.hdr.ah->rxpos = -1;
-		wsi->u.hdr.ah->rxlen = -1;
-		wsi->hdr_parsing_completed = 1;
-	}
-}
-
-int
-lws_header_table_is_in_detachable_state(struct lws *wsi)
-{
-	struct allocated_headers *ah = wsi->u.hdr.ah;
-
-	return ah && ah->rxpos == ah->rxlen && wsi->hdr_parsing_completed;
-}
-
-int lws_header_table_detach(struct lws *wsi, int autoservice)
-{
-	struct lws_context *context = wsi->context;
-	struct allocated_headers *ah = wsi->u.hdr.ah;
-	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-	struct lws_pollargs pa;
-	struct lws **pwsi, **pwsi_eligible;
-	time_t now;
-
-	lws_pt_lock(pt);
-	__lws_remove_from_ah_waiting_list(wsi);
-	lws_pt_unlock(pt);
-
-	if (!ah)
-		return 0;
-
-	lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
-		  (void *)wsi, (void *)ah, wsi->tsi,
-		  pt->ah_count_in_use);
-
-	if (wsi->u.hdr.preamble_rx)
-		lws_free_set_NULL(wsi->u.hdr.preamble_rx);
-
-	/* may not be detached while he still has unprocessed rx */
-	if (!lws_header_table_is_in_detachable_state(wsi)) {
-		lwsl_err("%s: %p: CANNOT DETACH rxpos:%d, rxlen:%d, "
-			 "wsi->hdr_parsing_completed = %d\n", __func__, wsi,
-			 ah->rxpos, ah->rxlen, wsi->hdr_parsing_completed);
-		return 0;
-	}
-
-	lws_pt_lock(pt);
-
-	/* we did have an ah attached */
-	time(&now);
-	if (ah->assigned && now - ah->assigned > 3) {
-		/*
-		 * we're detaching the ah, but it was held an
-		 * unreasonably long time
-		 */
-		lwsl_debug("%s: wsi %p: ah held %ds, "
-			    "ah.rxpos %d, ah.rxlen %d, mode/state %d %d,"
-			    "wsi->more_rx_waiting %d\n", __func__, wsi,
-			    (int)(now - ah->assigned),
-			    ah->rxpos, ah->rxlen, wsi->mode, wsi->state,
-			    wsi->more_rx_waiting);
-	}
-
-	ah->assigned = 0;
-
-	/* if we think we're detaching one, there should be one in use */
-	assert(pt->ah_count_in_use > 0);
-	/* and this specific one should have been in use */
-	assert(ah->in_use);
-	wsi->u.hdr.ah = NULL;
-	ah->wsi = NULL; /* no owner */
-#if defined(LWS_WITH_PEER_LIMITS)
-	lws_peer_track_ah_detach(context, wsi->peer);
-#endif
-
-	pwsi = &pt->ah_wait_list;
-
-	/* oh there is nobody on the waiting list... leave the ah unattached */
-	if (!*pwsi)
-		goto nobody_usable_waiting;
-
-	/*
-	 * at least one wsi on the same tsi is waiting, give it to oldest guy
-	 * who is allowed to take it (if any)
-	 */
-	lwsl_info("pt wait list %p\n", *pwsi);
-	wsi = NULL;
-	pwsi_eligible = NULL;
-
-	while (*pwsi) {
-#if defined(LWS_WITH_PEER_LIMITS)
-		/* are we willing to give this guy an ah? */
-		if (!lws_peer_confirm_ah_attach_ok(context, (*pwsi)->peer))
-#endif
-		{
-			wsi = *pwsi;
-			pwsi_eligible = pwsi;
-		}
-#if defined(LWS_WITH_PEER_LIMITS)
-		else
-			if (!(*pwsi)->u.hdr.ah_wait_list)
-				lws_stats_atomic_bump(context, pt,
-					LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
-#endif
-		pwsi = &(*pwsi)->u.hdr.ah_wait_list;
-	}
-
-	if (!wsi) /* everybody waiting already has too many ah... */
-		goto nobody_usable_waiting;
-
-	lwsl_info("%s: last eligible wsi in wait list %p\n", __func__, wsi);
-
-	wsi->u.hdr.ah = ah;
-	ah->wsi = wsi; /* new owner */
-
-	/* and reset the rx state */
-	ah->rxpos = 0;
-	ah->rxlen = 0;
-	lws_header_table_reset(wsi, autoservice);
-#if defined(LWS_WITH_PEER_LIMITS)
-	if (wsi->peer)
-		wsi->peer->count_ah++;
-#endif
-
-	/* clients acquire the ah and then insert themselves in fds table... */
-	if (wsi->position_in_fds_table != -1) {
-		lwsl_info("%s: Enabling %p POLLIN\n", __func__, wsi);
-
-		/* he has been stuck waiting for an ah, but now his wait is
-		 * over, let him progress */
-
-		_lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
-	}
-
-	/* point prev guy to next guy in list instead */
-	*pwsi_eligible = wsi->u.hdr.ah_wait_list;
-	/* the guy who got one is out of the list */
-	wsi->u.hdr.ah_wait_list = NULL;
-	pt->ah_wait_list_length--;
-
-#ifndef LWS_NO_CLIENT
-	if (wsi->state == LWSS_CLIENT_UNCONNECTED) {
-		lws_pt_unlock(pt);
-
-		if (!lws_client_connect_via_info2(wsi)) {
-			/* our client connect has failed, the wsi
-			 * has been closed
-			 */
-
-			return -1;
-		}
-		return 0;
-	}
-#endif
-
-	assert(!!pt->ah_wait_list_length == !!(lws_intptr_t)pt->ah_wait_list);
-bail:
-	lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
-		  (void *)wsi, (void *)ah, pt->tid, pt->ah_count_in_use);
-
-	lws_pt_unlock(pt);
-
-	return 0;
-
-nobody_usable_waiting:
-	lwsl_info("%s: nobody usable waiting\n", __func__);
-	_lws_destroy_ah(pt, ah);
-	pt->ah_count_in_use--;
-
-	goto bail;
-}
-
-LWS_VISIBLE int
-lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx)
-{
-	int n;
-
-	if (!wsi->u.hdr.ah)
-		return 0;
-
-	n = wsi->u.hdr.ah->frag_index[h];
-	if (!n)
-		return 0;
-	do {
-		if (!frag_idx)
-			return wsi->u.hdr.ah->frags[n].len;
-		n = wsi->u.hdr.ah->frags[n].nfrag;
-	} while (frag_idx-- && n);
-
-	return 0;
-}
-
-LWS_VISIBLE int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h)
-{
-	int n;
-	int len = 0;
-
-	if (!wsi->u.hdr.ah)
-		return 0;
-
-	n = wsi->u.hdr.ah->frag_index[h];
-	if (!n)
-		return 0;
-	do {
-		len += wsi->u.hdr.ah->frags[n].len;
-		n = wsi->u.hdr.ah->frags[n].nfrag;
-	} while (n);
-
-	return len;
-}
-
-LWS_VISIBLE int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len,
-				      enum lws_token_indexes h, int frag_idx)
-{
-	int n = 0;
-	int f;
-
-	if (!wsi->u.hdr.ah)
-		return -1;
-
-	f = wsi->u.hdr.ah->frag_index[h];
-
-	if (!f)
-		return -1;
-
-	while (n < frag_idx) {
-		f = wsi->u.hdr.ah->frags[f].nfrag;
-		if (!f)
-			return -1;
-		n++;
-	}
-
-	if (wsi->u.hdr.ah->frags[f].len >= len)
-		return -1;
-
-	memcpy(dst, wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[f].offset,
-	       wsi->u.hdr.ah->frags[f].len);
-	dst[wsi->u.hdr.ah->frags[f].len] = '\0';
-
-	return wsi->u.hdr.ah->frags[f].len;
-}
-
-LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len,
-			     enum lws_token_indexes h)
-{
-	int toklen = lws_hdr_total_length(wsi, h);
-	int n;
-
-	if (toklen >= len)
-		return -1;
-
-	if (!wsi->u.hdr.ah)
-		return -1;
-
-	n = wsi->u.hdr.ah->frag_index[h];
-	if (!n)
-		return 0;
-
-	do {
-		if (wsi->u.hdr.ah->frags[n].len >= len)
-			return -1;
-		strncpy(dst, &wsi->u.hdr.ah->data[wsi->u.hdr.ah->frags[n].offset],
-		        wsi->u.hdr.ah->frags[n].len);
-		dst += wsi->u.hdr.ah->frags[n].len;
-		len -= wsi->u.hdr.ah->frags[n].len;
-		n = wsi->u.hdr.ah->frags[n].nfrag;
-	} while (n);
-	*dst = '\0';
-
-	return toklen;
-}
-
-char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)
-{
-	int n;
-
-	n = wsi->u.hdr.ah->frag_index[h];
-	if (!n)
-		return NULL;
-
-	return wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[n].offset;
-}
-
-int LWS_WARN_UNUSED_RESULT
-lws_pos_in_bounds(struct lws *wsi)
-{
-	if (wsi->u.hdr.ah->pos <
-	    (unsigned int)wsi->context->max_http_header_data)
-		return 0;
-
-	if (wsi->u.hdr.ah->pos == wsi->context->max_http_header_data) {
-		lwsl_err("Ran out of header data space\n");
-		return 1;
-	}
-
-	/*
-	 * with these tests everywhere, it should never be able to exceed
-	 * the limit, only meet it
-	 */
-	lwsl_err("%s: pos %d, limit %d\n", __func__, wsi->u.hdr.ah->pos,
-		 wsi->context->max_http_header_data);
-	assert(0);
-
-	return 1;
-}
-
-int LWS_WARN_UNUSED_RESULT
-lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
-{
-	wsi->u.hdr.ah->nfrag++;
-	if (wsi->u.hdr.ah->nfrag == ARRAY_SIZE(wsi->u.hdr.ah->frags)) {
-		lwsl_warn("More hdr frags than we can deal with, dropping\n");
-		return -1;
-	}
-
-	wsi->u.hdr.ah->frag_index[h] = wsi->u.hdr.ah->nfrag;
-
-	wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].offset = wsi->u.hdr.ah->pos;
-	wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len = 0;
-	wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].nfrag = 0;
-
-	do {
-		if (lws_pos_in_bounds(wsi))
-			return -1;
-
-		wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = *s;
-		if (*s)
-			wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++;
-	} while (*s++);
-
-	return 0;
-}
-
-signed char char_to_hex(const char c)
-{
-	if (c >= '0' && c <= '9')
-		return c - '0';
-
-	if (c >= 'a' && c <= 'f')
-		return c - 'a' + 10;
-
-	if (c >= 'A' && c <= 'F')
-		return c - 'A' + 10;
-
-	return -1;
-}
-
-static int LWS_WARN_UNUSED_RESULT
-issue_char(struct lws *wsi, unsigned char c)
-{
-	unsigned short frag_len;
-
-	if (lws_pos_in_bounds(wsi))
-		return -1;
-
-	frag_len = wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len;
-	/*
-	 * If we haven't hit the token limit, just copy the character into
-	 * the header
-	 */
-	if (frag_len < wsi->u.hdr.current_token_limit) {
-		wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = c;
-		if (c)
-			wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++;
-		return 0;
-	}
-
-	/* Insert a null character when we *hit* the limit: */
-	if (frag_len == wsi->u.hdr.current_token_limit) {
-		if (lws_pos_in_bounds(wsi))
-			return -1;
-
-		wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = '\0';
-		lwsl_warn("header %i exceeds limit %d\n",
-			  wsi->u.hdr.parser_state,
-			  wsi->u.hdr.current_token_limit);
-	}
-
-	return 1;
-}
-
-int
-lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
-{
-	struct allocated_headers *ah = wsi->u.hdr.ah;
-	unsigned int enc = 0;
-	uint8_t c = *_c;
-
-	/*
-	 * PRIORITY 1
-	 * special URI processing... convert %xx
-	 */
-	switch (wsi->u.hdr.ues) {
-	case URIES_IDLE:
-		if (c == '%') {
-			wsi->u.hdr.ues = URIES_SEEN_PERCENT;
-			goto swallow;
-		}
-		break;
-	case URIES_SEEN_PERCENT:
-		if (char_to_hex(c) < 0)
-			/* illegal post-% char */
-			goto forbid;
-
-		wsi->u.hdr.esc_stash = c;
-		wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1;
-		goto swallow;
-
-	case URIES_SEEN_PERCENT_H1:
-		if (char_to_hex(c) < 0)
-			/* illegal post-% char */
-			goto forbid;
-
-		*_c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) |
-				char_to_hex(c);
-		c = *_c;
-		enc = 1;
-		wsi->u.hdr.ues = URIES_IDLE;
-		break;
-	}
-
-	/*
-	 * PRIORITY 2
-	 * special URI processing...
-	 *  convert /.. or /... or /../ etc to /
-	 *  convert /./ to /
-	 *  convert // or /// etc to /
-	 *  leave /.dir or whatever alone
-	 */
-
-	switch (wsi->u.hdr.ups) {
-	case URIPS_IDLE:
-		if (!c)
-			return -1;
-		/* genuine delimiter */
-		if ((c == '&' || c == ';') && !enc) {
-			if (issue_char(wsi, c) < 0)
-				return -1;
-			/* swallow the terminator */
-			ah->frags[ah->nfrag].len--;
-			/* link to next fragment */
-			ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
-			ah->nfrag++;
-			if (ah->nfrag >= ARRAY_SIZE(ah->frags))
-				goto excessive;
-			/* start next fragment after the & */
-			wsi->u.hdr.post_literal_equal = 0;
-			ah->frags[ah->nfrag].offset = ah->pos;
-			ah->frags[ah->nfrag].len = 0;
-			ah->frags[ah->nfrag].nfrag = 0;
-			goto swallow;
-		}
-		/* uriencoded = in the name part, disallow */
-		if (c == '=' && enc &&
-		    ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
-		    !wsi->u.hdr.post_literal_equal) {
-			c = '_';
-			*_c =c;
-		}
-
-		/* after the real =, we don't care how many = */
-		if (c == '=' && !enc)
-			wsi->u.hdr.post_literal_equal = 1;
-
-		/* + to space */
-		if (c == '+' && !enc) {
-			c = ' ';
-			*_c = c;
-		}
-		/* issue the first / always */
-		if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
-			wsi->u.hdr.ups = URIPS_SEEN_SLASH;
-		break;
-	case URIPS_SEEN_SLASH:
-		/* swallow subsequent slashes */
-		if (c == '/')
-			goto swallow;
-		/* track and swallow the first . after / */
-		if (c == '.') {
-			wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT;
-			goto swallow;
-		}
-		wsi->u.hdr.ups = URIPS_IDLE;
-		break;
-	case URIPS_SEEN_SLASH_DOT:
-		/* swallow second . */
-		if (c == '.') {
-			wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT;
-			goto swallow;
-		}
-		/* change /./ to / */
-		if (c == '/') {
-			wsi->u.hdr.ups = URIPS_SEEN_SLASH;
-			goto swallow;
-		}
-		/* it was like /.dir ... regurgitate the . */
-		wsi->u.hdr.ups = URIPS_IDLE;
-		if (issue_char(wsi, '.') < 0)
-			return -1;
-		break;
-
-	case URIPS_SEEN_SLASH_DOT_DOT:
-
-		/* /../ or /..[End of URI] --> backup to last / */
-		if (c == '/' || c == '?') {
-			/*
-			 * back up one dir level if possible
-			 * safe against header fragmentation because
-			 * the method URI can only be in 1 fragment
-			 */
-			if (ah->frags[ah->nfrag].len > 2) {
-				ah->pos--;
-				ah->frags[ah->nfrag].len--;
-				do {
-					ah->pos--;
-					ah->frags[ah->nfrag].len--;
-				} while (ah->frags[ah->nfrag].len > 1 &&
-					 ah->data[ah->pos] != '/');
-			}
-			wsi->u.hdr.ups = URIPS_SEEN_SLASH;
-			if (ah->frags[ah->nfrag].len > 1)
-				break;
-			goto swallow;
-		}
-
-		/*  /..[^/] ... regurgitate and allow */
-
-		if (issue_char(wsi, '.') < 0)
-			return -1;
-		if (issue_char(wsi, '.') < 0)
-			return -1;
-		wsi->u.hdr.ups = URIPS_IDLE;
-		break;
-	}
-
-	if (c == '?' && !enc &&
-	    !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */
-		if (wsi->u.hdr.ues != URIES_IDLE)
-			goto forbid;
-
-		/* seal off uri header */
-		if (issue_char(wsi, '\0') < 0)
-			return -1;
-
-		/* move to using WSI_TOKEN_HTTP_URI_ARGS */
-		ah->nfrag++;
-		if (ah->nfrag >= ARRAY_SIZE(ah->frags))
-			goto excessive;
-		ah->frags[ah->nfrag].offset = ah->pos;
-		ah->frags[ah->nfrag].len = 0;
-		ah->frags[ah->nfrag].nfrag = 0;
-
-		wsi->u.hdr.post_literal_equal = 0;
-		ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
-		wsi->u.hdr.ups = URIPS_IDLE;
-		goto swallow;
-	}
-
-	return LPUR_CONTINUE;
-
-swallow:
-	return LPUR_SWALLOW;
-
-forbid:
-	return LPUR_FORBID;
-
-excessive:
-	return LPUR_EXCESSIVE;
-}
-
-static const unsigned char methods[] = {
-	WSI_TOKEN_GET_URI,
-	WSI_TOKEN_POST_URI,
-	WSI_TOKEN_OPTIONS_URI,
-	WSI_TOKEN_PUT_URI,
-	WSI_TOKEN_PATCH_URI,
-	WSI_TOKEN_DELETE_URI,
-	WSI_TOKEN_CONNECT,
-	WSI_TOKEN_HEAD_URI,
-};
-
-int LWS_WARN_UNUSED_RESULT
-lws_parse(struct lws *wsi, unsigned char c)
-{
-	struct allocated_headers *ah = wsi->u.hdr.ah;
-	struct lws_context *context = wsi->context;
-	unsigned int n, m;
-	int r;
-
-	assert(wsi->u.hdr.ah);
-
-	switch (wsi->u.hdr.parser_state) {
-	default:
-
-		lwsl_parser("WSI_TOK_(%d) '%c'\n", wsi->u.hdr.parser_state, c);
-
-		/* collect into malloc'd buffers */
-		/* optional initial space swallow */
-		if (!ah->frags[ah->frag_index[wsi->u.hdr.parser_state]].len &&
-		    c == ' ')
-			break;
-
-		for (m = 0; m < ARRAY_SIZE(methods); m++)
-			if (wsi->u.hdr.parser_state == methods[m])
-				break;
-		if (m == ARRAY_SIZE(methods))
-			/* it was not any of the methods */
-			goto check_eol;
-
-		/* special URI processing... end at space */
-
-		if (c == ' ') {
-			/* enforce starting with / */
-			if (!ah->frags[ah->nfrag].len)
-				if (issue_char(wsi, '/') < 0)
-					return -1;
-
-			if (wsi->u.hdr.ups == URIPS_SEEN_SLASH_DOT_DOT) {
-				/*
-				 * back up one dir level if possible
-				 * safe against header fragmentation because
-				 * the method URI can only be in 1 fragment
-				 */
-				if (ah->frags[ah->nfrag].len > 2) {
-					ah->pos--;
-					ah->frags[ah->nfrag].len--;
-					do {
-						ah->pos--;
-						ah->frags[ah->nfrag].len--;
-					} while (ah->frags[ah->nfrag].len > 1 &&
-						 ah->data[ah->pos] != '/');
-				}
-			}
-
-			/* begin parsing HTTP version: */
-			if (issue_char(wsi, '\0') < 0)
-				return -1;
-			wsi->u.hdr.parser_state = WSI_TOKEN_HTTP;
-			goto start_fragment;
-		}
-
-		r = lws_parse_urldecode(wsi, &c);
-		switch (r) {
-		case LPUR_CONTINUE:
-			break;
-		case LPUR_SWALLOW:
-			goto swallow;
-		case LPUR_FORBID:
-			goto forbid;
-		case LPUR_EXCESSIVE:
-			goto excessive;
-		default:
-			return -1;
-		}
-check_eol:
-		/* bail at EOL */
-		if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE &&
-		    c == '\x0d') {
-			if (wsi->u.hdr.ues != URIES_IDLE)
-				goto forbid;
-
-			c = '\0';
-			wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
-			lwsl_parser("*\n");
-		}
-
-		n = issue_char(wsi, c);
-		if ((int)n < 0)
-			return -1;
-		if (n > 0)
-			wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
-
-swallow:
-		/* per-protocol end of headers management */
-
-		if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE)
-			goto set_parsing_complete;
-		break;
-
-		/* collecting and checking a name part */
-	case WSI_TOKEN_NAME_PART:
-		lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X (mode=%d) wsi->u.hdr.lextable_pos=%d\n", c, c, wsi->mode, wsi->u.hdr.lextable_pos);
-
-		wsi->u.hdr.lextable_pos =
-				lextable_decode(wsi->u.hdr.lextable_pos, c);
-		/*
-		 * Server needs to look out for unknown methods...
-		 */
-		if (wsi->u.hdr.lextable_pos < 0 &&
-		    (wsi->mode == LWSCM_HTTP_SERVING)) {
-			/* this is not a header we know about */
-			for (m = 0; m < ARRAY_SIZE(methods); m++)
-				if (ah->frag_index[methods[m]]) {
-					/*
-					 * already had the method, no idea what
-					 * this crap from the client is, ignore
-					 */
-					wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
-					break;
-				}
-			/*
-			 * hm it's an unknown http method from a client in fact,
-			 * it cannot be valid http
-			 */
-			if (m == ARRAY_SIZE(methods)) {
-				/*
-				 * are we set up to accept raw in these cases?
-				 */
-				if (lws_check_opt(wsi->vhost->options,
-					   LWS_SERVER_OPTION_FALLBACK_TO_RAW))
-					return 2; /* transition to raw */
-
-				lwsl_info("Unknown method - dropping\n");
-				goto forbid;
-			}
-			break;
-		}
-		/*
-		 * ...otherwise for a client, let him ignore unknown headers
-		 * coming from the server
-		 */
-		if (wsi->u.hdr.lextable_pos < 0) {
-			wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
-			break;
-		}
-
-		if (lextable[wsi->u.hdr.lextable_pos] < FAIL_CHAR) {
-			/* terminal state */
-
-			n = ((unsigned int)lextable[wsi->u.hdr.lextable_pos] << 8) |
-					lextable[wsi->u.hdr.lextable_pos + 1];
-
-			lwsl_parser("known hdr %d\n", n);
-			for (m = 0; m < ARRAY_SIZE(methods); m++)
-				if (n == methods[m] &&
-				    ah->frag_index[methods[m]]) {
-					lwsl_warn("Duplicated method\n");
-					return -1;
-				}
-
-			/*
-			 * WSORIGIN is protocol equiv to ORIGIN,
-			 * JWebSocket likes to send it, map to ORIGIN
-			 */
-			if (n == WSI_TOKEN_SWORIGIN)
-				n = WSI_TOKEN_ORIGIN;
-
-			wsi->u.hdr.parser_state = (enum lws_token_indexes)
-							(WSI_TOKEN_GET_URI + n);
-
-			if (context->token_limits)
-				wsi->u.hdr.current_token_limit =
-					context->token_limits->token_limit[
-						       wsi->u.hdr.parser_state];
-			else
-				wsi->u.hdr.current_token_limit =
-					wsi->context->max_http_header_data;
-
-			if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE)
-				goto set_parsing_complete;
-
-			goto start_fragment;
-		}
-		break;
-
-start_fragment:
-		ah->nfrag++;
-excessive:
-		if (ah->nfrag == ARRAY_SIZE(ah->frags)) {
-			lwsl_warn("More hdr frags than we can deal with\n");
-			return -1;
-		}
-
-		ah->frags[ah->nfrag].offset = ah->pos;
-		ah->frags[ah->nfrag].len = 0;
-		ah->frags[ah->nfrag].nfrag = 0;
-		ah->frags[ah->nfrag].flags = 2;
-
-		n = ah->frag_index[wsi->u.hdr.parser_state];
-		if (!n) { /* first fragment */
-			ah->frag_index[wsi->u.hdr.parser_state] = ah->nfrag;
-			ah->hdr_token_idx = wsi->u.hdr.parser_state;
-			break;
-		}
-		/* continuation */
-		while (ah->frags[n].nfrag)
-			n = ah->frags[n].nfrag;
-		ah->frags[n].nfrag = ah->nfrag;
-
-		if (issue_char(wsi, ' ') < 0)
-			return -1;
-		break;
-
-		/* skipping arg part of a name we didn't recognize */
-	case WSI_TOKEN_SKIPPING:
-		lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c);
-
-		if (c == '\x0d')
-			wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
-		break;
-
-	case WSI_TOKEN_SKIPPING_SAW_CR:
-		lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
-		if (wsi->u.hdr.ues != URIES_IDLE)
-			goto forbid;
-		if (c == '\x0a') {
-			wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
-			wsi->u.hdr.lextable_pos = 0;
-		} else
-			wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
-		break;
-		/* we're done, ignore anything else */
-
-	case WSI_PARSING_COMPLETE:
-		lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c);
-		break;
-	}
-
-	return 0;
-
-set_parsing_complete:
-	if (wsi->u.hdr.ues != URIES_IDLE)
-		goto forbid;
-	if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
-		if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
-			wsi->ietf_spec_revision =
-			       atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
-
-		lwsl_parser("v%02d hdrs completed\n", wsi->ietf_spec_revision);
-	}
-	wsi->u.hdr.parser_state = WSI_PARSING_COMPLETE;
-	wsi->hdr_parsing_completed = 1;
-
-	return 0;
-
-forbid:
-	lwsl_notice(" forbidding on uri sanitation\n");
-	lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
-
-	return -1;
-}
-
-LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi)
-{
-	return wsi->u.ws.frame_is_binary;
-}
-
-void
-lws_add_wsi_to_draining_ext_list(struct lws *wsi)
-{
-	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-
-	if (wsi->u.ws.rx_draining_ext)
-		return;
-
-	lwsl_ext("%s: RX EXT DRAINING: Adding to list\n", __func__);
-
-	wsi->u.ws.rx_draining_ext = 1;
-	wsi->u.ws.rx_draining_ext_list = pt->rx_draining_ext_list;
-	pt->rx_draining_ext_list = wsi;
-}
-
-void
-lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
-{
-	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-	struct lws **w = &pt->rx_draining_ext_list;
-
-	if (!wsi->u.ws.rx_draining_ext)
-		return;
-
-	lwsl_ext("%s: RX EXT DRAINING: Removing from list\n", __func__);
-
-	wsi->u.ws.rx_draining_ext = 0;
-
-	/* remove us from context draining ext list */
-	while (*w) {
-		if (*w == wsi) {
-			/* if us, point it instead to who we were pointing to */
-			*w = wsi->u.ws.rx_draining_ext_list;
-			break;
-		}
-		w = &((*w)->u.ws.rx_draining_ext_list);
-	}
-	wsi->u.ws.rx_draining_ext_list = NULL;
-}
-
-/*
- * client-parser.c: lws_client_rx_sm() needs to be roughly kept in
- *   sync with changes here, esp related to ext draining
- */
-
-int
-lws_rx_sm(struct lws *wsi, unsigned char c)
-{
-	int callback_action = LWS_CALLBACK_RECEIVE;
-	int ret = 0, n, rx_draining_ext = 0;
-	struct lws_tokens eff_buf;
-
-	eff_buf.token = NULL;
-	eff_buf.token_len = 0;
-	if (wsi->socket_is_permanently_unusable)
-		return -1;
-
-	switch (wsi->lws_rx_parse_state) {
-	case LWS_RXPS_NEW:
-		if (wsi->u.ws.rx_draining_ext) {
-			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;
-		}
-		switch (wsi->ietf_spec_revision) {
-		case 13:
-			/*
-			 * no prepended frame key any more
-			 */
-			wsi->u.ws.all_zero_nonce = 1;
-			goto handle_first;
-
-		default:
-			lwsl_warn("lws_rx_sm: unknown spec version %d\n",
-						       wsi->ietf_spec_revision);
-			break;
-		}
-		break;
-	case LWS_RXPS_04_mask_1:
-		wsi->u.ws.mask[1] = c;
-		if (c)
-			wsi->u.ws.all_zero_nonce = 0;
-		wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2;
-		break;
-	case LWS_RXPS_04_mask_2:
-		wsi->u.ws.mask[2] = c;
-		if (c)
-			wsi->u.ws.all_zero_nonce = 0;
-		wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3;
-		break;
-	case LWS_RXPS_04_mask_3:
-		wsi->u.ws.mask[3] = c;
-		if (c)
-			wsi->u.ws.all_zero_nonce = 0;
-
-		/*
-		 * start from the zero'th byte in the XOR key buffer since
-		 * this is the start of a frame with a new key
-		 */
-
-		wsi->u.ws.mask_idx = 0;
-
-		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1;
-		break;
-
-	/*
-	 *  04 logical framing from the spec (all this is masked when incoming
-	 *  and has to be unmasked)
-	 *
-	 * We ignore the possibility of extension data because we don't
-	 * negotiate any extensions at the moment.
-	 *
-	 *    0                   1                   2                   3
-	 *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-	 *   +-+-+-+-+-------+-+-------------+-------------------------------+
-	 *   |F|R|R|R| opcode|R| Payload len |    Extended payload length    |
-	 *   |I|S|S|S|  (4)  |S|     (7)     |             (16/63)           |
-	 *   |N|V|V|V|       |V|             |   (if payload len==126/127)   |
-	 *   | |1|2|3|       |4|             |                               |
-	 *   +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
-	 *   |     Extended payload length continued, if payload len == 127  |
-	 *   + - - - - - - - - - - - - - - - +-------------------------------+
-	 *   |                               |         Extension data        |
-	 *   +-------------------------------+ - - - - - - - - - - - - - - - +
-	 *   :                                                               :
-	 *   +---------------------------------------------------------------+
-	 *   :                       Application data                        :
-	 *   +---------------------------------------------------------------+
-	 *
-	 *  We pass payload through to userland as soon as we get it, ignoring
-	 *  FIN.  It's up to userland to buffer it up if it wants to see a
-	 *  whole unfragmented block of the original size (which may be up to
-	 *  2^63 long!)
-	 */
-
-	case LWS_RXPS_04_FRAME_HDR_1:
-handle_first:
-
-		wsi->u.ws.opcode = c & 0xf;
-		wsi->u.ws.rsv = c & 0x70;
-		wsi->u.ws.final = !!((c >> 7) & 1);
-
-		switch (wsi->u.ws.opcode) {
-		case LWSWSOPC_TEXT_FRAME:
-		case LWSWSOPC_BINARY_FRAME:
-			wsi->u.ws.rsv_first_msg = (c & 0x70);
-			wsi->u.ws.frame_is_binary =
-			     wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME;
-			wsi->u.ws.first_fragment = 1;
-			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;
-		}
-		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
-		break;
-
-	case LWS_RXPS_04_FRAME_HDR_LEN:
-
-		wsi->u.ws.this_frame_masked = !!(c & 0x80);
-
-		switch (c & 0x7f) {
-		case 126:
-			/* control frames are not allowed to have big lengths */
-			if (wsi->u.ws.opcode & 8)
-				goto illegal_ctl_length;
-
-			wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
-			break;
-		case 127:
-			/* control frames are not allowed to have big lengths */
-			if (wsi->u.ws.opcode & 8)
-				goto illegal_ctl_length;
-
-			wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
-			break;
-		default:
-			wsi->u.ws.rx_packet_length = c & 0x7f;
-			if (wsi->u.ws.this_frame_masked)
-				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 =
-					LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
-				else {
-					wsi->lws_rx_parse_state = LWS_RXPS_NEW;
-					goto spill;
-				}
-			break;
-		}
-		break;
-
-	case LWS_RXPS_04_FRAME_HDR_LEN16_2:
-		wsi->u.ws.rx_packet_length = c << 8;
-		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
-		break;
-
-	case LWS_RXPS_04_FRAME_HDR_LEN16_1:
-		wsi->u.ws.rx_packet_length |= c;
-		if (wsi->u.ws.this_frame_masked)
-			wsi->lws_rx_parse_state =
-					LWS_RXPS_07_COLLECT_FRAME_KEY_1;
-		else
-			wsi->lws_rx_parse_state =
-				LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
-		break;
-
-	case LWS_RXPS_04_FRAME_HDR_LEN64_8:
-		if (c & 0x80) {
-			lwsl_warn("b63 of length must be zero\n");
-			/* kill the connection */
-			return -1;
-		}
-#if defined __LP64__
-		wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
-#else
-		wsi->u.ws.rx_packet_length = 0;
-#endif
-		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
-		break;
-
-	case LWS_RXPS_04_FRAME_HDR_LEN64_7:
-#if defined __LP64__
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
-#endif
-		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
-		break;
-
-	case LWS_RXPS_04_FRAME_HDR_LEN64_6:
-#if defined __LP64__
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
-#endif
-		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
-		break;
-
-	case LWS_RXPS_04_FRAME_HDR_LEN64_5:
-#if defined __LP64__
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
-#endif
-		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
-		break;
-
-	case LWS_RXPS_04_FRAME_HDR_LEN64_4:
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
-		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
-		break;
-
-	case LWS_RXPS_04_FRAME_HDR_LEN64_3:
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
-		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
-		break;
-
-	case LWS_RXPS_04_FRAME_HDR_LEN64_2:
-		wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
-		wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
-		break;
-
-	case LWS_RXPS_04_FRAME_HDR_LEN64_1:
-		wsi->u.ws.rx_packet_length |= ((size_t)c);
-		if (wsi->u.ws.this_frame_masked)
-			wsi->lws_rx_parse_state =
-					LWS_RXPS_07_COLLECT_FRAME_KEY_1;
-		else
-			wsi->lws_rx_parse_state =
-				LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
-		break;
-
-	case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
-		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.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.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.mask[3] = c;
-		if (c)
-			wsi->u.ws.all_zero_nonce = 0;
-		wsi->lws_rx_parse_state =
-					LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
-		wsi->u.ws.mask_idx = 0;
-		if (wsi->u.ws.rx_packet_length == 0) {
-			wsi->lws_rx_parse_state = LWS_RXPS_NEW;
-			goto spill;
-		}
-		break;
-
-
-	case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
-		assert(wsi->u.ws.rx_ubuf);
-
-		if (wsi->u.ws.rx_draining_ext)
-			goto drain_extension;
-
-		if (wsi->u.ws.rx_ubuf_head + LWS_PRE >=
-		    wsi->u.ws.rx_ubuf_alloc) {
-			lwsl_err("Attempted overflow \n");
-			return -1;
-		}
-		if (wsi->u.ws.all_zero_nonce)
-			wsi->u.ws.rx_ubuf[LWS_PRE +
-					 (wsi->u.ws.rx_ubuf_head++)] = c;
-		else
-			wsi->u.ws.rx_ubuf[LWS_PRE +
-			       (wsi->u.ws.rx_ubuf_head++)] =
-				   c ^ wsi->u.ws.mask[
-					    (wsi->u.ws.mask_idx++) & 3];
-
-		if (--wsi->u.ws.rx_packet_length == 0) {
-			/* spill because we have the whole frame */
-			wsi->lws_rx_parse_state = LWS_RXPS_NEW;
-			goto spill;
-		}
-
-		/*
-		 * if there's no protocol max frame size given, we are
-		 * supposed to default to context->pt_serv_buf_size
-		 */
-		if (!wsi->protocol->rx_buffer_size &&
-		    wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size)
-			break;
-
-		if (wsi->protocol->rx_buffer_size &&
-		    wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size)
-			break;
-
-		/* spill because we filled our rx buffer */
-spill:
-		/*
-		 * is this frame a control packet we should take care of at this
-		 * layer?  If so service it and hide it from the user callback
-		 */
-
-		lwsl_parser("spill on %s\n", wsi->protocol->name);
-
-		switch (wsi->u.ws.opcode) {
-		case LWSWSOPC_CLOSE:
-
-			/* is this an acknowledgement of our close? */
-			if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
-				/*
-				 * fine he has told us he is closing too, let's
-				 * finish our close
-				 */
-				lwsl_parser("seen client close ack\n");
-				return -1;
-			}
-			if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
-				/* if he sends us 2 CLOSE, kill him */
-				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;
-				lwsl_parser("Closing on peer close due to Pending tx\n");
-				return -1;
-			}
-
-			if (user_callback_handle_rxflow(
-					wsi->protocol->callback, wsi,
-					LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
-					wsi->user_space,
-					&wsi->u.ws.rx_ubuf[LWS_PRE],
-					wsi->u.ws.rx_ubuf_head))
-				return -1;
-
-			lwsl_parser("server sees client close packet\n");
-			wsi->state = LWSS_RETURNED_CLOSE_ALREADY;
-			/* deal with the close packet contents as a PONG */
-			wsi->u.ws.payload_is_close = 1;
-			goto process_as_ping;
-
-		case LWSWSOPC_PING:
-			lwsl_info("received %d byte ping, sending pong\n",
-						 wsi->u.ws.rx_ubuf_head);
-
-			if (wsi->u.ws.ping_pending_flag) {
-				/*
-				 * there is already a pending ping payload
-				 * we should just log and drop
-				 */
-				lwsl_parser("DROP PING since one pending\n");
-				goto ping_drop;
-			}
-process_as_ping:
-			/* control packets can only be < 128 bytes long */
-			if (wsi->u.ws.rx_ubuf_head > 128 - 3) {
-				lwsl_parser("DROP PING payload too large\n");
-				goto ping_drop;
-			}
-
-			/* stash the pong payload */
-			memcpy(wsi->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_ubuf_head;
-			wsi->u.ws.ping_pending_flag = 1;
-
-			/* get it sent as soon as possible */
-			lws_callback_on_writable(wsi);
-ping_drop:
-			wsi->u.ws.rx_ubuf_head = 0;
-			return 0;
-
-		case LWSWSOPC_PONG:
-			lwsl_info("received pong\n");
-			lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE],
-			             wsi->u.ws.rx_ubuf_head);
-
-			if (wsi->pending_timeout == PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
-				lwsl_info("received expected PONG on wsi %p\n", wsi);
-				lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-			}
-
-			/* issue it */
-			callback_action = LWS_CALLBACK_RECEIVE_PONG;
-			break;
-
-		case LWSWSOPC_TEXT_FRAME:
-		case LWSWSOPC_BINARY_FRAME:
-		case LWSWSOPC_CONTINUATION:
-			break;
-
-		default:
-			lwsl_parser("passing opc %x up to exts\n",
-				    wsi->u.ws.opcode);
-			/*
-			 * It's something special we can't understand here.
-			 * Pass the payload up to the extension's parsing
-			 * state machine.
-			 */
-
-			eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
-			eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
-
-			if (lws_ext_cb_active(wsi, LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
-					      &eff_buf, 0) <= 0)
-				/* not handle or fail */
-				lwsl_ext("ext opc opcode 0x%x unknown\n",
-					 wsi->u.ws.opcode);
-
-			wsi->u.ws.rx_ubuf_head = 0;
-			return 0;
-		}
-
-		/*
-		 * No it's real payload, pass it up to the user callback.
-		 * It's nicely buffered with the pre-padding taken care of
-		 * so it can be sent straight out again using lws_write
-		 */
-
-		eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
-		eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
-
-		if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len)
-			goto already_done;
-
-drain_extension:
-		lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len);
-
-		if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
-		    wsi->state == LWSS_AWAITING_CLOSE_ACK)
-			goto already_done;
-
-		n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
-		/*
-		 * eff_buf may be pointing somewhere completely different now,
-		 * it's the output
-		 */
-		wsi->u.ws.first_fragment = 0;
-		if (n < 0) {
-			/*
-			 * we may rely on this to get RX, just drop connection
-			 */
-			wsi->socket_is_permanently_unusable = 1;
-			return -1;
-		}
-
-		if (rx_draining_ext && eff_buf.token_len == 0)
-			goto already_done;
-
-		if (n && eff_buf.token_len)
-			/* extension had more... main loop will come back */
-			lws_add_wsi_to_draining_ext_list(wsi);
-		else
-			lws_remove_wsi_from_draining_ext_list(wsi);
-
-		if (eff_buf.token_len > 0 ||
-		    callback_action == LWS_CALLBACK_RECEIVE_PONG) {
-			eff_buf.token[eff_buf.token_len] = '\0';
-
-			if (wsi->protocol->callback) {
-
-				if (callback_action == LWS_CALLBACK_RECEIVE_PONG)
-					lwsl_info("Doing pong callback\n");
-
-				ret = user_callback_handle_rxflow(
-						wsi->protocol->callback,
-						wsi,
-						(enum lws_callback_reasons)callback_action,
-						wsi->user_space,
-						eff_buf.token,
-						eff_buf.token_len);
-			}
-			else
-				lwsl_err("No callback on payload spill!\n");
-		}
-
-already_done:
-		wsi->u.ws.rx_ubuf_head = 0;
-		break;
-	}
-
-	return ret;
-
-illegal_ctl_length:
-
-	lwsl_warn("Control frame with xtended length is illegal\n");
-	/* kill the connection */
-	return -1;
-}
-
-LWS_VISIBLE size_t
-lws_remaining_packet_payload(struct lws *wsi)
-{
-	return wsi->u.ws.rx_packet_length;
-}
-
-/* Once we reach LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED, we know how much
- * to expect in that state and can deal with it in bulk more efficiently.
- */
-
-int
-lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf,
-				   size_t *len)
-{
-	unsigned char *buffer = *buf, mask[4];
-	int buffer_size, n;
-	unsigned int avail;
-	char *rx_ubuf;
-
-	if (wsi->protocol->rx_buffer_size)
-		buffer_size = wsi->protocol->rx_buffer_size;
-	else
-		buffer_size = wsi->context->pt_serv_buf_size;
-	avail = buffer_size - wsi->u.ws.rx_ubuf_head;
-
-	/* do not consume more than we should */
-	if (avail > wsi->u.ws.rx_packet_length)
-		avail = wsi->u.ws.rx_packet_length;
-
-	/* do not consume more than what is in the buffer */
-	if (avail > *len)
-		avail = *len;
-
-	/* we want to leave 1 byte for the parser to handle properly */
-	if (avail <= 1)
-		return 0;
-
-	avail--;
-	rx_ubuf = wsi->u.ws.rx_ubuf + LWS_PRE + wsi->u.ws.rx_ubuf_head;
-	if (wsi->u.ws.all_zero_nonce)
-		memcpy(rx_ubuf, buffer, avail);
-	else {
-
-		for (n = 0; n < 4; n++)
-			mask[n] = wsi->u.ws.mask[(wsi->u.ws.mask_idx + n) & 3];
-
-		/* deal with 4-byte chunks using unwrapped loop */
-		n = avail >> 2;
-		while (n--) {
-			*(rx_ubuf++) = *(buffer++) ^ mask[0];
-			*(rx_ubuf++) = *(buffer++) ^ mask[1];
-			*(rx_ubuf++) = *(buffer++) ^ mask[2];
-			*(rx_ubuf++) = *(buffer++) ^ mask[3];
-		}
-		/* and the remaining bytes bytewise */
-		for (n = 0; n < (int)(avail & 3); n++)
-			*(rx_ubuf++) = *(buffer++) ^ mask[n];
-
-		wsi->u.ws.mask_idx = (wsi->u.ws.mask_idx + avail) & 3;
-	}
-
-	(*buf) += avail;
-	wsi->u.ws.rx_ubuf_head += avail;
-	wsi->u.ws.rx_packet_length -= avail;
-	*len -= avail;
-
-	return avail;
-}

+ 0 - 214
thirdparty/libwebsockets/server/ranges.c

@@ -1,214 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * RFC7233 ranges parser
- *
- * Copyright (C) 2016 Andy Green <[email protected]>
- *
- *  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;
-}

+ 0 - 360
thirdparty/libwebsockets/server/server-handshake.c

@@ -1,360 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2013 Andy Green <[email protected]>
- *
- *  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"
-
-#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
-
-#ifndef LWS_NO_EXTENSIONS
-static int
-lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
-{
-	struct lws_context *context = wsi->context;
-	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-	char ext_name[64], *args, *end = (*p) + budget - 1;
-	const struct lws_ext_options *opts, *po;
-	const struct lws_extension *ext;
-	struct lws_ext_option_arg oa;
-	int n, m, more = 1;
-	int ext_count = 0;
-	char ignore;
-	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;
-
-	/*
-	 * break down the list of client extensions
-	 * and go through them
-	 */
-
-	if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size,
-			 WSI_TOKEN_EXTENSIONS) < 0)
-		return 1;
-
-	c = (char *)pt->serv_buf;
-	lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
-	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 != '\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 {
-			c++;
-			if (!n)
-				continue;
-		}
-
-		while (args && *args && *args == ' ')
-			args++;
-
-		/* check a client's extension against our support */
-
-		ext = wsi->vhost->extensions;
-
-		while (ext && ext->callback) {
-
-			if (strcmp(ext_name, ext->name)) {
-				ext++;
-				continue;
-			}
-
-			/*
-			 * oh, we do support this one he asked for... but let's
-			 * confirm he only gave it once
-			 */
-			for (m = 0; m < wsi->count_act_ext; m++)
-				if (wsi->active_extensions[m] == ext) {
-					lwsl_info("extension mentioned twice\n");
-					return 1; /* shenanigans */
-				}
-
-			/*
-			 * ask user code if it's OK to apply it on this
-			 * 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 (m) {
-				ext++;
-				continue;
-			}
-
-			/* apply it */
-
-			ext_count++;
-
-			/* instantiate the extension on this conn */
-
-			wsi->active_extensions[wsi->count_act_ext] = ext;
-
-			/* allow him to construct his context */
-
-			if (ext->callback(lws_get_context(wsi), ext, wsi,
-					  LWS_EXT_CB_CONSTRUCT,
-					  (void *)&wsi->act_ext_user[
-					                    wsi->count_act_ext],
-					  (void *)&opts, 0)) {
-				lwsl_info("ext %s failed construction\n",
-					    ext_name);
-				ext_count--;
-				ext++;
-
-				continue;
-			}
-
-			if (ext_count > 1)
-				*(*p)++ = ',';
-			else
-				LWS_CPYAPP(*p,
-					  "\x0d\x0aSec-WebSocket-Extensions: ");
-			*p += 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 lws_context *context, struct lws *wsi)
-{
-	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-	struct lws_process_html_args args;
-	unsigned char hash[20];
-	int n, accept_len;
-	char *response;
-	char *p;
-
-	if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
-	    !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) {
-		lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
-		goto bail;
-	}
-
-	/*
-	 * since key length is restricted above (currently 128), cannot
-	 * overflow
-	 */
-	n = sprintf((char *)pt->serv_buf,
-		    "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
-		    lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
-
-	lws_SHA1(pt->serv_buf, n, hash);
-
-	accept_len = lws_b64_encode_string((char *)hash, 20,
-			(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 (lws_ensure_user_space(wsi))
-		goto bail;
-
-	/* create the response packet */
-
-	/* make a buffer big enough for everything */
-
-	response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE;
-	p = response;
-	LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
-		      "Upgrade: WebSocket\x0d\x0a"
-		      "Connection: Upgrade\x0d\x0a"
-		      "Sec-WebSocket-Accept: ");
-	strcpy(p, (char *)pt->serv_buf);
-	p += accept_len;
-
-	/* we can only return the protocol header if:
-	 *  - one came in, and ... */
-	if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
-	    /*  - it is not an empty string */
-	    wsi->protocol->name &&
-	    wsi->protocol->name[0]) {
-		LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
-		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.
-	 *
-	 * Give him a limited write bugdet
-	 */
-	if (lws_extension_server_handshake(wsi, &p, 192))
-		goto bail;
-#endif
-	LWS_CPYAPP(p, "\x0d\x0a");
-
-	args.p = p;
-	args.max_len = ((char *)pt->serv_buf + context->pt_serv_buf_size) - p;
-	if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
-					LWS_CALLBACK_ADD_HEADERS,
-					wsi->user_space, &args, 0))
-		goto bail;
-
-	p = args.p;
-
-	/* end of response packet */
-
-	LWS_CPYAPP(p, "\x0d\x0a");
-
-	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));
-#if defined(DEBUG) && ! defined(LWS_WITH_ESP8266)
-		fwrite(response, 1,  p - response, stderr);
-#endif
-		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;
-		}
-
-	}
-
-	/* alright clean up and set ourselves into established state */
-
-	wsi->state = LWSS_ESTABLISHED;
-	wsi->lws_rx_parse_state = LWS_RXPS_NEW;
-
-	{
-		const char * uri_ptr =
-			lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
-		int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
-		const struct lws_http_mount *hit =
-			lws_find_mount(wsi, uri_ptr, uri_len);
-		if (hit && hit->cgienv &&
-		    wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
-			wsi->user_space, (void *)hit->cgienv, 0))
-			return 1;
-	}
-
-	return 0;
-
-
-bail:
-	/* caller will free up his parsing allocations */
-	return -1;
-}
-

+ 0 - 477
thirdparty/libwebsockets/server/ssl-server.c

@@ -1,477 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <[email protected]>
- *
- *  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_MBEDTLS)
-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)
-{
-#if defined(LWS_HAVE_OPENSSL_ECDH_H) && !defined(LWS_WITH_MBEDTLS)
-	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_MBEDTLS)
-	lwsl_notice(" OpenSSL doesn't support ECDH\n");
-#endif
-#endif
-	return 0;
-}
-
-#if !defined(LWS_WITH_MBEDTLS) && defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT)
-static int
-lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
-{
-	struct lws_context *context = (struct lws_context *)arg;
-	struct lws_vhost *vhost, *vh;
-	const char *servername;
-
-	if (!ssl)
-		return SSL_TLSEXT_ERR_NOACK;
-
-	/*
-	 * 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 && ssl && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
-			break;
-		vh = vh->vhost_next;
-	}
-
-	if (!vh) {
-		assert(vh); /* can't match the incoming vh? */
-		return SSL_TLSEXT_ERR_OK;
-	}
-
-	servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
-	if (!servername) {
-		/* the client doesn't know what hostname it wants */
-		lwsl_info("SNI: Unknown ServerName: %s\n", servername);
-
-		return SSL_TLSEXT_ERR_OK;
-	}
-
-	vhost = lws_select_vhost(context, vh->listen_port, servername);
-	if (!vhost) {
-		lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port);
-
-		return SSL_TLSEXT_ERR_OK;
-	}
-
-	lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port);
-
-	/* select the ssl ctx from the selected vhost for this conn */
-	SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
-
-	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 he is giving a cert filepath, take it as a sign he wants to use
-	 * it on this vhost.  User code can leave the cert filepath NULL and
-	 * set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in
-	 * which case he's expected to set up the cert himself at
-	 * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
-	 * provides the vhost SSL_CTX * in the user parameter.
-	 */
-	if (info->ssl_cert_filepath)
-		info->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX;
-
-	if (info->port != CONTEXT_PORT_NO_LISTEN) {
-
-		vhost->use_ssl = lws_check_opt(info->options,
-					LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX);
-
-		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_MBEDTLS)
-	{
-		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_MBEDTLS)
-
-	/* 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_MBEDTLS)
-		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
-	}
-
-#if !defined(LWS_WITH_MBEDTLS) && !defined(OPENSSL_NO_TLSEXT)
-	SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx,
-					       lws_ssl_server_name_cb);
-	SSL_CTX_set_tlsext_servername_arg(vhost->ssl_ctx, context);
-#endif
-
-	/*
-	 * give user code a chance to load certs into the server
-	 * allowing it to verify incoming client certs
-	 */
-#if !defined(LWS_WITH_MBEDTLS)
-	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 !defined(LWS_WITH_MBEDTLS)
-#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
-#endif
-
-	lwsl_info(" SSL options 0x%lX\n", SSL_CTX_get_options(vhost->ssl_ctx));
-
-	if (vhost->use_ssl && info->ssl_cert_filepath) {
-		/*
-		 * The user code can choose to either pass the cert and
-		 * key filepaths using the info members like this, or it can
-		 * leave them NULL; force the vhost SSL_CTX init using the info
-		 * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
-		 * set up the cert himself using the user callback
-		 * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
-		 * happened just above and has the vhost SSL_CTX * in the user
-		 * parameter.
-		 */
-#if !defined(LWS_WITH_MBEDTLS)
-		/* 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 !defined(LWS_WITH_ESP32)
-		free(p);
-		p = NULL;
-#endif
-
-		if (info->ssl_private_key_filepath) {
-			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;
-			}
-		}
-
-#if !defined(LWS_WITH_ESP32)
-		free(p);
-		p = NULL;
-#endif
-#endif
-		if (info->ssl_private_key_filepath != NULL) {
-#if !defined(LWS_WITH_MBEDTLS)
-			/* 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_MBEDTLS)
-		/* 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 (vhost->use_ssl) {
-		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;
-}
-

+ 0 - 1714
thirdparty/libwebsockets/service.c

@@ -1,1714 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <[email protected]>
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public
- *  License as published by the Free Software Foundation:
- *  version 2.1 of the License.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- *  MA  02110-1301  USA
- */
-
-#include "private-libwebsockets.h"
-
-static int
-lws_calllback_as_writeable(struct lws *wsi)
-{
-	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-	int n;
-
-	lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB, 1);
-#if defined(LWS_WITH_STATS)
-	if (wsi->active_writable_req_us) {
-		uint64_t ul = time_in_microseconds() -
-			      wsi->active_writable_req_us;
-
-		lws_stats_atomic_bump(wsi->context, pt,
-				      LWSSTATS_MS_WRITABLE_DELAY, ul);
-		lws_stats_atomic_max(wsi->context, pt,
-				     LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
-		wsi->active_writable_req_us = 0;
-	}
-#endif
-
-	switch (wsi->mode) {
-	case LWSCM_RAW:
-		n = LWS_CALLBACK_RAW_WRITEABLE;
-		break;
-	case LWSCM_RAW_FILEDESC:
-		n = LWS_CALLBACK_RAW_WRITEABLE_FILE;
-		break;
-	case LWSCM_WS_CLIENT:
-		n = LWS_CALLBACK_CLIENT_WRITEABLE;
-		break;
-	case LWSCM_WSCL_ISSUE_HTTP_BODY:
-		n = LWS_CALLBACK_CLIENT_HTTP_WRITEABLE;
-		break;
-	case LWSCM_WS_SERVING:
-		n = LWS_CALLBACK_SERVER_WRITEABLE;
-		break;
-	default:
-		n = LWS_CALLBACK_HTTP_WRITEABLE;
-		break;
-	}
-
-	return user_callback_handle_rxflow(wsi->protocol->callback,
-					   wsi, (enum lws_callback_reasons) n,
-					   wsi->user_space, NULL, 0);
-}
-
-LWS_VISIBLE int
-lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
-{
-	int write_type = LWS_WRITE_PONG;
-	struct lws_tokens eff_buf;
-#ifdef LWS_WITH_HTTP2
-	struct lws **wsi2, *wsi2a;
-#endif
-	int ret, m, n;
-
-	wsi->leave_pollout_active = 0;
-	wsi->handling_pollout = 1;
-	/*
-	 * if another thread wants POLLOUT on us, from here on while
-	 * handling_pollout is set, he will only set leave_pollout_active.
-	 * If we are going to disable POLLOUT, we will check that first.
-	 */
-
-	/*
-	 * user callback is lowest priority to get these notifications
-	 * actually, since other pending things cannot be disordered
-	 */
-
-	/* Priority 1: pending truncated sends are incomplete ws fragments
-	 *	       If anything else sent first the protocol would be
-	 *	       corrupted.
-	 */
-	if (wsi->trunc_len) {
-		//lwsl_notice("%s: completing partial\n", __func__);
-		if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
-				  wsi->trunc_len) < 0) {
-			lwsl_info("%s signalling to close\n", __func__);
-			goto bail_die;
-		}
-		/* leave POLLOUT active either way */
-		goto bail_ok;
-	} else
-		if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
-			wsi->socket_is_permanently_unusable = 1;
-			goto bail_die; /* retry closing now */
-		}
-
-	if (wsi->mode == LWSCM_WSCL_ISSUE_HTTP_BODY)
-		goto user_service;
-
-#ifdef LWS_WITH_HTTP2
-	/*
-	 * Priority 2: protocol packets
-	 */
-	if (wsi->upgraded_to_http2 && wsi->u.h2.h2n->pps) {
-		lwsl_info("servicing pps\n");
-		if (lws_h2_do_pps_send(wsi)) {
-			wsi->socket_is_permanently_unusable = 1;
-			goto bail_die;
-		}
-		if (wsi->u.h2.h2n->pps)
-			goto bail_ok;
-
-		/* we can resume whatever we were doing */
-		lws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_ENABLE |
-					 LWS_RXFLOW_REASON_H2_PPS_PENDING);
-
-		goto bail_ok; /* leave POLLOUT active */
-	}
-#endif
-
-#ifdef LWS_WITH_CGI
-	if (wsi->cgi) {
-		/* also one shot */
-		if (pollfd)
-			if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
-				lwsl_info("failed at set pollfd\n");
-				return 1;
-			}
-		goto user_service_go_again;
-	}
-#endif
-
-	/* Priority 3: pending control packets (pong or close)
-	 *
-	 * 3a: close notification packet requested from close api
-	 */
-
-	if (wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION) {
-		lwsl_debug("sending close packet\n");
-		wsi->waiting_to_send_close_frame = 0;
-		n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
-			      wsi->u.ws.close_in_ping_buffer_len,
-			      LWS_WRITE_CLOSE);
-		if (n >= 0) {
-			wsi->state = LWSS_AWAITING_CLOSE_ACK;
-			lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1);
-			lwsl_debug("sent close indication, awaiting ack\n");
-
-			goto bail_ok;
-		}
-
-		goto bail_die;
-	}
-
-	/* else, the send failed and we should just hang up */
-
-	if ((wsi->state == LWSS_ESTABLISHED &&
-	     wsi->u.ws.ping_pending_flag) ||
-	    (wsi->state == LWSS_RETURNED_CLOSE_ALREADY &&
-	     wsi->u.ws.payload_is_close)) {
-
-		if (wsi->u.ws.payload_is_close)
-			write_type = LWS_WRITE_CLOSE;
-
-		n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
-			      wsi->u.ws.ping_payload_len, write_type);
-		if (n < 0)
-			goto bail_die;
-
-		/* well he is sent, mark him done */
-		wsi->u.ws.ping_pending_flag = 0;
-		if (wsi->u.ws.payload_is_close)
-			/* oh... a close frame was it... then we are done */
-			goto bail_die;
-
-		/* otherwise for PING, leave POLLOUT active either way */
-		goto bail_ok;
-	}
-
-	if (wsi->state == LWSS_ESTABLISHED &&
-	    !wsi->socket_is_permanently_unusable &&
-	    wsi->u.ws.send_check_ping) {
-
-		lwsl_info("issuing ping on wsi %p\n", wsi);
-		wsi->u.ws.send_check_ping = 0;
-		n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
-			      0, LWS_WRITE_PING);
-		if (n < 0)
-			goto bail_die;
-
-		/*
-		 * we apparently were able to send the PING in a reasonable time
-		 * now reset the clock on our peer to be able to send the
-		 * PONG in a reasonable time.
-		 */
-
-		lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG,
-				wsi->context->timeout_secs);
-
-		goto bail_ok;
-	}
-
-	/* Priority 4: if we are closing, not allowed to send more data frags
-	 *	       which means user callback or tx ext flush banned now
-	 */
-	if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
-		goto user_service;
-
-	/* Priority 5: Tx path extension with more to send
-	 *
-	 *	       These are handled as new fragments each time around
-	 *	       So while we must block new writeable callback to enforce
-	 *	       payload ordering, but since they are always complete
-	 *	       fragments control packets can interleave OK.
-	 */
-	if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) {
-		lwsl_ext("SERVICING TX EXT DRAINING\n");
-		if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0)
-			goto bail_die;
-		/* leave POLLOUT active */
-		goto bail_ok;
-	}
-
-	/* Priority 6: user can get the callback
-	 */
-	m = lws_ext_cb_active(wsi, LWS_EXT_CB_IS_WRITEABLE, NULL, 0);
-	if (m)
-		goto bail_die;
-#ifndef LWS_NO_EXTENSIONS
-	if (!wsi->extension_data_pending)
-		goto user_service;
-#endif
-	/*
-	 * check in on the active extensions, see if they
-	 * had pending stuff to spill... they need to get the
-	 * first look-in otherwise sequence will be disordered
-	 *
-	 * NULL, zero-length eff_buf means just spill pending
-	 */
-
-	ret = 1;
-	if (wsi->mode == LWSCM_RAW || wsi->mode == LWSCM_RAW_FILEDESC)
-		ret = 0;
-
-	while (ret == 1) {
-
-		/* default to nobody has more to spill */
-
-		ret = 0;
-		eff_buf.token = NULL;
-		eff_buf.token_len = 0;
-
-		/* give every extension a chance to spill */
-
-		m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND,
-				      &eff_buf, 0);
-		if (m < 0) {
-			lwsl_err("ext reports fatal error\n");
-			goto bail_die;
-		}
-		if (m)
-			/*
-			 * at least one extension told us he has more
-			 * to spill, so we will go around again after
-			 */
-			ret = 1;
-
-		/* assuming they gave us something to send, send it */
-
-		if (eff_buf.token_len) {
-			n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
-					  eff_buf.token_len);
-			if (n < 0) {
-				lwsl_info("closing from POLLOUT spill\n");
-				goto bail_die;
-			}
-			/*
-			 * Keep amount spilled small to minimize chance of this
-			 */
-			if (n != eff_buf.token_len) {
-				lwsl_err("Unable to spill ext %d vs %d\n",
-							  eff_buf.token_len, n);
-				goto bail_die;
-			}
-		} else
-			continue;
-
-		/* no extension has more to spill */
-
-		if (!ret)
-			continue;
-
-		/*
-		 * There's more to spill from an extension, but we just sent
-		 * something... did that leave the pipe choked?
-		 */
-
-		if (!lws_send_pipe_choked(wsi))
-			/* no we could add more */
-			continue;
-
-		lwsl_info("choked in POLLOUT service\n");
-
-		/*
-		 * Yes, he's choked.  Leave the POLLOUT masked on so we will
-		 * come back here when he is unchoked.  Don't call the user
-		 * callback to enforce ordering of spilling, he'll get called
-		 * when we come back here and there's nothing more to spill.
-		 */
-
-		goto bail_ok;
-	}
-#ifndef LWS_NO_EXTENSIONS
-	wsi->extension_data_pending = 0;
-#endif
-user_service:
-	/* one shot */
-
-	if (wsi->parent_carries_io) {
-		wsi->handling_pollout = 0;
-		wsi->leave_pollout_active = 0;
-
-		return lws_calllback_as_writeable(wsi);
-	}
-
-	if (pollfd) {
-		int eff = wsi->leave_pollout_active;
-
-		if (!eff)
-			if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
-				lwsl_info("failed at set pollfd\n");
-				goto bail_die;
-			}
-
-		wsi->handling_pollout = 0;
-
-		/* cannot get leave_pollout_active set after the above */
-		if (!eff && wsi->leave_pollout_active)
-			/* got set inbetween sampling eff and clearing
-			 * handling_pollout, force POLLOUT on */
-			lws_calllback_as_writeable(wsi);
-
-		wsi->leave_pollout_active = 0;
-	}
-
-	if (wsi->mode != LWSCM_WSCL_ISSUE_HTTP_BODY &&
-	    !wsi->hdr_parsing_completed)
-		goto bail_ok;
-
-
-#ifdef LWS_WITH_CGI
-user_service_go_again:
-#endif
-
-#ifdef LWS_WITH_HTTP2
-	/*
-	 * we are the 'network wsi' for potentially many muxed child wsi with
-	 * no network connection of their own, who have to use us for all their
-	 * network actions.  So we use a round-robin scheme to share out the
-	 * POLLOUT notifications to our children.
-	 *
-	 * But because any child could exhaust the socket's ability to take
-	 * writes, we can only let one child get notified each time.
-	 *
-	 * In addition children may be closed / deleted / added between POLLOUT
-	 * notifications, so we can't hold pointers
-	 */
-
-	if (wsi->mode != LWSCM_HTTP2_SERVING) {
-		lwsl_info("%s: non http2\n", __func__);
-		goto notify;
-	}
-
-	wsi->u.h2.requested_POLLOUT = 0;
-	if (!wsi->u.h2.initialized) {
-		lwsl_info("pollout on uninitialized http2 conn\n");
-		goto bail_ok;
-	}
-
-//	if (SSL_want_read(wsi->ssl) || SSL_want_write(wsi->ssl)) {
-//		lws_callback_on_writable(wsi);
-//		goto bail_ok;
-//	}
-
-	lwsl_info("%s: %p: children waiting for POLLOUT service:\n", __func__, wsi);
-	wsi2a = wsi->u.h2.child_list;
-	while (wsi2a) {
-		if (wsi2a->u.h2.requested_POLLOUT)
-			lwsl_debug("  * %p\n", wsi2a);
-		else
-			lwsl_debug("    %p\n", wsi2a);
-
-		wsi2a = wsi2a->u.h2.sibling_list;
-	}
-
-	wsi2 = &wsi->u.h2.child_list;
-	if (!*wsi2)
-		goto bail_ok;
-
-	do {
-		struct lws *w, **wa;
-	
-		wa = &(*wsi2)->u.h2.sibling_list;
-		if (!(*wsi2)->u.h2.requested_POLLOUT) {
-			lwsl_debug("  child %p doesn't want POLLOUT\n", *wsi2);
-			goto next_child;
-		}
-
-		/*
-		 * we're going to do writable callback for this child.
-		 * move him to be the last child
-		 */
-
-		lwsl_debug("servicing child %p\n", *wsi2);
-
-		w = *wsi2;
-		while (w) {
-			if (!w->u.h2.sibling_list) { /* w is the current last */
-				lwsl_debug("w=%p, *wsi2 = %p\n", w, *wsi2);
-				if (w == *wsi2) /* we are already last */
-					break;
-				w->u.h2.sibling_list = *wsi2; /* last points to us as new last */
-				*wsi2 = (*wsi2)->u.h2.sibling_list; /* guy pointing to us until now points to our old next */
-				w->u.h2.sibling_list->u.h2.sibling_list = NULL; /* we point to nothing because we are last */
-				w = w->u.h2.sibling_list; /* w becomes us */
-				break;
-			}
-			w = w->u.h2.sibling_list;
-		}
-
-		w->u.h2.requested_POLLOUT = 0;
-		lwsl_info("%s: child %p (state %d)\n", __func__, (*wsi2), (*wsi2)->state);
-
-		if (w->u.h2.pending_status_body) {
-			w->u.h2.send_END_STREAM = 1;
-			n = lws_write(w,
-				      (uint8_t *)w->u.h2.pending_status_body + LWS_PRE,
-				      strlen(w->u.h2.pending_status_body + LWS_PRE),
-				      LWS_WRITE_HTTP_FINAL);
-			lws_free_set_NULL(w->u.h2.pending_status_body);
-			lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
-			wa = &wsi->u.h2.child_list;
-			goto next_child;
-		}
-
-		if (w->state == LWSS_HTTP_ISSUING_FILE) {
-
-			w->leave_pollout_active = 0;
-
-			/* >0 == completion, <0 == error
-			 *
-			 * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
-			 * it's done.  That's the case even if we just completed the
-			 * send, so wait for that.
-			 */
-			n = lws_serve_http_file_fragment(w);
-			lwsl_debug("lws_serve_http_file_fragment says %d\n", n);
-
-			/*
-			 * We will often hear about out having sent the final
-			 * DATA here... if so close the actual wsi
-			 */
-			if (n < 0 || w->u.h2.send_END_STREAM) {
-				lwsl_debug("Closing POLLOUT child %p\n", w);
-				lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
-				wa = &wsi->u.h2.child_list;
-				goto next_child;
-			}
-			if (n > 0)
-				if (lws_http_transaction_completed(w))
-					goto bail_die;
-			if (!n) {
-				lws_callback_on_writable(w);
-				(w)->u.h2.requested_POLLOUT = 1;
-			}
-
-			goto next_child;
-		}
-
-		if (lws_calllback_as_writeable(w) || w->u.h2.send_END_STREAM) {
-			lwsl_debug("Closing POLLOUT child\n");
-			lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
-			wa = &wsi->u.h2.child_list;
-		}
-
-next_child:
-		wsi2 = wa;
-	} while (wsi2 && *wsi2 && !lws_send_pipe_choked(wsi));
-
-	lwsl_info("%s: %p: children waiting for POLLOUT service: %p\n", __func__, wsi, wsi->u.h2.child_list);
-	wsi2a = wsi->u.h2.child_list;
-	while (wsi2a) {
-		if (wsi2a->u.h2.requested_POLLOUT)
-			lwsl_debug("  * %p\n", wsi2a);
-		else
-			lwsl_debug("    %p\n", wsi2a);
-
-		wsi2a = wsi2a->u.h2.sibling_list;
-	}
-
-
-	wsi2a = wsi->u.h2.child_list;
-	while (wsi2a) {
-		if (wsi2a->u.h2.requested_POLLOUT) {
-			lws_change_pollfd(wsi, 0, LWS_POLLOUT);
-			break;
-		}
-		wsi2a = wsi2a->u.h2.sibling_list;
-	}
-
-	goto bail_ok;
-
-
-notify:
-#endif
-	wsi->leave_pollout_active = 0;
-
-	n = lws_calllback_as_writeable(wsi);
-	wsi->handling_pollout = 0;
-
-	if (wsi->leave_pollout_active)
-		lws_change_pollfd(wsi, 0, LWS_POLLOUT);
-
-	return n;
-
-	/*
-	 * since these don't disable the POLLOUT, they are always doing the
-	 * right thing for leave_pollout_active whether it was set or not.
-	 */
-
-bail_ok:
-	wsi->handling_pollout = 0;
-	wsi->leave_pollout_active = 0;
-
-	return 0;
-
-bail_die:
-	wsi->handling_pollout = 0;
-	wsi->leave_pollout_active = 0;
-
-	return -1;
-}
-
-int
-lws_service_timeout_check(struct lws *wsi, unsigned int sec)
-{
-	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-	int n = 0;
-
-	(void)n;
-
-	/*
-	 * if extensions want in on it (eg, we are a mux parent)
-	 * give them a chance to service child timeouts
-	 */
-	if (lws_ext_cb_active(wsi, LWS_EXT_CB_1HZ, NULL, sec) < 0)
-		return 0;
-
-	if (!wsi->pending_timeout)
-		return 0;
-
-	/*
-	 * if we went beyond the allowed time, kill the
-	 * connection
-	 */
-	if ((time_t)sec > wsi->pending_timeout_limit) {
-
-		if (wsi->desc.sockfd != LWS_SOCK_INVALID &&
-		    wsi->position_in_fds_table >= 0)
-			n = pt->fds[wsi->position_in_fds_table].events;
-
-		lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_TIMEOUTS, 1);
-
-		/* no need to log normal idle keepalive timeout */
-		if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE)
-			lwsl_info("wsi %p: TIMEDOUT WAITING on %d "
-				  "(did hdr %d, ah %p, wl %d, pfd "
-				  "events %d) %llu vs %llu\n",
-				  (void *)wsi, wsi->pending_timeout,
-				  wsi->hdr_parsing_completed, wsi->u.hdr.ah,
-				  pt->ah_wait_list_length, n,
-				  (unsigned long long)sec,
-				  (unsigned long long)wsi->pending_timeout_limit);
-
-		/*
-		 * Since he failed a timeout, he already had a chance to do
-		 * something and was unable to... that includes situations like
-		 * half closed connections.  So process this "failed timeout"
-		 * close as a violent death and don't try to do protocol
-		 * cleanup like flush partials.
-		 */
-		wsi->socket_is_permanently_unusable = 1;
-		if (wsi->mode == LWSCM_WSCL_WAITING_SSL)
-			wsi->vhost->protocols[0].callback(wsi,
-				LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
-				wsi->user_space,
-				(void *)"Timed out waiting SSL", 21);
-
-		lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-
-		return 1;
-	}
-
-	return 0;
-}
-
-int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len)
-{
-#if defined(LWS_WITH_HTTP2)
-	if (wsi->upgraded_to_http2) {
-		struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
-
-		assert(h2n->rx_scratch);
-		buf += n;
-		len -= n;
-		assert ((char *)buf >= (char *)h2n->rx_scratch &&
-			(char *)&buf[len] <= (char *)&h2n->rx_scratch[LWS_H2_RX_SCRATCH_SIZE]);
-
-		h2n->rx_scratch_pos = ((char *)buf - (char *)h2n->rx_scratch);
-		h2n->rx_scratch_len = len;
-
-		lwsl_info("%s: %p: pausing h2 rx_scratch\n", __func__, wsi);
-
-		return 0;
-	}
-#endif
-	/* his RX is flowcontrolled, don't send remaining now */
-	if (wsi->rxflow_buffer) {
-		if (buf >= wsi->rxflow_buffer &&
-		    &buf[len - 1] < &wsi->rxflow_buffer[wsi->rxflow_len]) {
-			/* rxflow while we were spilling prev rxflow */
-			lwsl_info("%s: staying in rxflow buf\n", __func__);
-			return 1;
-		} else {
-			lwsl_err("%s: conflicting rxflow buf, "
-				 "current %p len %d, new %p len %d\n", __func__,
-				 wsi->rxflow_buffer, wsi->rxflow_len, buf, len);
-			assert(0);
-			return 1;
-		}
-	}
-
-	/* a new rxflow, buffer it and warn caller */
-	lwsl_info("%s: new rxflow input buffer len %d\n", __func__, len - n);
-	wsi->rxflow_buffer = lws_malloc(len - n, "rxflow buf");
-	if (!wsi->rxflow_buffer)
-		return -1;
-
-	wsi->rxflow_len = len - n;
-	wsi->rxflow_pos = 0;
-	memcpy(wsi->rxflow_buffer, buf + n, len - n);
-
-	return 0;
-}
-
-/* this is used by the platform service code to stop us waiting for network
- * activity in poll() when we have something that already needs service
- */
-
-LWS_VISIBLE LWS_EXTERN int
-lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
-{
-	struct lws_context_per_thread *pt = &context->pt[tsi];
-	struct allocated_headers *ah;
-
-	/* Figure out if we really want to wait in poll()
-	 * We only need to wait if really nothing already to do and we have
-	 * to wait for something from network
-	 */
-
-	/* 1) if we know we are draining rx ext, do not wait in poll */
-	if (pt->rx_draining_ext_list)
-		return 0;
-
-#ifdef LWS_OPENSSL_SUPPORT
-	/* 2) if we know we have non-network pending data, do not wait in poll */
-	if (lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) {
-		lwsl_info("ssl buffered read\n");
-		return 0;
-	}
-#endif
-
-	/* 3) if any ah has pending rx, do not wait in poll */
-	ah = pt->ah_list;
-	while (ah) {
-		if (ah->rxpos != ah->rxlen) {
-			if (!ah->wsi) {
-				assert(0);
-			}
-			return 0;
-		}
-		ah = ah->next;
-	}
-
-	return timeout_ms;
-}
-
-/*
- * guys that need POLLIN service again without waiting for network action
- * can force POLLIN here if not flowcontrolled, so they will get service.
- *
- * Return nonzero if anybody got their POLLIN faked
- */
-int
-lws_service_flag_pending(struct lws_context *context, int tsi)
-{
-	struct lws_context_per_thread *pt = &context->pt[tsi];
-	struct allocated_headers *ah;
-#ifdef LWS_OPENSSL_SUPPORT
-	struct lws *wsi_next;
-#endif
-	struct lws *wsi;
-	int forced = 0;
-
-	/* POLLIN faking */
-
-	/*
-	 * 1) For all guys with already-available ext data to drain, if they are
-	 * not flowcontrolled, fake their POLLIN status
-	 */
-	wsi = pt->rx_draining_ext_list;
-	while (wsi) {
-		pt->fds[wsi->position_in_fds_table].revents |=
-			pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
-		if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) {
-			forced = 1;
-			break;
-		}
-		wsi = wsi->u.ws.rx_draining_ext_list;
-	}
-
-#ifdef LWS_OPENSSL_SUPPORT
-	/*
-	 * 2) For all guys with buffered SSL read data already saved up, if they
-	 * are not flowcontrolled, fake their POLLIN status so they'll get
-	 * service to use up the buffered incoming data, even though their
-	 * network socket may have nothing
-	 */
-	wsi = pt->pending_read_list;
-	while (wsi) {
-		wsi_next = wsi->pending_read_list_next;
-		pt->fds[wsi->position_in_fds_table].revents |=
-			pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
-		if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) {
-			forced = 1;
-			/*
-			 * he's going to get serviced now, take him off the
-			 * list of guys with buffered SSL.  If he still has some
-			 * at the end of the service, he'll get put back on the
-			 * list then.
-			 */
-			lws_ssl_remove_wsi_from_buffered_list(wsi);
-		}
-
-		wsi = wsi_next;
-	}
-#endif
-	/*
-	 * 3) For any wsi who have an ah with pending RX who did not
-	 * complete their current headers, and are not flowcontrolled,
-	 * fake their POLLIN status so they will be able to drain the
-	 * rx buffered in the ah
-	 */
-	ah = pt->ah_list;
-	while (ah) {
-		if (ah->rxpos != ah->rxlen && !ah->wsi->hdr_parsing_completed) {
-			pt->fds[ah->wsi->position_in_fds_table].revents |=
-				pt->fds[ah->wsi->position_in_fds_table].events &
-					LWS_POLLIN;
-			if (pt->fds[ah->wsi->position_in_fds_table].revents &
-			    LWS_POLLIN) {
-				forced = 1;
-				break;
-			}
-		}
-		ah = ah->next;
-	}
-
-	return forced;
-}
-
-#ifndef LWS_NO_CLIENT
-
-LWS_VISIBLE int
-lws_http_client_read(struct lws *wsi, char **buf, int *len)
-{
-	int rlen, n;
-
-	rlen = lws_ssl_capable_read(wsi, (unsigned char *)*buf, *len);
-	*len = 0;
-
-	/* allow the source to signal he has data again next time */
-	lws_change_pollfd(wsi, 0, LWS_POLLIN);
-
-	if (rlen == LWS_SSL_CAPABLE_ERROR) {
-		lwsl_notice("%s: SSL capable error\n", __func__);
-		return -1;
-	}
-
-	if (rlen == 0)
-		return -1;
-
-	if (rlen < 0)
-		return 0;
-
-	*len = rlen;
-	wsi->client_rx_avail = 0;
-
-	/*
-	 * server may insist on transfer-encoding: chunked,
-	 * so http client must deal with it
-	 */
-spin_chunks:
-	while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) {
-		switch (wsi->chunk_parser) {
-		case ELCP_HEX:
-			if ((*buf)[0] == '\x0d') {
-				wsi->chunk_parser = ELCP_CR;
-				break;
-			}
-			n = char_to_hex((*buf)[0]);
-			if (n < 0) {
-				lwsl_debug("chunking failure\n");
-				return -1;
-			}
-			wsi->chunk_remaining <<= 4;
-			wsi->chunk_remaining |= n;
-			break;
-		case ELCP_CR:
-			if ((*buf)[0] != '\x0a') {
-				lwsl_debug("chunking failure\n");
-				return -1;
-			}
-			wsi->chunk_parser = ELCP_CONTENT;
-			lwsl_info("chunk %d\n", wsi->chunk_remaining);
-			if (wsi->chunk_remaining)
-				break;
-			lwsl_info("final chunk\n");
-			goto completed;
-
-		case ELCP_CONTENT:
-			break;
-
-		case ELCP_POST_CR:
-			if ((*buf)[0] != '\x0d') {
-				lwsl_debug("chunking failure\n");
-
-				return -1;
-			}
-
-			wsi->chunk_parser = ELCP_POST_LF;
-			break;
-
-		case ELCP_POST_LF:
-			if ((*buf)[0] != '\x0a')
-				return -1;
-
-			wsi->chunk_parser = ELCP_HEX;
-			wsi->chunk_remaining = 0;
-			break;
-		}
-		(*buf)++;
-		(*len)--;
-	}
-
-	if (wsi->chunked && !wsi->chunk_remaining)
-		return 0;
-
-	if (wsi->u.http.rx_content_remain &&
-	    wsi->u.http.rx_content_remain < *len)
-		n = (int)wsi->u.http.rx_content_remain;
-	else
-		n = *len;
-
-	if (wsi->chunked && wsi->chunk_remaining &&
-	    wsi->chunk_remaining < n)
-		n = wsi->chunk_remaining;
-
-#ifdef LWS_WITH_HTTP_PROXY
-	/* hubbub */
-	if (wsi->perform_rewrite)
-		lws_rewrite_parse(wsi->rw, (unsigned char *)*buf, n);
-	else
-#endif
-		if (user_callback_handle_rxflow(wsi->protocol->callback,
-				wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
-				wsi->user_space, *buf, n)) {
-			lwsl_debug("%s: LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ returned -1\n", __func__);
-
-			return -1;
-		}
-
-	if (wsi->chunked && wsi->chunk_remaining) {
-		(*buf) += n;
-		wsi->chunk_remaining -= n;
-		*len -= n;
-	}
-
-	if (wsi->chunked && !wsi->chunk_remaining)
-		wsi->chunk_parser = ELCP_POST_CR;
-
-	if (wsi->chunked && *len)
-		goto spin_chunks;
-
-	if (wsi->chunked)
-		return 0;
-
-	/* if we know the content length, decrement the content remaining */
-	if (wsi->u.http.rx_content_length > 0)
-		wsi->u.http.rx_content_remain -= n;
-
-	if (wsi->u.http.rx_content_remain || !wsi->u.http.rx_content_length)
-		return 0;
-
-completed:
-	if (user_callback_handle_rxflow(wsi->protocol->callback,
-			wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
-			wsi->user_space, NULL, 0)) {
-		lwsl_debug("Completed call returned -1\n");
-		return -1;
-	}
-
-	if (lws_http_transaction_completed_client(wsi)) {
-		lwsl_notice("%s: transaction completed says -1\n", __func__);
-		return -1;
-	}
-
-	return 0;
-}
-#endif
-
-static int
-lws_is_ws_with_ext(struct lws *wsi)
-{
-#if defined(LWS_NO_EXTENSIONS)
-	return 0;
-#else
-	return wsi->state == LWSS_ESTABLISHED &&
-	       !!wsi->count_act_ext;
-#endif
-}
-
-LWS_VISIBLE int
-lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int tsi)
-{
-	struct lws_context_per_thread *pt = &context->pt[tsi];
-	lws_sockfd_type our_fd = 0, tmp_fd;
-	struct allocated_headers *ah;
-	struct lws_tokens eff_buf;
-	unsigned int pending = 0;
-	struct lws *wsi, *wsi1;
-	char draining_flow = 0;
-	int timed_out = 0;
-	time_t now;
-	int n = 0, m;
-	int more;
-
-	if (!context->protocol_init_done)
-		lws_protocol_init(context);
-
-	time(&now);
-
-	/*
-	 * handle case that system time was uninitialized when lws started
-	 * at boot, and got initialized a little later
-	 */
-	if (context->time_up < 1464083026 && now > 1464083026)
-		context->time_up = now;
-
-	/* TODO: if using libev, we should probably use timeout watchers... */
-	if (context->last_timeout_check_s != now) {
-		context->last_timeout_check_s = now;
-
-#if defined(LWS_WITH_STATS)
-		if (!tsi && now - context->last_dump > 10) {
-			lws_stats_log_dump(context);
-			context->last_dump = now;
-		}
-#endif
-
-		lws_plat_service_periodic(context);
-
-		lws_check_deferred_free(context, 0);
-
-#if defined(LWS_WITH_PEER_LIMITS)
-		lws_peer_cull_peer_wait_list(context);
-#endif
-
-		/* retire unused deprecated context */
-#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_ESP32)
-#if LWS_POSIX && !defined(_WIN32)
-		if (context->deprecated && !context->count_wsi_allocated) {
-			lwsl_notice("%s: ending deprecated context\n", __func__);
-			kill(getpid(), SIGINT);
-			return 0;
-		}
-#endif
-#endif
-		/* global timeout check once per second */
-
-		if (pollfd)
-			our_fd = pollfd->fd;
-
-		/*
-		 * Phase 1: check every wsi on the timeout check list
-		 */
-
-		wsi = context->pt[tsi].timeout_list;
-		while (wsi) {
-			/* we have to take copies, because he may be deleted */
-			wsi1 = wsi->timeout_list;
-			tmp_fd = wsi->desc.sockfd;
-			if (lws_service_timeout_check(wsi, (unsigned int)now)) {
-				/* he did time out... */
-				if (tmp_fd == our_fd)
-					/* it was the guy we came to service! */
-					timed_out = 1;
-					/* he's gone, no need to mark as handled */
-			}
-			wsi = wsi1;
-		}
-
-		/*
-		 * Phase 2: double-check active ah timeouts independent of wsi
-		 *	    timeout status
-		 */
-
-		ah = pt->ah_list;
-		while (ah) {
-			int len;
-			char buf[256];
-			const unsigned char *c;
-
-			if (!ah->in_use || !ah->wsi || !ah->assigned ||
-			    (ah->wsi->vhost && now - ah->assigned <
-			    ah->wsi->vhost->timeout_secs_ah_idle + 60)) {
-				ah = ah->next;
-				continue;
-			}
-
-			/*
-			 * a single ah session somehow got held for
-			 * an unreasonable amount of time.
-			 *
-			 * Dump info on the connection...
-			 */
-			wsi = ah->wsi;
-			buf[0] = '\0';
-			lws_get_peer_simple(wsi, buf, sizeof(buf));
-			lwsl_notice("ah excessive hold: wsi %p\n"
-				    "  peer address: %s\n"
-				    "  ah rxpos %u, rxlen %u, pos %u\n",
-				    wsi, buf, ah->rxpos, ah->rxlen,
-				    ah->pos);
-			buf[0] = '\0';
-			m = 0;
-			do {
-				c = lws_token_to_string(m);
-				if (!c)
-					break;
-				if (!(*c))
-					break;
-
-				len = lws_hdr_total_length(wsi, m);
-				if (!len || len > sizeof(buf) - 1) {
-					m++;
-					continue;
-				}
-
-				if (lws_hdr_copy(wsi, buf,
-						 sizeof buf, m) > 0) {
-					buf[sizeof(buf) - 1] = '\0';
-
-					lwsl_notice("   %s = %s\n",
-						    (const char *)c, buf);
-				}
-				m++;
-			} while (1);
-
-			/* explicitly detach the ah */
-
-			lws_header_table_force_to_detachable_state(wsi);
-			lws_header_table_detach(wsi, 0);
-
-			/* ... and then drop the connection */
-
-			if (wsi->desc.sockfd == our_fd)
-				/* it was the guy we came to service! */
-				timed_out = 1;
-
-			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-
-			ah = pt->ah_list;
-		}
-
-#ifdef LWS_WITH_CGI
-		/*
-		 * Phase 3: handle cgi timeouts
-		 */
-		lws_cgi_kill_terminated(pt);
-#endif
-#if 0
-		{
-			char s[300], *p = s;
-
-			for (n = 0; n < context->count_threads; n++)
-				p += sprintf(p, " %7lu (%5d), ",
-					     context->pt[n].count_conns,
-					     context->pt[n].fds_count);
-
-			lwsl_notice("load: %s\n", s);
-		}
-#endif
-	}
-
-	/*
-	 * at intervals, check for ws connections needing ping-pong checks
-	 */
-
-	if (context->ws_ping_pong_interval &&
-	    context->last_ws_ping_pong_check_s < now + 10) {
-		struct lws_vhost *vh = context->vhost_list;
-		context->last_ws_ping_pong_check_s = now;
-
-		while (vh) {
-			for (n = 0; n < vh->count_protocols; n++) {
-				wsi = vh->same_vh_protocol_list[n];
-
-				while (wsi) {
-					if (wsi->state == LWSS_ESTABLISHED &&
-					    !wsi->socket_is_permanently_unusable &&
-					    !wsi->u.ws.send_check_ping &&
-					    wsi->u.ws.time_next_ping_check &&
-					    wsi->u.ws.time_next_ping_check < now) {
-
-						lwsl_info("requesting ping-pong on wsi %p\n", wsi);
-						wsi->u.ws.send_check_ping = 1;
-						lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING,
-								context->timeout_secs);
-						lws_callback_on_writable(wsi);
-						wsi->u.ws.time_next_ping_check = now +
-								wsi->context->ws_ping_pong_interval;
-					}
-					wsi = wsi->same_vh_protocol_next;
-				}
-			}
-			vh = vh->vhost_next;
-		}
-	}
-
-
-	/* the socket we came to service timed out, nothing to do */
-	if (timed_out)
-		return 0;
-
-	/* just here for timeout management? */
-	if (!pollfd)
-		return 0;
-
-	/* no, here to service a socket descriptor */
-	wsi = wsi_from_fd(context, pollfd->fd);
-	if (!wsi)
-		/* not lws connection ... leave revents alone and return */
-		return 0;
-
-	/*
-	 * so that caller can tell we handled, past here we need to
-	 * zero down pollfd->revents after handling
-	 */
-
-#if LWS_POSIX
-	/* handle session socket closed */
-
-	if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
-	    (pollfd->revents & LWS_POLLHUP)) {
-		wsi->socket_is_permanently_unusable = 1;
-		lwsl_debug("Session Socket %p (fd=%d) dead\n",
-						       (void *)wsi, pollfd->fd);
-
-		goto close_and_handled;
-	}
-
-#ifdef _WIN32
-	if (pollfd->revents & LWS_POLLOUT)
-		wsi->sock_send_blocking = FALSE;
-#endif
-
-#endif
-
-	if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
-			(pollfd->revents & LWS_POLLHUP)) {
-		lwsl_debug("pollhup\n");
-		wsi->socket_is_permanently_unusable = 1;
-		goto close_and_handled;
-	}
-
-#ifdef LWS_OPENSSL_SUPPORT
-	if ((wsi->state == LWSS_SHUTDOWN) && lws_is_ssl(wsi) && wsi->ssl) {
-		n = SSL_shutdown(wsi->ssl);
-		lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd);
-		switch (n) {
-		case 1:
-			n = shutdown(wsi->desc.sockfd, SHUT_WR);
-			goto close_and_handled;
-
-		case 0:
-			lws_change_pollfd(wsi, 0, LWS_POLLIN);
-			n = 0;
-			goto handled;
-
-		default:
-			n = SSL_get_error(wsi->ssl, n);
-			if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
-				if (SSL_want_read(wsi->ssl)) {
-					lwsl_debug("(wants read)\n");
-					lws_change_pollfd(wsi, 0, LWS_POLLIN);
-					n = 0;
-					goto handled;
-				}
-				if (SSL_want_write(wsi->ssl)) {
-					lwsl_debug("(wants write)\n");
-					lws_change_pollfd(wsi, 0, LWS_POLLOUT);
-					n = 0;
-					goto handled;
-				}
-			}
-
-			/* actual error occurred, just close the connection */
-			n = shutdown(wsi->desc.sockfd, SHUT_WR);
-			goto close_and_handled;
-		}
-	}
-#endif
-
-	/* okay, what we came here to do... */
-
-	switch (wsi->mode) {
-	case LWSCM_HTTP_SERVING:
-	case LWSCM_HTTP_CLIENT:
-	case LWSCM_HTTP_SERVING_ACCEPTED:
-	case LWSCM_SERVER_LISTENER:
-	case LWSCM_SSL_ACK_PENDING:
-	case LWSCM_SSL_ACK_PENDING_RAW:
-		if (wsi->state == LWSS_CLIENT_HTTP_ESTABLISHED)
-			goto handled;
-
-#ifdef LWS_WITH_CGI
-		if (wsi->cgi && (pollfd->revents & LWS_POLLOUT)) {
-			n = lws_handle_POLLOUT_event(wsi, pollfd);
-			if (n)
-				goto close_and_handled;
-			goto handled;
-		}
-#endif
-		/* fallthru */
-	case LWSCM_RAW:
-		n = lws_server_socket_service(context, wsi, pollfd);
-		if (n) /* closed by above */
-			return 1;
-		goto handled;
-
-	case LWSCM_RAW_FILEDESC:
-
-		if (pollfd->revents & LWS_POLLOUT) {
-			n = lws_calllback_as_writeable(wsi);
-			if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
-				lwsl_info("failed at set pollfd\n");
-				return 1;
-			}
-			if (n)
-				goto close_and_handled;
-		}
-		n = LWS_CALLBACK_RAW_RX;
-		if (wsi->mode == LWSCM_RAW_FILEDESC)
-			n = LWS_CALLBACK_RAW_RX_FILE;
-
-		if (pollfd->revents & LWS_POLLIN) {
-			if (user_callback_handle_rxflow(
-					wsi->protocol->callback,
-					wsi, n,
-					wsi->user_space, NULL, 0)) {
-				lwsl_debug("raw rx callback closed it\n");
-				goto close_and_handled;
-			}
-		}
-
-		if (pollfd->revents & LWS_POLLHUP)
-			goto close_and_handled;
-		n = 0;
-		goto handled;
-
-	case LWSCM_WS_SERVING:
-	case LWSCM_WS_CLIENT:
-	case LWSCM_HTTP2_SERVING:
-	case LWSCM_HTTP_CLIENT_ACCEPTED:
-
-		/* 1: something requested a callback when it was OK to write */
-
-		if ((pollfd->revents & LWS_POLLOUT) &&
-		    ((wsi->state == LWSS_ESTABLISHED ||
-		     wsi->state == LWSS_HTTP2_ESTABLISHED ||
-		     wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS ||
-		     wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
-		     wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
-		     wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE)) &&
-		    lws_handle_POLLOUT_event(wsi, pollfd)) {
-			if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
-				wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE;
-			lwsl_info("lws_service_fd: closing\n");
-			goto close_and_handled;
-		}
-
-		if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
-		    wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
-		    wsi->state == LWSS_AWAITING_CLOSE_ACK) {
-			/*
-			 * we stopped caring about anything except control
-			 * packets.  Force flow control off, defeat tx
-			 * draining.
-			 */
-			lws_rx_flow_control(wsi, 1);
-			wsi->u.ws.tx_draining_ext = 0;
-		}
-
-		if (wsi->u.ws.tx_draining_ext)
-			/* we cannot deal with new RX until the TX ext
-			 * path has been drained.  It's because new
-			 * rx will, eg, crap on the wsi rx buf that
-			 * may be needed to retain state.
-			 *
-			 * TX ext drain path MUST go through event loop
-			 * to avoid blocking.
-			 */
-			break;
-
-		if (lws_is_flowcontrolled(wsi))
-			/* We cannot deal with any kind of new RX
-			 * because we are RX-flowcontrolled.
-			 */
-			break;
-
-#if defined(LWS_WITH_HTTP2)
-		if (wsi->http2_substream || wsi->upgraded_to_http2) {
-			wsi1 = lws_get_network_wsi(wsi);
-			if (wsi1 && wsi1->trunc_len)
-				/* We cannot deal with any kind of new RX
-				 * because we are dealing with a partial send
-				 * (new RX may trigger new http_action() that
-				 * expect to be able to send)
-				 */
-				break;
-		}
-#endif
-
-		/* 2: RX Extension needs to be drained
-		 */
-
-		if (wsi->state == LWSS_ESTABLISHED &&
-		    wsi->u.ws.rx_draining_ext) {
-
-			lwsl_ext("%s: RX EXT DRAINING: Service\n", __func__);
-#ifndef LWS_NO_CLIENT
-			if (wsi->mode == LWSCM_WS_CLIENT) {
-				n = lws_client_rx_sm(wsi, 0);
-				if (n < 0)
-					/* we closed wsi */
-					n = 0;
-			} else
-#endif
-				n = lws_rx_sm(wsi, 0);
-
-			goto handled;
-		}
-
-		if (wsi->u.ws.rx_draining_ext)
-			/*
-			 * We have RX EXT content to drain, but can't do it
-			 * right now.  That means we cannot do anything lower
-			 * priority either.
-			 */
-			break;
-
-		/* 3: RX Flowcontrol buffer / h2 rx scratch needs to be drained
-		 */
-
-		if (wsi->rxflow_buffer) {
-			lwsl_info("draining rxflow (len %d)\n",
-				wsi->rxflow_len - wsi->rxflow_pos);
-			assert(wsi->rxflow_pos < wsi->rxflow_len);
-			/* well, drain it */
-			eff_buf.token = (char *)wsi->rxflow_buffer +
-						wsi->rxflow_pos;
-			eff_buf.token_len = wsi->rxflow_len - wsi->rxflow_pos;
-			draining_flow = 1;
-			goto drain;
-		}
-
-#if defined(LWS_WITH_HTTP2)
-		if (wsi->upgraded_to_http2) {
-			struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
-
-			if (h2n->rx_scratch_len) {
-				lwsl_info("%s: %p: resuming h2 rx_scratch pos = %d len = %d\n",
-					  __func__, wsi, h2n->rx_scratch_pos, h2n->rx_scratch_len);
-				eff_buf.token = (char *)h2n->rx_scratch +
-						h2n->rx_scratch_pos;
-				eff_buf.token_len = h2n->rx_scratch_len;
-
-				h2n->rx_scratch_len = 0;
-				goto drain;
-			}
-		}
-#endif
-
-		/* 4: any incoming (or ah-stashed incoming rx) data ready?
-		 * notice if rx flow going off raced poll(), rx flow wins
-		 */
-
-		if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
-			break;
-read:
-		if (lws_is_flowcontrolled(wsi)) {
-			lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n",
-				    __func__, wsi, wsi->rxflow_bitmap);
-			break;
-		}
-
-		/* all the union members start with hdr, so even in ws mode
-		 * we can deal with the ah via u.hdr
-		 */
-		if (wsi->u.hdr.ah) {
-			lwsl_info("%s: %p: inherited ah rx\n", __func__, wsi);
-			eff_buf.token_len = wsi->u.hdr.ah->rxlen -
-					    wsi->u.hdr.ah->rxpos;
-			eff_buf.token = (char *)wsi->u.hdr.ah->rx +
-					wsi->u.hdr.ah->rxpos;
-		} else {
-			if (wsi->mode != LWSCM_HTTP_CLIENT_ACCEPTED) {
-				/*
-				 * extension may not consume everything (eg, pmd may be constrained
-				 * as to what it can output...) has to go in per-wsi rx buf area.
-				 * Otherwise in large temp serv_buf area.
-				 */
-
-#if defined(LWS_WITH_HTTP2)
-				if (wsi->upgraded_to_http2) {
-					if (!wsi->u.h2.h2n->rx_scratch) {
-						wsi->u.h2.h2n->rx_scratch = lws_malloc(LWS_H2_RX_SCRATCH_SIZE, "h2 rx scratch");
-						if (!wsi->u.h2.h2n->rx_scratch)
-							goto close_and_handled;
-					}
-					eff_buf.token = wsi->u.h2.h2n->rx_scratch;
-					eff_buf.token_len = LWS_H2_RX_SCRATCH_SIZE;
-				} else
-#endif
-				{
-					eff_buf.token = (char *)pt->serv_buf;
-					if (lws_is_ws_with_ext(wsi)) {
-						eff_buf.token_len = wsi->u.ws.rx_ubuf_alloc;
-					} else {
-						eff_buf.token_len = context->pt_serv_buf_size;
-					}
-
-					if ((unsigned int)eff_buf.token_len > context->pt_serv_buf_size)
-						eff_buf.token_len = context->pt_serv_buf_size;
-				}
-
-				if ((int)pending > eff_buf.token_len)
-					pending = eff_buf.token_len;
-
-				eff_buf.token_len = lws_ssl_capable_read(wsi,
-					(unsigned char *)eff_buf.token, pending ? pending :
-					eff_buf.token_len);
-				switch (eff_buf.token_len) {
-				case 0:
-					lwsl_info("%s: zero length read\n", __func__);
-					goto close_and_handled;
-				case LWS_SSL_CAPABLE_MORE_SERVICE:
-					lwsl_info("SSL Capable more service\n");
-					n = 0;
-					goto handled;
-				case LWS_SSL_CAPABLE_ERROR:
-					lwsl_info("Closing when error\n");
-					goto close_and_handled;
-				}
-				// lwsl_notice("Actual RX %d\n", eff_buf.token_len);
-			}
-		}
-
-drain:
-#ifndef LWS_NO_CLIENT
-		if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED &&
-		    !wsi->told_user_closed) {
-
-			/*
-			 * In SSL mode we get POLLIN notification about
-			 * encrypted data in.
-			 *
-			 * But that is not necessarily related to decrypted
-			 * data out becoming available; in may need to perform
-			 * other in or out before that happens.
-			 *
-			 * simply mark ourselves as having readable data
-			 * and turn off our POLLIN
-			 */
-			wsi->client_rx_avail = 1;
-			lws_change_pollfd(wsi, LWS_POLLIN, 0);
-
-			/* let user code know, he'll usually ask for writeable
-			 * callback and drain / re-enable it there
-			 */
-			if (user_callback_handle_rxflow(
-					wsi->protocol->callback,
-					wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
-					wsi->user_space, NULL, 0)) {
-				lwsl_info("RECEIVE_CLIENT_HTTP closed it\n");
-				goto close_and_handled;
-			}
-
-			n = 0;
-			goto handled;
-		}
-#endif
-		/*
-		 * give any active extensions a chance to munge the buffer
-		 * before parse.  We pass in a pointer to an lws_tokens struct
-		 * prepared with the default buffer and content length that's in
-		 * there.  Rather than rewrite the default buffer, extensions
-		 * that expect to grow the buffer can adapt .token to
-		 * point to their own per-connection buffer in the extension
-		 * user allocation.  By default with no extensions or no
-		 * extension callback handling, just the normal input buffer is
-		 * used then so it is efficient.
-		 */
-		do {
-			more = 0;
-
-			m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_RX_PREPARSE,
-					      &eff_buf, 0);
-			if (m < 0)
-				goto close_and_handled;
-			if (m)
-				more = 1;
-
-			/* service incoming data */
-
-			if (eff_buf.token_len) {
-				/*
-				 * if draining from rxflow buffer, not
-				 * critical to track what was used since at the
-				 * use it bumps wsi->rxflow_pos.  If we come
-				 * around again it will pick up from where it
-				 * left off.
-				 */
-				n = lws_read(wsi, (unsigned char *)eff_buf.token,
-					     eff_buf.token_len);
-				if (n < 0) {
-					/* we closed wsi */
-					n = 0;
-					goto handled;
-				}
-			}
-
-			eff_buf.token = NULL;
-			eff_buf.token_len = 0;
-		} while (more);
-
-		if (wsi->u.hdr.ah) {
-			lwsl_debug("%s: %p: detaching\n", __func__, wsi);
-			lws_header_table_force_to_detachable_state(wsi);
-			/* we can run the normal ah detach flow despite
-			 * being in ws union mode, since all union members
-			 * start with hdr */
-			lws_header_table_detach(wsi, 0);
-		}
-
-		pending = lws_ssl_pending(wsi);
-		if (pending) {
-			if (lws_is_ws_with_ext(wsi))
-				pending = pending > wsi->u.ws.rx_ubuf_alloc ?
-					wsi->u.ws.rx_ubuf_alloc : pending;
-			else
-				pending = pending > context->pt_serv_buf_size ?
-					context->pt_serv_buf_size : pending;
-			goto read;
-		}
-
-		if (draining_flow && wsi->rxflow_buffer &&
-		    wsi->rxflow_pos == wsi->rxflow_len) {
-			lwsl_info("%s: %p flow buf: drained\n", __func__, wsi);
-			lws_free_set_NULL(wsi->rxflow_buffer);
-			/* having drained the rxflow buffer, can rearm POLLIN */
-#ifdef LWS_NO_SERVER
-			n =
-#endif
-			_lws_rx_flow_control(wsi);
-			/* n ignored, needed for NO_SERVER case */
-		}
-
-		break;
-#ifdef LWS_WITH_CGI
-	case LWSCM_CGI: /* we exist to handle a cgi's stdin/out/err data...
-			 * do the callback on our master wsi
-			 */
-		{
-			struct lws_cgi_args args;
-
-			if (wsi->cgi_channel >= LWS_STDOUT &&
-			    !(pollfd->revents & pollfd->events & LWS_POLLIN))
-				break;
-			if (wsi->cgi_channel == LWS_STDIN &&
-			    !(pollfd->revents & pollfd->events & LWS_POLLOUT))
-				break;
-
-			if (wsi->cgi_channel == LWS_STDIN)
-				if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
-					lwsl_info("failed at set pollfd\n");
-					return 1;
-				}
-
-			args.ch = wsi->cgi_channel;
-			args.stdwsi = &wsi->parent->cgi->stdwsi[0];
-			args.hdr_state = wsi->hdr_state;
-
-			lwsl_debug("CGI LWS_STDOUT %p mode %d state %d\n",
-				   wsi->parent, wsi->parent->mode,
-				   wsi->parent->state);
-
-			if (user_callback_handle_rxflow(
-					wsi->parent->protocol->callback,
-					wsi->parent, LWS_CALLBACK_CGI,
-					wsi->parent->user_space,
-					(void *)&args, 0))
-				return 1;
-
-			break;
-		}
-#endif
-	/*
-	 * something went wrong with parsing the handshake, and
-	 * we ended up back in the event loop without completing it
-	 */
-	case LWSCM_PRE_WS_SERVING_ACCEPT:
-		wsi->socket_is_permanently_unusable = 1;
-		goto close_and_handled;
-
-	default:
-#ifdef LWS_NO_CLIENT
-		break;
-#else
-		if ((pollfd->revents & LWS_POLLOUT) &&
-		    lws_handle_POLLOUT_event(wsi, pollfd)) {
-			lwsl_debug("POLLOUT event closed it\n");
-			goto close_and_handled;
-		}
-
-		n = lws_client_socket_service(context, wsi, pollfd);
-		if (n)
-			return 1;
-		goto handled;
-#endif
-	}
-
-	n = 0;
-	goto handled;
-
-close_and_handled:
-	lwsl_debug("%p: Close and handled\n", wsi);
-	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
-	/*
-	 * pollfd may point to something else after the close
-	 * due to pollfd swapping scheme on delete on some platforms
-	 * we can't clear revents now because it'd be the wrong guy's revents
-	 */
-	return 1;
-
-handled:
-	pollfd->revents = 0;
-	return n;
-}
-
-LWS_VISIBLE int
-lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd)
-{
-	return lws_service_fd_tsi(context, pollfd, 0);
-}
-
-LWS_VISIBLE int
-lws_service(struct lws_context *context, int timeout_ms)
-{
-	return lws_plat_service(context, timeout_ms);
-}
-
-LWS_VISIBLE int
-lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
-{
-	return _lws_plat_service_tsi(context, timeout_ms, tsi);
-}
-

+ 0 - 973
thirdparty/libwebsockets/ssl.c

@@ -1,973 +0,0 @@
-/*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <[email protected]>
- *
- *  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 <errno.h>
-
-int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf,
-		lws_filepos_t *amount)
-{
-	lws_filepos_t len;
-	lws_fop_flags_t	flags = LWS_O_RDONLY;
-	lws_fop_fd_t fops_fd = lws_vfs_file_open(
-				lws_get_fops(context), filename, &flags);
-	int ret = 1;
-
-	if (!fops_fd)
-		return 1;
-
-	len = lws_vfs_get_length(fops_fd);
-
-	*buf = lws_malloc((size_t)len, "lws_alloc_vfs_file");
-	if (!*buf)
-		goto bail;
-
-	if (lws_vfs_file_read(fops_fd, amount, *buf, len))
-		goto bail;
-
-	ret = 0;
-bail:
-	lws_vfs_file_close(&fops_fd);
-
-	return ret;
-}
-
-#if defined(LWS_WITH_MBEDTLS)
-#if defined(LWS_WITH_ESP32)
-int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
-	       lws_filepos_t *amount)
-{
-	nvs_handle nvh;
-	size_t s;
-	int n = 0;
-
-	ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
-	if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
-		n = 1;
-		goto bail;
-	}
-	*buf = lws_malloc(s, "alloc_file");
-	if (!*buf) {
-		n = 2;
-		goto bail;
-	}
-	if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
-		lws_free(*buf);
-		n = 1;
-		goto bail;
-	}
-
-	*amount = s;
-
-bail:
-	nvs_close(nvh);
-
-	return n;
-}
-#else
-int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
-		lws_filepos_t *amount)
-{
-	FILE *f;
-	size_t s;
-	int n = 0;
-
-	f = fopen(filename, "rb");
-	if (f == NULL) {
-		n = 1;
-		goto bail;
-	}
-
-	if (fseek(f, 0, SEEK_END) != 0) {
-		n = 1;
-		goto bail;
-	}
-
-	s = ftell(f);
-	if (s == -1) {
-		n = 1;
-		goto bail;
-	}
-
-	if (fseek(f, 0, SEEK_SET) != 0) {
-		n = 1;
-		goto bail;
-	}
-
-	*buf = lws_malloc(s, "alloc_file");
-	if (!*buf) {
-		n = 2;
-		goto bail;
-	}
-
-	if (fread(*buf, s, 1, f) != 1) {
-		lws_free(*buf);
-		n = 1;
-		goto bail;
-	}
-
-	*amount = s;
-
-bail:
-	if (f)
-		fclose(f);
-
-	return n;
-
-}
-#endif
-int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf,
-	       lws_filepos_t *amount)
-{
-	uint8_t *pem, *p, *q, *end;
-	lws_filepos_t len;
-	int n;
-
-	n = alloc_file(context, filename, &pem, &len);
-	if (n)
-		return n;
-
-	/* trim the first line */
-
-	p = pem;
-	end = p + len;
-	if (strncmp((char *)p, "-----", 5))
-		goto bail;
-	p += 5;
-	while (p < end && *p != '\n' && *p != '-')
-		p++;
-
-	if (*p != '-')
-		goto bail;
-
-	while (p < end && *p != '\n')
-		p++;
-
-	if (p >= end)
-		goto bail;
-
-	p++;
-
-	/* trim the last line */
-
-	q = end - 2;
-
-	while (q > pem && *q != '\n')
-		q--;
-
-	if (*q != '\n')
-		goto bail;
-
-	*q = '\0';
-
-	*amount = lws_b64_decode_string((char *)p, (char *)pem, len);
-	*buf = pem;
-
-	return 0;
-
-bail:
-	lws_free(pem);
-
-	return 4;
-}
-#endif
-
-int openssl_websocket_private_data_index,
-    openssl_SSL_CTX_private_data_index;
-
-int lws_ssl_get_error(struct lws *wsi, int n)
-{
-	int m;
-
-	if (!wsi->ssl)
-		return 99;
-
-	m = SSL_get_error(wsi->ssl, n);
-	lwsl_debug("%s: %p %d -> %d\n", __func__, wsi->ssl, n, m);
-
-	return m;
-}
-
-/* Copies a string describing the code returned by lws_ssl_get_error(),
- * which may also contain system error information in the case of SSL_ERROR_SYSCALL,
- * into buf up to len.
- * Returns a pointer to buf.
- *
- * Note: the lws_ssl_get_error() code is *not* an error code that can be passed
- * to ERR_error_string(),
- *
- * ret is the return value originally passed to lws_ssl_get_error(), needed to disambiguate
- * SYS_ERROR_SYSCALL.
- *
- * See man page for SSL_get_error().
- *
- * Not thread safe, uses strerror()
- */
-char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) {
-	switch (status) {
-	case SSL_ERROR_NONE: return strncpy(buf, "SSL_ERROR_NONE", len);
-	case SSL_ERROR_ZERO_RETURN: return strncpy(buf, "SSL_ERROR_ZERO_RETURN", len);
-	case SSL_ERROR_WANT_READ: return strncpy(buf, "SSL_ERROR_WANT_READ", len);
-	case SSL_ERROR_WANT_WRITE: return strncpy(buf, "SSL_ERROR_WANT_WRITE", len);
-	case SSL_ERROR_WANT_CONNECT: return strncpy(buf, "SSL_ERROR_WANT_CONNECT", len);
-	case SSL_ERROR_WANT_ACCEPT: return strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len);
-	case SSL_ERROR_WANT_X509_LOOKUP: return strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len);
-	case SSL_ERROR_SYSCALL:
-		switch (ret) {
-                case 0:
-                        lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: EOF");
-                        return buf;
-                case -1:
-#ifndef LWS_PLAT_OPTEE
-			lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %s", strerror(errno));
-#else
-			lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %d", errno);
-#endif
-			return buf;
-                default:
-                        return strncpy(buf, "SSL_ERROR_SYSCALL", len);
-	}
-	case SSL_ERROR_SSL: return "SSL_ERROR_SSL";
-	default: return "SSL_ERROR_UNKNOWN";
-	}
-}
-
-void
-lws_ssl_elaborate_error(void)
-{
-#if defined(LWS_WITH_MBEDTLS)
-#else
-	char buf[256];
-	u_long err;
-
-	while ((err = ERR_get_error()) != 0) {
-		ERR_error_string_n(err, buf, sizeof(buf));
-		lwsl_info("*** %s\n", buf);
-	}
-#endif
-}
-
-#if !defined(LWS_WITH_MBEDTLS)
-
-static int
-lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, void *userdata)
-{
-	struct lws_context_creation_info * info =
-			(struct lws_context_creation_info *)userdata;
-
-	strncpy(buf, info->ssl_private_key_password, size);
-	buf[size - 1] = '\0';
-
-	return strlen(buf);
-}
-
-void
-lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info)
-{
-	if (!info->ssl_private_key_password)
-		return;
-	/*
-	 * password provided, set ssl callback and user data
-	 * for checking password which will be trigered during
-	 * SSL_CTX_use_PrivateKey_file function
-	 */
-	SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info);
-	SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb);
-}
-#endif
-
-int
-lws_context_init_ssl_library(struct lws_context_creation_info *info)
-{
-#ifdef USE_WOLFSSL
-#ifdef USE_OLD_CYASSL
-	lwsl_info(" Compiled with CyaSSL support\n");
-#else
-	lwsl_info(" Compiled with wolfSSL support\n");
-#endif
-#else
-#if defined(LWS_WITH_BORINGSSL)
-	lwsl_info(" Compiled with BoringSSL support\n");
-#else
-#if defined(LWS_WITH_MBEDTLS)
-	lwsl_info(" Compiled with MbedTLS support\n");
-#else
-	lwsl_info(" Compiled with OpenSSL support\n");
-#endif
-#endif
-#endif
-	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
-		lwsl_info(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
-		return 0;
-	}
-
-	/* basic openssl init */
-
-	lwsl_info("Doing SSL library init\n");
-
-#if !defined(LWS_WITH_MBEDTLS)
-	SSL_library_init();
-	OpenSSL_add_all_algorithms();
-	SSL_load_error_strings();
-
-	openssl_websocket_private_data_index =
-		SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL);
-
-	openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0,
-			NULL, NULL, NULL, NULL);
-#endif
-
-	return 0;
-}
-
-LWS_VISIBLE void
-lws_ssl_destroy(struct lws_vhost *vhost)
-{
-	if (!lws_check_opt(vhost->context->options,
-			   LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
-		return;
-
-	if (vhost->ssl_ctx)
-		SSL_CTX_free(vhost->ssl_ctx);
-	if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
-		SSL_CTX_free(vhost->ssl_client_ctx);
-
-#if defined(LWS_WITH_MBEDTLS)
-	if (vhost->x509_client_CA)
-		X509_free(vhost->x509_client_CA);
-#else
-// after 1.1.0 no need
-#if (OPENSSL_VERSION_NUMBER <  0x10100000)
-// <= 1.0.1f = old api, 1.0.1g+ = new api
-#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
-	ERR_remove_state(0);
-#else
-#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
-    !defined(LIBRESSL_VERSION_NUMBER) && \
-    !defined(OPENSSL_IS_BORINGSSL)
-	ERR_remove_thread_state();
-#else
-	ERR_remove_thread_state(NULL);
-#endif
-#endif
-	// after 1.1.0 no need
-#if  (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
-	SSL_COMP_free_compression_methods();
-#endif
-	ERR_free_strings();
-	EVP_cleanup();
-	CRYPTO_cleanup_all_ex_data();
-#endif
-#endif
-}
-
-int
-lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi)
-{
-	struct lws_context_per_thread *pt = &context->pt[tsi];
-	struct lws *wsi, *wsi_next;
-
-	wsi = pt->pending_read_list;
-	while (wsi) {
-		wsi_next = wsi->pending_read_list_next;
-		pt->fds[wsi->position_in_fds_table].revents |=
-			pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
-		if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN)
-			return 1;
-
-		wsi = wsi_next;
-	}
-
-	return 0;
-}
-
-LWS_VISIBLE void
-lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
-{
-	struct lws_context *context = wsi->context;
-	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-
-	if (!wsi->pending_read_list_prev &&
-	    !wsi->pending_read_list_next &&
-	    pt->pending_read_list != wsi)
-		/* we are not on the list */
-		return;
-
-	/* point previous guy's next to our next */
-	if (!wsi->pending_read_list_prev)
-		pt->pending_read_list = wsi->pending_read_list_next;
-	else
-		wsi->pending_read_list_prev->pending_read_list_next =
-			wsi->pending_read_list_next;
-
-	/* point next guy's previous to our previous */
-	if (wsi->pending_read_list_next)
-		wsi->pending_read_list_next->pending_read_list_prev =
-			wsi->pending_read_list_prev;
-
-	wsi->pending_read_list_prev = NULL;
-	wsi->pending_read_list_next = NULL;
-}
-
-LWS_VISIBLE int
-lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
-{
-	struct lws_context *context = wsi->context;
-	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-	int n = 0, m;
-
-	if (!wsi->ssl)
-		return lws_ssl_capable_read_no_ssl(wsi, buf, len);
-
-	lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
-
-	errno = 0;
-	n = SSL_read(wsi->ssl, buf, len);
-#if defined(LWS_WITH_ESP32)
-	if (!n && errno == ENOTCONN) {
-		lwsl_debug("%p: SSL_read ENOTCONN\n", wsi);
-		return LWS_SSL_CAPABLE_ERROR;
-	}
-#endif
-#if defined(LWS_WITH_STATS)
-	if (!wsi->seen_rx) {
-                lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_MS_SSL_RX_DELAY,
-				time_in_microseconds() - wsi->accept_start_us);
-                lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNS_HAD_RX, 1);
-		wsi->seen_rx = 1;
-	}
-#endif
-
-
-	lwsl_debug("%p: SSL_read says %d\n", wsi, n);
-	/* manpage: returning 0 means connection shut down */
-	if (!n || (n == -1 && errno == ENOTCONN)) {
-		wsi->socket_is_permanently_unusable = 1;
-
-		return LWS_SSL_CAPABLE_ERROR;
-	}
-
-	if (n < 0) {
-		m = lws_ssl_get_error(wsi, n);
-		lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
-		if (m == SSL_ERROR_ZERO_RETURN ||
-		    m == SSL_ERROR_SYSCALL)
-			return LWS_SSL_CAPABLE_ERROR;
-
-		if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
-			lwsl_debug("%s: WANT_READ\n", __func__);
-			lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
-			return LWS_SSL_CAPABLE_MORE_SERVICE;
-		}
-		if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
-			lwsl_debug("%s: WANT_WRITE\n", __func__);
-			lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
-			return LWS_SSL_CAPABLE_MORE_SERVICE;
-		}
-		wsi->socket_is_permanently_unusable = 1;
-
-		return LWS_SSL_CAPABLE_ERROR;
-	}
-
-	lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
-
-	if (wsi->vhost)
-		wsi->vhost->conn_stats.rx += n;
-
-	lws_restart_ws_ping_pong_timer(wsi);
-
-	/*
-	 * if it was our buffer that limited what we read,
-	 * check if SSL has additional data pending inside SSL buffers.
-	 *
-	 * Because these won't signal at the network layer with POLLIN
-	 * and if we don't realize, this data will sit there forever
-	 */
-	if (n != len)
-		goto bail;
-	if (!wsi->ssl)
-		goto bail;
-
-	if (!SSL_pending(wsi->ssl))
-		goto bail;
-
-	if (wsi->pending_read_list_next)
-		return n;
-	if (wsi->pending_read_list_prev)
-		return n;
-	if (pt->pending_read_list == wsi)
-		return n;
-
-	/* add us to the linked list of guys with pending ssl */
-	if (pt->pending_read_list)
-		pt->pending_read_list->pending_read_list_prev = wsi;
-
-	wsi->pending_read_list_next = pt->pending_read_list;
-	wsi->pending_read_list_prev = NULL;
-	pt->pending_read_list = wsi;
-
-	return n;
-bail:
-	lws_ssl_remove_wsi_from_buffered_list(wsi);
-
-	return n;
-}
-
-LWS_VISIBLE int
-lws_ssl_pending(struct lws *wsi)
-{
-	if (!wsi->ssl)
-		return 0;
-
-	return SSL_pending(wsi->ssl);
-}
-
-LWS_VISIBLE int
-lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
-{
-	int n, m;
-
-	if (!wsi->ssl)
-		return lws_ssl_capable_write_no_ssl(wsi, buf, len);
-
-	n = SSL_write(wsi->ssl, buf, len);
-	if (n > 0)
-		return n;
-
-	m = lws_ssl_get_error(wsi, n);
-	if (m != SSL_ERROR_SYSCALL) {
-
-		if (SSL_want_read(wsi->ssl)) {
-			lwsl_notice("%s: want read\n", __func__);
-
-			return LWS_SSL_CAPABLE_MORE_SERVICE;
-		}
-
-		if (SSL_want_write(wsi->ssl)) {
-			lws_set_blocking_send(wsi);
-
-			lwsl_notice("%s: want write\n", __func__);
-
-			return LWS_SSL_CAPABLE_MORE_SERVICE;
-		}
-	}
-
-	lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL));
-	lws_ssl_elaborate_error();
-
-	wsi->socket_is_permanently_unusable = 1;
-
-	return LWS_SSL_CAPABLE_ERROR;
-}
-
-static int
-lws_gate_accepts(struct lws_context *context, int on)
-{
-	struct lws_vhost *v = context->vhost_list;
-
-	lwsl_info("gating accepts %d\n", on);
-	context->ssl_gate_accepts = !on;
-#if defined(LWS_WITH_STATS)
-	context->updated = 1;
-#endif
-
-	while (v) {
-		if (v->use_ssl &&  v->lserv_wsi) /* gate ability to accept incoming connections */
-			if (lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on,
-					      (LWS_POLLIN) * on))
-				lwsl_info("Unable to set accept POLLIN %d\n", on);
-
-		v = v->vhost_next;
-	}
-
-	return 0;
-}
-
-void
-lws_ssl_info_callback(const SSL *ssl, int where, int ret)
-{
-	struct lws *wsi;
-	struct lws_context *context;
-	struct lws_ssl_info si;
-
-	context = (struct lws_context *)SSL_CTX_get_ex_data(
-					SSL_get_SSL_CTX(ssl),
-					openssl_SSL_CTX_private_data_index);
-	if (!context)
-		return;
-	wsi = wsi_from_fd(context, SSL_get_fd(ssl));
-	if (!wsi)
-		return;
-
-	if (!(where & wsi->vhost->ssl_info_event_mask))
-		return;
-
-	si.where = where;
-	si.ret = ret;
-
-	if (user_callback_handle_rxflow(wsi->protocol->callback,
-						   wsi, LWS_CALLBACK_SSL_INFO,
-						   wsi->user_space, &si, 0))
-		lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1);
-}
-
-
-LWS_VISIBLE int
-lws_ssl_close(struct lws *wsi)
-{
-	lws_sockfd_type n;
-
-	if (!wsi->ssl)
-		return 0; /* not handled */
-
-#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
-	/* kill ssl callbacks, becausse we will remove the fd from the
-	 * table linking it to the wsi
-	 */
-	if (wsi->vhost->ssl_info_event_mask)
-		SSL_set_info_callback(wsi->ssl, NULL);
-#endif
-
-	n = SSL_get_fd(wsi->ssl);
-	if (!wsi->socket_is_permanently_unusable)
-		SSL_shutdown(wsi->ssl);
-	compatible_close(n);
-	SSL_free(wsi->ssl);
-	wsi->ssl = NULL;
-
-	if (wsi->context->simultaneous_ssl_restriction &&
-	    wsi->context->simultaneous_ssl-- ==
-			    wsi->context->simultaneous_ssl_restriction)
-		/* we made space and can do an accept */
-		lws_gate_accepts(wsi->context, 1);
-#if defined(LWS_WITH_STATS)
-	wsi->context->updated = 1;
-#endif
-
-	return 1; /* handled */
-}
-
-/* leave all wsi close processing to the caller */
-
-LWS_VISIBLE int
-lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
-{
-	struct lws_context *context = wsi->context;
-	struct lws_vhost *vh;
-	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-	int n, m;
-#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
-	BIO *bio;
-#endif
-        char buf[256];
-
-        (void)buf;
-
-	if (!LWS_SSL_ENABLED(wsi->vhost))
-		return 0;
-
-	switch (wsi->mode) {
-	case LWSCM_SSL_INIT:
-	case LWSCM_SSL_INIT_RAW:
-		if (wsi->ssl)
-			lwsl_err("%s: leaking ssl\n", __func__);
-		if (accept_fd == LWS_SOCK_INVALID)
-			assert(0);
-		if (context->simultaneous_ssl_restriction &&
-		    context->simultaneous_ssl >= context->simultaneous_ssl_restriction) {
-			lwsl_notice("unable to deal with SSL connection\n");
-			return 1;
-		}
-		errno = 0;
-		wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
-		if (wsi->ssl == NULL) {
-			lwsl_err("SSL_new failed: %d (errno %d)\n",
-				 lws_ssl_get_error(wsi, 0), errno);
-
-			lws_ssl_elaborate_error();
-			if (accept_fd != LWS_SOCK_INVALID)
-				compatible_close(accept_fd);
-			goto fail;
-		}
-#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 (context->simultaneous_ssl_restriction &&
-		    ++context->simultaneous_ssl == context->simultaneous_ssl_restriction)
-			/* that was the last allowed SSL connection */
-			lws_gate_accepts(context, 0);
-#if defined(LWS_WITH_STATS)
-	context->updated = 1;
-#endif
-
-#if !defined(LWS_WITH_MBEDTLS)
-		SSL_set_ex_data(wsi->ssl,
-			openssl_websocket_private_data_index, wsi);
-#endif
-		SSL_set_fd(wsi->ssl, accept_fd);
-
-#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_MBEDTLS)
-		lws_plat_set_socket_options(wsi->vhost, accept_fd);
-#else
-		SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-		bio = SSL_get_rbio(wsi->ssl);
-		if (bio)
-			BIO_set_nbio(bio, 1); /* nonblocking */
-		else
-			lwsl_notice("NULL rbio\n");
-		bio = SSL_get_wbio(wsi->ssl);
-		if (bio)
-			BIO_set_nbio(bio, 1); /* nonblocking */
-		else
-			lwsl_notice("NULL rbio\n");
-#endif
-#endif
-
-		/*
-		 * we are not accepted yet, but we need to enter ourselves
-		 * as a live connection.  That way we can retry when more
-		 * pieces come if we're not sorted yet
-		 */
-
-		if (wsi->mode == LWSCM_SSL_INIT)
-			wsi->mode = LWSCM_SSL_ACK_PENDING;
-		else
-			wsi->mode = LWSCM_SSL_ACK_PENDING_RAW;
-
-		if (insert_wsi_socket_into_fds(context, wsi)) {
-			lwsl_err("%s: failed to insert into fds\n", __func__);
-			goto fail;
-		}
-
-		lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
-				context->timeout_secs);
-
-		lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n");
-
-		/* fallthru */
-
-	case LWSCM_SSL_ACK_PENDING:
-	case LWSCM_SSL_ACK_PENDING_RAW:
-		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
-			lwsl_err("%s: lws_change_pollfd failed\n", __func__);
-			goto fail;
-		}
-
-		lws_latency_pre(context, wsi);
-
-		if (wsi->vhost->allow_non_ssl_on_ssl_port) {
-
-			n = recv(wsi->desc.sockfd, (char *)pt->serv_buf,
-				 context->pt_serv_buf_size, MSG_PEEK);
-
-		/*
-		 * optionally allow non-SSL connect on SSL listening socket
-		 * This is disabled by default, if enabled it goes around any
-		 * SSL-level access control (eg, client-side certs) so leave
-		 * it disabled unless you know it's not a problem for you
-		 */
-
-			if (n >= 1 && pt->serv_buf[0] >= ' ') {
-				/*
-				* TLS content-type for Handshake is 0x16, and
-				* for ChangeCipherSpec Record, it's 0x14
-				*
-				* A non-ssl session will start with the HTTP
-				* method in ASCII.  If we see it's not a legit
-				* SSL handshake kill the SSL for this
-				* connection and try to handle as a HTTP
-				* connection upgrade directly.
-				*/
-				wsi->use_ssl = 0;
-
-				SSL_shutdown(wsi->ssl);
-				SSL_free(wsi->ssl);
-				wsi->ssl = NULL;
-				if (lws_check_opt(context->options,
-				    LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
-					wsi->redirect_to_https = 1;
-				goto accepted;
-			}
-			if (!n) /*
-				 * connection is gone, or nothing to read
-				 * if it's gone, we will timeout on
-				 * PENDING_TIMEOUT_SSL_ACCEPT
-				 */
-				break;
-			if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
-				      LWS_ERRNO == LWS_EWOULDBLOCK)) {
-				/*
-				 * well, we get no way to know ssl or not
-				 * so go around again waiting for something
-				 * to come and give us a hint, or timeout the
-				 * connection.
-				 */
-				m = SSL_ERROR_WANT_READ;
-				goto go_again;
-			}
-		}
-
-		/* normal SSL connection processing path */
-
-#if defined(LWS_WITH_STATS)
-		if (!wsi->accept_start_us)
-			wsi->accept_start_us = time_in_microseconds();
-#endif
-		errno = 0;
-		lws_stats_atomic_bump(wsi->context, pt,
-				      LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
-		n = SSL_accept(wsi->ssl);
-		lws_latency(context, wsi,
-			"SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1);
-		lwsl_info("SSL_accept says %d\n", n);
-		if (n == 1)
-			goto accepted;
-
-		m = lws_ssl_get_error(wsi, n);
-
-#if defined(LWS_WITH_MBEDTLS)
-		if (m == SSL_ERROR_SYSCALL && errno == 11)
-			m = SSL_ERROR_WANT_READ;
-#endif
-		if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
-			goto failed;
-
-go_again:
-		if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
-			if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
-				lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__);
-				goto fail;
-			}
-
-			lwsl_info("SSL_ERROR_WANT_READ\n");
-			break;
-		}
-		if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
-			lwsl_debug("%s: WANT_WRITE\n", __func__);
-
-			if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
-				lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__);
-				goto fail;
-			}
-
-			break;
-		}
-failed:
-		lws_stats_atomic_bump(wsi->context, pt,
-				      LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
-		wsi->socket_is_permanently_unusable = 1;
-                lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd,
-                         lws_ssl_get_error_string(m, n, buf, sizeof(buf)));
-		lws_ssl_elaborate_error();
-		goto fail;
-
-accepted:
-		lws_stats_atomic_bump(wsi->context, pt,
-				      LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1);
-#if defined(LWS_WITH_STATS)
-		lws_stats_atomic_bump(wsi->context, pt,
-				      LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY,
-				      time_in_microseconds() - wsi->accept_start_us);
-		wsi->accept_start_us = time_in_microseconds();
-#endif
-
-		/* adapt our vhost to match the SNI SSL_CTX that was chosen */
-		vh = context->vhost_list;
-		while (vh) {
-			if (!vh->being_destroyed && wsi->ssl &&
-			    vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) {
-				lwsl_info("setting wsi to vh %s\n", vh->name);
-				wsi->vhost = vh;
-				break;
-			}
-			vh = vh->vhost_next;
-		}
-
-		/* OK, we are accepted... give him some time to negotiate */
-		lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
-				context->timeout_secs);
-
-		if (wsi->mode == LWSCM_SSL_ACK_PENDING_RAW)
-			wsi->mode = LWSCM_RAW;
-		else
-			wsi->mode = LWSCM_HTTP_SERVING;
-#if defined(LWS_WITH_HTTP2)
-		if (lws_h2_configure_if_upgraded(wsi))
-			goto fail;
-#endif
-		lwsl_debug("accepted new SSL conn\n");
-		break;
-	}
-
-	return 0;
-
-fail:
-	return 1;
-}
-
-void
-lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
-{
-	if (vhost->ssl_ctx)
-		SSL_CTX_free(vhost->ssl_ctx);
-
-	if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
-		SSL_CTX_free(vhost->ssl_client_ctx);
-}
-
-void
-lws_ssl_context_destroy(struct lws_context *context)
-{
-
-#if !defined(LWS_WITH_MBEDTLS)
-
-// after 1.1.0 no need
-#if (OPENSSL_VERSION_NUMBER <  0x10100000)
-// <= 1.0.1f = old api, 1.0.1g+ = new api
-#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
-	ERR_remove_state(0);
-#else
-#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
-    !defined(LIBRESSL_VERSION_NUMBER) && \
-    !defined(OPENSSL_IS_BORINGSSL)
-	ERR_remove_thread_state();
-#else
-	ERR_remove_thread_state(NULL);
-#endif
-#endif
-	// after 1.1.0 no need
-#if  (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
-	SSL_COMP_free_compression_methods();
-#endif
-	ERR_free_strings();
-	EVP_cleanup();
-	CRYPTO_cleanup_all_ex_data();
-#endif
-#endif
-}

+ 202 - 0
thirdparty/libwebsockets/tls/mbedtls/lws-genhash.c

@@ -0,0 +1,202 @@
+/*
+ * libwebsockets - generic hash and HMAC api hiding the backend
+ *
+ * Copyright (C) 2017 Andy Green <[email protected]>
+ *
+ *  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
+ *
+ *  lws_genhash provides a hash / hmac abstraction api in lws that works the
+ *  same whether you are using openssl or mbedtls hash functions underneath.
+ */
+#include "libwebsockets.h"
+#include <mbedtls/version.h>
+
+#if (MBEDTLS_VERSION_NUMBER >= 0x02070000)
+#define MBA(fn) fn##_ret
+#else
+#define MBA(fn) fn
+#endif
+
+size_t
+lws_genhash_size(enum lws_genhash_types type)
+{
+	switch(type) {
+	case LWS_GENHASH_TYPE_SHA1:
+		return 20;
+	case LWS_GENHASH_TYPE_SHA256:
+		return 32;
+	case LWS_GENHASH_TYPE_SHA384:
+		return 48;
+	case LWS_GENHASH_TYPE_SHA512:
+		return 64;
+	}
+
+	return 0;
+}
+
+int
+lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type)
+{
+	ctx->type = type;
+
+	switch (ctx->type) {
+	case LWS_GENHASH_TYPE_SHA1:
+		mbedtls_sha1_init(&ctx->u.sha1);
+		MBA(mbedtls_sha1_starts)(&ctx->u.sha1);
+		break;
+	case LWS_GENHASH_TYPE_SHA256:
+		mbedtls_sha256_init(&ctx->u.sha256);
+		MBA(mbedtls_sha256_starts)(&ctx->u.sha256, 0);
+		break;
+	case LWS_GENHASH_TYPE_SHA384:
+		mbedtls_sha512_init(&ctx->u.sha512);
+		MBA(mbedtls_sha512_starts)(&ctx->u.sha512, 1 /* is384 */);
+		break;
+	case LWS_GENHASH_TYPE_SHA512:
+		mbedtls_sha512_init(&ctx->u.sha512);
+		MBA(mbedtls_sha512_starts)(&ctx->u.sha512, 0);
+		break;
+	default:
+		return 1;
+	}
+
+	return 0;
+}
+
+int
+lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len)
+{
+	switch (ctx->type) {
+	case LWS_GENHASH_TYPE_SHA1:
+		MBA(mbedtls_sha1_update)(&ctx->u.sha1, in, len);
+		break;
+	case LWS_GENHASH_TYPE_SHA256:
+		MBA(mbedtls_sha256_update)(&ctx->u.sha256, in, len);
+		break;
+	case LWS_GENHASH_TYPE_SHA384:
+		MBA(mbedtls_sha512_update)(&ctx->u.sha512, in, len);
+		break;
+	case LWS_GENHASH_TYPE_SHA512:
+		MBA(mbedtls_sha512_update)(&ctx->u.sha512, in, len);
+		break;
+	}
+
+	return 0;
+}
+
+int
+lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result)
+{
+	switch (ctx->type) {
+	case LWS_GENHASH_TYPE_SHA1:
+		MBA(mbedtls_sha1_finish)(&ctx->u.sha1, result);
+		mbedtls_sha1_free(&ctx->u.sha1);
+		break;
+	case LWS_GENHASH_TYPE_SHA256:
+		MBA(mbedtls_sha256_finish)(&ctx->u.sha256, result);
+		mbedtls_sha256_free(&ctx->u.sha256);
+		break;
+	case LWS_GENHASH_TYPE_SHA384:
+		MBA(mbedtls_sha512_finish)(&ctx->u.sha512, result);
+		mbedtls_sha512_free(&ctx->u.sha512);
+		break;
+	case LWS_GENHASH_TYPE_SHA512:
+		MBA(mbedtls_sha512_finish)(&ctx->u.sha512, result);
+		mbedtls_sha512_free(&ctx->u.sha512);
+		break;
+	}
+
+	return 0;
+}
+
+size_t
+lws_genhmac_size(enum lws_genhmac_types type)
+{
+	switch(type) {
+	case LWS_GENHMAC_TYPE_SHA256:
+		return 32;
+	case LWS_GENHMAC_TYPE_SHA384:
+		return 48;
+	case LWS_GENHMAC_TYPE_SHA512:
+		return 64;
+	}
+
+	return 0;
+}
+
+int
+lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
+		 const uint8_t *key, size_t key_len)
+{
+	int t;
+
+	ctx->type = type;
+
+	switch (type) {
+	case LWS_GENHMAC_TYPE_SHA256:
+		t = MBEDTLS_MD_SHA256;
+		break;
+	case LWS_GENHMAC_TYPE_SHA384:
+		t = MBEDTLS_MD_SHA384;
+		break;
+	case LWS_GENHMAC_TYPE_SHA512:
+		t = MBEDTLS_MD_SHA512;
+		break;
+	default:
+		return -1;
+	}
+
+	ctx->hmac = mbedtls_md_info_from_type(t);
+	if (!ctx->hmac)
+		return -1;
+
+	if (mbedtls_md_init_ctx(&ctx->ctx, ctx->hmac))
+		return -1;
+
+	if (mbedtls_md_hmac_starts(&ctx->ctx, key, key_len)) {
+		mbedtls_md_free(&ctx->ctx);
+		ctx->hmac = NULL;
+
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len)
+{
+	if (mbedtls_md_hmac_update(&ctx->ctx, in, len))
+		return -1;
+
+	return 0;
+}
+
+int
+lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result)
+{
+	int n = 0;
+
+	if (result)
+		n = mbedtls_md_hmac_finish(&ctx->ctx, result);
+
+	mbedtls_md_free(&ctx->ctx);
+	ctx->hmac = NULL;
+	if (n)
+		return -1;
+
+	return 0;
+}

+ 329 - 0
thirdparty/libwebsockets/tls/mbedtls/lws-genrsa.c

@@ -0,0 +1,329 @@
+/*
+ * libwebsockets - generic RSA api hiding the backend
+ *
+ * Copyright (C) 2017 Andy Green <[email protected]>
+ *
+ *  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
+ *
+ *  lws_genhash provides a hash / hmac abstraction api in lws that works the
+ *  same whether you are using openssl or mbedtls hash functions underneath.
+ */
+#include "core/private.h"
+
+LWS_VISIBLE void
+lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el)
+{
+	int n;
+
+	for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
+		if (el->e[n].buf)
+			lws_free_set_NULL(el->e[n].buf);
+}
+
+LWS_VISIBLE int
+lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el)
+{
+	int n;
+
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa");
+	if (!ctx->ctx)
+		return 1;
+
+	mbedtls_rsa_init(ctx->ctx, MBEDTLS_RSA_PKCS_V15, 0);
+
+	{
+		mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
+			&ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P,
+			&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
+			&ctx->ctx->QP,
+		};
+
+		for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
+			if (el->e[n].buf &&
+			    mbedtls_mpi_read_binary(mpi[n], el->e[n].buf,
+					    	    el->e[n].len)) {
+				lwsl_notice("mpi load failed\n");
+				lws_free_set_NULL(ctx->ctx);
+
+				return -1;
+			}
+	}
+
+	ctx->ctx->len = el->e[JWK_KEY_N].len;
+
+	return 0;
+}
+
+static int
+_rngf(void *context, unsigned char *buf, size_t len)
+{
+	if ((size_t)lws_get_random(context, buf, len) == len)
+		return 0;
+
+	return -1;
+}
+
+LWS_VISIBLE int
+lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
+		       struct lws_genrsa_elements *el, int bits)
+{
+	int n;
+
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa");
+	if (!ctx->ctx)
+		return -1;
+
+	mbedtls_rsa_init(ctx->ctx, MBEDTLS_RSA_PKCS_V15, 0);
+
+	n = mbedtls_rsa_gen_key(ctx->ctx, _rngf, context, bits, 65537);
+	if (n) {
+		lwsl_err("mbedtls_rsa_gen_key failed 0x%x\n", -n);
+		goto cleanup_1;
+	}
+
+	{
+		mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
+			&ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P,
+			&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
+			&ctx->ctx->QP,
+		};
+
+		for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
+			if (mbedtls_mpi_size(mpi[n])) {
+				el->e[n].buf = lws_malloc(
+					mbedtls_mpi_size(mpi[n]), "genrsakey");
+				if (!el->e[n].buf)
+					goto cleanup;
+				el->e[n].len = mbedtls_mpi_size(mpi[n]);
+				mbedtls_mpi_write_binary(mpi[n], el->e[n].buf,
+							 el->e[n].len);
+			}
+	}
+
+	return 0;
+
+cleanup:
+	for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
+		if (el->e[n].buf)
+			lws_free_set_NULL(el->e[n].buf);
+cleanup_1:
+	lws_free(ctx->ctx);
+
+	return -1;
+}
+
+LWS_VISIBLE int
+lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+			   size_t in_len, uint8_t *out, size_t out_max)
+{
+	size_t olen = 0;
+	int n;
+
+	ctx->ctx->len = in_len;
+	n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, NULL, NULL,
+						MBEDTLS_RSA_PUBLIC,
+						&olen, in, out, out_max);
+	if (n) {
+		lwsl_notice("%s: -0x%x\n", __func__, -n);
+
+		return -1;
+	}
+
+	return olen;
+}
+
+LWS_VISIBLE int
+lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+			   size_t in_len, uint8_t *out)
+{
+	int n;
+
+	ctx->ctx->len = in_len;
+	n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, NULL, NULL,
+						MBEDTLS_RSA_PRIVATE,
+						in_len, in, out);
+	if (n) {
+		lwsl_notice("%s: -0x%x\n", __func__, -n);
+
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+lws_genrsa_genrsa_hash_to_mbed_hash(enum lws_genhash_types hash_type)
+{
+	int h = -1;
+
+	switch (hash_type) {
+	case LWS_GENHASH_TYPE_SHA1:
+		h = MBEDTLS_MD_SHA1;
+		break;
+	case LWS_GENHASH_TYPE_SHA256:
+		h = MBEDTLS_MD_SHA256;
+		break;
+	case LWS_GENHASH_TYPE_SHA384:
+		h = MBEDTLS_MD_SHA384;
+		break;
+	case LWS_GENHASH_TYPE_SHA512:
+		h = MBEDTLS_MD_SHA512;
+		break;
+	}
+
+	return h;
+}
+
+LWS_VISIBLE int
+lws_genrsa_public_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+			 enum lws_genhash_types hash_type, const uint8_t *sig,
+			 size_t sig_len)
+{
+	int n, h = lws_genrsa_genrsa_hash_to_mbed_hash(hash_type);
+
+	if (h < 0)
+		return -1;
+
+	n = mbedtls_rsa_rsassa_pkcs1_v15_verify(ctx->ctx, NULL, NULL,
+						MBEDTLS_RSA_PUBLIC,
+						h, 0, in, sig);
+	if (n < 0) {
+		lwsl_notice("%s: -0x%x\n", __func__, -n);
+
+		return -1;
+	}
+
+	return n;
+}
+
+LWS_VISIBLE int
+lws_genrsa_public_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+			 enum lws_genhash_types hash_type, uint8_t *sig,
+			 size_t sig_len)
+{
+	int n, h = lws_genrsa_genrsa_hash_to_mbed_hash(hash_type);
+
+	if (h < 0)
+		return -1;
+
+	/*
+	 * The "sig" buffer must be as large as the size of ctx->N
+	 * (eg. 128 bytes if RSA-1024 is used).
+	 */
+	if (sig_len < ctx->ctx->len)
+		return -1;
+
+	n = mbedtls_rsa_rsassa_pkcs1_v15_sign(ctx->ctx, NULL, NULL,
+					      MBEDTLS_RSA_PRIVATE, h, 0, in,
+					      sig);
+	if (n < 0) {
+		lwsl_notice("%s: -0x%x\n", __func__, -n);
+
+		return -1;
+	}
+
+	return ctx->ctx->len;
+}
+
+LWS_VISIBLE int
+lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private,
+			    uint8_t *pkey_asn1, size_t pkey_asn1_len)
+{
+	uint8_t *p = pkey_asn1, *totlen, *end = pkey_asn1 + pkey_asn1_len - 1;
+	mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
+		&ctx->ctx->N, &ctx->ctx->E, &ctx->ctx->D, &ctx->ctx->P,
+		&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
+		&ctx->ctx->QP,
+	};
+	int n;
+
+	/* 30 82  - sequence
+	 *   09 29  <-- length(0x0929) less 4 bytes
+	 * 02 01 <- length (1)
+	 *  00
+	 * 02 82
+	 *  02 01 <- length (513)  N
+	 *  ...
+	 *
+	 *  02 03 <- length (3) E
+	 *    01 00 01
+	 *
+	 * 02 82
+	 *   02 00 <- length (512) D P Q EXP1 EXP2 COEFF
+	 *
+	 *  */
+
+	*p++ = 0x30;
+	*p++ = 0x82;
+	totlen = p;
+	p += 2;
+
+	*p++ = 0x02;
+	*p++ = 0x01;
+	*p++ = 0x00;
+
+	for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++) {
+		int m = mbedtls_mpi_size(mpi[n]);
+		uint8_t *elen;
+
+		*p++ = 0x02;
+		elen = p;
+		if (m < 0x7f)
+			*p++ = m;
+		else {
+			*p++ = 0x82;
+			*p++ = m >> 8;
+			*p++ = m & 0xff;
+		}
+
+		if (p + m > end)
+			return -1;
+
+		mbedtls_mpi_write_binary(mpi[n], p, m);
+		if (p[0] & 0x80) {
+			p[0] = 0x00;
+			mbedtls_mpi_write_binary(mpi[n], &p[1], m);
+			m++;
+		}
+		if (m < 0x7f)
+			*elen = m;
+		else {
+			*elen++ = 0x82;
+			*elen++ = m >> 8;
+			*elen = m & 0xff;
+		}
+		p += m;
+	}
+
+	n = lws_ptr_diff(p, pkey_asn1);
+
+	*totlen++ = (n - 4) >> 8;
+	*totlen = (n - 4) & 0xff;
+
+	return n;
+}
+
+LWS_VISIBLE void
+lws_genrsa_destroy(struct lws_genrsa_ctx *ctx)
+{
+	if (!ctx->ctx)
+		return;
+	mbedtls_rsa_free(ctx->ctx);
+	lws_free(ctx->ctx);
+	ctx->ctx = NULL;
+}

+ 240 - 0
thirdparty/libwebsockets/tls/mbedtls/mbedtls-client.c

@@ -0,0 +1,240 @@
+/*
+ * libwebsockets - mbedtls-specific client TLS code
+ *
+ * Copyright (C) 2010-2017 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "core/private.h"
+
+static int
+OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+	return 0;
+}
+
+int
+lws_ssl_client_bio_create(struct lws *wsi)
+{
+	X509_VERIFY_PARAM *param;
+	char hostname[128], *p;
+	const char *alpn_comma = wsi->context->tls.alpn_default;
+	struct alpn_ctx protos;
+
+	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->tls.ssl = SSL_new(wsi->vhost->tls.ssl_client_ctx);
+	if (!wsi->tls.ssl)
+		return -1;
+
+	if (wsi->vhost->tls.ssl_info_event_mask)
+		SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback);
+
+	if (!(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
+		param = SSL_get0_param(wsi->tls.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);
+	}
+
+	if (wsi->vhost->tls.alpn)
+		alpn_comma = wsi->vhost->tls.alpn;
+
+	if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
+			 _WSI_TOKEN_CLIENT_ALPN) > 0)
+		alpn_comma = hostname;
+
+	lwsl_info("%s: %p: client conn sending ALPN list '%s'\n",
+		  __func__, wsi, alpn_comma);
+
+	protos.len = lws_alpn_comma_to_openssl(alpn_comma, protos.data,
+					       sizeof(protos.data) - 1);
+
+	/* with mbedtls, protos is not pointed to after exit from this call */
+	SSL_set_alpn_select_cb(wsi->tls.ssl, &protos);
+
+	/*
+	 * use server name indication (SNI), if supported,
+	 * when establishing connection
+	 */
+	SSL_set_verify(wsi->tls.ssl, SSL_VERIFY_PEER,
+		       OpenSSL_client_verify_callback);
+
+	SSL_set_fd(wsi->tls.ssl, wsi->desc.sockfd);
+
+	return 0;
+}
+
+int ERR_get_error(void)
+{
+	return 0;
+}
+
+enum lws_ssl_capable_status
+lws_tls_client_connect(struct lws *wsi)
+{
+	int m, n = SSL_connect(wsi->tls.ssl);
+	const unsigned char *prot;
+	unsigned int len;
+
+	if (n == 1) {
+		SSL_get0_alpn_selected(wsi->tls.ssl, &prot, &len);
+		lws_role_call_alpn_negotiated(wsi, (const char *)prot);
+		lwsl_info("client connect OK\n");
+		return LWS_SSL_CAPABLE_DONE;
+	}
+
+	m = SSL_get_error(wsi->tls.ssl, n);
+
+	if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl))
+		return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
+
+	if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl))
+		return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
+
+	if (!n) /* we don't know what he wants, but he says to retry */
+		return LWS_SSL_CAPABLE_MORE_SERVICE;
+
+	return LWS_SSL_CAPABLE_ERROR;
+}
+
+int
+lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len)
+{
+	int n;
+	X509 *peer = SSL_get_peer_certificate(wsi->tls.ssl);
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+	char *sb = (char *)&pt->serv_buf[0];
+
+	if (!peer) {
+		lwsl_info("peer did not provide cert\n");
+
+		return -1;
+	}
+	lwsl_info("peer provided cert\n");
+
+	n = SSL_get_verify_result(wsi->tls.ssl);
+	lws_latency(wsi->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)
+		return 0;
+
+	if (n == X509_V_ERR_HOSTNAME_MISMATCH &&
+	    (wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
+		lwsl_info("accepting certificate for invalid hostname\n");
+		return 0;
+	}
+
+	if (n == X509_V_ERR_INVALID_CA &&
+	    (wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED)) {
+		lwsl_info("accepting certificate from untrusted CA\n");
+		return 0;
+	}
+
+	if ((n == X509_V_ERR_CERT_NOT_YET_VALID ||
+	     n == X509_V_ERR_CERT_HAS_EXPIRED) &&
+	     (wsi->tls.use_ssl & LCCSCF_ALLOW_EXPIRED)) {
+		lwsl_info("accepting expired or not yet valid certificate\n");
+
+		return 0;
+	}
+	lws_snprintf(ebuf, ebuf_len,
+		"server's cert didn't look good, X509_V_ERR = %d: %s\n",
+		 n, ERR_error_string(n, sb));
+	lwsl_info("%s\n", ebuf);
+	lws_ssl_elaborate_error();
+
+	return -1;
+}
+
+int
+lws_tls_client_create_vhost_context(struct lws_vhost *vh,
+				    const struct lws_context_creation_info *info,
+				    const char *cipher_list,
+				    const char *ca_filepath,
+				    const char *cert_filepath,
+				    const char *private_key_filepath)
+{
+	X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len);
+	SSL_METHOD *method = (SSL_METHOD *)TLS_client_method();
+	unsigned long error;
+	lws_filepos_t len;
+	uint8_t *buf;
+
+	if (!method) {
+		error = ERR_get_error();
+		lwsl_err("problem creating ssl method %lu: %s\n",
+			error, ERR_error_string(error,
+				      (char *)vh->context->pt[0].serv_buf));
+		return 1;
+	}
+	/* create context */
+	vh->tls.ssl_client_ctx = SSL_CTX_new(method);
+	if (!vh->tls.ssl_client_ctx) {
+		error = ERR_get_error();
+		lwsl_err("problem creating ssl context %lu: %s\n",
+			error, ERR_error_string(error,
+				      (char *)vh->context->pt[0].serv_buf));
+		return 1;
+	}
+
+	if (!ca_filepath)
+		return 0;
+
+	if (alloc_file(vh->context, ca_filepath, &buf, &len)) {
+		lwsl_err("Load CA cert file %s failed\n", ca_filepath);
+		return 1;
+	}
+
+	vh->tls.x509_client_CA = d2i_X509(NULL, buf, len);
+	free(buf);
+	if (!vh->tls.x509_client_CA) {
+		lwsl_err("client CA: x509 parse failed\n");
+		return 1;
+	}
+
+	if (!vh->tls.ssl_ctx)
+		SSL_CTX_add_client_CA(vh->tls.ssl_client_ctx, vh->tls.x509_client_CA);
+	else
+		SSL_CTX_add_client_CA(vh->tls.ssl_ctx, vh->tls.x509_client_CA);
+
+	lwsl_notice("client loaded CA for verification %s\n", ca_filepath);
+
+	return 0;
+}

+ 694 - 0
thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c

@@ -0,0 +1,694 @@
+/*
+ * libwebsockets - mbedTLS-specific server functions
+ *
+ * Copyright (C) 2010-2017 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "core/private.h"
+#include <mbedtls/x509_csr.h>
+
+int
+lws_tls_server_client_cert_verify_config(struct lws_vhost *vh)
+{
+	int verify_options = SSL_VERIFY_PEER;
+
+	/* as a server, are we requiring clients to identify themselves? */
+	if (!lws_check_opt(vh->options,
+			  LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) {
+		lwsl_notice("no client cert required\n");
+		return 0;
+	}
+
+	/*
+	 * The wrapper has this messed-up mapping:
+	 *
+	 * 	   else if (ctx->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+	 *     mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
+	 *
+	 * ie the meaning is inverted.  So where we should test for ! we don't
+	 */
+	if (lws_check_opt(vh->options, LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED))
+		verify_options = SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+
+	lwsl_notice("%s: vh %s requires client cert %d\n", __func__, vh->name,
+		    verify_options);
+
+	SSL_CTX_set_verify(vh->tls.ssl_ctx, verify_options, NULL);
+
+	return 0;
+}
+
+static int
+lws_mbedtls_sni_cb(void *arg, mbedtls_ssl_context *mbedtls_ctx,
+		   const unsigned char *servername, size_t len)
+{
+	SSL *ssl = SSL_SSL_from_mbedtls_ssl_context(mbedtls_ctx);
+	struct lws_context *context = (struct lws_context *)arg;
+	struct lws_vhost *vhost, *vh;
+
+	lwsl_notice("%s: %s\n", __func__, servername);
+
+	/*
+	 * 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->tls.ssl_ctx == SSL_get_SSL_CTX(ssl))
+			break;
+		vh = vh->vhost_next;
+	}
+
+	if (!vh) {
+		assert(vh); /* can't match the incoming vh? */
+		return 0;
+	}
+
+	vhost = lws_select_vhost(context, vh->listen_port,
+				 (const char *)servername);
+	if (!vhost) {
+		lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port);
+
+		return 0;
+	}
+
+	lwsl_info("SNI: Found: %s:%d at vhost '%s'\n", servername,
+					vh->listen_port, vhost->name);
+
+	/* select the ssl ctx from the selected vhost for this conn */
+	SSL_set_SSL_CTX(ssl, vhost->tls.ssl_ctx);
+
+	return 0;
+}
+
+int
+lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi,
+			  const char *cert, const char *private_key,
+			  const char *mem_cert, size_t len_mem_cert,
+			  const char *mem_privkey, size_t mem_privkey_len)
+{
+	int n, f = 0;
+	const char *filepath = private_key;
+	uint8_t *mem = NULL, *p = NULL;
+	size_t mem_len = 0;
+	lws_filepos_t flen;
+	long err;
+
+	if ((!cert || !private_key) && (!mem_cert || !mem_privkey)) {
+		lwsl_notice("%s: no usable input\n", __func__);
+		return 0;
+	}
+
+	n = lws_tls_generic_cert_checks(vhost, cert, private_key);
+
+	if (n == LWS_TLS_EXTANT_NO && (!mem_cert || !mem_privkey))
+		return 0;
+
+	/*
+	 * we can't read the root-privs files.  But if mem_cert is provided,
+	 * we should use that.
+	 */
+	if (n == LWS_TLS_EXTANT_NO)
+		n = LWS_TLS_EXTANT_ALTERNATIVE;
+
+	if (n == LWS_TLS_EXTANT_ALTERNATIVE && (!mem_cert || !mem_privkey))
+		return 1; /* no alternative */
+
+	if (n == LWS_TLS_EXTANT_ALTERNATIVE) {
+		/*
+		 * Although we have prepared update certs, we no longer have
+		 * the rights to read our own cert + key we saved.
+		 *
+		 * If we were passed copies in memory buffers, use those
+		 * instead.
+		 *
+		 * The passed memory-buffer cert image is in DER, and the
+		 * memory-buffer private key image is PEM.
+		 */
+		/* mem cert is already DER */
+		p = (uint8_t *)mem_cert;
+		flen = len_mem_cert;
+		/* mem private key is PEM, so go through the motions */
+		mem = (uint8_t *)mem_privkey;
+		mem_len = mem_privkey_len;
+		filepath = NULL;
+	} else {
+		if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, NULL,
+						  0, &p, &flen)) {
+			lwsl_err("couldn't find cert file %s\n", cert);
+
+			return 1;
+		}
+		f = 1;
+	}
+	err = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, flen, p);
+	if (!err) {
+		free(p);
+		lwsl_err("Problem loading cert\n");
+		return 1;
+	}
+
+	if (f)
+		free(p);
+	p = NULL;
+
+	if (private_key || n == LWS_TLS_EXTANT_ALTERNATIVE) {
+		if (lws_tls_alloc_pem_to_der_file(vhost->context, filepath,
+						  (char *)mem, mem_len, &p,
+						  &flen)) {
+			lwsl_err("couldn't find private key file %s\n",
+					private_key);
+
+			return 1;
+		}
+		err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->tls.ssl_ctx, p, flen);
+		if (!err) {
+			free(p);
+			lwsl_err("Problem loading key\n");
+
+			return 1;
+		}
+	}
+
+	if (p && !mem_privkey) {
+		free(p);
+		p = NULL;
+	}
+
+	if (!private_key && !mem_privkey &&
+	    vhost->protocols[0].callback(wsi,
+			LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
+			vhost->tls.ssl_ctx, NULL, 0)) {
+		lwsl_err("ssl private key not set\n");
+
+		return 1;
+	}
+
+	vhost->tls.skipped_certs = 0;
+
+	return 0;
+}
+
+int
+lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info,
+				  struct lws_vhost *vhost, struct lws *wsi)
+{
+	const SSL_METHOD *method = TLS_server_method();
+	uint8_t *p;
+	lws_filepos_t flen;
+	int n;
+
+	vhost->tls.ssl_ctx = SSL_CTX_new(method);	/* create context */
+	if (!vhost->tls.ssl_ctx) {
+		lwsl_err("problem creating ssl context\n");
+		return 1;
+	}
+
+	if (!vhost->tls.use_ssl || !info->ssl_cert_filepath)
+		return 0;
+
+	if (info->ssl_ca_filepath) {
+		lwsl_notice("%s: vh %s: loading CA filepath %s\n", __func__,
+			    vhost->name, info->ssl_ca_filepath);
+		if (lws_tls_alloc_pem_to_der_file(vhost->context,
+				info->ssl_ca_filepath, NULL, 0, &p, &flen)) {
+			lwsl_err("couldn't find client CA file %s\n",
+					info->ssl_ca_filepath);
+
+			return 1;
+		}
+
+		if (SSL_CTX_add_client_CA_ASN1(vhost->tls.ssl_ctx, (int)flen, p) != 1) {
+			lwsl_err("%s: SSL_CTX_add_client_CA_ASN1 unhappy\n",
+				 __func__);
+			free(p);
+			return 1;
+		}
+		free(p);
+	}
+
+	n = lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath,
+				      info->ssl_private_key_filepath, NULL,
+				      0, NULL, 0);
+	if (n)
+		return n;
+
+	return 0;
+}
+
+int
+lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd)
+{
+	errno = 0;
+	wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_ctx);
+	if (wsi->tls.ssl == NULL) {
+		lwsl_err("SSL_new failed: errno %d\n", errno);
+
+		lws_ssl_elaborate_error();
+		return 1;
+	}
+
+	SSL_set_fd(wsi->tls.ssl, accept_fd);
+
+	if (wsi->vhost->tls.ssl_info_event_mask)
+		SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback);
+
+	SSL_set_sni_callback(wsi->tls.ssl, lws_mbedtls_sni_cb, wsi->context);
+
+	return 0;
+}
+
+int
+lws_tls_server_abort_connection(struct lws *wsi)
+{
+	__lws_tls_shutdown(wsi);
+	SSL_free(wsi->tls.ssl);
+
+	return 0;
+}
+
+enum lws_ssl_capable_status
+lws_tls_server_accept(struct lws *wsi)
+{
+	union lws_tls_cert_info_results ir;
+	int m, n;
+
+	n = SSL_accept(wsi->tls.ssl);
+	if (n == 1) {
+
+		if (strstr(wsi->vhost->name, ".invalid")) {
+			lwsl_notice("%s: vhost has .invalid, rejecting accept\n", __func__);
+
+			return LWS_SSL_CAPABLE_ERROR;
+		}
+
+		n = lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME, &ir,
+					   sizeof(ir.ns.name));
+		if (!n)
+			lwsl_notice("%s: client cert CN '%s'\n",
+				    __func__, ir.ns.name);
+		else
+			lwsl_info("%s: couldn't get client cert CN\n", __func__);
+		return LWS_SSL_CAPABLE_DONE;
+	}
+
+	m = SSL_get_error(wsi->tls.ssl, n);
+	lwsl_debug("%s: %p: accept SSL_get_error %d errno %d\n", __func__,
+		   wsi, m, errno);
+
+	// mbedtls wrapper only
+	if (m == SSL_ERROR_SYSCALL && errno == 11)
+		return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
+
+	if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
+		return LWS_SSL_CAPABLE_ERROR;
+
+	if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) {
+		if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
+			lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__);
+			return LWS_SSL_CAPABLE_ERROR;
+		}
+
+		lwsl_info("SSL_ERROR_WANT_READ\n");
+		return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
+	}
+	if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) {
+		lwsl_debug("%s: WANT_WRITE\n", __func__);
+
+		if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
+			lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__);
+			return LWS_SSL_CAPABLE_ERROR;
+		}
+		return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
+	}
+
+	return LWS_SSL_CAPABLE_ERROR;
+}
+
+#if defined(LWS_WITH_ACME)
+/*
+ * mbedtls doesn't support SAN for cert creation.  So we use a known-good
+ * tls-sni-01 cert from OpenSSL that worked on Let's Encrypt, and just replace
+ * the pubkey n part and the signature part.
+ *
+ * This will need redoing for tls-sni-02...
+ */
+
+static uint8_t ss_cert_leadin[] = {
+	0x30, 0x82,
+	  0x05, 0x56, /* total length: LEN1 (+2 / +3) (correct for 513 + 512)*/
+
+	0x30, 0x82, /* length: LEN2  (+6 / +7) (correct for 513) */
+		0x03, 0x3e,
+
+	/* addition: v3 cert (+5 bytes)*/
+	0xa0, 0x03,
+		0x02, 0x01, 0x02,
+
+	0x02, 0x01, 0x01,
+	0x30, 0x0d, 0x06, 0x09, 0x2a,
+	0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x3f,
+	0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47,
+	0x42, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b,
+	0x73, 0x6f, 0x6d, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x31,
+	0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11, 0x74, 0x65,
+	0x6d, 0x70, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69, 0x6e, 0x76, 0x61,
+	0x6c, 0x69, 0x64, 0x30, 0x1e, 0x17, 0x0d,
+
+	/* from 2017-10-29 ... */
+	0x31, 0x37, 0x31, 0x30, 0x32, 0x39, 0x31, 0x31, 0x34, 0x39, 0x34, 0x35,
+	0x5a, 0x17, 0x0d,
+
+	/* thru 2049-10-29 we immediately discard the private key, no worries */
+	0x34, 0x39, 0x31, 0x30, 0x32, 0x39, 0x31, 0x32, 0x34, 0x39, 0x34, 0x35,
+	0x5a,
+
+	0x30, 0x3f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+	0x02, 0x47, 0x42, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a,
+	0x0c, 0x0b, 0x73, 0x6f, 0x6d, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e,
+	0x79, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11,
+	0x74, 0x65, 0x6d, 0x70, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69, 0x6e,
+	0x76, 0x61, 0x6c, 0x69, 0x64, 0x30,
+
+	0x82,
+		0x02, 0x22, /* LEN3 (+C3 / C4) */
+	0x30, 0x0d, 0x06,
+	0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+	0x03,
+
+	0x82,
+		0x02, 0x0f, /* LEN4 (+D6 / D7) */
+
+	0x00, 0x30, 0x82,
+
+		0x02, 0x0a, /* LEN5 (+ DB / DC) */
+
+	0x02, 0x82,
+
+	//0x02, 0x01, /* length of n in bytes (including leading 00 if any) */
+	},
+
+	/* 1 + (keybits / 8) bytes N */
+
+	ss_cert_san_leadin[] = {
+		/* e - fixed */
+		0x02, 0x03, 0x01, 0x00, 0x01,
+
+		0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x59, 0x06, 0x03, 0x55, 0x1d,
+		0x11, 0x04, 0x52, 0x30, 0x50, /* <-- SAN length + 2 */
+
+		0x82, 0x4e, /* <-- SAN length */
+	},
+
+	/* 78 bytes of SAN (tls-sni-01)
+	0x61, 0x64, 0x34, 0x31, 0x61, 0x66, 0x62, 0x65, 0x30, 0x63, 0x61, 0x34,
+	0x36, 0x34, 0x32, 0x66, 0x30, 0x61, 0x34, 0x34, 0x39, 0x64, 0x39, 0x63,
+	0x61, 0x37, 0x36, 0x65, 0x62, 0x61, 0x61, 0x62, 0x2e, 0x32, 0x38, 0x39,
+	0x34, 0x64, 0x34, 0x31, 0x36, 0x63, 0x39, 0x38, 0x33, 0x66, 0x31, 0x32,
+	0x65, 0x64, 0x37, 0x33, 0x31, 0x61, 0x33, 0x30, 0x66, 0x35, 0x63, 0x34,
+	0x34, 0x37, 0x37, 0x66, 0x65, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69,
+	0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, */
+
+	/* end of LEN2 area */
+
+	ss_cert_sig_leadin[] = {
+		/* it's saying that the signature is SHA256 + RSA */
+		0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+		0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
+
+		0x82,
+			0x02, 0x01,
+		0x00,
+	};
+
+	/* (keybits / 8) bytes signature to end of LEN1 area */
+
+#define SAN_A_LENGTH 78
+
+LWS_VISIBLE int
+lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a,
+			     const char *san_b)
+{
+	int buflen = 0x560;
+	uint8_t *buf = lws_malloc(buflen, "tmp cert buf"), *p = buf, *pkey_asn1;
+	struct lws_genrsa_ctx ctx;
+	struct lws_genrsa_elements el;
+	uint8_t digest[32];
+	struct lws_genhash_ctx hash_ctx;
+	int pkey_asn1_len = 3 * 1024;
+	int n, m, keybits = lws_plat_recommended_rsa_bits(), adj;
+
+	if (!buf)
+		return 1;
+
+	n = lws_genrsa_new_keypair(vhost->context, &ctx, &el, keybits);
+	if (n < 0) {
+		lws_jwk_destroy_genrsa_elements(&el);
+		goto bail1;
+	}
+
+	n = sizeof(ss_cert_leadin);
+	memcpy(p, ss_cert_leadin, n);
+	p += n;
+
+	adj = (0x0556 - 0x401) + (keybits / 4) + 1;
+	buf[2] = adj >> 8;
+	buf[3] = adj & 0xff;
+
+	adj = (0x033e - 0x201) + (keybits / 8) + 1;
+	buf[6] = adj >> 8;
+	buf[7] = adj & 0xff;
+
+	adj = (0x0222 - 0x201) + (keybits / 8) + 1;
+	buf[0xc3] = adj >> 8;
+	buf[0xc4] = adj & 0xff;
+
+	adj = (0x020f - 0x201) + (keybits / 8) + 1;
+	buf[0xd6] = adj >> 8;
+	buf[0xd7] = adj & 0xff;
+
+	adj = (0x020a - 0x201) + (keybits / 8) + 1;
+	buf[0xdb] = adj >> 8;
+	buf[0xdc] = adj & 0xff;
+
+	*p++ = ((keybits / 8) + 1) >> 8;
+	*p++ = ((keybits / 8) + 1) & 0xff;
+
+	/* we need to drop 1 + (keybits / 8) bytes of n in here, 00 + key */
+
+	*p++ = 0x00;
+	memcpy(p, el.e[JWK_KEY_N].buf, el.e[JWK_KEY_N].len);
+	p += el.e[JWK_KEY_N].len;
+
+	memcpy(p, ss_cert_san_leadin, sizeof(ss_cert_san_leadin));
+	p += sizeof(ss_cert_san_leadin);
+
+	/* drop in 78 bytes of san_a */
+
+	memcpy(p, san_a, SAN_A_LENGTH);
+	p += SAN_A_LENGTH;
+	memcpy(p, ss_cert_sig_leadin, sizeof(ss_cert_sig_leadin));
+
+	p[17] = ((keybits / 8) + 1) >> 8;
+	p[18] = ((keybits / 8) + 1) & 0xff;
+
+	p += sizeof(ss_cert_sig_leadin);
+
+	/* hash the cert plaintext */
+
+	if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
+		goto bail2;
+
+	if (lws_genhash_update(&hash_ctx, buf, lws_ptr_diff(p, buf))) {
+		lws_genhash_destroy(&hash_ctx, NULL);
+
+		goto bail2;
+	}
+	if (lws_genhash_destroy(&hash_ctx, digest))
+		goto bail2;
+
+	/* sign the hash */
+
+	n = lws_genrsa_public_sign(&ctx, digest, LWS_GENHASH_TYPE_SHA256, p,
+				 buflen - lws_ptr_diff(p, buf));
+	if (n < 0)
+		goto bail2;
+	p += n;
+
+	pkey_asn1 = lws_malloc(pkey_asn1_len, "mbed crt tmp");
+	if (!pkey_asn1)
+		goto bail2;
+
+	m = lws_genrsa_render_pkey_asn1(&ctx, 1, pkey_asn1, pkey_asn1_len);
+	if (m < 0) {
+		lws_free(pkey_asn1);
+		goto bail2;
+	}
+
+//	lwsl_hexdump_level(LLL_DEBUG, buf, lws_ptr_diff(p, buf));
+	n = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx,
+				 lws_ptr_diff(p, buf), buf);
+	if (n != 1) {
+		lws_free(pkey_asn1);
+		lwsl_err("%s: generated cert failed to load 0x%x\n",
+				__func__, -n);
+	} else {
+		//lwsl_debug("private key\n");
+		//lwsl_hexdump_level(LLL_DEBUG, pkey_asn1, n);
+
+		/* and to use our generated private key */
+		n = SSL_CTX_use_PrivateKey_ASN1(0, vhost->tls.ssl_ctx, pkey_asn1, m);
+		lws_free(pkey_asn1);
+		if (n != 1) {
+			lwsl_err("%s: SSL_CTX_use_PrivateKey_ASN1 failed\n",
+				    __func__);
+		}
+	}
+
+	lws_genrsa_destroy(&ctx);
+	lws_jwk_destroy_genrsa_elements(&el);
+
+	lws_free(buf);
+
+	return n != 1;
+
+bail2:
+	lws_genrsa_destroy(&ctx);
+	lws_jwk_destroy_genrsa_elements(&el);
+bail1:
+	lws_free(buf);
+
+	return -1;
+}
+
+void
+lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost)
+{
+}
+
+#if defined(LWS_WITH_JWS)
+static int
+_rngf(void *context, unsigned char *buf, size_t len)
+{
+	if ((size_t)lws_get_random(context, buf, len) == len)
+		return 0;
+
+	return -1;
+}
+
+static const char *x5[] = { "C", "ST", "L", "O", "CN" };
+
+/*
+ * CSR is output formatted as b64url(DER)
+ * Private key is output as a PEM in memory
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[],
+			    uint8_t *dcsr, size_t csr_len, char **privkey_pem,
+			    size_t *privkey_len)
+{
+	mbedtls_x509write_csr csr;
+	mbedtls_pk_context mpk;
+	int buf_size = 4096, n;
+	char subject[200], *p = subject, *end = p + sizeof(subject) - 1;
+	uint8_t *buf = malloc(buf_size); /* malloc because given to user code */
+
+	if (!buf)
+		return -1;
+
+	mbedtls_x509write_csr_init(&csr);
+
+	mbedtls_pk_init(&mpk);
+	if (mbedtls_pk_setup(&mpk, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA))) {
+		lwsl_notice("%s: pk_setup failed\n", __func__);
+		goto fail;
+	}
+
+	n = mbedtls_rsa_gen_key(mbedtls_pk_rsa(mpk), _rngf, context,
+				lws_plat_recommended_rsa_bits(), 65537);
+	if (n) {
+		lwsl_notice("%s: failed to generate keys\n", __func__);
+
+		goto fail1;
+	}
+
+	/* subject must be formatted like "C=TW,O=warmcat,CN=myserver" */
+
+	for (n = 0; n < (int)ARRAY_SIZE(x5); n++) {
+		if (p != subject)
+			*p++ = ',';
+		if (elements[n])
+			p += lws_snprintf(p, end - p, "%s=%s", x5[n],
+					  elements[n]);
+	}
+
+	if (mbedtls_x509write_csr_set_subject_name(&csr, subject))
+		goto fail1;
+
+	mbedtls_x509write_csr_set_key(&csr, &mpk);
+	mbedtls_x509write_csr_set_md_alg(&csr, MBEDTLS_MD_SHA256);
+
+	/*
+	 * data is written at the end of the buffer! Use the
+	 * return value to determine where you should start
+	 * using the buffer
+	 */
+	n = mbedtls_x509write_csr_der(&csr, buf, buf_size, _rngf, context);
+	if (n < 0) {
+		lwsl_notice("%s: write csr der failed\n", __func__);
+		goto fail1;
+	}
+
+	/* we have it in DER, we need it in b64URL */
+
+	n = lws_jws_base64_enc((char *)(buf + buf_size) - n, n,
+			       (char *)dcsr, csr_len);
+	if (n < 0)
+		goto fail1;
+
+	/*
+	 * okay, the CSR is done, last we need the private key in PEM
+	 * re-use the DER CSR buf as the result buffer since we cn do it in
+	 * one step
+	 */
+
+	if (mbedtls_pk_write_key_pem(&mpk, buf, buf_size)) {
+		lwsl_notice("write key pem failed\n");
+		goto fail1;
+	}
+
+	*privkey_pem = (char *)buf;
+	*privkey_len = strlen((const char *)buf);
+
+	mbedtls_pk_free(&mpk);
+	mbedtls_x509write_csr_free(&csr);
+
+	return n;
+
+fail1:
+	mbedtls_pk_free(&mpk);
+fail:
+	mbedtls_x509write_csr_free(&csr);
+	free(buf);
+
+	return -1;
+}
+#endif
+#endif

+ 520 - 0
thirdparty/libwebsockets/tls/mbedtls/ssl.c

@@ -0,0 +1,520 @@
+/*
+ * libwebsockets - mbedTLS-specific lws apis
+ *
+ * Copyright (C) 2010-2017 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "core/private.h"
+#include <mbedtls/oid.h>
+
+void
+lws_ssl_elaborate_error(void)
+{
+}
+
+int
+lws_context_init_ssl_library(const struct lws_context_creation_info *info)
+{
+	lwsl_info(" Compiled with MbedTLS support\n");
+
+	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
+		lwsl_info(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
+
+	return 0;
+}
+
+LWS_VISIBLE void
+lws_ssl_destroy(struct lws_vhost *vhost)
+{
+	if (!lws_check_opt(vhost->context->options,
+			   LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
+		return;
+
+	if (vhost->tls.ssl_ctx)
+		SSL_CTX_free(vhost->tls.ssl_ctx);
+	if (!vhost->tls.user_supplied_ssl_ctx && vhost->tls.ssl_client_ctx)
+		SSL_CTX_free(vhost->tls.ssl_client_ctx);
+
+	if (vhost->tls.x509_client_CA)
+		X509_free(vhost->tls.x509_client_CA);
+}
+
+LWS_VISIBLE int
+lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
+{
+	struct lws_context *context = wsi->context;
+	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+	int n = 0, m;
+
+	if (!wsi->tls.ssl)
+		return lws_ssl_capable_read_no_ssl(wsi, buf, len);
+
+	lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
+
+	errno = 0;
+	n = SSL_read(wsi->tls.ssl, buf, len);
+#if defined(LWS_WITH_ESP32)
+	if (!n && errno == ENOTCONN) {
+		lwsl_debug("%p: SSL_read ENOTCONN\n", wsi);
+		return LWS_SSL_CAPABLE_ERROR;
+	}
+#endif
+#if defined(LWS_WITH_STATS)
+	if (!wsi->seen_rx) {
+                lws_stats_atomic_bump(wsi->context, pt,
+                		      LWSSTATS_MS_SSL_RX_DELAY,
+				time_in_microseconds() - wsi->accept_start_us);
+                lws_stats_atomic_bump(wsi->context, pt,
+                		      LWSSTATS_C_SSL_CONNS_HAD_RX, 1);
+		wsi->seen_rx = 1;
+	}
+#endif
+
+
+	lwsl_debug("%p: SSL_read says %d\n", wsi, n);
+	/* manpage: returning 0 means connection shut down */
+	if (!n) {
+		wsi->socket_is_permanently_unusable = 1;
+
+		return LWS_SSL_CAPABLE_ERROR;
+	}
+
+	if (n < 0) {
+		m = SSL_get_error(wsi->tls.ssl, n);
+		lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
+		if (m == SSL_ERROR_ZERO_RETURN ||
+		    m == SSL_ERROR_SYSCALL)
+			return LWS_SSL_CAPABLE_ERROR;
+
+		if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) {
+			lwsl_debug("%s: WANT_READ\n", __func__);
+			lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
+			return LWS_SSL_CAPABLE_MORE_SERVICE;
+		}
+		if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) {
+			lwsl_debug("%s: WANT_WRITE\n", __func__);
+			lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
+			return LWS_SSL_CAPABLE_MORE_SERVICE;
+		}
+		wsi->socket_is_permanently_unusable = 1;
+
+		return LWS_SSL_CAPABLE_ERROR;
+	}
+
+	lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
+
+	if (wsi->vhost)
+		wsi->vhost->conn_stats.rx += n;
+
+	lws_restart_ws_ping_pong_timer(wsi);
+
+	/*
+	 * if it was our buffer that limited what we read,
+	 * check if SSL has additional data pending inside SSL buffers.
+	 *
+	 * Because these won't signal at the network layer with POLLIN
+	 * and if we don't realize, this data will sit there forever
+	 */
+	if (n != len)
+		goto bail;
+	if (!wsi->tls.ssl)
+		goto bail;
+
+	if (!SSL_pending(wsi->tls.ssl))
+		goto bail;
+
+	if (wsi->tls.pending_read_list_next)
+		return n;
+	if (wsi->tls.pending_read_list_prev)
+		return n;
+	if (pt->tls.pending_read_list == wsi)
+		return n;
+
+	/* add us to the linked list of guys with pending ssl */
+	if (pt->tls.pending_read_list)
+		pt->tls.pending_read_list->tls.pending_read_list_prev = wsi;
+
+	wsi->tls.pending_read_list_next = pt->tls.pending_read_list;
+	wsi->tls.pending_read_list_prev = NULL;
+	pt->tls.pending_read_list = wsi;
+
+	return n;
+bail:
+	lws_ssl_remove_wsi_from_buffered_list(wsi);
+
+	return n;
+}
+
+LWS_VISIBLE int
+lws_ssl_pending(struct lws *wsi)
+{
+	if (!wsi->tls.ssl)
+		return 0;
+
+	return SSL_pending(wsi->tls.ssl);
+}
+
+LWS_VISIBLE int
+lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
+{
+	int n, m;
+
+	if (!wsi->tls.ssl)
+		return lws_ssl_capable_write_no_ssl(wsi, buf, len);
+
+	n = SSL_write(wsi->tls.ssl, buf, len);
+	if (n > 0)
+		return n;
+
+	m = SSL_get_error(wsi->tls.ssl, n);
+	if (m != SSL_ERROR_SYSCALL) {
+		if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) {
+			lwsl_notice("%s: want read\n", __func__);
+
+			return LWS_SSL_CAPABLE_MORE_SERVICE;
+		}
+
+		if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) {
+			lws_set_blocking_send(wsi);
+			lwsl_notice("%s: want write\n", __func__);
+
+			return LWS_SSL_CAPABLE_MORE_SERVICE;
+		}
+	}
+
+	lwsl_debug("%s failed: %d\n",__func__, m);
+	wsi->socket_is_permanently_unusable = 1;
+
+	return LWS_SSL_CAPABLE_ERROR;
+}
+
+int openssl_SSL_CTX_private_data_index;
+
+void
+lws_ssl_info_callback(const SSL *ssl, int where, int ret)
+{
+	struct lws *wsi;
+	struct lws_context *context;
+	struct lws_ssl_info si;
+
+	context = (struct lws_context *)SSL_CTX_get_ex_data(
+					SSL_get_SSL_CTX(ssl),
+					openssl_SSL_CTX_private_data_index);
+	if (!context)
+		return;
+	wsi = wsi_from_fd(context, SSL_get_fd(ssl));
+	if (!wsi)
+		return;
+
+	if (!(where & wsi->vhost->tls.ssl_info_event_mask))
+		return;
+
+	si.where = where;
+	si.ret = ret;
+
+	if (user_callback_handle_rxflow(wsi->protocol->callback,
+					wsi, LWS_CALLBACK_SSL_INFO,
+					wsi->user_space, &si, 0))
+		lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1);
+}
+
+
+LWS_VISIBLE int
+lws_ssl_close(struct lws *wsi)
+{
+	lws_sockfd_type n;
+
+	if (!wsi->tls.ssl)
+		return 0; /* not handled */
+
+#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
+	/* kill ssl callbacks, becausse we will remove the fd from the
+	 * table linking it to the wsi
+	 */
+	if (wsi->vhost->tls.ssl_info_event_mask)
+		SSL_set_info_callback(wsi->tls.ssl, NULL);
+#endif
+
+	n = SSL_get_fd(wsi->tls.ssl);
+	if (!wsi->socket_is_permanently_unusable)
+		SSL_shutdown(wsi->tls.ssl);
+	compatible_close(n);
+	SSL_free(wsi->tls.ssl);
+	wsi->tls.ssl = NULL;
+
+	if (!lwsi_role_client(wsi) &&
+	    wsi->context->simultaneous_ssl_restriction &&
+	    wsi->context->simultaneous_ssl-- ==
+			    wsi->context->simultaneous_ssl_restriction)
+		/* we made space and can do an accept */
+		lws_gate_accepts(wsi->context, 1);
+
+#if defined(LWS_WITH_STATS)
+	wsi->context->updated = 1;
+#endif
+
+	return 1; /* handled */
+}
+
+void
+lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
+{
+	if (vhost->tls.ssl_ctx)
+		SSL_CTX_free(vhost->tls.ssl_ctx);
+
+	if (!vhost->tls.user_supplied_ssl_ctx && vhost->tls.ssl_client_ctx)
+		SSL_CTX_free(vhost->tls.ssl_client_ctx);
+#if defined(LWS_WITH_ACME)
+	lws_tls_acme_sni_cert_destroy(vhost);
+#endif
+}
+
+void
+lws_ssl_context_destroy(struct lws_context *context)
+{
+}
+
+lws_tls_ctx *
+lws_tls_ctx_from_wsi(struct lws *wsi)
+{
+	if (!wsi->tls.ssl)
+		return NULL;
+
+	return SSL_get_SSL_CTX(wsi->tls.ssl);
+}
+
+enum lws_ssl_capable_status
+__lws_tls_shutdown(struct lws *wsi)
+{
+	int n = SSL_shutdown(wsi->tls.ssl);
+
+	lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd);
+
+	switch (n) {
+	case 1: /* successful completion */
+		n = shutdown(wsi->desc.sockfd, SHUT_WR);
+		return LWS_SSL_CAPABLE_DONE;
+
+	case 0: /* needs a retry */
+		__lws_change_pollfd(wsi, 0, LWS_POLLIN);
+		return LWS_SSL_CAPABLE_MORE_SERVICE;
+
+	default: /* fatal error, or WANT */
+		n = SSL_get_error(wsi->tls.ssl, n);
+		if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
+			if (SSL_want_read(wsi->tls.ssl)) {
+				lwsl_debug("(wants read)\n");
+				__lws_change_pollfd(wsi, 0, LWS_POLLIN);
+				return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
+			}
+			if (SSL_want_write(wsi->tls.ssl)) {
+				lwsl_debug("(wants write)\n");
+				__lws_change_pollfd(wsi, 0, LWS_POLLOUT);
+				return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
+			}
+		}
+		return LWS_SSL_CAPABLE_ERROR;
+	}
+}
+
+static time_t
+lws_tls_mbedtls_time_to_unix(mbedtls_x509_time *xtime)
+{
+	struct tm t;
+
+	if (!xtime || !xtime->year || xtime->year < 0)
+		return (time_t)(long long)-1;
+
+	memset(&t, 0, sizeof(t));
+
+	t.tm_year = xtime->year - 1900;
+	t.tm_mon = xtime->mon - 1; /* mbedtls months are 1+, tm are 0+ */
+	t.tm_mday = xtime->day - 1; /* mbedtls days are 1+, tm are 0+ */
+	t.tm_hour = xtime->hour;
+	t.tm_min = xtime->min;
+	t.tm_sec = xtime->sec;
+	t.tm_isdst = -1;
+
+	return mktime(&t);
+}
+
+static int
+lws_tls_mbedtls_get_x509_name(mbedtls_x509_name *name,
+			      union lws_tls_cert_info_results *buf, size_t len)
+{
+	while (name) {
+		if (MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid)) {
+			name = name->next;
+			continue;
+		}
+
+		if (len - 1 < name->val.len)
+			return -1;
+
+		memcpy(&buf->ns.name[0], name->val.p, name->val.len);
+		buf->ns.name[name->val.len] = '\0';
+		buf->ns.len = name->val.len;
+
+		return 0;
+	}
+
+	return -1;
+}
+
+static int
+lws_tls_mbedtls_cert_info(mbedtls_x509_crt *x509, enum lws_tls_cert_info type,
+			  union lws_tls_cert_info_results *buf, size_t len)
+{
+	if (!x509)
+		return -1;
+
+	switch (type) {
+	case LWS_TLS_CERT_INFO_VALIDITY_FROM:
+		buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_from);
+		if (buf->time == (time_t)(long long)-1)
+			return -1;
+		break;
+
+	case LWS_TLS_CERT_INFO_VALIDITY_TO:
+		buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_to);
+		if (buf->time == (time_t)(long long)-1)
+			return -1;
+		break;
+
+	case LWS_TLS_CERT_INFO_COMMON_NAME:
+		return lws_tls_mbedtls_get_x509_name(&x509->subject, buf, len);
+
+	case LWS_TLS_CERT_INFO_ISSUER_NAME:
+		return lws_tls_mbedtls_get_x509_name(&x509->issuer, buf, len);
+
+	case LWS_TLS_CERT_INFO_USAGE:
+		buf->usage = x509->key_usage;
+		break;
+
+	case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY:
+	{
+		char *p = buf->ns.name;
+		size_t r = len, u;
+
+		switch (mbedtls_pk_get_type(&x509->pk)) {
+		case MBEDTLS_PK_RSA:
+		{
+			mbedtls_rsa_context *rsa = mbedtls_pk_rsa(x509->pk);
+
+			if (mbedtls_mpi_write_string(&rsa->N, 16, p, r, &u))
+				return -1;
+			r -= u;
+			p += u;
+			if (mbedtls_mpi_write_string(&rsa->E, 16, p, r, &u))
+				return -1;
+
+			p += u;
+			buf->ns.len = lws_ptr_diff(p, buf->ns.name);
+			break;
+		}
+		case MBEDTLS_PK_ECKEY:
+		{
+			mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(x509->pk);
+
+			if (mbedtls_mpi_write_string(&ecp->Q.X, 16, p, r, &u))
+				 return -1;
+			r -= u;
+			p += u;
+			if (mbedtls_mpi_write_string(&ecp->Q.Y, 16, p, r, &u))
+				 return -1;
+			r -= u;
+			p += u;
+			if (mbedtls_mpi_write_string(&ecp->Q.Z, 16, p, r, &u))
+				 return -1;
+			p += u;
+			buf->ns.len = lws_ptr_diff(p, buf->ns.name);
+			break;
+		}
+		default:
+			lwsl_notice("%s: x509 has unsupported pubkey type %d\n",
+				    __func__,
+				    mbedtls_pk_get_type(&x509->pk));
+
+			return -1;
+		}
+		break;
+	}
+
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type,
+		        union lws_tls_cert_info_results *buf, size_t len)
+{
+	mbedtls_x509_crt *x509 = ssl_ctx_get_mbedtls_x509_crt(vhost->tls.ssl_ctx);
+
+	return lws_tls_mbedtls_cert_info(x509, type, buf, len);
+}
+
+LWS_VISIBLE int
+lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
+		       union lws_tls_cert_info_results *buf, size_t len)
+{
+	mbedtls_x509_crt *x509;
+
+	wsi = lws_get_network_wsi(wsi);
+
+	x509 = ssl_get_peer_mbedtls_x509_crt(wsi->tls.ssl);
+
+	if (!x509)
+		return -1;
+
+	switch (type) {
+	case LWS_TLS_CERT_INFO_VERIFIED:
+		buf->verified = SSL_get_verify_result(wsi->tls.ssl) == X509_V_OK;
+		return 0;
+	default:
+		return lws_tls_mbedtls_cert_info(x509, type, buf, len);
+	}
+
+	return -1;
+}
+
+static int
+tops_fake_POLLIN_for_buffered_mbedtls(struct lws_context_per_thread *pt)
+{
+	return lws_tls_fake_POLLIN_for_buffered(pt);
+}
+
+static int
+tops_periodic_housekeeping_mbedtls(struct lws_context *context, time_t now)
+{
+	int n;
+
+	n = lws_compare_time_t(context, now, context->tls.last_cert_check_s);
+	if ((!context->tls.last_cert_check_s || n > (24 * 60 * 60)) &&
+	    !lws_tls_check_all_cert_lifetimes(context))
+		context->tls.last_cert_check_s = now;
+
+	return 0;
+}
+
+const struct lws_tls_ops tls_ops_mbedtls = {
+	/* fake_POLLIN_for_buffered */	tops_fake_POLLIN_for_buffered_mbedtls,
+	/* periodic_housekeeping */	tops_periodic_housekeeping_mbedtls,
+};

+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/ssl3.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl3.h


+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/ssl_cert.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_cert.h


+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/ssl_code.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_code.h


+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/ssl_dbg.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_dbg.h


+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/ssl_lib.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_lib.h


+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/ssl_methods.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_methods.h


+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/ssl_pkey.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_pkey.h


+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/ssl_stack.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_stack.h


+ 2 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/ssl_types.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h

@@ -203,6 +203,8 @@ struct ssl_st
 
 
     const SSL_METHOD *method;
     const SSL_METHOD *method;
 
 
+    const char **alpn_protos;
+
     RECORD_LAYER rlayer;
     RECORD_LAYER rlayer;
 
 
     /* where we are */
     /* where we are */

+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/ssl_x509.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_x509.h


+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/tls1.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/tls1.h


+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/internal/x509_vfy.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/x509_vfy.h


+ 17 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/openssl/ssl.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/openssl/ssl.h

@@ -35,6 +35,22 @@
 #define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS		(1 << 3)
 #define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS		(1 << 3)
 #define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS		(1 << 4)
 #define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS		(1 << 4)
 
 
+ mbedtls_x509_crt *
+ ssl_ctx_get_mbedtls_x509_crt(SSL_CTX *ssl_ctx);
+
+ mbedtls_x509_crt *
+ ssl_get_peer_mbedtls_x509_crt(SSL *ssl);
+
+ int SSL_set_sni_callback(SSL *ssl, int(*cb)(void *, mbedtls_ssl_context *,
+ 				const unsigned char *, size_t), void *param);
+
+ void SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx);
+
+ int SSL_CTX_add_client_CA_ASN1(SSL_CTX *ssl, int len,
+                 const unsigned char *d);
+
+ SSL *SSL_SSL_from_mbedtls_ssl_context(mbedtls_ssl_context *msc);
+
 /**
 /**
  * @brief create a SSL context
  * @brief create a SSL context
  *
  *
@@ -305,6 +321,7 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
                                            void *arg),
                                            void *arg),
                                 void *arg);
                                 void *arg);
 
 
+void SSL_set_alpn_select_cb(SSL *ssl, void *arg);
 
 
 /**
 /**
  * @brief set the SSL context ALPN select protocol
  * @brief set the SSL context ALPN select protocol

+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/include/platform/ssl_pm.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/platform/ssl_pm.h


+ 2 - 10
thirdparty/libwebsockets/mbedtls_wrapper/include/platform/ssl_port.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/platform/ssl_port.h

@@ -19,19 +19,11 @@
  extern "C" {
  extern "C" {
 #endif
 #endif
 
 
-/*
-#include "esp_types.h"
-#include "esp_log.h"
-*/
 #include "string.h"
 #include "string.h"
-
-/* GODOT ADDITION */
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
-#include <stdlib.h>
-#else
+#include "stdlib.h"
+#if defined(LWS_HAVE_MALLOC_H)
 #include "malloc.h"
 #include "malloc.h"
 #endif
 #endif
-/* END GODOT ADDITION */
 
 
 void *ssl_mem_zalloc(size_t size);
 void *ssl_mem_zalloc(size_t size);
 
 

+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/library/ssl_cert.c → thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_cert.c


+ 71 - 14
thirdparty/libwebsockets/mbedtls_wrapper/library/ssl_lib.c → thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_lib.c

@@ -19,6 +19,9 @@
 #include "ssl_dbg.h"
 #include "ssl_dbg.h"
 #include "ssl_port.h"
 #include "ssl_port.h"
 
 
+char *
+lws_strncpy(char *dest, const char *src, size_t size);
+
 #define SSL_SEND_DATA_MAX_LENGTH 1460
 #define SSL_SEND_DATA_MAX_LENGTH 1460
 
 
 /**
 /**
@@ -348,6 +351,9 @@ void SSL_free(SSL *ssl)
 
 
     SSL_SESSION_free(ssl->session);
     SSL_SESSION_free(ssl->session);
 
 
+    if (ssl->alpn_protos)
+	    ssl_mem_free(ssl->alpn_protos);
+
     ssl_mem_free(ssl);
     ssl_mem_free(ssl);
 }
 }
 
 
@@ -1582,7 +1588,7 @@ void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_C
 
 
 void ERR_error_string_n(unsigned long e, char *buf, size_t len)
 void ERR_error_string_n(unsigned long e, char *buf, size_t len)
 {
 {
-	strncpy(buf, "unknown", len);
+	lws_strncpy(buf, "unknown", len);
 }
 }
 
 
 void ERR_free_strings(void)
 void ERR_free_strings(void)
@@ -1591,11 +1597,34 @@ void ERR_free_strings(void)
 
 
 char *ERR_error_string(unsigned long e, char *buf)
 char *ERR_error_string(unsigned long e, char *buf)
 {
 {
-	if (buf) {
-		strcpy(buf, "unknown");
+	if (!buf)
+		return "unknown";
+
+	switch(e) {
+		case X509_V_ERR_INVALID_CA:
+			strcpy(buf, "CA is not trusted");
+			break;
+		case X509_V_ERR_HOSTNAME_MISMATCH:
+			strcpy(buf, "Hostname mismatch");
+			break;
+		case X509_V_ERR_CA_KEY_TOO_SMALL:
+			strcpy(buf, "CA key too small");
+			break;
+		case X509_V_ERR_CA_MD_TOO_WEAK:
+			strcpy(buf, "MD key too weak");
+			break;
+		case X509_V_ERR_CERT_NOT_YET_VALID:
+			strcpy(buf, "Cert from the future");
+			break;
+		case X509_V_ERR_CERT_HAS_EXPIRED:
+			strcpy(buf, "Cert expired");
+			break;
+		default:
+			strcpy(buf, "unknown");
+			break;
 	}
 	}
 
 
-	return "unknown";
+	return buf;
 }
 }
 
 
 void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx)
 void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx)
@@ -1619,15 +1648,16 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx)
  */
  */
 
 
 struct alpn_ctx {
 struct alpn_ctx {
-	unsigned char *data;
-	unsigned short len;
+	unsigned char data[23];
+	unsigned char len;
 };
 };
 
 
-void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
+static void
+_openssl_alpn_to_mbedtls(struct alpn_ctx *ac, char ***palpn_protos)
 {
 {
-	struct alpn_ctx *ac = arg;
 	unsigned char *p = ac->data, *q;
 	unsigned char *p = ac->data, *q;
 	unsigned char len;
 	unsigned char len;
+	char **alpn_protos;
 	int count = 0;
 	int count = 0;
 
 
 	/* find out how many entries he gave us */
 	/* find out how many entries he gave us */
@@ -1644,23 +1674,28 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
 			break;
 			break;
 	}
 	}
 
 
+	if (!len)
+		count++;
+
 	if (!count)
 	if (!count)
 		return;
 		return;
 
 
 	/* allocate space for count + 1 pointers and the data afterwards */
 	/* allocate space for count + 1 pointers and the data afterwards */
 
 
-	ctx->alpn_protos = ssl_mem_zalloc((count + 1) * sizeof(char *) + ac->len + 1);
-	if (!ctx->alpn_protos)
+	alpn_protos = ssl_mem_zalloc((count + 1) * sizeof(char *) + ac->len + 1);
+	if (!alpn_protos)
 		return;
 		return;
 
 
+	*palpn_protos = alpn_protos;
+
 	/* convert to mbedtls format */
 	/* convert to mbedtls format */
 
 
-	q = (unsigned char *)ctx->alpn_protos + (count + 1) * sizeof(char *);
+	q = (unsigned char *)alpn_protos + (count + 1) * sizeof(char *);
 	p = ac->data;
 	p = ac->data;
 	count = 0;
 	count = 0;
 
 
 	len = *p++;
 	len = *p++;
-	ctx->alpn_protos[count] = (char *)q;
+	alpn_protos[count] = (char *)q;
 	while (p - ac->data < ac->len) {
 	while (p - ac->data < ac->len) {
 		if (len--) {
 		if (len--) {
 			*q++ = *p++;
 			*q++ = *p++;
@@ -1669,11 +1704,33 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
 		*q++ = '\0';
 		*q++ = '\0';
 		count++;
 		count++;
 		len = *p++;
 		len = *p++;
-		ctx->alpn_protos[count] = (char *)q;
+		alpn_protos[count] = (char *)q;
 		if (!len)
 		if (!len)
 			break;
 			break;
 	}
 	}
-	ctx->alpn_protos[count] = NULL; /* last pointer ends list with NULL */
+	if (!len) {
+		*q++ = '\0';
+		count++;
+		len = *p++;
+		alpn_protos[count] = (char *)q;
+	}
+	alpn_protos[count] = NULL; /* last pointer ends list with NULL */
+}
+
+void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
+{
+	struct alpn_ctx *ac = arg;
 
 
 	ctx->alpn_cb = cb;
 	ctx->alpn_cb = cb;
+
+	_openssl_alpn_to_mbedtls(ac, (char ***)&ctx->alpn_protos);
+}
+
+void SSL_set_alpn_select_cb(SSL *ssl, void *arg)
+{
+	struct alpn_ctx *ac = arg;
+
+	_openssl_alpn_to_mbedtls(ac, (char ***)&ssl->alpn_protos);
+
+	_ssl_set_alpn_list(ssl);
 }
 }

+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/library/ssl_methods.c → thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_methods.c


+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/library/ssl_pkey.c → thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_pkey.c


+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/library/ssl_stack.c → thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_stack.c


+ 25 - 1
thirdparty/libwebsockets/mbedtls_wrapper/library/ssl_x509.c → thirdparty/libwebsockets/tls/mbedtls/wrapper/library/ssl_x509.c

@@ -17,6 +17,8 @@
 #include "ssl_dbg.h"
 #include "ssl_dbg.h"
 #include "ssl_port.h"
 #include "ssl_port.h"
 
 
+#include <assert.h>
+
 /**
 /**
  * @brief show X509 certification information
  * @brief show X509 certification information
  */
  */
@@ -155,7 +157,7 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x)
 {
 {
     SSL_ASSERT1(ctx);
     SSL_ASSERT1(ctx);
     SSL_ASSERT1(x);
     SSL_ASSERT1(x);
-
+    assert(ctx);
     if (ctx->client_CA == x)
     if (ctx->client_CA == x)
         return 1;
         return 1;
 
 
@@ -166,6 +168,28 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x)
     return 1;
     return 1;
 }
 }
 
 
+/**
+ * @brief add CA client certification into the SSL
+ */
+int SSL_CTX_add_client_CA_ASN1(SSL_CTX *ctx, int len,
+                const unsigned char *d)
+{
+	X509 *x;
+
+	x = d2i_X509(NULL, d, len);
+	if (!x) {
+		SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_X509() return NULL");
+		return 0;
+	}
+    SSL_ASSERT1(ctx);
+
+    X509_free(ctx->client_CA);
+
+    ctx->client_CA = x;
+
+    return 1;
+}
+
 /**
 /**
  * @brief add CA client certification into the SSL
  * @brief add CA client certification into the SSL
  */
  */

+ 164 - 56
thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_pm.c → thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_pm.c

@@ -25,6 +25,8 @@
 #include "mbedtls/error.h"
 #include "mbedtls/error.h"
 #include "mbedtls/certs.h"
 #include "mbedtls/certs.h"
 
 
+#include <libwebsockets.h>
+
 #define X509_INFO_STRING_LENGTH 8192
 #define X509_INFO_STRING_LENGTH 8192
 
 
 struct ssl_pm
 struct ssl_pm
@@ -41,6 +43,8 @@ struct ssl_pm
     mbedtls_ssl_context ssl;
     mbedtls_ssl_context ssl;
 
 
     mbedtls_entropy_context entropy;
     mbedtls_entropy_context entropy;
+
+    SSL *owner;
 };
 };
 
 
 struct x509_pm
 struct x509_pm
@@ -62,7 +66,7 @@ unsigned int max_content_len;
 /*********************************************************************************************/
 /*********************************************************************************************/
 /************************************ SSL arch interface *************************************/
 /************************************ SSL arch interface *************************************/
 
 
-#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG
+//#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG
 
 
 /* mbedtls debug level */
 /* mbedtls debug level */
 #define MBEDTLS_DEBUG_LEVEL 4
 #define MBEDTLS_DEBUG_LEVEL 4
@@ -79,13 +83,13 @@ static void ssl_platform_debug(void *ctx, int level,
        This is a bit wasteful because the macros are compiled in with
        This is a bit wasteful because the macros are compiled in with
        the full _FILE_ path in each case.
        the full _FILE_ path in each case.
     */
     */
-    char *file_sep = rindex(file, '/');
-    if(file_sep)
-        file = file_sep + 1;
+//    char *file_sep = rindex(file, '/');
+  //  if(file_sep)
+    //    file = file_sep + 1;
 
 
-    SSL_DEBUG(SSL_DEBUG_ON, "%s:%d %s", file, line, str);
+    printf("%s:%d %s", file, line, str);
 }
 }
-#endif
+//#endif
 
 
 /**
 /**
  * @brief create SSL low-level object
  * @brief create SSL low-level object
@@ -109,6 +113,8 @@ int ssl_pm_new(SSL *ssl)
         goto no_mem;
         goto no_mem;
     }
     }
 
 
+    ssl_pm->owner = ssl;
+
     if (!ssl->ctx->read_buffer_len)
     if (!ssl->ctx->read_buffer_len)
 	    ssl->ctx->read_buffer_len = 2048;
 	    ssl->ctx->read_buffer_len = 2048;
 
 
@@ -159,12 +165,12 @@ int ssl_pm_new(SSL *ssl)
 
 
     mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg);
     mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg);
 
 
-#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG
-    mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL);
+//#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG
+ //   mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL);
+//    mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL);
+//#else
     mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL);
     mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL);
-#else
-    mbedtls_ssl_conf_dbg(&ssl_pm->conf, NULL, NULL);
-#endif
+//#endif
 
 
     ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf);
     ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf);
     if (ret) {
     if (ret) {
@@ -261,7 +267,7 @@ static int mbedtls_handshake( mbedtls_ssl_context *ssl )
     while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) {
     while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) {
         ret = mbedtls_ssl_handshake_step(ssl);
         ret = mbedtls_ssl_handshake_step(ssl);
 
 
-        SSL_DEBUG(SSL_PLATFORM_DEBUG_LEVEL, "ssl ret %d state %d", ret, ssl->state);
+        lwsl_info("%s: ssl ret -%x state %d\n", __func__, -ret, ssl->state);
 
 
         if (ret != 0)
         if (ret != 0)
             break;
             break;
@@ -270,14 +276,21 @@ static int mbedtls_handshake( mbedtls_ssl_context *ssl )
     return ret;
     return ret;
 }
 }
 
 
+#include <errno.h>
+
 int ssl_pm_handshake(SSL *ssl)
 int ssl_pm_handshake(SSL *ssl)
 {
 {
     int ret;
     int ret;
     struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
     struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
 
 
+    ssl->err = 0;
+    errno = 0;
+
     ret = ssl_pm_reload_crt(ssl);
     ret = ssl_pm_reload_crt(ssl);
-    if (ret)
+    if (ret) {
+	    printf("%s: cert reload failed\n", __func__);
         return 0;
         return 0;
+    }
 
 
     if (ssl_pm->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) {
     if (ssl_pm->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) {
 	    ssl_speed_up_enter();
 	    ssl_speed_up_enter();
@@ -298,6 +311,7 @@ int ssl_pm_handshake(SSL *ssl)
      *   <0 = death
      *   <0 = death
      */
      */
     if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
     if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+	    ssl->err = ret;
         SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_handshake() return -0x%x", -ret);
         SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_handshake() return -0x%x", -ret);
         return 0; /* OpenSSL: did not complete but may be retried */
         return 0; /* OpenSSL: did not complete but may be retried */
     }
     }
@@ -309,6 +323,14 @@ int ssl_pm_handshake(SSL *ssl)
         return 1; /* openssl successful */
         return 1; /* openssl successful */
     }
     }
 
 
+    if (errno == 11) {
+	    ssl->err = ret == MBEDTLS_ERR_SSL_WANT_READ;
+
+	    return 0;
+    }
+
+    printf("%s: mbedtls_ssl_handshake() returned -0x%x\n", __func__, -ret);
+
     /* it's had it */
     /* it's had it */
 
 
     ssl->err = SSL_ERROR_SYSCALL;
     ssl->err = SSL_ERROR_SYSCALL;
@@ -316,6 +338,28 @@ int ssl_pm_handshake(SSL *ssl)
     return -1; /* openssl death */
     return -1; /* openssl death */
 }
 }
 
 
+mbedtls_x509_crt *
+ssl_ctx_get_mbedtls_x509_crt(SSL_CTX *ssl_ctx)
+{
+	struct x509_pm *x509_pm = (struct x509_pm *)ssl_ctx->cert->x509->x509_pm;
+
+	if (!x509_pm)
+		return NULL;
+
+	return x509_pm->x509_crt;
+}
+
+mbedtls_x509_crt *
+ssl_get_peer_mbedtls_x509_crt(SSL *ssl)
+{
+	struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm;
+
+	if (!x509_pm)
+		return NULL;
+
+	return x509_pm->ex_crt;
+}
+
 int ssl_pm_shutdown(SSL *ssl)
 int ssl_pm_shutdown(SSL *ssl)
 {
 {
     int ret;
     int ret;
@@ -351,8 +395,10 @@ int ssl_pm_read(SSL *ssl, void *buffer, int len)
 
 
     ret = mbedtls_ssl_read(&ssl_pm->ssl, buffer, len);
     ret = mbedtls_ssl_read(&ssl_pm->ssl, buffer, len);
     if (ret < 0) {
     if (ret < 0) {
+	 //   lwsl_notice("%s: mbedtls_ssl_read says -0x%x\n", __func__, -ret);
         SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_read() return -0x%x", -ret);
         SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_read() return -0x%x", -ret);
-        if (ret == MBEDTLS_ERR_NET_CONN_RESET)
+        if (ret == MBEDTLS_ERR_NET_CONN_RESET ||
+            ret <= MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE) /* fatal errors */
 		ssl->err = SSL_ERROR_SYSCALL;
 		ssl->err = SSL_ERROR_SYSCALL;
         ret = -1;
         ret = -1;
     }
     }
@@ -392,6 +438,7 @@ int ssl_pm_send(SSL *ssl, const void *buffer, int len)
     if (ret < 0) {
     if (ret < 0) {
 	    SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret);
 	    SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret);
 	switch (ret) {
 	switch (ret) {
+	case MBEDTLS_ERR_NET_SEND_FAILED:
 	case MBEDTLS_ERR_NET_CONN_RESET:
 	case MBEDTLS_ERR_NET_CONN_RESET:
 		ssl->err = SSL_ERROR_SYSCALL;
 		ssl->err = SSL_ERROR_SYSCALL;
 		break;
 		break;
@@ -589,22 +636,27 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len)
         }
         }
     }
     }
 
 
-    load_buf = ssl_mem_malloc(len + 1);
-    if (!load_buf) {
-        SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)");
-        goto failed;
-    }
-
-    ssl_memcpy(load_buf, buffer, len);
-    load_buf[len] = '\0';
-
     mbedtls_x509_crt_init(x509_pm->x509_crt);
     mbedtls_x509_crt_init(x509_pm->x509_crt);
+    if (buffer[0] != 0x30) {
+	    load_buf = ssl_mem_malloc(len + 1);
+	    if (!load_buf) {
+		SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)");
+		goto failed;
+	    }
+
+	    ssl_memcpy(load_buf, buffer, len);
+	    load_buf[len] = '\0';
+
+	    ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1);
+	    ssl_mem_free(load_buf);
+    } else {
+	    printf("parsing as der\n");
 
 
-    ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1);
-    ssl_mem_free(load_buf);
+	    ret = mbedtls_x509_crt_parse_der(x509_pm->x509_crt, buffer, len);
+    }
 
 
     if (ret) {
     if (ret) {
-        SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_x509_crt_parse return -0x%x", -ret);
+        printf("mbedtls_x509_crt_parse return -0x%x", -ret);
         goto failed;
         goto failed;
     }
     }
 
 
@@ -707,46 +759,44 @@ void ssl_pm_set_bufflen(SSL *ssl, int len)
 
 
 long ssl_pm_get_verify_result(const SSL *ssl)
 long ssl_pm_get_verify_result(const SSL *ssl)
 {
 {
-    uint32_t ret;
-    long verify_result;
-    struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
-
-    ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
+	uint32_t ret;
+	long verify_result;
+	struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
 
 
-    if (!ret)
-            return X509_V_OK;
+	ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
+	if (!ret)
+		return X509_V_OK;
 
 
-    if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
-        (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
-        // Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification
-        verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+	if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
+		(ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
+		verify_result = X509_V_ERR_INVALID_CA;
 
 
-    else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
-        verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
+	else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
+		verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
 
 
-    else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
-        (ret & MBEDTLS_X509_BADCRL_BAD_KEY))
-        verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
+	else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
+		(ret & MBEDTLS_X509_BADCRL_BAD_KEY))
+		verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
 
 
-    else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
-        (ret & MBEDTLS_X509_BADCRL_BAD_MD))
-        verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
+	else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
+		(ret & MBEDTLS_X509_BADCRL_BAD_MD))
+		verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
 
 
-    else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
-        (ret & MBEDTLS_X509_BADCRL_FUTURE))
-        verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
+	else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
+		(ret & MBEDTLS_X509_BADCRL_FUTURE))
+		verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
 
 
-    else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
-        (ret & MBEDTLS_X509_BADCRL_EXPIRED))
-        verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
+	else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
+		(ret & MBEDTLS_X509_BADCRL_EXPIRED))
+		verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
 
 
-    else
-        verify_result = X509_V_ERR_UNSPECIFIED;
+	else
+		verify_result = X509_V_ERR_UNSPECIFIED;
 
 
-    SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
-              "mbedtls_ssl_get_verify_result() return 0x%x", ret);
+	SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
+		  "mbedtls_ssl_get_verify_result() return 0x%x", ret);
 
 
-    return verify_result;
+	return verify_result;
 }
 }
 
 
 /**
 /**
@@ -779,6 +829,12 @@ int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
 
 
 void _ssl_set_alpn_list(const SSL *ssl)
 void _ssl_set_alpn_list(const SSL *ssl)
 {
 {
+	if (ssl->alpn_protos) {
+		if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->alpn_protos))
+			fprintf(stderr, "mbedtls_ssl_conf_alpn_protocols failed\n");
+
+		return;
+	}
 	if (!ssl->ctx->alpn_protos)
 	if (!ssl->ctx->alpn_protos)
 		return;
 		return;
 	if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->ctx->alpn_protos))
 	if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->ctx->alpn_protos))
@@ -797,3 +853,55 @@ void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data,
 		*len = 0;
 		*len = 0;
 }
 }
 
 
+int SSL_set_sni_callback(SSL *ssl, int(*cb)(void *, mbedtls_ssl_context *,
+			 const unsigned char *, size_t), void *param)
+{
+	struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
+
+	mbedtls_ssl_conf_sni(&ssl_pm->conf, cb, param);
+
+	return 0;
+}
+
+SSL *SSL_SSL_from_mbedtls_ssl_context(mbedtls_ssl_context *msc)
+{
+	struct ssl_pm *ssl_pm = (struct ssl_pm *)((char *)msc - offsetof(struct ssl_pm, ssl));
+
+	return ssl_pm->owner;
+}
+
+#include "ssl_cert.h"
+
+void SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx)
+{
+	struct ssl_pm *ssl_pm = ssl->ssl_pm;
+	struct x509_pm *x509_pm = (struct x509_pm *)ctx->cert->x509->x509_pm;
+	struct x509_pm *x509_pm_ca = (struct x509_pm *)ctx->client_CA->x509_pm;
+
+	struct pkey_pm *pkey_pm = (struct pkey_pm *)ctx->cert->pkey->pkey_pm;
+	int mode;
+
+	if (ssl->cert)
+		ssl_cert_free(ssl->cert);
+	ssl->ctx = ctx;
+	ssl->cert = __ssl_cert_new(ctx->cert);
+
+	if (ctx->verify_mode == SSL_VERIFY_PEER)
+		mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
+	else if (ctx->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+		mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
+	else if (ctx->verify_mode == SSL_VERIFY_CLIENT_ONCE)
+		mode = MBEDTLS_SSL_VERIFY_UNSET;
+	else
+	        mode = MBEDTLS_SSL_VERIFY_NONE;
+
+	    // printf("ssl: %p, client ca x509_crt %p, mbedtls mode %d\n", ssl, x509_pm_ca->x509_crt, mode);
+
+	/* apply new ctx cert to ssl */
+
+	ssl->verify_mode = ctx->verify_mode;
+
+	mbedtls_ssl_set_hs_ca_chain(&ssl_pm->ssl, x509_pm_ca->x509_crt, NULL);
+	mbedtls_ssl_set_hs_own_cert(&ssl_pm->ssl, x509_pm->x509_crt, pkey_pm->pkey);
+	mbedtls_ssl_set_hs_authmode(&ssl_pm->ssl, mode);
+}

+ 0 - 0
thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_port.c → thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_port.c


+ 281 - 0
thirdparty/libwebsockets/tls/private.h

@@ -0,0 +1,281 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ *
+ *  This is included from core/private.h if LWS_WITH_TLS
+ */
+
+#if defined(LWS_WITH_TLS)
+
+#if defined(USE_WOLFSSL)
+ #if defined(USE_OLD_CYASSL)
+  #if defined(_WIN32)
+   #include <IDE/WIN/user_settings.h>
+   #include <cyassl/ctaocrypt/settings.h>
+  #else
+   #include <cyassl/options.h>
+  #endif
+  #include <cyassl/openssl/ssl.h>
+  #include <cyassl/error-ssl.h>
+ #else
+  #if defined(_WIN32)
+   #include <IDE/WIN/user_settings.h>
+   #include <wolfssl/wolfcrypt/settings.h>
+  #else
+   #include <wolfssl/options.h>
+  #endif
+  #include <wolfssl/openssl/ssl.h>
+  #include <wolfssl/error-ssl.h>
+  #define OPENSSL_NO_TLSEXT
+ #endif /* not USE_OLD_CYASSL */
+#else /* WOLFSSL */
+ #if defined(LWS_WITH_ESP32)
+  #define OPENSSL_NO_TLSEXT
+  #undef MBEDTLS_CONFIG_FILE
+  #define MBEDTLS_CONFIG_FILE <mbedtls/esp_config.h>
+  #include <mbedtls/ssl.h>
+  #include <mbedtls/x509_crt.h>
+  #include "tls/mbedtls/wrapper/include/openssl/ssl.h" /* wrapper !!!! */
+ #else /* not esp32 */
+  #if defined(LWS_WITH_MBEDTLS)
+   #include <mbedtls/ssl.h>
+   #include <mbedtls/x509_crt.h>
+   #include <mbedtls/x509_csr.h>
+   #include "tls/mbedtls/wrapper/include/openssl/ssl.h" /* wrapper !!!! */
+  #else
+   #include <openssl/ssl.h>
+   #include <openssl/evp.h>
+   #include <openssl/err.h>
+   #include <openssl/md5.h>
+   #include <openssl/sha.h>
+   #ifdef LWS_HAVE_OPENSSL_ECDH_H
+    #include <openssl/ecdh.h>
+   #endif
+   #include <openssl/x509v3.h>
+  #endif /* not mbedtls */
+  #if defined(OPENSSL_VERSION_NUMBER)
+   #if (OPENSSL_VERSION_NUMBER < 0x0009080afL)
+/* later openssl defines this to negate the presence of tlsext... but it was only
+ * introduced at 0.9.8j.  Earlier versions don't know it exists so don't
+ * define it... making it look like the feature exists...
+ */
+    #define OPENSSL_NO_TLSEXT
+   #endif
+  #endif
+ #endif /* not ESP32 */
+#endif /* not USE_WOLFSSL */
+
+#endif /* LWS_WITH_TLS */
+
+enum lws_tls_extant {
+	LWS_TLS_EXTANT_NO,
+	LWS_TLS_EXTANT_YES,
+	LWS_TLS_EXTANT_ALTERNATIVE
+};
+
+struct lws_context_per_thread;
+
+struct lws_tls_ops {
+	int (*fake_POLLIN_for_buffered)(struct lws_context_per_thread *pt);
+	int (*periodic_housekeeping)(struct lws_context *context, time_t now);
+};
+
+#if defined(LWS_WITH_TLS)
+
+typedef SSL lws_tls_conn;
+typedef SSL_CTX lws_tls_ctx;
+typedef BIO lws_tls_bio;
+typedef X509 lws_tls_x509;
+
+
+#define LWS_SSL_ENABLED(context) (context->tls.use_ssl)
+
+extern const struct lws_tls_ops tls_ops_openssl, tls_ops_mbedtls;
+
+struct lws_context_tls {
+	char alpn_discovered[32];
+	const char *alpn_default;
+	time_t last_cert_check_s;
+};
+
+struct lws_pt_tls {
+	struct lws *pending_read_list; /* linked list */
+};
+
+struct lws_tls_ss_pieces;
+
+struct alpn_ctx {
+	uint8_t data[23];
+	uint8_t len;
+};
+
+struct lws_vhost_tls {
+	lws_tls_ctx *ssl_ctx;
+	lws_tls_ctx *ssl_client_ctx;
+	const char *alpn;
+	struct lws_tls_ss_pieces *ss; /* for acme tls certs */
+	char *alloc_cert_path;
+	char *key_path;
+#if defined(LWS_WITH_MBEDTLS)
+	lws_tls_x509 *x509_client_CA;
+#endif
+	char ecdh_curve[16];
+	struct alpn_ctx alpn_ctx;
+
+	int use_ssl;
+	int allow_non_ssl_on_ssl_port;
+	int ssl_info_event_mask;
+
+	unsigned int user_supplied_ssl_ctx:1;
+	unsigned int skipped_certs:1;
+};
+
+struct lws_lws_tls {
+	lws_tls_conn *ssl;
+	lws_tls_bio *client_bio;
+	struct lws *pending_read_list_prev, *pending_read_list_next;
+	unsigned int use_ssl;
+	unsigned int redirect_to_https:1;
+};
+
+LWS_EXTERN void
+lws_context_init_alpn(struct lws_vhost *vhost);
+LWS_EXTERN enum lws_tls_extant
+lws_tls_use_any_upgrade_check_extant(const char *name);
+LWS_EXTERN int openssl_websocket_private_data_index;
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ssl_pending(struct lws *wsi);
+LWS_EXTERN int
+lws_context_init_ssl_library(const struct lws_context_creation_info *info);
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd);
+LWS_EXTERN int
+lws_ssl_close(struct lws *wsi);
+LWS_EXTERN void
+lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost);
+LWS_EXTERN void
+lws_ssl_context_destroy(struct lws_context *context);
+void
+__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi);
+LWS_VISIBLE void
+lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi);
+LWS_EXTERN int
+lws_ssl_client_bio_create(struct lws *wsi);
+LWS_EXTERN int
+lws_ssl_client_connect1(struct lws *wsi);
+LWS_EXTERN int
+lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len);
+LWS_EXTERN void
+lws_ssl_elaborate_error(void);
+LWS_EXTERN int
+lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt);
+LWS_EXTERN int
+lws_gate_accepts(struct lws_context *context, int on);
+LWS_EXTERN void
+lws_ssl_bind_passphrase(lws_tls_ctx *ssl_ctx,
+			const struct lws_context_creation_info *info);
+LWS_EXTERN void
+lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret);
+LWS_EXTERN int
+lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type,
+			  union lws_tls_cert_info_results *buf, size_t len);
+LWS_EXTERN int
+lws_tls_check_all_cert_lifetimes(struct lws_context *context);
+LWS_EXTERN int
+lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi,
+			  const char *cert, const char *private_key,
+			  const char *mem_cert, size_t len_mem_cert,
+			  const char *mem_privkey, size_t mem_privkey_len);
+LWS_EXTERN enum lws_tls_extant
+lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert,
+			    const char *private_key);
+LWS_EXTERN int
+lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
+			const char *inbuf, lws_filepos_t inlen,
+		      uint8_t **buf, lws_filepos_t *amount);
+
+#if !defined(LWS_NO_SERVER)
+ LWS_EXTERN int
+ lws_context_init_server_ssl(const struct lws_context_creation_info *info,
+			     struct lws_vhost *vhost);
+ void
+ lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost);
+#else
+ #define lws_context_init_server_ssl(_a, _b) (0)
+ #define lws_tls_acme_sni_cert_destroy(_a)
+#endif
+
+LWS_EXTERN void
+lws_ssl_destroy(struct lws_vhost *vhost);
+LWS_EXTERN char *
+lws_ssl_get_error_string(int status, int ret, char *buf, size_t len);
+
+/*
+ * lws_tls_ abstract backend implementations
+ */
+
+LWS_EXTERN int
+lws_tls_server_client_cert_verify_config(struct lws_vhost *vh);
+LWS_EXTERN int
+lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info,
+				  struct lws_vhost *vhost, struct lws *wsi);
+LWS_EXTERN int
+lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd);
+
+LWS_EXTERN enum lws_ssl_capable_status
+lws_tls_server_accept(struct lws *wsi);
+
+LWS_EXTERN enum lws_ssl_capable_status
+lws_tls_server_abort_connection(struct lws *wsi);
+
+LWS_EXTERN enum lws_ssl_capable_status
+__lws_tls_shutdown(struct lws *wsi);
+
+LWS_EXTERN enum lws_ssl_capable_status
+lws_tls_client_connect(struct lws *wsi);
+LWS_EXTERN int
+lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len);
+LWS_EXTERN int
+lws_tls_client_create_vhost_context(struct lws_vhost *vh,
+				    const struct lws_context_creation_info *info,
+				    const char *cipher_list,
+				    const char *ca_filepath,
+				    const char *cert_filepath,
+				    const char *private_key_filepath);
+
+LWS_EXTERN lws_tls_ctx *
+lws_tls_ctx_from_wsi(struct lws *wsi);
+LWS_EXTERN int
+lws_ssl_get_error(struct lws *wsi, int n);
+
+LWS_EXTERN int
+lws_context_init_client_ssl(const struct lws_context_creation_info *info,
+			    struct lws_vhost *vhost);
+
+LWS_EXTERN void
+lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret);
+
+int
+lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt);
+
+#endif

+ 150 - 0
thirdparty/libwebsockets/tls/tls-client.c

@@ -0,0 +1,150 @@
+/*
+ * libwebsockets - client-related ssl code independent of backend
+ *
+ * Copyright (C) 2010-2017 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "core/private.h"
+
+int
+lws_ssl_client_connect1(struct lws *wsi)
+{
+	struct lws_context *context = wsi->context;
+	int n = 0;
+
+	lws_latency_pre(context, wsi);
+	n = lws_tls_client_connect(wsi);
+	lws_latency(context, wsi, "SSL_connect hs", n, n > 0);
+
+	switch (n) {
+	case LWS_SSL_CAPABLE_ERROR:
+		return -1;
+	case LWS_SSL_CAPABLE_DONE:
+		return 1; /* connected */
+	case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
+		lws_callback_on_writable(wsi);
+		/* fallthru */
+	case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
+		lwsi_set_state(wsi, LRS_WAITING_SSL);
+		break;
+	case LWS_SSL_CAPABLE_MORE_SERVICE:
+		break;
+	}
+
+	return 0; /* retry */
+}
+
+int
+lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len)
+{
+	int n = 0;
+
+	if (lwsi_state(wsi) == LRS_WAITING_SSL) {
+		lws_latency_pre(wsi->context, wsi);
+
+		n = lws_tls_client_connect(wsi);
+		lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
+		lws_latency(wsi->context, wsi,
+			    "SSL_connect LRS_WAITING_SSL", n, n > 0);
+
+		switch (n) {
+		case LWS_SSL_CAPABLE_ERROR:
+			lws_snprintf(errbuf, len, "client connect failed");
+			return -1;
+		case LWS_SSL_CAPABLE_DONE:
+			break; /* connected */
+		case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
+			lws_callback_on_writable(wsi);
+			/* fallthru */
+		case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
+			lwsi_set_state(wsi, LRS_WAITING_SSL);
+			/* fallthru */
+		case LWS_SSL_CAPABLE_MORE_SERVICE:
+			return 0;
+		}
+	}
+
+	if (lws_tls_client_confirm_peer_cert(wsi, errbuf, len))
+		return -1;
+
+	return 1;
+}
+
+
+int lws_context_init_client_ssl(const struct lws_context_creation_info *info,
+				struct lws_vhost *vhost)
+{
+	const char *ca_filepath = info->ssl_ca_filepath;
+	const char *cipher_list = info->ssl_cipher_list;
+	const char *private_key_filepath = info->ssl_private_key_filepath;
+	const char *cert_filepath = info->ssl_cert_filepath;
+	struct lws wsi;
+
+	if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW)
+		return 0;
+
+	/*
+	 *  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_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;
+
+	if (info->client_ssl_ca_filepath)
+		ca_filepath = info->client_ssl_ca_filepath;
+
+	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
+		return 0;
+
+	if (vhost->tls.ssl_client_ctx)
+		return 0;
+
+	if (info->provided_client_ssl_ctx) {
+		/* use the provided OpenSSL context if given one */
+		vhost->tls.ssl_client_ctx = info->provided_client_ssl_ctx;
+		/* nothing for lib to delete */
+		vhost->tls.user_supplied_ssl_ctx = 1;
+
+		return 0;
+	}
+
+	if (lws_tls_client_create_vhost_context(vhost, info, cipher_list,
+						ca_filepath, cert_filepath,
+						private_key_filepath))
+		return 1;
+
+	lwsl_notice("created client ssl context for %s\n", vhost->name);
+
+	/*
+	 * 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->tls.ssl_client_ctx, NULL, 0);
+
+	return 0;
+}

+ 382 - 0
thirdparty/libwebsockets/tls/tls-server.c

@@ -0,0 +1,382 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "core/private.h"
+
+#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
+				  OPENSSL_VERSION_NUMBER >= 0x10002000L)
+static int
+alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
+	const unsigned char *in, unsigned int inlen, void *arg)
+{
+#if !defined(LWS_WITH_MBEDTLS)
+	struct alpn_ctx *alpn_ctx = (struct alpn_ctx *)arg;
+
+	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;
+#endif
+
+	return SSL_TLSEXT_ERR_OK;
+}
+#endif
+
+void
+lws_context_init_alpn(struct lws_vhost *vhost)
+{
+#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
+				  OPENSSL_VERSION_NUMBER >= 0x10002000L)
+	const char *alpn_comma = vhost->context->tls.alpn_default;
+
+	if (vhost->tls.alpn)
+		alpn_comma = vhost->tls.alpn;
+
+	lwsl_info(" Server '%s' advertising ALPN: %s\n",
+		    vhost->name, alpn_comma);
+	vhost->tls.alpn_ctx.len = lws_alpn_comma_to_openssl(alpn_comma,
+					vhost->tls.alpn_ctx.data,
+					sizeof(vhost->tls.alpn_ctx.data) - 1);
+
+	SSL_CTX_set_alpn_select_cb(vhost->tls.ssl_ctx, alpn_cb, &vhost->tls.alpn_ctx);
+#else
+	lwsl_err(
+		" HTTP2 / ALPN configured but not supported by OpenSSL 0x%lx\n",
+		    OPENSSL_VERSION_NUMBER);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+}
+
+int
+lws_tls_server_conn_alpn(struct lws *wsi)
+{
+#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
+				  OPENSSL_VERSION_NUMBER >= 0x10002000L)
+	const unsigned char *name = NULL;
+	char cstr[10];
+	unsigned len;
+
+	SSL_get0_alpn_selected(wsi->tls.ssl, &name, &len);
+	if (!len) {
+		lwsl_info("no ALPN upgrade\n");
+		return 0;
+	}
+
+	if (len > sizeof(cstr) - 1)
+		len = sizeof(cstr) - 1;
+
+	memcpy(cstr, name, len);
+	cstr[len] = '\0';
+
+	lwsl_info("negotiated '%s' using ALPN\n", cstr);
+	wsi->tls.use_ssl |= LCCSCF_USE_SSL;
+
+	return lws_role_call_alpn_negotiated(wsi, (const char *)cstr);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+
+	return 0;
+}
+
+LWS_VISIBLE int
+lws_context_init_server_ssl(const struct lws_context_creation_info *info,
+			    struct lws_vhost *vhost)
+{
+	struct lws_context *context = vhost->context;
+	struct lws wsi;
+
+	if (!lws_check_opt(info->options,
+			   LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
+		vhost->tls.use_ssl = 0;
+
+		return 0;
+	}
+
+	/*
+	 * If he is giving a cert filepath, take it as a sign he wants to use
+	 * it on this vhost.  User code can leave the cert filepath NULL and
+	 * set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in
+	 * which case he's expected to set up the cert himself at
+	 * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
+	 * provides the vhost SSL_CTX * in the user parameter.
+	 */
+	if (info->ssl_cert_filepath)
+		vhost->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX;
+
+	if (info->port != CONTEXT_PORT_NO_LISTEN) {
+
+		vhost->tls.use_ssl = lws_check_opt(vhost->options,
+					LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX);
+
+		if (vhost->tls.use_ssl && info->ssl_cipher_list)
+			lwsl_notice(" SSL ciphers: '%s'\n",
+						info->ssl_cipher_list);
+
+		if (vhost->tls.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;
+
+	/*
+	 * as a server, if we are requiring clients to identify themselves
+	 * then set the backend up for it
+	 */
+	if (lws_check_opt(info->options,
+			  LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT))
+		/* Normally SSL listener rejects non-ssl, optionally allow */
+		vhost->tls.allow_non_ssl_on_ssl_port = 1;
+
+	/*
+	 * give user code a chance to load certs into the server
+	 * allowing it to verify incoming client certs
+	 */
+	if (vhost->tls.use_ssl) {
+		if (lws_tls_server_vhost_backend_init(info, vhost, &wsi))
+			return -1;
+
+		lws_tls_server_client_cert_verify_config(vhost);
+
+		if (vhost->protocols[0].callback(&wsi,
+			    LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
+			    vhost->tls.ssl_ctx, vhost, 0))
+			return -1;
+	}
+
+	if (vhost->tls.use_ssl)
+		lws_context_init_alpn(vhost);
+
+	return 0;
+}
+
+LWS_VISIBLE int
+lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
+{
+	struct lws_context *context = wsi->context;
+	struct lws_vhost *vh;
+	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+	int n;
+        char buf[256];
+
+        (void)buf;
+
+	if (!LWS_SSL_ENABLED(wsi->vhost))
+		return 0;
+
+	switch (lwsi_state(wsi)) {
+	case LRS_SSL_INIT:
+
+		if (wsi->tls.ssl)
+			lwsl_err("%s: leaking ssl\n", __func__);
+		if (accept_fd == LWS_SOCK_INVALID)
+			assert(0);
+		if (context->simultaneous_ssl_restriction &&
+		    context->simultaneous_ssl >=
+		    	    context->simultaneous_ssl_restriction) {
+			lwsl_notice("unable to deal with SSL connection\n");
+			return 1;
+		}
+
+		if (lws_tls_server_new_nonblocking(wsi, accept_fd)) {
+			if (accept_fd != LWS_SOCK_INVALID)
+				compatible_close(accept_fd);
+			goto fail;
+		}
+
+		if (context->simultaneous_ssl_restriction &&
+		    ++context->simultaneous_ssl ==
+				    context->simultaneous_ssl_restriction)
+			/* that was the last allowed SSL connection */
+			lws_gate_accepts(context, 0);
+
+#if defined(LWS_WITH_STATS)
+		context->updated = 1;
+#endif
+		/*
+		 * we are not accepted yet, but we need to enter ourselves
+		 * as a live connection.  That way we can retry when more
+		 * pieces come if we're not sorted yet
+		 */
+		lwsi_set_state(wsi, LRS_SSL_ACK_PENDING);
+
+		lws_pt_lock(pt, __func__);
+		if (__insert_wsi_socket_into_fds(context, wsi)) {
+			lwsl_err("%s: failed to insert into fds\n", __func__);
+			goto fail;
+		}
+		lws_pt_unlock(pt);
+
+		lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
+				context->timeout_secs);
+
+		lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n");
+
+		/* fallthru */
+
+	case LRS_SSL_ACK_PENDING:
+
+		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+			lwsl_err("%s: lws_change_pollfd failed\n", __func__);
+			goto fail;
+		}
+
+		lws_latency_pre(context, wsi);
+
+		if (wsi->vhost->tls.allow_non_ssl_on_ssl_port) {
+
+			n = recv(wsi->desc.sockfd, (char *)pt->serv_buf,
+				 context->pt_serv_buf_size, MSG_PEEK);
+
+		/*
+		 * optionally allow non-SSL connect on SSL listening socket
+		 * This is disabled by default, if enabled it goes around any
+		 * SSL-level access control (eg, client-side certs) so leave
+		 * it disabled unless you know it's not a problem for you
+		 */
+			if (n >= 1 && pt->serv_buf[0] >= ' ') {
+				/*
+				* TLS content-type for Handshake is 0x16, and
+				* for ChangeCipherSpec Record, it's 0x14
+				*
+				* A non-ssl session will start with the HTTP
+				* method in ASCII.  If we see it's not a legit
+				* SSL handshake kill the SSL for this
+				* connection and try to handle as a HTTP
+				* connection upgrade directly.
+				*/
+				wsi->tls.use_ssl = 0;
+
+				lws_tls_server_abort_connection(wsi);
+				/*
+				 * care... this creates wsi with no ssl
+				 * when ssl is enabled and normally
+				 * mandatory
+				 */
+				wsi->tls.ssl = NULL;
+				if (lws_check_opt(context->options,
+				    LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
+					wsi->tls.redirect_to_https = 1;
+				lwsl_debug("accepted as non-ssl\n");
+				goto accepted;
+			}
+			if (!n) {
+				/*
+				 * connection is gone, fail out
+				 */
+				lwsl_debug("PEEKed 0\n");
+				goto fail;
+			}
+			if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
+				      LWS_ERRNO == LWS_EWOULDBLOCK)) {
+				/*
+				 * well, we get no way to know ssl or not
+				 * so go around again waiting for something
+				 * to come and give us a hint, or timeout the
+				 * connection.
+				 */
+				if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
+					lwsl_info("%s: change_pollfd failed\n",
+						  __func__);
+					return -1;
+				}
+
+				lwsl_info("SSL_ERROR_WANT_READ\n");
+				return 0;
+			}
+		}
+
+		/* normal SSL connection processing path */
+
+#if defined(LWS_WITH_STATS)
+		if (!wsi->accept_start_us)
+			wsi->accept_start_us = time_in_microseconds();
+#endif
+		errno = 0;
+		lws_stats_atomic_bump(wsi->context, pt,
+				      LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
+		n = lws_tls_server_accept(wsi);
+		lws_latency(context, wsi,
+			"SSL_accept LRS_SSL_ACK_PENDING\n", n, n == 1);
+		lwsl_info("SSL_accept says %d\n", n);
+		switch (n) {
+		case LWS_SSL_CAPABLE_DONE:
+			break;
+		case LWS_SSL_CAPABLE_ERROR:
+			lws_stats_atomic_bump(wsi->context, pt,
+					      LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
+	                lwsl_info("SSL_accept failed socket %u: %d\n",
+	                		wsi->desc.sockfd, n);
+			wsi->socket_is_permanently_unusable = 1;
+			goto fail;
+
+		default: /* MORE_SERVICE */
+			return 0;
+		}
+
+		lws_stats_atomic_bump(wsi->context, pt,
+				      LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1);
+#if defined(LWS_WITH_STATS)
+		lws_stats_atomic_bump(wsi->context, pt,
+				      LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY,
+				      time_in_microseconds() - wsi->accept_start_us);
+		wsi->accept_start_us = time_in_microseconds();
+#endif
+
+accepted:
+
+		/* adapt our vhost to match the SNI SSL_CTX that was chosen */
+		vh = context->vhost_list;
+		while (vh) {
+			if (!vh->being_destroyed && wsi->tls.ssl &&
+			    vh->tls.ssl_ctx == lws_tls_ctx_from_wsi(wsi)) {
+				lwsl_info("setting wsi to vh %s\n", vh->name);
+				wsi->vhost = vh;
+				break;
+			}
+			vh = vh->vhost_next;
+		}
+
+		/* OK, we are accepted... give him some time to negotiate */
+		lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
+				context->timeout_secs);
+
+		lwsi_set_state(wsi, LRS_ESTABLISHED);
+		if (lws_tls_server_conn_alpn(wsi))
+			goto fail;
+		lwsl_debug("accepted new SSL conn\n");
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+
+fail:
+	return 1;
+}
+

+ 522 - 0
thirdparty/libwebsockets/tls/tls.c

@@ -0,0 +1,522 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2017 Andy Green <[email protected]>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "core/private.h"
+
+/*
+ * fakes POLLIN on all tls guys with buffered rx
+ *
+ * returns nonzero if any tls guys had POLLIN faked
+ */
+
+int
+lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt)
+{
+	struct lws *wsi, *wsi_next;
+	int ret = 0;
+
+	wsi = pt->tls.pending_read_list;
+	while (wsi && wsi->position_in_fds_table != LWS_NO_FDS_POS) {
+		wsi_next = wsi->tls.pending_read_list_next;
+		pt->fds[wsi->position_in_fds_table].revents |=
+			pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
+		ret |= pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN;
+
+		wsi = wsi_next;
+	}
+
+	return !!ret;
+}
+
+void
+__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
+{
+	struct lws_context *context = wsi->context;
+	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+
+	if (!wsi->tls.pending_read_list_prev &&
+	    !wsi->tls.pending_read_list_next &&
+	    pt->tls.pending_read_list != wsi)
+		/* we are not on the list */
+		return;
+
+	/* point previous guy's next to our next */
+	if (!wsi->tls.pending_read_list_prev)
+		pt->tls.pending_read_list = wsi->tls.pending_read_list_next;
+	else
+		wsi->tls.pending_read_list_prev->tls.pending_read_list_next =
+			wsi->tls.pending_read_list_next;
+
+	/* point next guy's previous to our previous */
+	if (wsi->tls.pending_read_list_next)
+		wsi->tls.pending_read_list_next->tls.pending_read_list_prev =
+			wsi->tls.pending_read_list_prev;
+
+	wsi->tls.pending_read_list_prev = NULL;
+	wsi->tls.pending_read_list_next = NULL;
+}
+
+void
+lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
+{
+	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+	lws_pt_lock(pt, __func__);
+	__lws_ssl_remove_wsi_from_buffered_list(wsi);
+	lws_pt_unlock(pt);
+}
+
+#if defined(LWS_WITH_ESP32)
+int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
+	       lws_filepos_t *amount)
+{
+	nvs_handle nvh;
+	size_t s;
+	int n = 0;
+
+	ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
+	if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
+		n = 1;
+		goto bail;
+	}
+	*buf = lws_malloc(s + 1, "alloc_file");
+	if (!*buf) {
+		n = 2;
+		goto bail;
+	}
+	if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
+		lws_free(*buf);
+		n = 1;
+		goto bail;
+	}
+
+	*amount = s;
+	(*buf)[s] = '\0';
+
+	lwsl_notice("%s: nvs: read %s, %d bytes\n", __func__, filename, (int)s);
+
+bail:
+	nvs_close(nvh);
+
+	return n;
+}
+#else
+int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
+		lws_filepos_t *amount)
+{
+	FILE *f;
+	size_t s;
+	int n = 0;
+
+	f = fopen(filename, "rb");
+	if (f == NULL) {
+		n = 1;
+		goto bail;
+	}
+
+	if (fseek(f, 0, SEEK_END) != 0) {
+		n = 1;
+		goto bail;
+	}
+
+	s = ftell(f);
+	if (s == (size_t)-1) {
+		n = 1;
+		goto bail;
+	}
+
+	if (fseek(f, 0, SEEK_SET) != 0) {
+		n = 1;
+		goto bail;
+	}
+
+	*buf = lws_malloc(s, "alloc_file");
+	if (!*buf) {
+		n = 2;
+		goto bail;
+	}
+
+	if (fread(*buf, s, 1, f) != 1) {
+		lws_free(*buf);
+		n = 1;
+		goto bail;
+	}
+
+	*amount = s;
+
+bail:
+	if (f)
+		fclose(f);
+
+	return n;
+
+}
+#endif
+
+int
+lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
+			const char *inbuf, lws_filepos_t inlen,
+		      uint8_t **buf, lws_filepos_t *amount)
+{
+	const uint8_t *pem, *p, *end;
+	uint8_t *q;
+	lws_filepos_t len;
+	int n;
+
+	if (filename) {
+		n = alloc_file(context, filename, (uint8_t **)&pem, &len);
+		if (n)
+			return n;
+	} else {
+		pem = (const uint8_t *)inbuf;
+		len = inlen;
+	}
+
+	/* trim the first line */
+
+	p = pem;
+	end = p + len;
+	if (strncmp((char *)p, "-----", 5))
+		goto bail;
+	p += 5;
+	while (p < end && *p != '\n' && *p != '-')
+		p++;
+
+	if (*p != '-')
+		goto bail;
+
+	while (p < end && *p != '\n')
+		p++;
+
+	if (p >= end)
+		goto bail;
+
+	p++;
+
+	/* trim the last line */
+
+	q = (uint8_t *)end - 2;
+
+	while (q > pem && *q != '\n')
+		q--;
+
+	if (*q != '\n')
+		goto bail;
+
+	*q = '\0';
+
+	*amount = lws_b64_decode_string((char *)p, (char *)pem,
+					(int)(long long)len);
+	*buf = (uint8_t *)pem;
+
+	return 0;
+
+bail:
+	lws_free((uint8_t *)pem);
+
+	return 4;
+}
+
+int
+lws_tls_check_cert_lifetime(struct lws_vhost *v)
+{
+	union lws_tls_cert_info_results ir;
+	time_t now = (time_t)lws_now_secs(), life = 0;
+	struct lws_acme_cert_aging_args caa;
+	int n;
+
+	if (v->tls.ssl_ctx && !v->tls.skipped_certs) {
+
+		if (now < 1464083026) /* May 2016 */
+			/* our clock is wrong and we can't judge the certs */
+			return -1;
+
+		n = lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, &ir, 0);
+		if (n)
+			return 1;
+
+		life = (ir.time - now) / (24 * 3600);
+		lwsl_notice("   vhost %s: cert expiry: %dd\n", v->name, (int)life);
+	} else
+		lwsl_notice("   vhost %s: no cert\n", v->name);
+
+	memset(&caa, 0, sizeof(caa));
+	caa.vh = v;
+	lws_broadcast(v->context, LWS_CALLBACK_VHOST_CERT_AGING, (void *)&caa,
+		      (size_t)(ssize_t)life);
+
+	return 0;
+}
+
+int
+lws_tls_check_all_cert_lifetimes(struct lws_context *context)
+{
+	struct lws_vhost *v = context->vhost_list;
+
+	while (v) {
+		if (lws_tls_check_cert_lifetime(v) < 0)
+			return -1;
+		v = v->vhost_next;
+	}
+
+	return 0;
+}
+#if !defined(LWS_WITH_ESP32) && !defined(LWS_PLAT_OPTEE)
+static int
+lws_tls_extant(const char *name)
+{
+	/* it exists if we can open it... */
+	int fd = open(name, O_RDONLY), n;
+	char buf[1];
+
+	if (fd < 0)
+		return 1;
+
+	/* and we can read at least one byte out of it */
+	n = read(fd, buf, 1);
+	close(fd);
+
+	return n != 1;
+}
+#endif
+/*
+ * Returns 0 if the filepath "name" exists and can be read from.
+ *
+ * In addition, if "name".upd exists, backup "name" to "name.old.1"
+ * and rename "name".upd to "name" before reporting its existence.
+ *
+ * There are four situations and three results possible:
+ *
+ * 1) LWS_TLS_EXTANT_NO: There are no certs at all (we are waiting for them to
+ *    be provisioned).  We also feel like this if we need privs we don't have
+ *    any more to look in the directory.
+ *
+ * 2) There are provisioned certs written (xxx.upd) and we still have root
+ *    privs... in this case we rename any existing cert to have a backup name
+ *    and move the upd cert into place with the correct name.  This then becomes
+ *    situation 4 for the caller.
+ *
+ * 3) LWS_TLS_EXTANT_ALTERNATIVE: There are provisioned certs written (xxx.upd)
+ *    but we no longer have the privs needed to read or rename them.  In this
+ *    case, indicate that the caller should use temp copies if any we do have
+ *    rights to access.  This is normal after we have updated the cert.
+ *
+ *    But if we dropped privs, we can't detect the provisioned xxx.upd cert +
+ *    key, because we can't see in the dir.  So we have to upgrade NO to
+ *    ALTERNATIVE when we actually have the in-memory alternative.
+ *
+ * 4) LWS_TLS_EXTANT_YES: The certs are present with the correct name and we
+ *    have the rights to read them.
+ */
+enum lws_tls_extant
+lws_tls_use_any_upgrade_check_extant(const char *name)
+{
+#if !defined(LWS_PLAT_OPTEE)
+
+	int n;
+
+#if !defined(LWS_WITH_ESP32)
+	char buf[256];
+
+	lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
+	if (!lws_tls_extant(buf)) {
+		/* ah there is an updated file... how about the desired file? */
+		if (!lws_tls_extant(name)) {
+			/* rename the desired file */
+			for (n = 0; n < 50; n++) {
+				lws_snprintf(buf, sizeof(buf) - 1,
+					     "%s.old.%d", name, n);
+				if (!rename(name, buf))
+					break;
+			}
+			if (n == 50) {
+				lwsl_notice("unable to rename %s\n", name);
+
+				return LWS_TLS_EXTANT_ALTERNATIVE;
+			}
+			lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
+		}
+		/* desired file is out of the way, rename the updated file */
+		if (rename(buf, name)) {
+			lwsl_notice("unable to rename %s to %s\n", buf, name);
+
+			return LWS_TLS_EXTANT_ALTERNATIVE;
+		}
+	}
+
+	if (lws_tls_extant(name))
+		return LWS_TLS_EXTANT_NO;
+#else
+	nvs_handle nvh;
+	size_t s = 8192;
+
+	if (nvs_open("lws-station", NVS_READWRITE, &nvh)) {
+		lwsl_notice("%s: can't open nvs\n", __func__);
+		return LWS_TLS_EXTANT_NO;
+	}
+
+	n = nvs_get_blob(nvh, name, NULL, &s);
+	nvs_close(nvh);
+
+	if (n)
+		return LWS_TLS_EXTANT_NO;
+#endif
+#endif
+	return LWS_TLS_EXTANT_YES;
+}
+
+/*
+ * LWS_TLS_EXTANT_NO         : skip adding the cert
+ * LWS_TLS_EXTANT_YES        : use the cert and private key paths normally
+ * LWS_TLS_EXTANT_ALTERNATIVE: normal paths not usable, try alternate if poss
+ */
+enum lws_tls_extant
+lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert,
+			    const char *private_key)
+{
+	int n, m;
+
+	/*
+	 * The user code can choose to either pass the cert and
+	 * key filepaths using the info members like this, or it can
+	 * leave them NULL; force the vhost SSL_CTX init using the info
+	 * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
+	 * set up the cert himself using the user callback
+	 * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
+	 * happened just above and has the vhost SSL_CTX * in the user
+	 * parameter.
+	 */
+
+	if (!cert || !private_key)
+		return LWS_TLS_EXTANT_NO;
+
+	n = lws_tls_use_any_upgrade_check_extant(cert);
+	if (n == LWS_TLS_EXTANT_ALTERNATIVE)
+		return LWS_TLS_EXTANT_ALTERNATIVE;
+	m = lws_tls_use_any_upgrade_check_extant(private_key);
+	if (m == LWS_TLS_EXTANT_ALTERNATIVE)
+		return LWS_TLS_EXTANT_ALTERNATIVE;
+
+	if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) &&
+	    (vhost->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) {
+		lwsl_notice("Ignoring missing %s or %s\n", cert, private_key);
+		vhost->tls.skipped_certs = 1;
+
+		return LWS_TLS_EXTANT_NO;
+	}
+
+	/*
+	 * the cert + key exist
+	 */
+
+	return LWS_TLS_EXTANT_YES;
+}
+
+#if !defined(LWS_NO_SERVER)
+/*
+ * update the cert for every vhost using the given path
+ */
+
+LWS_VISIBLE int
+lws_tls_cert_updated(struct lws_context *context, const char *certpath,
+		     const char *keypath,
+		     const char *mem_cert, size_t len_mem_cert,
+		     const char *mem_privkey, size_t len_mem_privkey)
+{
+	struct lws wsi;
+
+	wsi.context = context;
+
+	lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
+		wsi.vhost = v;
+		if (v->tls.alloc_cert_path && v->tls.key_path &&
+		    !strcmp(v->tls.alloc_cert_path, certpath) &&
+		    !strcmp(v->tls.key_path, keypath)) {
+			lws_tls_server_certs_load(v, &wsi, certpath, keypath,
+						  mem_cert, len_mem_cert,
+						  mem_privkey, len_mem_privkey);
+
+			if (v->tls.skipped_certs)
+				lwsl_notice("%s: vhost %s: cert unset\n",
+					    __func__, v->name);
+		}
+	} lws_end_foreach_ll(v, vhost_next);
+
+	return 0;
+}
+#endif
+
+int
+lws_gate_accepts(struct lws_context *context, int on)
+{
+	struct lws_vhost *v = context->vhost_list;
+
+	lwsl_notice("%s: on = %d\n", __func__, on);
+
+#if defined(LWS_WITH_STATS)
+	context->updated = 1;
+#endif
+
+	while (v) {
+		if (v->tls.use_ssl && v->lserv_wsi &&
+		    lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on,
+				      (LWS_POLLIN) * on))
+			lwsl_notice("Unable to set accept POLLIN %d\n", on);
+
+		v = v->vhost_next;
+	}
+
+	return 0;
+}
+
+/* comma-separated alpn list, like "h2,http/1.1" to openssl alpn format */
+
+int
+lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len)
+{
+	uint8_t *oos = os, *plen = NULL;
+
+	while (*comma && len > 1) {
+		if (!plen && *comma == ' ') {
+			comma++;
+			continue;
+		}
+		if (!plen) {
+			plen = os++;
+			len--;
+		}
+
+		if (*comma == ',') {
+			*plen = lws_ptr_diff(os, plen + 1);
+			plen = NULL;
+			comma++;
+		} else {
+			*os++ = *comma++;
+			len--;
+		}
+	}
+
+	if (plen)
+		*plen = lws_ptr_diff(os, plen + 1);
+
+	return lws_ptr_diff(os, oos);
+}
+

+ 153 - 153
thirdparty/libwebsockets/win32helpers/getopt.c

@@ -1,153 +1,153 @@
-/*	$NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $	*/
-
-/*
- * Copyright (c) 1987, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by the University of
- *	California, Berkeley and its contributors.
- * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
- */
-
-#if 0
-static char sccsid[] = "@(#)getopt.c	8.3 (Berkeley) 4/27/95";
-#endif
-
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#define __P(x) x
-#define _DIAGASSERT(x) assert(x)
-
-#ifdef __weak_alias
-__weak_alias(getopt,_getopt);
-#endif
-
-
-int	opterr = 1,		/* if error message should be printed */
-	optind = 1,		/* index into parent argv vector */
-	optopt,			/* character checked for validity */
-	optreset;		/* reset getopt */
-char	*optarg;		/* argument associated with option */
-
-static char * _progname __P((char *));
-int getopt_internal __P((int, char * const *, const char *));
-
-static char *
-_progname(nargv0)
-	char * nargv0;
-{
-	char * tmp;
-
-	_DIAGASSERT(nargv0 != NULL);
-
-	tmp = strrchr(nargv0, '/');
-	if (tmp)
-		tmp++;
-	else
-		tmp = nargv0;
-	return(tmp);
-}
-
-#define	BADCH	(int)'?'
-#define	BADARG	(int)':'
-#define	EMSG	""
-
-/*
- * getopt --
- *	Parse argc/argv argument vector.
- */
-int
-getopt(nargc, nargv, ostr)
-	int nargc;
-	char * const nargv[];
-	const char *ostr;
-{
-	static char *__progname = 0;
-	static char *place = EMSG;		/* option letter processing */
-	char *oli;				/* option letter list index */
-        __progname = __progname?__progname:_progname(*nargv);
-
-	_DIAGASSERT(nargv != NULL);
-	_DIAGASSERT(ostr != NULL);
-
-	if (optreset || !*place) {		/* update scanning pointer */
-		optreset = 0;
-		if (optind >= nargc || *(place = nargv[optind]) != '-') {
-			place = EMSG;
-			return (-1);
-		}
-		if (place[1] && *++place == '-'	/* found "--" */
-		    && place[1] == '\0') {
-			++optind;
-			place = EMSG;
-			return (-1);
-		}
-	}					/* option letter okay? */
-	if ((optopt = (int)*place++) == (int)':' ||
-	    !(oli = strchr(ostr, optopt))) {
-		/*
-		 * if the user didn't specify '-' as an option,
-		 * assume it means -1.
-		 */
-		if (optopt == (int)'-')
-			return (-1);
-		if (!*place)
-			++optind;
-		if (opterr && *ostr != ':')
-			(void)fprintf(stderr,
-			    "%s: illegal option -- %c\n", __progname, optopt);
-		return (BADCH);
-	}
-	if (*++oli != ':') {			/* don't need argument */
-		optarg = NULL;
-		if (!*place)
-			++optind;
-	}
-	else {					/* need an argument */
-		if (*place)			/* no white space */
-			optarg = place;
-		else if (nargc <= ++optind) {	/* no arg */
-			place = EMSG;
-			if (*ostr == ':')
-				return (BADARG);
-			if (opterr)
-				(void)fprintf(stderr,
-				    "%s: option requires an argument -- %c\n",
-				    __progname, optopt);
-			return (BADCH);
-		}
-	 	else				/* white space */
-			optarg = nargv[optind];
-		place = EMSG;
-		++optind;
-	}
-	return (optopt);			/* dump back option letter */
-}
-
+/*	$NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $	*/
+
+/*
+ * Copyright (c) 1987, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ */
+
+#if 0
+static char sccsid[] = "@(#)getopt.c	8.3 (Berkeley) 4/27/95";
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#define __P(x) x
+#define _DIAGASSERT(x) assert(x)
+
+#ifdef __weak_alias
+__weak_alias(getopt,_getopt);
+#endif
+
+
+int	opterr = 1,		/* if error message should be printed */
+	optind = 1,		/* index into parent argv vector */
+	optopt,			/* character checked for validity */
+	optreset;		/* reset getopt */
+char	*optarg;		/* argument associated with option */
+
+static char * _progname __P((char *));
+int getopt_internal __P((int, char * const *, const char *));
+
+static char *
+_progname(nargv0)
+	char * nargv0;
+{
+	char * tmp;
+
+	_DIAGASSERT(nargv0 != NULL);
+
+	tmp = strrchr(nargv0, '/');
+	if (tmp)
+		tmp++;
+	else
+		tmp = nargv0;
+	return(tmp);
+}
+
+#define	BADCH	(int)'?'
+#define	BADARG	(int)':'
+#define	EMSG	""
+
+/*
+ * getopt --
+ *	Parse argc/argv argument vector.
+ */
+int
+getopt(nargc, nargv, ostr)
+	int nargc;
+	char * const nargv[];
+	const char *ostr;
+{
+	static char *__progname = 0;
+	static char *place = EMSG;		/* option letter processing */
+	char *oli;				/* option letter list index */
+        __progname = __progname?__progname:_progname(*nargv);
+
+	_DIAGASSERT(nargv != NULL);
+	_DIAGASSERT(ostr != NULL);
+
+	if (optreset || !*place) {		/* update scanning pointer */
+		optreset = 0;
+		if (optind >= nargc || *(place = nargv[optind]) != '-') {
+			place = EMSG;
+			return (-1);
+		}
+		if (place[1] && *++place == '-'	/* found "--" */
+		    && place[1] == '\0') {
+			++optind;
+			place = EMSG;
+			return (-1);
+		}
+	}					/* option letter okay? */
+	if ((optopt = (int)*place++) == (int)':' ||
+	    !(oli = strchr(ostr, optopt))) {
+		/*
+		 * if the user didn't specify '-' as an option,
+		 * assume it means -1.
+		 */
+		if (optopt == (int)'-')
+			return (-1);
+		if (!*place)
+			++optind;
+		if (opterr && *ostr != ':')
+			(void)fprintf(stderr,
+			    "%s: illegal option -- %c\n", __progname, optopt);
+		return (BADCH);
+	}
+	if (*++oli != ':') {			/* don't need argument */
+		optarg = NULL;
+		if (!*place)
+			++optind;
+	}
+	else {					/* need an argument */
+		if (*place)			/* no white space */
+			optarg = place;
+		else if (nargc <= ++optind) {	/* no arg */
+			place = EMSG;
+			if (*ostr == ':')
+				return (BADARG);
+			if (opterr)
+				(void)fprintf(stderr,
+				    "%s: option requires an argument -- %c\n",
+				    __progname, optopt);
+			return (BADCH);
+		}
+	 	else				/* white space */
+			optarg = nargv[optind];
+		place = EMSG;
+		++optind;
+	}
+	return (optopt);			/* dump back option letter */
+}
+

+ 240 - 237
thirdparty/libwebsockets/win32helpers/getopt_long.c

@@ -1,237 +1,240 @@
-
-/*
- * Copyright (c) 1987, 1993, 1994, 1996
- *	The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by the University of
- *	California, Berkeley and its contributors.
- * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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 <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "getopt.h"
-
-extern int	  opterr;	/* if error message should be printed */
-extern int	  optind;	/* index into parent argv vector */
-extern int	  optopt;	/* character checked for validity */
-extern int	  optreset;	/* reset getopt */
-extern char *optarg;	/* argument associated with option */
-
-#define __P(x) x
-#define _DIAGASSERT(x) assert(x)
-
-static char * __progname __P((char *));
-int getopt_internal __P((int, char * const *, const char *));
-
-static char *
-__progname(nargv0)
-	char * nargv0;
-{
-	char * tmp;
-
-	_DIAGASSERT(nargv0 != NULL);
-
-	tmp = strrchr(nargv0, '/');
-	if (tmp)
-		tmp++;
-	else
-		tmp = nargv0;
-	return(tmp);
-}
-
-#define	BADCH	(int)'?'
-#define	BADARG	(int)':'
-#define	EMSG	""
-
-/*
- * getopt --
- *	Parse argc/argv argument vector.
- */
-int
-getopt_internal(nargc, nargv, ostr)
-	int nargc;
-	char * const *nargv;
-	const char *ostr;
-{
-	static char *place = EMSG;		/* option letter processing */
-	char *oli;				/* option letter list index */
-
-	_DIAGASSERT(nargv != NULL);
-	_DIAGASSERT(ostr != NULL);
-
-	if (optreset || !*place) {		/* update scanning pointer */
-		optreset = 0;
-		if (optind >= nargc || *(place = nargv[optind]) != '-') {
-			place = EMSG;
-			return (-1);
-		}
-		if (place[1] && *++place == '-') {	/* found "--" */
-			/* ++optind; */
-			place = EMSG;
-			return (-2);
-		}
-	}					/* option letter okay? */
-	if ((optopt = (int)*place++) == (int)':' ||
-	    !(oli = strchr(ostr, optopt))) {
-		/*
-		 * if the user didn't specify '-' as an option,
-		 * assume it means -1.
-		 */
-		if (optopt == (int)'-')
-			return (-1);
-		if (!*place)
-			++optind;
-		if (opterr && *ostr != ':')
-			(void)fprintf(stderr,
-			    "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
-		return (BADCH);
-	}
-	if (*++oli != ':') {			/* don't need argument */
-		optarg = NULL;
-		if (!*place)
-			++optind;
-	} else {				/* need an argument */
-		if (*place)			/* no white space */
-			optarg = place;
-		else if (nargc <= ++optind) {	/* no arg */
-			place = EMSG;
-			if ((opterr) && (*ostr != ':'))
-				(void)fprintf(stderr,
-				    "%s: option requires an argument -- %c\n",
-				    __progname(nargv[0]), optopt);
-			return (BADARG);
-		} else				/* white space */
-			optarg = nargv[optind];
-		place = EMSG;
-		++optind;
-	}
-	return (optopt);			/* dump back option letter */
-}
-
-#if 0
-/*
- * getopt --
- *	Parse argc/argv argument vector.
- */
-int
-getopt2(nargc, nargv, ostr)
-	int nargc;
-	char * const *nargv;
-	const char *ostr;
-{
-	int retval;
-
-	if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
-		retval = -1;
-		++optind; 
-	}
-	return(retval);
-}
-#endif
-
-/*
- * getopt_long --
- *	Parse argc/argv argument vector.
- */
-int
-getopt_long(nargc, nargv, options, long_options, index)
-	int nargc;
-	char ** nargv;
-	char * options;
-	struct option * long_options;
-	int * index;
-{
-	int retval;
-
-	_DIAGASSERT(nargv != NULL);
-	_DIAGASSERT(options != NULL);
-	_DIAGASSERT(long_options != NULL);
-	/* index may be NULL */
-
-	if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
-		char *current_argv = nargv[optind++] + 2, *has_equal;
-		int i, current_argv_len, match = -1;
-
-		if (*current_argv == '\0') {
-			return(-1);
-		}
-		if ((has_equal = strchr(current_argv, '=')) != NULL) {
-			current_argv_len = has_equal - current_argv;
-			has_equal++;
-		} else
-			current_argv_len = strlen(current_argv);
-
-		for (i = 0; long_options[i].name; i++) { 
-			if (strncmp(current_argv, long_options[i].name, current_argv_len))
-				continue;
-
-			if (strlen(long_options[i].name) == (unsigned)current_argv_len) { 
-				match = i;
-				break;
-			}
-			if (match == -1)
-				match = i;
-		}
-		if (match != -1) {
-			if (long_options[match].has_arg == required_argument ||
-			    long_options[match].has_arg == optional_argument) {
-				if (has_equal)
-					optarg = has_equal;
-				else
-					optarg = nargv[optind++];
-			}
-			if ((long_options[match].has_arg == required_argument)
-			    && (optarg == NULL)) {
-				/*
-				 * Missing argument, leading :
-				 * indicates no error should be generated
-				 */
-				if ((opterr) && (*options != ':'))
-					(void)fprintf(stderr,
-				      "%s: option requires an argument -- %s\n",
-				      __progname(nargv[0]), current_argv);
-				return (BADARG);
-			}
-		} else { /* No matching argument */
-			if ((opterr) && (*options != ':'))
-				(void)fprintf(stderr,
-				    "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
-			return (BADCH);
-		}
-		if (long_options[match].flag) {
-			*long_options[match].flag = long_options[match].val;
-			retval = 0;
-		} else 
-			retval = long_options[match].val;
-		if (index)
-			*index = match;
-	}
-	return(retval);
-}
+
+/*
+ * Copyright (c) 1987, 1993, 1994, 1996
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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 <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "getopt.h"
+
+#define lws_ptr_diff(head, tail) \
+			((int)((char *)(head) - (char *)(tail)))
+
+extern int	  opterr;	/* if error message should be printed */
+extern int	  optind;	/* index into parent argv vector */
+extern int	  optopt;	/* character checked for validity */
+extern int	  optreset;	/* reset getopt */
+extern char *optarg;	/* argument associated with option */
+
+#define __P(x) x
+#define _DIAGASSERT(x) assert(x)
+
+static char * __progname __P((char *));
+int getopt_internal __P((int, char * const *, const char *));
+
+static char *
+__progname(nargv0)
+	char * nargv0;
+{
+	char * tmp;
+
+	_DIAGASSERT(nargv0 != NULL);
+
+	tmp = strrchr(nargv0, '/');
+	if (tmp)
+		tmp++;
+	else
+		tmp = nargv0;
+	return(tmp);
+}
+
+#define	BADCH	(int)'?'
+#define	BADARG	(int)':'
+#define	EMSG	""
+
+/*
+ * getopt --
+ *	Parse argc/argv argument vector.
+ */
+int
+getopt_internal(nargc, nargv, ostr)
+	int nargc;
+	char * const *nargv;
+	const char *ostr;
+{
+	static char *place = EMSG;		/* option letter processing */
+	char *oli;				/* option letter list index */
+
+	_DIAGASSERT(nargv != NULL);
+	_DIAGASSERT(ostr != NULL);
+
+	if (optreset || !*place) {		/* update scanning pointer */
+		optreset = 0;
+		if (optind >= nargc || *(place = nargv[optind]) != '-') {
+			place = EMSG;
+			return (-1);
+		}
+		if (place[1] && *++place == '-') {	/* found "--" */
+			/* ++optind; */
+			place = EMSG;
+			return (-2);
+		}
+	}					/* option letter okay? */
+	if ((optopt = (int)*place++) == (int)':' ||
+	    !(oli = strchr(ostr, optopt))) {
+		/*
+		 * if the user didn't specify '-' as an option,
+		 * assume it means -1.
+		 */
+		if (optopt == (int)'-')
+			return (-1);
+		if (!*place)
+			++optind;
+		if (opterr && *ostr != ':')
+			(void)fprintf(stderr,
+			    "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
+		return (BADCH);
+	}
+	if (*++oli != ':') {			/* don't need argument */
+		optarg = NULL;
+		if (!*place)
+			++optind;
+	} else {				/* need an argument */
+		if (*place)			/* no white space */
+			optarg = place;
+		else if (nargc <= ++optind) {	/* no arg */
+			place = EMSG;
+			if ((opterr) && (*ostr != ':'))
+				(void)fprintf(stderr,
+				    "%s: option requires an argument -- %c\n",
+				    __progname(nargv[0]), optopt);
+			return (BADARG);
+		} else				/* white space */
+			optarg = nargv[optind];
+		place = EMSG;
+		++optind;
+	}
+	return (optopt);			/* dump back option letter */
+}
+
+#if 0
+/*
+ * getopt --
+ *	Parse argc/argv argument vector.
+ */
+int
+getopt2(nargc, nargv, ostr)
+	int nargc;
+	char * const *nargv;
+	const char *ostr;
+{
+	int retval;
+
+	if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
+		retval = -1;
+		++optind; 
+	}
+	return(retval);
+}
+#endif
+
+/*
+ * getopt_long --
+ *	Parse argc/argv argument vector.
+ */
+int
+getopt_long(nargc, nargv, options, long_options, index)
+	int nargc;
+	char ** nargv;
+	char * options;
+	struct option * long_options;
+	int * index;
+{
+	int retval;
+
+	_DIAGASSERT(nargv != NULL);
+	_DIAGASSERT(options != NULL);
+	_DIAGASSERT(long_options != NULL);
+	/* index may be NULL */
+
+	if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
+		char *current_argv = nargv[optind++] + 2, *has_equal;
+		int i, current_argv_len, match = -1;
+
+		if (*current_argv == '\0') {
+			return(-1);
+		}
+		if ((has_equal = strchr(current_argv, '=')) != NULL) {
+			current_argv_len = lws_ptr_diff(has_equal, current_argv);
+			has_equal++;
+		} else
+			current_argv_len = (int)strlen(current_argv);
+
+		for (i = 0; long_options[i].name; i++) { 
+			if (strncmp(current_argv, long_options[i].name, current_argv_len))
+				continue;
+
+			if (strlen(long_options[i].name) == (unsigned)current_argv_len) { 
+				match = i;
+				break;
+			}
+			if (match == -1)
+				match = i;
+		}
+		if (match != -1) {
+			if (long_options[match].has_arg == required_argument ||
+			    long_options[match].has_arg == optional_argument) {
+				if (has_equal)
+					optarg = has_equal;
+				else
+					optarg = nargv[optind++];
+			}
+			if ((long_options[match].has_arg == required_argument)
+			    && (optarg == NULL)) {
+				/*
+				 * Missing argument, leading :
+				 * indicates no error should be generated
+				 */
+				if ((opterr) && (*options != ':'))
+					(void)fprintf(stderr,
+				      "%s: option requires an argument -- %s\n",
+				      __progname(nargv[0]), current_argv);
+				return (BADARG);
+			}
+		} else { /* No matching argument */
+			if ((opterr) && (*options != ':'))
+				(void)fprintf(stderr,
+				    "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
+			return (BADCH);
+		}
+		if (long_options[match].flag) {
+			*long_options[match].flag = long_options[match].val;
+			retval = 0;
+		} else 
+			retval = long_options[match].val;
+		if (index)
+			*index = match;
+	}
+	return(retval);
+}

+ 35 - 35
thirdparty/libwebsockets/win32helpers/gettimeofday.c

@@ -1,36 +1,36 @@
-#include <time.h>
-#include <windows.h> //I've omitted context line
-
-#include "gettimeofday.h"
-
-int gettimeofday(struct timeval *tv, struct timezone *tz)
-{
-	FILETIME ft;
-	unsigned __int64 tmpres = 0;
-	static int tzflag;
-
-	if (NULL != tv) {
-		GetSystemTimeAsFileTime(&ft);
-
-		tmpres |= ft.dwHighDateTime;
-		tmpres <<= 32;
-		tmpres |= ft.dwLowDateTime;
-
-		/*converting file time to unix epoch*/
-		tmpres /= 10;  /*convert into microseconds*/
+#include <time.h>
+#include <windows.h> //I've omitted context line
+
+#include "gettimeofday.h"
+
+int gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+	FILETIME ft;
+	unsigned __int64 tmpres = 0;
+	static int tzflag;
+
+	if (NULL != tv) {
+		GetSystemTimeAsFileTime(&ft);
+
+		tmpres |= ft.dwHighDateTime;
+		tmpres <<= 32;
+		tmpres |= ft.dwLowDateTime;
+
+		/*converting file time to unix epoch*/
+		tmpres /= 10;  /*convert into microseconds*/
 		tmpres -= DELTA_EPOCH_IN_MICROSECS;
 		tmpres -= DELTA_EPOCH_IN_MICROSECS;
-		tv->tv_sec = (long)(tmpres / 1000000UL);
-		tv->tv_usec = (long)(tmpres % 1000000UL);
-	}
- 
-	if (NULL != tz) {
-		if (!tzflag) {
-			_tzset();
-			tzflag++;
-		}
-		tz->tz_minuteswest = _timezone / 60;
-		tz->tz_dsttime = _daylight;
-	}
-
-	return 0;
-}
+		tv->tv_sec = (long)(tmpres / 1000000UL);
+		tv->tv_usec = (long)(tmpres % 1000000UL);
+	}
+ 
+	if (NULL != tz) {
+		if (!tzflag) {
+			_tzset();
+			tzflag++;
+		}
+		tz->tz_minuteswest = _timezone / 60;
+		tz->tz_dsttime = _daylight;
+	}
+
+	return 0;
+}

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