Update Files
This commit is contained in:
93
Kinc/Sources/kinc/libs/roles/CMakeLists.txt
Normal file
93
Kinc/Sources/kinc/libs/roles/CMakeLists.txt
Normal file
@ -0,0 +1,93 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
if (LWS_ROLE_MQTT)
|
||||
add_subdir_include_directories(mqtt)
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_DBUS AND NOT LWS_PLAT_FREERTOS)
|
||||
add_subdir_include_directories(dbus)
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_H1 OR LWS_ROLE_H2)
|
||||
add_subdir_include_directories(http)
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_H1)
|
||||
add_subdir_include_directories(h1)
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_H2)
|
||||
add_subdir_include_directories(h2)
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_WS)
|
||||
add_subdir_include_directories(ws)
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_RAW)
|
||||
add_subdir_include_directories(raw-skt)
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_RAW_FILE)
|
||||
add_subdir_include_directories(raw-file)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_CGI)
|
||||
add_subdir_include_directories(cgi)
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_RAW_PROXY)
|
||||
add_subdir_include_directories(raw-proxy)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITHOUT_SERVER OR LWS_WITH_SECURE_STREAMS_PROCESS_API)
|
||||
add_subdir_include_directories(listen)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_CLIENT AND (LWS_ROLE_H1 OR LWS_ROLE_H2))
|
||||
list(APPEND SOURCES
|
||||
roles/http/client/client-http.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_NETLINK)
|
||||
list(APPEND SOURCES roles/netlink/ops-netlink.c)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
set(LWS_DEPS_LIB_PATHS ${LWS_DEPS_LIB_PATHS} PARENT_SCOPE)
|
||||
|
||||
170
Kinc/Sources/kinc/libs/roles/README.md
Normal file
170
Kinc/Sources/kinc/libs/roles/README.md
Normal file
@ -0,0 +1,170 @@
|
||||
## Information for new role implementers
|
||||
|
||||
### Introduction
|
||||
|
||||
In lws the "role" is the job the wsi is doing in the system, eg,
|
||||
being an http1 or h2, or ws connection, or being a listen socket, etc.
|
||||
|
||||
This is different than, eg, a new ws protocol or a different callback
|
||||
for an existing role. A new role is needed when you want to add support for
|
||||
something completely new, like a completely new wire protocol that
|
||||
doesn't use http or ws.
|
||||
|
||||
So... what's the point of implementing the protocol inside the lws role framework?
|
||||
|
||||
You inherit all the well-maintained lws core functionality around:
|
||||
|
||||
- connection lifecycle sequencing in a valgrind-clean way
|
||||
|
||||
- client connection proxy support, for HTTP and Socks5
|
||||
|
||||
- tls support working equally on mbedTLS and OpenSSL and derivatives without any code in the role
|
||||
|
||||
- apis for cert lifecycle management and parsing
|
||||
|
||||
- event loop support working on all the lws event loops (poll, libuv , ev, and event)
|
||||
|
||||
- clean connection tracking and closing even on advanced event loops
|
||||
|
||||
- user code follows the same simple callbacks on wsi
|
||||
|
||||
- multi-vhost support
|
||||
|
||||
- core multithreaded service support with usually no locking requirement on the role code
|
||||
|
||||
- direct compatibility with all other lws roles + protocols in the same event loop
|
||||
|
||||
- compatibility with higher-level stuff like lwsws as the server application
|
||||
|
||||
### Code placement
|
||||
|
||||
The code specific to that role should live in `./lib/roles/**role name**`
|
||||
|
||||
If a role is asymmetic between a client and server side, like http is, it
|
||||
should generally be implemented as a single role.
|
||||
|
||||
### Allowing control over enabling roles
|
||||
|
||||
All roles should add a cmake define `LWS_ROLE_**role name**` and make its build
|
||||
dependent on it in CMakeLists.txt. Export the cmakedefine in `./cmake/lws_config.h.in`
|
||||
as well so user builds can understand if the role is available in the lws build it is
|
||||
trying to bind to.
|
||||
|
||||
If the role is disabled in cmake, nothing in its directory is built.
|
||||
|
||||
### Role ops struct
|
||||
|
||||
The role is defined by `struct lws_role_ops` in `lib/roles/private-lib-roles.h`,
|
||||
each role instantiates one of these and fills in the appropriate ops
|
||||
callbacks to perform its job. By convention that lives in
|
||||
`./lib/roles/**role name**/ops-**role_name**.c`.
|
||||
|
||||
### Private role declarations
|
||||
|
||||
Truly private declarations for the role can go in the role directory as you like.
|
||||
However when the declarations must be accessible to other things in lws build, eg,
|
||||
the role adds members to `struct lws` when enabled, they should be in the role
|
||||
directory in a file `private-lib-roles-myrole.h`.
|
||||
|
||||
Search for "bring in role private declarations" in `./lib/roles/private-lib-roles.h
|
||||
and add your private role file there following the style used for the other roles,
|
||||
eg,
|
||||
|
||||
```
|
||||
#if defined(LWS_ROLE_WS)
|
||||
#include "roles/ws/private-lib-roles-ws.h"
|
||||
#else
|
||||
#define lwsi_role_ws(wsi) (0)
|
||||
#endif
|
||||
```
|
||||
|
||||
If the role is disabled at cmake, nothing from its private.h should be used anywhere.
|
||||
|
||||
### Integrating role assets to lws
|
||||
|
||||
If your role needs special storage in lws objects, that's no problem. But to keep
|
||||
things sane, there are some rules.
|
||||
|
||||
- declare a "container struct" in your private.h for everything, eg, the ws role wants
|
||||
to add storage in lws_vhost for enabled extensions, it declares in its private.h
|
||||
|
||||
```
|
||||
struct lws_vhost_role_ws {
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
const struct lws_extension *extensions;
|
||||
#endif
|
||||
};
|
||||
```
|
||||
|
||||
- add your role content in one place in the lws struct, protected by `#if defined(LWS_ROLE_**role name**)`,
|
||||
eg, again for LWS_ROLE_WS
|
||||
|
||||
```
|
||||
struct lws_vhost {
|
||||
|
||||
...
|
||||
|
||||
#if defined(LWS_ROLE_WS)
|
||||
struct lws_vhost_role_ws ws;
|
||||
#endif
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
### Adding to lws available roles list
|
||||
|
||||
Edit the NULL-terminated array `available_roles` at the top of `./lib/core/context.c` to include
|
||||
a pointer to your new role's ops struct, following the style already there.
|
||||
|
||||
```
|
||||
const struct lws_role_ops * available_roles[] = {
|
||||
#if defined(LWS_ROLE_H2)
|
||||
&role_ops_h2,
|
||||
#endif
|
||||
...
|
||||
```
|
||||
|
||||
This makes lws aware that your role exists, and it can auto-generate some things like
|
||||
ALPN lists, and call your role ops callbacks for things like hooking vhost creation.
|
||||
|
||||
### Enabling role adoption
|
||||
|
||||
The primary way wsi get bound to a specific role is via the lws adoption api
|
||||
`lws_adopt_descriptor_vhost()`. Add flags as necessary in `./include/libwebsockets/lws-adopt.h`
|
||||
`enum lws_adoption_type` and follow the existing code in `lws_adopt_descriptor_vhost()`
|
||||
to bind a wsi with suitable flags to your role ops.
|
||||
|
||||
### Implementation of the role
|
||||
|
||||
After that plumbing-in is completed, the role ops you declare are "live" on a wsi
|
||||
bound to them via the adoption api.
|
||||
|
||||
The core support for wsis in lws has some generic concepts
|
||||
|
||||
- the wsi holds a pointer member `role_ops` that indicates which role ops the
|
||||
wsi is bound to
|
||||
|
||||
- the wsi holds a generic uint32 `wsistate` that contains role flags and wsi state
|
||||
|
||||
- role flags are provided (LWSIFR_CLIENT, LWSIFR_SERVER) to differentiate between
|
||||
client and server connections inside a wsi, along with helpers `lwsi_role_client(wsi)`
|
||||
and `lwsi_role_server(wsi)`.
|
||||
|
||||
- lws provides around 30 generic states for the wsi starting from 'unconnected' through
|
||||
various proxy or tunnel states, to 'established', and then various states shutting
|
||||
down until 'dead socket'. The states have testable flags and helpers to discover if
|
||||
the wsi state is before establishment `lwsi_state_est(wsi)` and if in the state it is
|
||||
in, it can handle pollout `lwsi_state_can_handle_POLLOUT(wsi)`.
|
||||
|
||||
- You set the initial binding, role flags and state using `lws_role_transition()`. Afterwards
|
||||
you can adjust the state using `lwsi_set_state()`.
|
||||
|
||||
### Role ops compression
|
||||
|
||||
Since the role ops struct is typically only sparsely filled, rather than have 20 function
|
||||
pointers most of which may be NULL, there is a separate array of a union of function
|
||||
pointers that is just long enough for functions that exist in the role, and a nybble index
|
||||
table with a nybble for each possible op, either 0 indicating that the operation is not
|
||||
provided in this role, or 1 - 15 indicating the position of the function pointer in the
|
||||
array.
|
||||
|
||||
42
Kinc/Sources/kinc/libs/roles/cgi/CMakeLists.txt
Normal file
42
Kinc/Sources/kinc/libs/roles/cgi/CMakeLists.txt
Normal file
@ -0,0 +1,42 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
list(APPEND SOURCES
|
||||
roles/cgi/cgi-server.c
|
||||
roles/cgi/ops-cgi.c)
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
1110
Kinc/Sources/kinc/libs/roles/cgi/cgi-server.c
Normal file
1110
Kinc/Sources/kinc/libs/roles/cgi/cgi-server.c
Normal file
File diff suppressed because it is too large
Load Diff
182
Kinc/Sources/kinc/libs/roles/cgi/ops-cgi.c
Normal file
182
Kinc/Sources/kinc/libs/roles/cgi/ops-cgi.c
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_cgi(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
struct lws_cgi_args args;
|
||||
|
||||
assert(wsi->role_ops == &role_ops_cgi);
|
||||
|
||||
if (wsi->lsp_channel >= LWS_STDOUT &&
|
||||
!(pollfd->revents & pollfd->events & LWS_POLLIN))
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
|
||||
if (wsi->lsp_channel == LWS_STDIN &&
|
||||
!(pollfd->revents & pollfd->events & LWS_POLLOUT))
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
|
||||
if (wsi->lsp_channel == LWS_STDIN &&
|
||||
lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_wsi_info(wsi, "failed at set pollfd");
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
}
|
||||
|
||||
if (!wsi->parent) {
|
||||
lwsl_wsi_debug(wsi, "stdwsi content with parent");
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
if (!wsi->parent->http.cgi) {
|
||||
lwsl_wsi_notice(wsi, "stdwsi content with deleted cgi object");
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
if (!wsi->parent->http.cgi->lsp) {
|
||||
lwsl_wsi_notice(wsi, "stdwsi content with reaped lsp");
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
args.ch = wsi->lsp_channel;
|
||||
args.stdwsi = &wsi->parent->http.cgi->lsp->stdwsi[0];
|
||||
args.hdr_state = (enum lws_cgi_hdr_state)wsi->hdr_state;
|
||||
|
||||
lwsl_wsi_debug(wsi, "CGI LWS_STDOUT %p wsistate 0x%x",
|
||||
wsi->parent, wsi->wsistate);
|
||||
|
||||
if (user_callback_handle_rxflow(wsi->parent->a.protocol->callback,
|
||||
wsi->parent, LWS_CALLBACK_CGI,
|
||||
wsi->parent->user_space,
|
||||
(void *)&args, 0))
|
||||
return 1;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_handle_POLLOUT_cgi(struct lws *wsi)
|
||||
{
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_destroy_role_cgi(struct lws *wsi)
|
||||
{
|
||||
#if defined(LWS_WITH_ZLIB)
|
||||
if (!wsi->http.cgi)
|
||||
return 0;
|
||||
if (!wsi->http.cgi->gzip_init)
|
||||
return 0;
|
||||
|
||||
inflateEnd(&wsi->http.cgi->inflate);
|
||||
wsi->http.cgi->gzip_init = 0;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_cgi_sul_cb(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
struct lws_context_per_thread *pt = lws_container_of(sul,
|
||||
struct lws_context_per_thread, sul_cgi);
|
||||
|
||||
lws_cgi_kill_terminated(pt);
|
||||
|
||||
if (pt->http.cgi_list)
|
||||
lws_sul_schedule(pt->context, (int)(pt - pt->context->pt),
|
||||
&pt->sul_cgi, lws_cgi_sul_cb, 3 * LWS_US_PER_SEC);
|
||||
}
|
||||
|
||||
static int
|
||||
rops_pt_init_destroy_cgi(struct lws_context *context,
|
||||
const struct lws_context_creation_info *info,
|
||||
struct lws_context_per_thread *pt, int destroy)
|
||||
{
|
||||
|
||||
lws_sul_cancel(&pt->sul_cgi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_close_role_cgi(struct lws_context_per_thread *pt, struct lws *wsi)
|
||||
{
|
||||
if (wsi->parent && wsi->parent->http.cgi && wsi->parent->http.cgi->lsp)
|
||||
lws_spawn_stdwsi_closed(wsi->parent->http.cgi->lsp, wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const lws_rops_t rops_table_cgi[] = {
|
||||
/* 1 */ { .pt_init_destroy = rops_pt_init_destroy_cgi },
|
||||
/* 2 */ { .handle_POLLIN = rops_handle_POLLIN_cgi },
|
||||
/* 3 */ { .handle_POLLOUT = rops_handle_POLLOUT_cgi },
|
||||
/* 4 */ { .close_role = rops_close_role_cgi },
|
||||
/* 5 */ { .destroy_role = rops_destroy_role_cgi },
|
||||
};
|
||||
|
||||
const struct lws_role_ops role_ops_cgi = {
|
||||
/* role name */ "cgi",
|
||||
/* alpn id */ NULL,
|
||||
|
||||
/* rops_table */ rops_table_cgi,
|
||||
/* rops_idx */ {
|
||||
/* LWS_ROPS_check_upgrades */
|
||||
/* LWS_ROPS_pt_init_destroy */ 0x01,
|
||||
/* LWS_ROPS_init_vhost */
|
||||
/* LWS_ROPS_destroy_vhost */ 0x00,
|
||||
/* LWS_ROPS_service_flag_pending */
|
||||
/* LWS_ROPS_handle_POLLIN */ 0x02,
|
||||
/* LWS_ROPS_handle_POLLOUT */
|
||||
/* LWS_ROPS_perform_user_POLLOUT */ 0x30,
|
||||
/* LWS_ROPS_callback_on_writable */
|
||||
/* LWS_ROPS_tx_credit */ 0x00,
|
||||
/* LWS_ROPS_write_role_protocol */
|
||||
/* LWS_ROPS_encapsulation_parent */ 0x00,
|
||||
/* LWS_ROPS_alpn_negotiated */
|
||||
/* LWS_ROPS_close_via_role_protocol */ 0x00,
|
||||
/* LWS_ROPS_close_role */
|
||||
/* LWS_ROPS_close_kill_connection */ 0x40,
|
||||
/* LWS_ROPS_destroy_role */
|
||||
/* LWS_ROPS_adoption_bind */ 0x50,
|
||||
/* LWS_ROPS_client_bind */
|
||||
/* LWS_ROPS_issue_keepalive */ 0x00,
|
||||
},
|
||||
|
||||
/* adoption_cb clnt, srv */ { 0, 0 },
|
||||
/* rx_cb clnt, srv */ { 0, 0 },
|
||||
/* writeable cb clnt, srv */ { 0, 0 },
|
||||
/* close cb clnt, srv */ { 0, 0 },
|
||||
/* protocol_bind_cb c,s */ { 0, 0 },
|
||||
/* protocol_unbind_cb c,s */ { 0, 0 },
|
||||
|
||||
/* file_handle */ 0,
|
||||
};
|
||||
93
Kinc/Sources/kinc/libs/roles/cgi/private-lib-roles-cgi.h
Normal file
93
Kinc/Sources/kinc/libs/roles/cgi/private-lib-roles-cgi.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* This is included from private-lib-core.h if LWS_ROLE_WS
|
||||
*/
|
||||
|
||||
#if defined(LWS_WITH_ZLIB)
|
||||
#if defined(LWS_WITH_MINIZ)
|
||||
#include <miniz.h>
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern const struct lws_role_ops role_ops_cgi;
|
||||
|
||||
#define lwsi_role_cgi(wsi) (wsi->role_ops == &role_ops_cgi)
|
||||
|
||||
#define LWS_HTTP_CHUNK_HDR_SIZE 16
|
||||
|
||||
enum {
|
||||
SIGNIFICANT_HDR_CONTENT_LENGTH, /* numeric */
|
||||
SIGNIFICANT_HDR_LOCATION,
|
||||
SIGNIFICANT_HDR_STATUS, /* numeric */
|
||||
SIGNIFICANT_HDR_TRANSFER_ENCODING,
|
||||
SIGNIFICANT_HDR_CONTENT_ENCODING_GZIP,
|
||||
|
||||
SIGNIFICANT_HDR_COUNT
|
||||
};
|
||||
|
||||
struct lws;
|
||||
|
||||
/* wsi who is owns the cgi points to an lws_cgi */
|
||||
|
||||
struct lws_cgi {
|
||||
struct lws_cgi *cgi_list;
|
||||
|
||||
struct lws_spawn_piped *lsp;
|
||||
lws_sorted_usec_list_t sul_grace;
|
||||
|
||||
struct lws *wsi; /* owner */
|
||||
unsigned char *headers_buf;
|
||||
unsigned char *headers_start;
|
||||
unsigned char *headers_pos;
|
||||
unsigned char *headers_dumped;
|
||||
unsigned char *headers_end;
|
||||
|
||||
char summary[128];
|
||||
#if defined(LWS_WITH_ZLIB)
|
||||
z_stream inflate;
|
||||
uint8_t inflate_buf[1024];
|
||||
#endif
|
||||
|
||||
lws_filepos_t post_in_expected;
|
||||
lws_filepos_t content_length;
|
||||
lws_filepos_t content_length_seen;
|
||||
|
||||
pid_t pi;
|
||||
|
||||
int match[SIGNIFICANT_HDR_COUNT];
|
||||
char l[12];
|
||||
int response_code;
|
||||
int lp;
|
||||
|
||||
unsigned char being_closed:1;
|
||||
unsigned char explicitly_chunked:1;
|
||||
unsigned char cgi_transaction_over:1;
|
||||
unsigned char implied_chunked:1;
|
||||
unsigned char gzip_inflate:1;
|
||||
unsigned char gzip_init:1;
|
||||
|
||||
unsigned char chunked_grace;
|
||||
};
|
||||
67
Kinc/Sources/kinc/libs/roles/dbus/CMakeLists.txt
Normal file
67
Kinc/Sources/kinc/libs/roles/dbus/CMakeLists.txt
Normal file
@ -0,0 +1,67 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
list(APPEND SOURCES
|
||||
roles/dbus/dbus.c)
|
||||
|
||||
if (NOT LWS_DBUS_LIB)
|
||||
set(LWS_DBUS_LIB "dbus-1")
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_DBUS1 dbus-1 QUIET)
|
||||
list(APPEND LWS_DBUS_INCLUDE1 ${PC_DBUS1_INCLUDE_DIRS})
|
||||
list(APPEND LWS_DBUS_LIB ${PC_DBUS1_LIBRARIES})
|
||||
list(APPEND LWS_DEPS_LIB_PATHS ${PC_DBUS1_LIBRARY_DIRS})
|
||||
|
||||
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES};${LWS_DBUS_INCLUDE1})
|
||||
|
||||
CHECK_C_SOURCE_COMPILES("#include <dbus/dbus.h>
|
||||
int main(void) {
|
||||
return 0;
|
||||
}" LWS_DBUS_CHECK_OK)
|
||||
|
||||
message("dbus include dir 1: ${LWS_DBUS_INCLUDE1}")
|
||||
if (LWS_DBUS_INCLUDE1)
|
||||
include_directories("${LWS_DBUS_INCLUDE1}")
|
||||
endif()
|
||||
list(APPEND LIB_LIST ${LWS_DBUS_LIB})
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
set(LWS_DBUS_CHECK_OK ${LWS_DBUS_CHECK_OK} PARENT_SCOPE)
|
||||
set(LWS_DEPS_LIB_PATHS ${LWS_DEPS_LIB_PATHS} PARENT_SCOPE)
|
||||
|
||||
83
Kinc/Sources/kinc/libs/roles/dbus/README.md
Normal file
83
Kinc/Sources/kinc/libs/roles/dbus/README.md
Normal file
@ -0,0 +1,83 @@
|
||||
# DBUS Role Support
|
||||
|
||||
## DBUS-related distro packages
|
||||
|
||||
Fedora: dbus-devel
|
||||
Debian / Ubuntu: libdbus-1-dev
|
||||
|
||||
## Enabling for build at cmake
|
||||
|
||||
Fedora example:
|
||||
```
|
||||
$ cmake .. -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2="/usr/lib64/dbus-1.0/include"
|
||||
```
|
||||
|
||||
Ubuntu example:
|
||||
```
|
||||
$ cmake .. -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2="/usr/lib/x86_64-linux-gnu/dbus-1.0/include"
|
||||
```
|
||||
|
||||
Dbus requires two include paths, which you can force by setting `LWS_DBUS_INCLUDE1`
|
||||
and `LWS_DBUS_INCLUDE2`. Although INCLUDE1 is usually guessable, both can be
|
||||
forced to allow cross-build.
|
||||
|
||||
If these are not forced, then lws cmake will try to check some popular places,
|
||||
for `LWS_DBUS_INCLUDE1`, on both Fedora and Debian / Ubuntu, this is
|
||||
`/usr/include/dbus-1.0`... if the directory exists, it is used.
|
||||
|
||||
For `LWS_DBUS_INCLUDE2`, it is the arch-specific dbus header which may be
|
||||
packaged separately than the main dbus headers. On Fedora, this is in
|
||||
`/usr/lib[64]/dbus-1.0/include`... if not given externally, lws cmake will
|
||||
try `/usr/lib64/dbus-1.0/include`. On Debian / Ubuntu, the package installs
|
||||
it in an arch-specific dir like `/usr/lib/x86_64-linux-gnu/dbus-1.0/include`,
|
||||
you should force the path.
|
||||
|
||||
The library path is usually \[lib\] "dbus-1", but this can also be forced if
|
||||
you want to build cross or use a special build, via `LWS_DBUS_LIB`.
|
||||
|
||||
## Building against local dbus build
|
||||
|
||||
If you built your own local dbus and installed it in /usr/local, then
|
||||
this is the incantation to direct lws to use the local version of dbus:
|
||||
|
||||
```
|
||||
cmake .. -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE1="/usr/local/include/dbus-1.0" -DLWS_DBUS_INCLUDE2="/usr/local/lib/dbus-1.0/include" -DLWS_DBUS_LIB="/usr/local/lib/libdbus-1.so"
|
||||
```
|
||||
|
||||
You'll also need to give the loader a helping hand to do what you want if
|
||||
there's a perfectly good dbus lib already in `/usr/lib[64]` using `LD_PRELOAD`
|
||||
like this
|
||||
|
||||
```
|
||||
LD_PRELOAD=/usr/local/lib/libdbus-1.so.3.24.0 myapp
|
||||
```
|
||||
|
||||
## Lws dbus api exports
|
||||
|
||||
Because of the irregular situation with libdbus includes, if lws exports the
|
||||
dbus helpers, which use dbus types, as usual from `#include <libwebsockets.h>`
|
||||
then if lws was compiled with dbus role support it forces all users to take
|
||||
care about the dbus include path mess whether they use dbus themselves or not.
|
||||
|
||||
For that reason, if you need access to the lws dbus apis, you must explicitly
|
||||
include them by
|
||||
|
||||
```
|
||||
#include <libwebsockets/lws-dbus.h>
|
||||
```
|
||||
|
||||
This includes `<dbus/dbus.h>` and so requires the include paths set up. But
|
||||
otherwise non-dbus users that don't include `libwebsockets/lws-dbus.h` don't
|
||||
have to care about it.
|
||||
|
||||
## DBUS and valgrind
|
||||
|
||||
https://cgit.freedesktop.org/dbus/dbus/tree/README.valgrind
|
||||
|
||||
1) One-time 6KiB "Still reachable" caused by abstract unix domain socket + libc
|
||||
`getgrouplist()` via nss... bug since 2004(!)
|
||||
|
||||
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=273051
|
||||
|
||||
|
||||
|
||||
570
Kinc/Sources/kinc/libs/roles/dbus/dbus.c
Normal file
570
Kinc/Sources/kinc/libs/roles/dbus/dbus.c
Normal file
@ -0,0 +1,570 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* This role for wrapping dbus fds in a wsi + role is unusual in that the
|
||||
* wsi it creates and binds to the role do not have control over the related fd
|
||||
* lifecycle. In fact dbus doesn't inform us directly about the lifecycle of
|
||||
* the fds it wants to be managed by the lws event loop.
|
||||
*
|
||||
* What it does tell us is when it wants to wait on POLLOUT and / or POLLIN,
|
||||
* and since it should stop any watchers before close, we take the approach to
|
||||
* create a lightweight "shadow" wsi for any fd from dbus that has a POLLIN or
|
||||
* POLLOUT wait active. When the dbus fd asks to have no wait active, we
|
||||
* destroy the wsi, since this is indistinguishable from dbus close path
|
||||
* behaviour. If it actually stays alive and later asks to wait again, well no
|
||||
* worries we create a new shadow wsi until it looks like it is closing again.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
#include <libwebsockets/lws-dbus.h>
|
||||
|
||||
/*
|
||||
* retreives existing or creates new shadow wsi for fd owned by dbus stuff.
|
||||
*
|
||||
* Requires context + vhost lock
|
||||
*/
|
||||
|
||||
static struct lws *
|
||||
__lws_shadow_wsi(struct lws_dbus_ctx *ctx, DBusWatch *w, int fd, int create_ok)
|
||||
{
|
||||
struct lws *wsi;
|
||||
|
||||
if (fd < 0 || fd >= (int)ctx->vh->context->fd_limit_per_thread) {
|
||||
lwsl_err("%s: fd %d vs fds_count %d\n", __func__, fd,
|
||||
(int)ctx->vh->context->fd_limit_per_thread);
|
||||
assert(0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wsi = wsi_from_fd(ctx->vh->context, fd);
|
||||
if (wsi) {
|
||||
assert(wsi->opaque_parent_data == ctx);
|
||||
|
||||
return wsi;
|
||||
}
|
||||
|
||||
if (!create_ok)
|
||||
return NULL;
|
||||
|
||||
lws_context_assert_lock_held(ctx->vh->context);
|
||||
lws_vhost_assert_lock_held(ctx->vh);
|
||||
|
||||
/* requires context lock */
|
||||
wsi = __lws_wsi_create_with_role(ctx->vh->context, ctx->tsi, NULL,
|
||||
ctx->vh->lc.log_cx);
|
||||
if (wsi == NULL) {
|
||||
lwsl_err("Out of mem\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lwsl_info("%s: creating shadow wsi\n", __func__);
|
||||
|
||||
wsi->desc.sockfd = fd;
|
||||
lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_dbus);
|
||||
wsi->a.protocol = ctx->vh->protocols;
|
||||
wsi->shadow = 1;
|
||||
wsi->opaque_parent_data = ctx;
|
||||
ctx->w[0] = w;
|
||||
|
||||
__lws_lc_tag(ctx->vh->context, &ctx->vh->context->lcg[LWSLCG_WSI],
|
||||
&wsi->lc, "dbus|%s", ctx->vh->name);
|
||||
|
||||
lws_vhost_bind_wsi(ctx->vh, wsi);
|
||||
if (__insert_wsi_socket_into_fds(ctx->vh->context, wsi)) {
|
||||
lwsl_err("inserting wsi socket into fds failed\n");
|
||||
__lws_vhost_unbind_wsi(wsi); /* cx + vh lock */
|
||||
lws_free(wsi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return wsi;
|
||||
}
|
||||
|
||||
/*
|
||||
* Requires cx + vhost lock
|
||||
*/
|
||||
|
||||
static int
|
||||
__lws_shadow_wsi_destroy(struct lws_dbus_ctx *ctx, struct lws *wsi)
|
||||
{
|
||||
lwsl_info("%s: destroying shadow wsi\n", __func__);
|
||||
|
||||
lws_context_assert_lock_held(wsi->a.context);
|
||||
lws_vhost_assert_lock_held(wsi->a.vhost);
|
||||
|
||||
if (__remove_wsi_socket_from_fds(wsi)) {
|
||||
lwsl_err("%s: unable to remove %d from fds\n", __func__,
|
||||
wsi->desc.sockfd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
__lws_vhost_unbind_wsi(wsi);
|
||||
|
||||
lws_free(wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_dispatch_status(DBusConnection *c, DBusDispatchStatus s, void *data)
|
||||
{
|
||||
lwsl_info("%s: new dbus dispatch status: %d\n", __func__, s);
|
||||
}
|
||||
|
||||
/*
|
||||
* These are complicated by the fact libdbus can have two separate DBusWatch
|
||||
* objects for the same fd, to control watching POLLIN and POLLOUT individually.
|
||||
*
|
||||
* However we will actually watch using poll(), where the unit is the fd, and
|
||||
* it has a unified events field with just POLLIN / POLLOUT flags.
|
||||
*
|
||||
* So we have to be prepared for one or two watchers coming in any order.
|
||||
*/
|
||||
|
||||
static dbus_bool_t
|
||||
lws_dbus_add_watch(DBusWatch *w, void *data)
|
||||
{
|
||||
struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
|
||||
struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
|
||||
unsigned int flags = 0, lws_flags = 0;
|
||||
struct lws *wsi;
|
||||
int n;
|
||||
|
||||
lws_context_lock(pt->context, __func__);
|
||||
lws_vhost_lock(ctx->vh);
|
||||
|
||||
wsi = __lws_shadow_wsi(ctx, w, dbus_watch_get_unix_fd(w), 1);
|
||||
lws_vhost_unlock(ctx->vh);
|
||||
if (!wsi) {
|
||||
lws_context_unlock(pt->context);
|
||||
lwsl_err("%s: unable to get wsi\n", __func__);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
|
||||
if (w == ctx->w[n])
|
||||
break;
|
||||
|
||||
if (n == (int)LWS_ARRAY_SIZE(ctx->w))
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
|
||||
if (!ctx->w[n]) {
|
||||
ctx->w[n] = w;
|
||||
break;
|
||||
}
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
|
||||
if (ctx->w[n] && dbus_watch_get_enabled(ctx->w[n]))
|
||||
flags |= dbus_watch_get_flags(ctx->w[n]);
|
||||
|
||||
if (flags & DBUS_WATCH_READABLE)
|
||||
lws_flags |= LWS_POLLIN;
|
||||
if (flags & DBUS_WATCH_WRITABLE)
|
||||
lws_flags |= LWS_POLLOUT;
|
||||
|
||||
lwsl_info("%s: %s: %p, fd %d, data %p, fl %d\n", __func__,
|
||||
lws_wsi_tag(wsi), w, dbus_watch_get_unix_fd(w),
|
||||
data, lws_flags);
|
||||
|
||||
if (lws_flags)
|
||||
/* does its own pt lock */
|
||||
__lws_change_pollfd(wsi, 0, (int)lws_flags);
|
||||
|
||||
lws_context_unlock(pt->context);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* cx + vh lock */
|
||||
static int
|
||||
__check_destroy_shadow_wsi(struct lws_dbus_ctx *ctx, struct lws *wsi)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (!wsi)
|
||||
return 0;
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
|
||||
if (ctx->w[n])
|
||||
return 0;
|
||||
|
||||
__lws_shadow_wsi_destroy(ctx, wsi);
|
||||
|
||||
if (!ctx->conn || !ctx->hup || ctx->timeouts)
|
||||
return 0;
|
||||
|
||||
if (dbus_connection_get_dispatch_status(ctx->conn) ==
|
||||
DBUS_DISPATCH_DATA_REMAINS)
|
||||
return 0;
|
||||
|
||||
if (ctx->cb_closing)
|
||||
ctx->cb_closing(ctx);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
lws_dbus_remove_watch(DBusWatch *w, void *data)
|
||||
{
|
||||
struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
|
||||
struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
|
||||
unsigned int flags = 0, lws_flags = 0;
|
||||
struct lws *wsi;
|
||||
int n;
|
||||
|
||||
lws_context_lock(pt->context, __func__);
|
||||
lws_vhost_lock(ctx->vh);
|
||||
|
||||
wsi = __lws_shadow_wsi(ctx, w, dbus_watch_get_unix_fd(w), 0);
|
||||
lws_vhost_unlock(ctx->vh);
|
||||
if (!wsi)
|
||||
goto bail;
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
|
||||
if (w == ctx->w[n]) {
|
||||
ctx->w[n] = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
|
||||
if (ctx->w[n])
|
||||
flags |= dbus_watch_get_flags(ctx->w[n]);
|
||||
|
||||
if ((~flags) & DBUS_WATCH_READABLE)
|
||||
lws_flags |= LWS_POLLIN;
|
||||
if ((~flags) & DBUS_WATCH_WRITABLE)
|
||||
lws_flags |= LWS_POLLOUT;
|
||||
|
||||
lwsl_info("%s: %p, fd %d, data %p, clearing lws flags %d\n",
|
||||
__func__, w, dbus_watch_get_unix_fd(w),
|
||||
data, lws_flags);
|
||||
|
||||
/* does its own pt lock */
|
||||
__lws_change_pollfd(wsi, (int)lws_flags, 0);
|
||||
|
||||
bail:
|
||||
lws_context_unlock(pt->context);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_dbus_toggle_watch(DBusWatch *w, void *data)
|
||||
{
|
||||
if (dbus_watch_get_enabled(w))
|
||||
lws_dbus_add_watch(w, data);
|
||||
else
|
||||
lws_dbus_remove_watch(w, data);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_dbus_sul_cb(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
struct lws_context_per_thread *pt = lws_container_of(sul,
|
||||
struct lws_context_per_thread, dbus.sul);
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, rdt, nx,
|
||||
lws_dll2_get_head(&pt->dbus.timer_list_owner)) {
|
||||
struct lws_role_dbus_timer *r = lws_container_of(rdt,
|
||||
struct lws_role_dbus_timer, timer_list);
|
||||
|
||||
if (time(NULL) > r->fire) {
|
||||
lwsl_notice("%s: firing timer\n", __func__);
|
||||
dbus_timeout_handle(r->data);
|
||||
}
|
||||
} lws_end_foreach_dll_safe(rdt, nx);
|
||||
|
||||
if (pt->dbus.timer_list_owner.count)
|
||||
lws_sul_schedule(pt->context, pt->tid, &pt->dbus.sul,
|
||||
lws_dbus_sul_cb, 3 * LWS_US_PER_SEC);
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
lws_dbus_add_timeout(DBusTimeout *t, void *data)
|
||||
{
|
||||
struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
|
||||
struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
|
||||
int ms = dbus_timeout_get_interval(t);
|
||||
struct lws_role_dbus_timer *dbt;
|
||||
time_t ti = time(NULL);
|
||||
|
||||
if (!dbus_timeout_get_enabled(t))
|
||||
return TRUE;
|
||||
|
||||
if (ms < 1000)
|
||||
ms = 1000;
|
||||
|
||||
dbt = lws_malloc(sizeof(*dbt), "dbus timer");
|
||||
if (!dbt)
|
||||
return FALSE;
|
||||
|
||||
lwsl_info("%s: adding timeout %dms\n", __func__,
|
||||
dbus_timeout_get_interval(t));
|
||||
|
||||
dbt->data = t;
|
||||
dbt->fire = ti + (ms < 1000);
|
||||
dbt->timer_list.prev = NULL;
|
||||
dbt->timer_list.next = NULL;
|
||||
dbt->timer_list.owner = NULL;
|
||||
lws_dll2_add_head(&dbt->timer_list, &pt->dbus.timer_list_owner);
|
||||
|
||||
if (!pt->dbus.sul.list.owner)
|
||||
lws_sul_schedule(pt->context, pt->tid, &pt->dbus.sul,
|
||||
lws_dbus_sul_cb, 3 * LWS_US_PER_SEC);
|
||||
|
||||
ctx->timeouts++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
lws_dbus_remove_timeout(DBusTimeout *t, void *data)
|
||||
{
|
||||
struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
|
||||
struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
|
||||
|
||||
lwsl_info("%s: t %p, data %p\n", __func__, t, data);
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, rdt, nx,
|
||||
lws_dll2_get_head(&pt->dbus.timer_list_owner)) {
|
||||
struct lws_role_dbus_timer *r = lws_container_of(rdt,
|
||||
struct lws_role_dbus_timer, timer_list);
|
||||
if (t == r->data) {
|
||||
lws_dll2_remove(rdt);
|
||||
lws_free(rdt);
|
||||
ctx->timeouts--;
|
||||
break;
|
||||
}
|
||||
} lws_end_foreach_dll_safe(rdt, nx);
|
||||
|
||||
if (!pt->dbus.timer_list_owner.count)
|
||||
lws_sul_cancel(&pt->dbus.sul);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_dbus_toggle_timeout(DBusTimeout *t, void *data)
|
||||
{
|
||||
if (dbus_timeout_get_enabled(t))
|
||||
lws_dbus_add_timeout(t, data);
|
||||
else
|
||||
lws_dbus_remove_timeout(t, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This sets up a connection along the same lines as
|
||||
* dbus_connection_setup_with_g_main(), but for using the lws event loop.
|
||||
*/
|
||||
|
||||
int
|
||||
lws_dbus_connection_setup(struct lws_dbus_ctx *ctx, DBusConnection *conn,
|
||||
lws_dbus_closing_t cb_closing)
|
||||
{
|
||||
int n;
|
||||
|
||||
ctx->conn = conn;
|
||||
ctx->cb_closing = cb_closing;
|
||||
ctx->hup = 0;
|
||||
ctx->timeouts = 0;
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
|
||||
ctx->w[n] = NULL;
|
||||
|
||||
if (!dbus_connection_set_watch_functions(conn, lws_dbus_add_watch,
|
||||
lws_dbus_remove_watch,
|
||||
lws_dbus_toggle_watch,
|
||||
ctx, NULL)) {
|
||||
lwsl_err("%s: dbus_connection_set_watch_functions fail\n",
|
||||
__func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!dbus_connection_set_timeout_functions(conn,
|
||||
lws_dbus_add_timeout,
|
||||
lws_dbus_remove_timeout,
|
||||
lws_dbus_toggle_timeout,
|
||||
ctx, NULL)) {
|
||||
lwsl_err("%s: dbus_connection_set_timeout_functions fail\n",
|
||||
__func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dbus_connection_set_dispatch_status_function(conn,
|
||||
handle_dispatch_status,
|
||||
ctx, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This wraps dbus_server_listen(), additionally taking care of the event loop
|
||||
* -related setups.
|
||||
*/
|
||||
|
||||
DBusServer *
|
||||
lws_dbus_server_listen(struct lws_dbus_ctx *ctx, const char *ads, DBusError *e,
|
||||
DBusNewConnectionFunction new_conn)
|
||||
{
|
||||
ctx->cb_closing = NULL;
|
||||
ctx->hup = 0;
|
||||
ctx->timeouts = 0;
|
||||
|
||||
ctx->dbs = dbus_server_listen(ads, e);
|
||||
if (!ctx->dbs)
|
||||
return NULL;
|
||||
|
||||
dbus_server_set_new_connection_function(ctx->dbs, new_conn, ctx, NULL);
|
||||
|
||||
if (!dbus_server_set_watch_functions(ctx->dbs, lws_dbus_add_watch,
|
||||
lws_dbus_remove_watch,
|
||||
lws_dbus_toggle_watch,
|
||||
ctx, NULL)) {
|
||||
lwsl_err("%s: dbus_connection_set_watch_functions fail\n",
|
||||
__func__);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (!dbus_server_set_timeout_functions(ctx->dbs, lws_dbus_add_timeout,
|
||||
lws_dbus_remove_timeout,
|
||||
lws_dbus_toggle_timeout,
|
||||
ctx, NULL)) {
|
||||
lwsl_err("%s: dbus_connection_set_timeout_functions fail\n",
|
||||
__func__);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
return ctx->dbs;
|
||||
|
||||
bail:
|
||||
dbus_server_disconnect(ctx->dbs);
|
||||
dbus_server_unref(ctx->dbs);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* There shouldn't be a race here with watcher removal and poll wait, because
|
||||
* everything including the dbus activity is serialized in one event loop.
|
||||
*
|
||||
* If it removes the watcher and we remove the wsi and fd entry before this,
|
||||
* actually we can no longer map the fd to this invalidated wsi pointer to call
|
||||
* this.
|
||||
*/
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_dbus(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
struct lws_dbus_ctx *ctx =
|
||||
(struct lws_dbus_ctx *)wsi->opaque_parent_data;
|
||||
unsigned int flags = 0;
|
||||
int n;
|
||||
|
||||
if (pollfd->revents & LWS_POLLIN)
|
||||
flags |= DBUS_WATCH_READABLE;
|
||||
if (pollfd->revents & LWS_POLLOUT)
|
||||
flags |= DBUS_WATCH_WRITABLE;
|
||||
|
||||
if (pollfd->revents & (LWS_POLLHUP))
|
||||
ctx->hup = 1;
|
||||
|
||||
/*
|
||||
* POLLIN + POLLOUT gets us called here on the corresponding shadow
|
||||
* wsi. wsi->opaque_parent_data is the watcher handle bound to the wsi
|
||||
*/
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
|
||||
if (ctx->w[n] && !dbus_watch_handle(ctx->w[n], flags))
|
||||
lwsl_err("%s: dbus_watch_handle failed\n", __func__);
|
||||
|
||||
if (ctx->conn) {
|
||||
lwsl_info("%s: conn: flags %d\n", __func__, flags);
|
||||
|
||||
while (dbus_connection_get_dispatch_status(ctx->conn) ==
|
||||
DBUS_DISPATCH_DATA_REMAINS)
|
||||
dbus_connection_dispatch(ctx->conn);
|
||||
|
||||
handle_dispatch_status(NULL, DBUS_DISPATCH_DATA_REMAINS, NULL);
|
||||
|
||||
__check_destroy_shadow_wsi(ctx, wsi);
|
||||
} else
|
||||
if (ctx->dbs)
|
||||
/* ??? */
|
||||
lwsl_debug("%s: dbs: %d\n", __func__, flags);
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_pt_init_destroy_dbus(struct lws_context *context,
|
||||
const struct lws_context_creation_info *info,
|
||||
struct lws_context_per_thread *pt, int destroy)
|
||||
{
|
||||
if (destroy)
|
||||
lws_sul_cancel(&pt->dbus.sul);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const lws_rops_t rops_table_dbus[] = {
|
||||
/* 1 */ { .pt_init_destroy = rops_pt_init_destroy_dbus },
|
||||
/* 2 */ { .handle_POLLIN = rops_handle_POLLIN_dbus },
|
||||
};
|
||||
|
||||
const struct lws_role_ops role_ops_dbus = {
|
||||
/* role name */ "dbus",
|
||||
/* alpn id */ NULL,
|
||||
|
||||
/* rops_table */ rops_table_dbus,
|
||||
/* rops_idx */ {
|
||||
/* LWS_ROPS_check_upgrades */
|
||||
/* LWS_ROPS_pt_init_destroy */ 0x01,
|
||||
/* LWS_ROPS_init_vhost */
|
||||
/* LWS_ROPS_destroy_vhost */ 0x00,
|
||||
/* LWS_ROPS_service_flag_pending */
|
||||
/* LWS_ROPS_handle_POLLIN */ 0x02,
|
||||
/* LWS_ROPS_handle_POLLOUT */
|
||||
/* LWS_ROPS_perform_user_POLLOUT */ 0x00,
|
||||
/* LWS_ROPS_callback_on_writable */
|
||||
/* LWS_ROPS_tx_credit */ 0x00,
|
||||
/* LWS_ROPS_write_role_protocol */
|
||||
/* LWS_ROPS_encapsulation_parent */ 0x00,
|
||||
/* LWS_ROPS_alpn_negotiated */
|
||||
/* LWS_ROPS_close_via_role_protocol */ 0x00,
|
||||
/* LWS_ROPS_close_role */
|
||||
/* LWS_ROPS_close_kill_connection */ 0x00,
|
||||
/* LWS_ROPS_destroy_role */
|
||||
/* LWS_ROPS_adoption_bind */ 0x00,
|
||||
/* LWS_ROPS_client_bind */
|
||||
/* LWS_ROPS_issue_keepalive */ 0x00,
|
||||
},
|
||||
|
||||
/* adoption_cb clnt, srv */ { 0, 0 },
|
||||
/* rx_cb clnt, srv */ { 0, 0 },
|
||||
/* writeable cb clnt, srv */ { 0, 0 },
|
||||
/* close cb clnt, srv */ { 0, 0 },
|
||||
/* protocol_bind_cb c,s */ { 0, 0 },
|
||||
/* protocol_unbind_cb c,s */ { 0, 0 },
|
||||
/* file_handle */ 0,
|
||||
};
|
||||
46
Kinc/Sources/kinc/libs/roles/dbus/private-lib-roles-dbus.h
Normal file
46
Kinc/Sources/kinc/libs/roles/dbus/private-lib-roles-dbus.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* This is included from private-lib-core.h if LWS_ROLE_DBUS
|
||||
*/
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
extern const struct lws_role_ops role_ops_dbus;
|
||||
|
||||
#define lwsi_role_dbus(wsi) (wsi->role_ops == &role_ops_dbus)
|
||||
|
||||
struct lws_role_dbus_timer {
|
||||
struct lws_dll2 timer_list;
|
||||
void *data;
|
||||
time_t fire;
|
||||
};
|
||||
|
||||
struct lws_pt_role_dbus {
|
||||
struct lws_dll2_owner timer_list_owner;
|
||||
lws_sorted_usec_list_t sul;
|
||||
};
|
||||
|
||||
struct _lws_dbus_mode_related {
|
||||
DBusConnection *conn;
|
||||
};
|
||||
41
Kinc/Sources/kinc/libs/roles/h1/CMakeLists.txt
Normal file
41
Kinc/Sources/kinc/libs/roles/h1/CMakeLists.txt
Normal file
@ -0,0 +1,41 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
list(APPEND SOURCES
|
||||
roles/h1/ops-h1.c)
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
1184
Kinc/Sources/kinc/libs/roles/h1/ops-h1.c
Normal file
1184
Kinc/Sources/kinc/libs/roles/h1/ops-h1.c
Normal file
File diff suppressed because it is too large
Load Diff
31
Kinc/Sources/kinc/libs/roles/h1/private-lib-roles-h1.h
Normal file
31
Kinc/Sources/kinc/libs/roles/h1/private-lib-roles-h1.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* This is included from private-lib-core.h if LWS_ROLE_H1
|
||||
*
|
||||
* Most of the h1 business is defined in the h1 / h2 common roles/http dir
|
||||
*/
|
||||
int
|
||||
_lws_is_http_method(const char *method);
|
||||
extern const struct lws_role_ops role_ops_h1;
|
||||
#define lwsi_role_h1(wsi) (wsi->role_ops == &role_ops_h1)
|
||||
44
Kinc/Sources/kinc/libs/roles/h2/CMakeLists.txt
Normal file
44
Kinc/Sources/kinc/libs/roles/h2/CMakeLists.txt
Normal file
@ -0,0 +1,44 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
list(APPEND SOURCES
|
||||
roles/h2/http2.c
|
||||
roles/h2/hpack.c
|
||||
roles/h2/ops-h2.c)
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
|
||||
1478
Kinc/Sources/kinc/libs/roles/h2/hpack.c
Normal file
1478
Kinc/Sources/kinc/libs/roles/h2/hpack.c
Normal file
File diff suppressed because it is too large
Load Diff
2921
Kinc/Sources/kinc/libs/roles/h2/http2.c
Normal file
2921
Kinc/Sources/kinc/libs/roles/h2/http2.c
Normal file
File diff suppressed because it is too large
Load Diff
530
Kinc/Sources/kinc/libs/roles/h2/huftable.h
Normal file
530
Kinc/Sources/kinc/libs/roles/h2/huftable.h
Normal file
@ -0,0 +1,530 @@
|
||||
static unsigned char lextable[] = {
|
||||
/* pos 0000: 0 */ /* 0 */ 0x42 /* (to 0x0084 state 98) */,
|
||||
/* 1 */ 0x01 /* (to 0x0002 state 1) */,
|
||||
/* pos 0002: 1 */ /* 0 */ 0x5C /* (to 0x00BA state 151) */,
|
||||
/* 1 */ 0x01 /* (to 0x0004 state 2) */,
|
||||
/* pos 0004: 2 */ /* 0 */ 0x66 /* (to 0x00D0 state 173) */,
|
||||
/* 1 */ 0x01 /* (to 0x0006 state 3) */,
|
||||
/* pos 0006: 3 */ /* 0 */ 0x74 /* (to 0x00EE state 204) */,
|
||||
/* 1 */ 0x01 /* (to 0x0008 state 4) */,
|
||||
/* pos 0008: 4 */ /* 0 */ 0x8C /* (to 0x0120 state 263) */,
|
||||
/* 1 */ 0x01 /* (to 0x000A state 5) */,
|
||||
/* pos 000a: 5 */ /* 0 */ 0x46 /* (to 0x0096 state 113) */,
|
||||
/* 1 */ 0x01 /* (to 0x000C state 6) */,
|
||||
/* pos 000c: 6 */ /* 0 */ 0x75 /* (to 0x00F6 state 211) */,
|
||||
/* 1 */ 0x01 /* (to 0x000E state 7) */,
|
||||
/* pos 000e: 7 */ /* 0 */ 0x40 /* (to 0x008E state 104) */,
|
||||
/* 1 */ 0x01 /* (to 0x0010 state 8) */,
|
||||
/* pos 0010: 8 */ /* 0 */ 0x45 /* (to 0x009A state 116) */,
|
||||
/* 1 */ 0x01 /* (to 0x0012 state 9) */,
|
||||
/* pos 0012: 9 */ /* 0 */ 0x40 /* (to 0x0092 state 108) */,
|
||||
/* 1 */ 0x01 /* (to 0x0014 state 10) */,
|
||||
/* pos 0014: 10 */ /* 0 */ 0x01 /* (to 0x0016 state 11) */,
|
||||
/* 1 */ 0x03 /* (to 0x001A state 14) */,
|
||||
/* pos 0016: 11 */ /* 0 */ 0x01 /* (to 0x0018 state 12) */,
|
||||
/* 1 */ 0x5B /* (to 0x00CC state 166) */,
|
||||
/* pos 0018: 12 */ /* terminal 0 */ 0x00,
|
||||
/* terminal 36 */ 0x24,
|
||||
/* pos 001a: 14 */ /* 0 */ 0x72 /* (to 0x00FE state 220) */,
|
||||
/* 1 */ 0x01 /* (to 0x001C state 15) */,
|
||||
/* pos 001c: 15 */ /* 0 */ 0x72 /* (to 0x0100 state 222) */,
|
||||
/* 1 */ 0x01 /* (to 0x001E state 16) */,
|
||||
/* pos 001e: 16 */ /* 0 */ 0x53 /* (to 0x00C4 state 158) */,
|
||||
/* 1 */ 0x01 /* (to 0x0020 state 17) */,
|
||||
/* pos 0020: 17 */ /* terminal 123 */ 0x7B,
|
||||
/* 1 */ 0x01 /* (to 0x0022 state 18) */,
|
||||
/* pos 0022: 18 */ /* 0 */ 0x6B /* (to 0x00F8 state 216) */,
|
||||
/* 1 */ 0x01 /* (to 0x0024 state 19) */,
|
||||
/* pos 0024: 19 */ /* 0 */ 0x84 /* (to 0x012C state 279) */,
|
||||
/* 1 */ 0x01 /* (to 0x0026 state 20) */,
|
||||
/* pos 0026: 20 */ /* 0 */ 0x01 /* (to 0x0028 state 21) */,
|
||||
/* 1 */ 0x06 /* (to 0x0032 state 27) */,
|
||||
/* pos 0028: 21 */ /* 0 */ 0xB3 /* (to 0x018E state 377) */,
|
||||
/* 1 */ 0x01 /* (to 0x002A state 22) */,
|
||||
/* pos 002a: 22 */ /* 0 */ 0xC3 /* (to 0x01B0 state 414) */,
|
||||
/* 1 */ 0x01 /* (to 0x002C state 23) */,
|
||||
/* pos 002c: 23 */ /* 0 */ 0x01 /* (to 0x002E state 24) */,
|
||||
/* 1 */ 0x8C /* (to 0x0144 state 301) */,
|
||||
/* pos 002e: 24 */ /* 0 */ 0x01 /* (to 0x0030 state 25) */,
|
||||
/* 1 */ 0x8A /* (to 0x0142 state 298) */,
|
||||
/* pos 0030: 25 */ /* terminal 1 */ 0x01,
|
||||
/* terminal 135 */ 0x87,
|
||||
/* pos 0032: 27 */ /* 0 */ 0x8E /* (to 0x014E state 314) */,
|
||||
/* 1 */ 0x01 /* (to 0x0034 state 28) */,
|
||||
/* pos 0034: 28 */ /* 0 */ 0x0F /* (to 0x0052 state 50) */,
|
||||
/* 1 */ 0x01 /* (to 0x0036 state 29) */,
|
||||
/* pos 0036: 29 */ /* 0 */ 0xA4 /* (to 0x017E state 362) */,
|
||||
/* 1 */ 0x01 /* (to 0x0038 state 30) */,
|
||||
/* pos 0038: 30 */ /* 0 */ 0xB7 /* (to 0x01A6 state 403) */,
|
||||
/* 1 */ 0x01 /* (to 0x003A state 31) */,
|
||||
/* pos 003a: 31 */ /* 0 */ 0xC8 /* (to 0x01CA state 440) */,
|
||||
/* 1 */ 0x01 /* (to 0x003C state 32) */,
|
||||
/* pos 003c: 32 */ /* 0 */ 0x01 /* (to 0x003E state 33) */,
|
||||
/* 1 */ 0x0F /* (to 0x005A state 55) */,
|
||||
/* pos 003e: 33 */ /* 0 */ 0x01 /* (to 0x0040 state 34) */,
|
||||
/* 1 */ 0x07 /* (to 0x004C state 46) */,
|
||||
/* pos 0040: 34 */ /* 0 */ 0x01 /* (to 0x0042 state 35) */,
|
||||
/* 1 */ 0x03 /* (to 0x0046 state 39) */,
|
||||
/* pos 0042: 35 */ /* terminal 254 */ 0xFE,
|
||||
/* 1 */ 0x01 /* (to 0x0044 state 36) */,
|
||||
/* pos 0044: 36 */ /* terminal 2 */ 0x02,
|
||||
/* terminal 3 */ 0x03,
|
||||
/* pos 0046: 39 */ /* 0 */ 0x01 /* (to 0x0048 state 40) */,
|
||||
/* 1 */ 0x02 /* (to 0x004A state 43) */,
|
||||
/* pos 0048: 40 */ /* terminal 4 */ 0x04,
|
||||
/* terminal 5 */ 0x05,
|
||||
/* pos 004a: 43 */ /* terminal 6 */ 0x06,
|
||||
/* terminal 7 */ 0x07,
|
||||
/* pos 004c: 46 */ /* 0 */ 0x01 /* (to 0x004E state 47) */,
|
||||
/* 1 */ 0x0E /* (to 0x0068 state 67) */,
|
||||
/* pos 004e: 47 */ /* 0 */ 0x01 /* (to 0x0050 state 48) */,
|
||||
/* 1 */ 0x0C /* (to 0x0066 state 63) */,
|
||||
/* pos 0050: 48 */ /* terminal 8 */ 0x08,
|
||||
/* terminal 11 */ 0x0B,
|
||||
/* pos 0052: 50 */ /* 0 */ 0xA7 /* (to 0x01A0 state 396) */,
|
||||
/* 1 */ 0x01 /* (to 0x0054 state 51) */,
|
||||
/* pos 0054: 51 */ /* 0 */ 0x01 /* (to 0x0056 state 52) */,
|
||||
/* 1 */ 0x7B /* (to 0x014A state 309) */,
|
||||
/* pos 0056: 52 */ /* terminal 239 */ 0xEF,
|
||||
/* 1 */ 0x01 /* (to 0x0058 state 53) */,
|
||||
/* pos 0058: 53 */ /* terminal 9 */ 0x09,
|
||||
/* terminal 142 */ 0x8E,
|
||||
/* pos 005a: 55 */ /* 0 */ 0x0A /* (to 0x006E state 74) */,
|
||||
/* 1 */ 0x01 /* (to 0x005C state 56) */,
|
||||
/* pos 005c: 56 */ /* 0 */ 0x11 /* (to 0x007E state 91) */,
|
||||
/* 1 */ 0x01 /* (to 0x005E state 57) */,
|
||||
/* pos 005e: 57 */ /* 0 */ 0x64 /* (to 0x0126 state 274) */,
|
||||
/* 1 */ 0x01 /* (to 0x0060 state 58) */,
|
||||
/* pos 0060: 58 */ /* terminal 249 */ 0xF9,
|
||||
/* 1 */ 0x01 /* (to 0x0062 state 59) */,
|
||||
/* pos 0062: 59 */ /* 0 */ 0x01 /* (to 0x0064 state 60) */,
|
||||
/* 1 */ 0x0A /* (to 0x0076 state 81) */,
|
||||
/* pos 0064: 60 */ /* terminal 10 */ 0x0A,
|
||||
/* terminal 13 */ 0x0D,
|
||||
/* pos 0066: 63 */ /* terminal 12 */ 0x0C,
|
||||
/* terminal 14 */ 0x0E,
|
||||
/* pos 0068: 67 */ /* 0 */ 0x01 /* (to 0x006A state 68) */,
|
||||
/* 1 */ 0x02 /* (to 0x006C state 71) */,
|
||||
/* pos 006a: 68 */ /* terminal 15 */ 0x0F,
|
||||
/* terminal 16 */ 0x10,
|
||||
/* pos 006c: 71 */ /* terminal 17 */ 0x11,
|
||||
/* terminal 18 */ 0x12,
|
||||
/* pos 006e: 74 */ /* 0 */ 0x01 /* (to 0x0070 state 75) */,
|
||||
/* 1 */ 0x05 /* (to 0x0078 state 84) */,
|
||||
/* pos 0070: 75 */ /* 0 */ 0x01 /* (to 0x0072 state 76) */,
|
||||
/* 1 */ 0x02 /* (to 0x0074 state 79) */,
|
||||
/* pos 0072: 76 */ /* terminal 19 */ 0x13,
|
||||
/* terminal 20 */ 0x14,
|
||||
/* pos 0074: 79 */ /* terminal 21 */ 0x15,
|
||||
/* terminal 23 */ 0x17,
|
||||
/* pos 0076: 81 */ /* terminal 22 */ 0x16,
|
||||
/* terminal 256 */ 0x00,
|
||||
/* pos 0078: 84 */ /* 0 */ 0x01 /* (to 0x007A state 85) */,
|
||||
/* 1 */ 0x02 /* (to 0x007C state 88) */,
|
||||
/* pos 007a: 85 */ /* terminal 24 */ 0x18,
|
||||
/* terminal 25 */ 0x19,
|
||||
/* pos 007c: 88 */ /* terminal 26 */ 0x1A,
|
||||
/* terminal 27 */ 0x1B,
|
||||
/* pos 007e: 91 */ /* 0 */ 0x01 /* (to 0x0080 state 92) */,
|
||||
/* 1 */ 0x02 /* (to 0x0082 state 95) */,
|
||||
/* pos 0080: 92 */ /* terminal 28 */ 0x1C,
|
||||
/* terminal 29 */ 0x1D,
|
||||
/* pos 0082: 95 */ /* terminal 30 */ 0x1E,
|
||||
/* terminal 31 */ 0x1F,
|
||||
/* pos 0084: 98 */ /* 0 */ 0x13 /* (to 0x00AA state 133) */,
|
||||
/* 1 */ 0x01 /* (to 0x0086 state 99) */,
|
||||
/* pos 0086: 99 */ /* 0 */ 0x01 /* (to 0x0088 state 100) */,
|
||||
/* 1 */ 0x0F /* (to 0x00A4 state 129) */,
|
||||
/* pos 0088: 100 */ /* 0 */ 0x4B /* (to 0x011E state 258) */,
|
||||
/* 1 */ 0x01 /* (to 0x008A state 101) */,
|
||||
/* pos 008a: 101 */ /* 0 */ 0x01 /* (to 0x008C state 102) */,
|
||||
/* 1 */ 0x0C /* (to 0x00A2 state 126) */,
|
||||
/* pos 008c: 102 */ /* terminal 32 */ 0x20,
|
||||
/* terminal 37 */ 0x25,
|
||||
/* pos 008e: 104 */ /* 0 */ 0x01 /* (to 0x0090 state 105) */,
|
||||
/* 1 */ 0x08 /* (to 0x009E state 119) */,
|
||||
/* pos 0090: 105 */ /* terminal 33 */ 0x21,
|
||||
/* terminal 34 */ 0x22,
|
||||
/* pos 0092: 108 */ /* terminal 124 */ 0x7C,
|
||||
/* 1 */ 0x01 /* (to 0x0094 state 109) */,
|
||||
/* pos 0094: 109 */ /* terminal 35 */ 0x23,
|
||||
/* terminal 62 */ 0x3E,
|
||||
/* pos 0096: 113 */ /* 0 */ 0x01 /* (to 0x0098 state 114) */,
|
||||
/* 1 */ 0x05 /* (to 0x00A0 state 124) */,
|
||||
/* pos 0098: 114 */ /* terminal 38 */ 0x26,
|
||||
/* terminal 42 */ 0x2A,
|
||||
/* pos 009a: 116 */ /* terminal 63 */ 0x3F,
|
||||
/* 1 */ 0x01 /* (to 0x009C state 117) */,
|
||||
/* pos 009c: 117 */ /* terminal 39 */ 0x27,
|
||||
/* terminal 43 */ 0x2B,
|
||||
/* pos 009e: 119 */ /* terminal 40 */ 0x28,
|
||||
/* terminal 41 */ 0x29,
|
||||
/* pos 00a0: 124 */ /* terminal 44 */ 0x2C,
|
||||
/* terminal 59 */ 0x3B,
|
||||
/* pos 00a2: 126 */ /* terminal 45 */ 0x2D,
|
||||
/* terminal 46 */ 0x2E,
|
||||
/* pos 00a4: 129 */ /* 0 */ 0x01 /* (to 0x00A6 state 130) */,
|
||||
/* 1 */ 0x08 /* (to 0x00B4 state 144) */,
|
||||
/* pos 00a6: 130 */ /* 0 */ 0x01 /* (to 0x00A8 state 131) */,
|
||||
/* 1 */ 0x06 /* (to 0x00B2 state 141) */,
|
||||
/* pos 00a8: 131 */ /* terminal 47 */ 0x2F,
|
||||
/* terminal 51 */ 0x33,
|
||||
/* pos 00aa: 133 */ /* 0 */ 0x01 /* (to 0x00AC state 134) */,
|
||||
/* 1 */ 0x2D /* (to 0x0104 state 229) */,
|
||||
/* pos 00ac: 134 */ /* 0 */ 0x01 /* (to 0x00AE state 135) */,
|
||||
/* 1 */ 0x02 /* (to 0x00B0 state 138) */,
|
||||
/* pos 00ae: 135 */ /* terminal 48 */ 0x30,
|
||||
/* terminal 49 */ 0x31,
|
||||
/* pos 00b0: 138 */ /* terminal 50 */ 0x32,
|
||||
/* terminal 97 */ 0x61,
|
||||
/* pos 00b2: 141 */ /* terminal 52 */ 0x34,
|
||||
/* terminal 53 */ 0x35,
|
||||
/* pos 00b4: 144 */ /* 0 */ 0x01 /* (to 0x00B6 state 145) */,
|
||||
/* 1 */ 0x02 /* (to 0x00B8 state 148) */,
|
||||
/* pos 00b6: 145 */ /* terminal 54 */ 0x36,
|
||||
/* terminal 55 */ 0x37,
|
||||
/* pos 00b8: 148 */ /* terminal 56 */ 0x38,
|
||||
/* terminal 57 */ 0x39,
|
||||
/* pos 00ba: 151 */ /* 0 */ 0x06 /* (to 0x00C6 state 160) */,
|
||||
/* 1 */ 0x01 /* (to 0x00BC state 152) */,
|
||||
/* pos 00bc: 152 */ /* 0 */ 0x2C /* (to 0x0114 state 246) */,
|
||||
/* 1 */ 0x01 /* (to 0x00BE state 153) */,
|
||||
/* pos 00be: 153 */ /* 0 */ 0x2F /* (to 0x011C state 256) */,
|
||||
/* 1 */ 0x01 /* (to 0x00C0 state 154) */,
|
||||
/* pos 00c0: 154 */ /* 0 */ 0x01 /* (to 0x00C2 state 155) */,
|
||||
/* 1 */ 0x07 /* (to 0x00CE state 170) */,
|
||||
/* pos 00c2: 155 */ /* terminal 58 */ 0x3A,
|
||||
/* terminal 66 */ 0x42,
|
||||
/* pos 00c4: 158 */ /* terminal 60 */ 0x3C,
|
||||
/* terminal 96 */ 0x60,
|
||||
/* pos 00c6: 160 */ /* 0 */ 0x01 /* (to 0x00C8 state 161) */,
|
||||
/* 1 */ 0x21 /* (to 0x0108 state 232) */,
|
||||
/* pos 00c8: 161 */ /* 0 */ 0x01 /* (to 0x00CA state 162) */,
|
||||
/* 1 */ 0x1D /* (to 0x0102 state 224) */,
|
||||
/* pos 00ca: 162 */ /* terminal 61 */ 0x3D,
|
||||
/* terminal 65 */ 0x41,
|
||||
/* pos 00cc: 166 */ /* terminal 64 */ 0x40,
|
||||
/* terminal 91 */ 0x5B,
|
||||
/* pos 00ce: 170 */ /* terminal 67 */ 0x43,
|
||||
/* terminal 68 */ 0x44,
|
||||
/* pos 00d0: 173 */ /* 0 */ 0x01 /* (to 0x00D2 state 174) */,
|
||||
/* 1 */ 0x08 /* (to 0x00E0 state 189) */,
|
||||
/* pos 00d2: 174 */ /* 0 */ 0x01 /* (to 0x00D4 state 175) */,
|
||||
/* 1 */ 0x04 /* (to 0x00DA state 182) */,
|
||||
/* pos 00d4: 175 */ /* 0 */ 0x01 /* (to 0x00D6 state 176) */,
|
||||
/* 1 */ 0x02 /* (to 0x00D8 state 179) */,
|
||||
/* pos 00d6: 176 */ /* terminal 69 */ 0x45,
|
||||
/* terminal 70 */ 0x46,
|
||||
/* pos 00d8: 179 */ /* terminal 71 */ 0x47,
|
||||
/* terminal 72 */ 0x48,
|
||||
/* pos 00da: 182 */ /* 0 */ 0x01 /* (to 0x00DC state 183) */,
|
||||
/* 1 */ 0x02 /* (to 0x00DE state 186) */,
|
||||
/* pos 00dc: 183 */ /* terminal 73 */ 0x49,
|
||||
/* terminal 74 */ 0x4A,
|
||||
/* pos 00de: 186 */ /* terminal 75 */ 0x4B,
|
||||
/* terminal 76 */ 0x4C,
|
||||
/* pos 00e0: 189 */ /* 0 */ 0x01 /* (to 0x00E2 state 190) */,
|
||||
/* 1 */ 0x04 /* (to 0x00E8 state 197) */,
|
||||
/* pos 00e2: 190 */ /* 0 */ 0x01 /* (to 0x00E4 state 191) */,
|
||||
/* 1 */ 0x02 /* (to 0x00E6 state 194) */,
|
||||
/* pos 00e4: 191 */ /* terminal 77 */ 0x4D,
|
||||
/* terminal 78 */ 0x4E,
|
||||
/* pos 00e6: 194 */ /* terminal 79 */ 0x4F,
|
||||
/* terminal 80 */ 0x50,
|
||||
/* pos 00e8: 197 */ /* 0 */ 0x01 /* (to 0x00EA state 198) */,
|
||||
/* 1 */ 0x02 /* (to 0x00EC state 201) */,
|
||||
/* pos 00ea: 198 */ /* terminal 81 */ 0x51,
|
||||
/* terminal 82 */ 0x52,
|
||||
/* pos 00ec: 201 */ /* terminal 83 */ 0x53,
|
||||
/* terminal 84 */ 0x54,
|
||||
/* pos 00ee: 204 */ /* 0 */ 0x01 /* (to 0x00F0 state 205) */,
|
||||
/* 1 */ 0x11 /* (to 0x0110 state 242) */,
|
||||
/* pos 00f0: 205 */ /* 0 */ 0x01 /* (to 0x00F2 state 206) */,
|
||||
/* 1 */ 0x02 /* (to 0x00F4 state 209) */,
|
||||
/* pos 00f2: 206 */ /* terminal 85 */ 0x55,
|
||||
/* terminal 86 */ 0x56,
|
||||
/* pos 00f4: 209 */ /* terminal 87 */ 0x57,
|
||||
/* terminal 89 */ 0x59,
|
||||
/* pos 00f6: 211 */ /* terminal 88 */ 0x58,
|
||||
/* terminal 90 */ 0x5A,
|
||||
/* pos 00f8: 216 */ /* 0 */ 0x01 /* (to 0x00FA state 217) */,
|
||||
/* 1 */ 0x1F /* (to 0x0136 state 286) */,
|
||||
/* pos 00fa: 217 */ /* 0 */ 0x01 /* (to 0x00FC state 218) */,
|
||||
/* 1 */ 0x17 /* (to 0x0128 state 276) */,
|
||||
/* pos 00fc: 218 */ /* terminal 92 */ 0x5C,
|
||||
/* terminal 195 */ 0xC3,
|
||||
/* pos 00fe: 220 */ /* terminal 93 */ 0x5D,
|
||||
/* terminal 126 */ 0x7E,
|
||||
/* pos 0100: 222 */ /* terminal 94 */ 0x5E,
|
||||
/* terminal 125 */ 0x7D,
|
||||
/* pos 0102: 224 */ /* terminal 95 */ 0x5F,
|
||||
/* terminal 98 */ 0x62,
|
||||
/* pos 0104: 229 */ /* 0 */ 0x01 /* (to 0x0106 state 230) */,
|
||||
/* 1 */ 0x05 /* (to 0x010E state 240) */,
|
||||
/* pos 0106: 230 */ /* terminal 99 */ 0x63,
|
||||
/* terminal 101 */ 0x65,
|
||||
/* pos 0108: 232 */ /* 0 */ 0x01 /* (to 0x010A state 233) */,
|
||||
/* 1 */ 0x02 /* (to 0x010C state 237) */,
|
||||
/* pos 010a: 233 */ /* terminal 100 */ 0x64,
|
||||
/* terminal 102 */ 0x66,
|
||||
/* pos 010c: 237 */ /* terminal 103 */ 0x67,
|
||||
/* terminal 104 */ 0x68,
|
||||
/* pos 010e: 240 */ /* terminal 105 */ 0x69,
|
||||
/* terminal 111 */ 0x6F,
|
||||
/* pos 0110: 242 */ /* 0 */ 0x01 /* (to 0x0112 state 243) */,
|
||||
/* 1 */ 0x05 /* (to 0x011A state 254) */,
|
||||
/* pos 0112: 243 */ /* terminal 106 */ 0x6A,
|
||||
/* terminal 107 */ 0x6B,
|
||||
/* pos 0114: 246 */ /* 0 */ 0x01 /* (to 0x0116 state 247) */,
|
||||
/* 1 */ 0x02 /* (to 0x0118 state 250) */,
|
||||
/* pos 0116: 247 */ /* terminal 108 */ 0x6C,
|
||||
/* terminal 109 */ 0x6D,
|
||||
/* pos 0118: 250 */ /* terminal 110 */ 0x6E,
|
||||
/* terminal 112 */ 0x70,
|
||||
/* pos 011a: 254 */ /* terminal 113 */ 0x71,
|
||||
/* terminal 118 */ 0x76,
|
||||
/* pos 011c: 256 */ /* terminal 114 */ 0x72,
|
||||
/* terminal 117 */ 0x75,
|
||||
/* pos 011e: 258 */ /* terminal 115 */ 0x73,
|
||||
/* terminal 116 */ 0x74,
|
||||
/* pos 0120: 263 */ /* 0 */ 0x01 /* (to 0x0122 state 264) */,
|
||||
/* 1 */ 0x02 /* (to 0x0124 state 267) */,
|
||||
/* pos 0122: 264 */ /* terminal 119 */ 0x77,
|
||||
/* terminal 120 */ 0x78,
|
||||
/* pos 0124: 267 */ /* terminal 121 */ 0x79,
|
||||
/* terminal 122 */ 0x7A,
|
||||
/* pos 0126: 274 */ /* terminal 127 */ 0x7F,
|
||||
/* terminal 220 */ 0xDC,
|
||||
/* pos 0128: 276 */ /* terminal 208 */ 0xD0,
|
||||
/* 1 */ 0x01 /* (to 0x012A state 277) */,
|
||||
/* pos 012a: 277 */ /* terminal 128 */ 0x80,
|
||||
/* terminal 130 */ 0x82,
|
||||
/* pos 012c: 279 */ /* 0 */ 0x2E /* (to 0x0188 state 372) */,
|
||||
/* 1 */ 0x01 /* (to 0x012E state 280) */,
|
||||
/* pos 012e: 280 */ /* 0 */ 0x01 /* (to 0x0130 state 281) */,
|
||||
/* 1 */ 0x1B /* (to 0x0164 state 332) */,
|
||||
/* pos 0130: 281 */ /* 0 */ 0x01 /* (to 0x0132 state 282) */,
|
||||
/* 1 */ 0x06 /* (to 0x013C state 291) */,
|
||||
/* pos 0132: 282 */ /* terminal 230 */ 0xE6,
|
||||
/* 1 */ 0x01 /* (to 0x0134 state 283) */,
|
||||
/* pos 0134: 283 */ /* terminal 129 */ 0x81,
|
||||
/* terminal 132 */ 0x84,
|
||||
/* pos 0136: 286 */ /* 0 */ 0x01 /* (to 0x0138 state 287) */,
|
||||
/* 1 */ 0x14 /* (to 0x015E state 328) */,
|
||||
/* pos 0138: 287 */ /* 0 */ 0x01 /* (to 0x013A state 288) */,
|
||||
/* 1 */ 0x30 /* (to 0x0198 state 388) */,
|
||||
/* pos 013a: 288 */ /* terminal 131 */ 0x83,
|
||||
/* terminal 162 */ 0xA2,
|
||||
/* pos 013c: 291 */ /* 0 */ 0x01 /* (to 0x013E state 292) */,
|
||||
/* 1 */ 0x02 /* (to 0x0140 state 296) */,
|
||||
/* pos 013e: 292 */ /* terminal 133 */ 0x85,
|
||||
/* terminal 134 */ 0x86,
|
||||
/* pos 0140: 296 */ /* terminal 136 */ 0x88,
|
||||
/* terminal 146 */ 0x92,
|
||||
/* pos 0142: 298 */ /* terminal 137 */ 0x89,
|
||||
/* terminal 138 */ 0x8A,
|
||||
/* pos 0144: 301 */ /* 0 */ 0x01 /* (to 0x0146 state 302) */,
|
||||
/* 1 */ 0x02 /* (to 0x0148 state 305) */,
|
||||
/* pos 0146: 302 */ /* terminal 139 */ 0x8B,
|
||||
/* terminal 140 */ 0x8C,
|
||||
/* pos 0148: 305 */ /* terminal 141 */ 0x8D,
|
||||
/* terminal 143 */ 0x8F,
|
||||
/* pos 014a: 309 */ /* 0 */ 0x01 /* (to 0x014C state 310) */,
|
||||
/* 1 */ 0x06 /* (to 0x0156 state 319) */,
|
||||
/* pos 014c: 310 */ /* terminal 144 */ 0x90,
|
||||
/* terminal 145 */ 0x91,
|
||||
/* pos 014e: 314 */ /* 0 */ 0x01 /* (to 0x0150 state 315) */,
|
||||
/* 1 */ 0x12 /* (to 0x0172 state 350) */,
|
||||
/* pos 0150: 315 */ /* 0 */ 0x01 /* (to 0x0152 state 316) */,
|
||||
/* 1 */ 0x05 /* (to 0x015A state 325) */,
|
||||
/* pos 0152: 316 */ /* 0 */ 0x01 /* (to 0x0154 state 317) */,
|
||||
/* 1 */ 0x03 /* (to 0x0158 state 322) */,
|
||||
/* pos 0154: 317 */ /* terminal 147 */ 0x93,
|
||||
/* terminal 149 */ 0x95,
|
||||
/* pos 0156: 319 */ /* terminal 148 */ 0x94,
|
||||
/* terminal 159 */ 0x9F,
|
||||
/* pos 0158: 322 */ /* terminal 150 */ 0x96,
|
||||
/* terminal 151 */ 0x97,
|
||||
/* pos 015a: 325 */ /* 0 */ 0x01 /* (to 0x015C state 326) */,
|
||||
/* 1 */ 0x08 /* (to 0x016A state 338) */,
|
||||
/* pos 015c: 326 */ /* terminal 152 */ 0x98,
|
||||
/* terminal 155 */ 0x9B,
|
||||
/* pos 015e: 328 */ /* 0 */ 0x42 /* (to 0x01E2 state 465) */,
|
||||
/* 1 */ 0x01 /* (to 0x0160 state 329) */,
|
||||
/* pos 0160: 329 */ /* 0 */ 0x01 /* (to 0x0162 state 330) */,
|
||||
/* 1 */ 0x0C /* (to 0x0178 state 355) */,
|
||||
/* pos 0162: 330 */ /* terminal 153 */ 0x99,
|
||||
/* terminal 161 */ 0xA1,
|
||||
/* pos 0164: 332 */ /* 0 */ 0x01 /* (to 0x0166 state 333) */,
|
||||
/* 1 */ 0x05 /* (to 0x016E state 347) */,
|
||||
/* pos 0166: 333 */ /* 0 */ 0x01 /* (to 0x0168 state 334) */,
|
||||
/* 1 */ 0x03 /* (to 0x016C state 342) */,
|
||||
/* pos 0168: 334 */ /* terminal 154 */ 0x9A,
|
||||
/* terminal 156 */ 0x9C,
|
||||
/* pos 016a: 338 */ /* terminal 157 */ 0x9D,
|
||||
/* terminal 158 */ 0x9E,
|
||||
/* pos 016c: 342 */ /* terminal 160 */ 0xA0,
|
||||
/* terminal 163 */ 0xA3,
|
||||
/* pos 016e: 347 */ /* 0 */ 0x01 /* (to 0x0170 state 348) */,
|
||||
/* 1 */ 0x07 /* (to 0x017C state 360) */,
|
||||
/* pos 0170: 348 */ /* terminal 164 */ 0xA4,
|
||||
/* terminal 169 */ 0xA9,
|
||||
/* pos 0172: 350 */ /* 0 */ 0x01 /* (to 0x0174 state 351) */,
|
||||
/* 1 */ 0x09 /* (to 0x0184 state 369) */,
|
||||
/* pos 0174: 351 */ /* 0 */ 0x01 /* (to 0x0176 state 352) */,
|
||||
/* 1 */ 0x03 /* (to 0x017A state 357) */,
|
||||
/* pos 0176: 352 */ /* terminal 165 */ 0xA5,
|
||||
/* terminal 166 */ 0xA6,
|
||||
/* pos 0178: 355 */ /* terminal 167 */ 0xA7,
|
||||
/* terminal 172 */ 0xAC,
|
||||
/* pos 017a: 357 */ /* terminal 168 */ 0xA8,
|
||||
/* terminal 174 */ 0xAE,
|
||||
/* pos 017c: 360 */ /* terminal 170 */ 0xAA,
|
||||
/* terminal 173 */ 0xAD,
|
||||
/* pos 017e: 362 */ /* 0 */ 0x01 /* (to 0x0180 state 363) */,
|
||||
/* 1 */ 0x1B /* (to 0x01B4 state 417) */,
|
||||
/* pos 0180: 363 */ /* 0 */ 0x01 /* (to 0x0182 state 364) */,
|
||||
/* 1 */ 0x2A /* (to 0x01D4 state 449) */,
|
||||
/* pos 0182: 364 */ /* terminal 171 */ 0xAB,
|
||||
/* terminal 206 */ 0xCE,
|
||||
/* pos 0184: 369 */ /* 0 */ 0x01 /* (to 0x0186 state 370) */,
|
||||
/* 1 */ 0x09 /* (to 0x0196 state 385) */,
|
||||
/* pos 0186: 370 */ /* terminal 175 */ 0xAF,
|
||||
/* terminal 180 */ 0xB4,
|
||||
/* pos 0188: 372 */ /* 0 */ 0x01 /* (to 0x018A state 373) */,
|
||||
/* 1 */ 0x27 /* (to 0x01D6 state 451) */,
|
||||
/* pos 018a: 373 */ /* 0 */ 0x01 /* (to 0x018C state 374) */,
|
||||
/* 1 */ 0x05 /* (to 0x0194 state 381) */,
|
||||
/* pos 018c: 374 */ /* terminal 176 */ 0xB0,
|
||||
/* terminal 177 */ 0xB1,
|
||||
/* pos 018e: 377 */ /* 0 */ 0x01 /* (to 0x0190 state 378) */,
|
||||
/* 1 */ 0x07 /* (to 0x019C state 393) */,
|
||||
/* pos 0190: 378 */ /* 0 */ 0x01 /* (to 0x0192 state 379) */,
|
||||
/* 1 */ 0x05 /* (to 0x019A state 390) */,
|
||||
/* pos 0192: 379 */ /* terminal 178 */ 0xB2,
|
||||
/* terminal 181 */ 0xB5,
|
||||
/* pos 0194: 381 */ /* terminal 179 */ 0xB3,
|
||||
/* terminal 209 */ 0xD1,
|
||||
/* pos 0196: 385 */ /* terminal 182 */ 0xB6,
|
||||
/* terminal 183 */ 0xB7,
|
||||
/* pos 0198: 388 */ /* terminal 184 */ 0xB8,
|
||||
/* terminal 194 */ 0xC2,
|
||||
/* pos 019a: 390 */ /* terminal 185 */ 0xB9,
|
||||
/* terminal 186 */ 0xBA,
|
||||
/* pos 019c: 393 */ /* 0 */ 0x01 /* (to 0x019E state 394) */,
|
||||
/* 1 */ 0x04 /* (to 0x01A4 state 400) */,
|
||||
/* pos 019e: 394 */ /* terminal 187 */ 0xBB,
|
||||
/* terminal 189 */ 0xBD,
|
||||
/* pos 01a0: 396 */ /* 0 */ 0x01 /* (to 0x01A2 state 397) */,
|
||||
/* 1 */ 0x07 /* (to 0x01AE state 412) */,
|
||||
/* pos 01a2: 397 */ /* terminal 188 */ 0xBC,
|
||||
/* terminal 191 */ 0xBF,
|
||||
/* pos 01a4: 400 */ /* terminal 190 */ 0xBE,
|
||||
/* terminal 196 */ 0xC4,
|
||||
/* pos 01a6: 403 */ /* 0 */ 0x01 /* (to 0x01A8 state 404) */,
|
||||
/* 1 */ 0x0D /* (to 0x01C0 state 427) */,
|
||||
/* pos 01a8: 404 */ /* 0 */ 0x01 /* (to 0x01AA state 405) */,
|
||||
/* 1 */ 0x0A /* (to 0x01BC state 424) */,
|
||||
/* pos 01aa: 405 */ /* 0 */ 0x01 /* (to 0x01AC state 406) */,
|
||||
/* 1 */ 0x08 /* (to 0x01BA state 421) */,
|
||||
/* pos 01ac: 406 */ /* terminal 192 */ 0xC0,
|
||||
/* terminal 193 */ 0xC1,
|
||||
/* pos 01ae: 412 */ /* terminal 197 */ 0xC5,
|
||||
/* terminal 231 */ 0xE7,
|
||||
/* pos 01b0: 414 */ /* 0 */ 0x01 /* (to 0x01B2 state 415) */,
|
||||
/* 1 */ 0x1B /* (to 0x01E6 state 475) */,
|
||||
/* pos 01b2: 415 */ /* terminal 198 */ 0xC6,
|
||||
/* terminal 228 */ 0xE4,
|
||||
/* pos 01b4: 417 */ /* 0 */ 0x1B /* (to 0x01EA state 481) */,
|
||||
/* 1 */ 0x01 /* (to 0x01B6 state 418) */,
|
||||
/* pos 01b6: 418 */ /* 0 */ 0x01 /* (to 0x01B8 state 419) */,
|
||||
/* 1 */ 0x19 /* (to 0x01E8 state 478) */,
|
||||
/* pos 01b8: 419 */ /* terminal 199 */ 0xC7,
|
||||
/* terminal 207 */ 0xCF,
|
||||
/* pos 01ba: 421 */ /* terminal 200 */ 0xC8,
|
||||
/* terminal 201 */ 0xC9,
|
||||
/* pos 01bc: 424 */ /* 0 */ 0x01 /* (to 0x01BE state 425) */,
|
||||
/* 1 */ 0x06 /* (to 0x01C8 state 438) */,
|
||||
/* pos 01be: 425 */ /* terminal 202 */ 0xCA,
|
||||
/* terminal 205 */ 0xCD,
|
||||
/* pos 01c0: 427 */ /* 0 */ 0x0D /* (to 0x01DA state 455) */,
|
||||
/* 1 */ 0x01 /* (to 0x01C2 state 428) */,
|
||||
/* pos 01c2: 428 */ /* 0 */ 0x17 /* (to 0x01F0 state 490) */,
|
||||
/* 1 */ 0x01 /* (to 0x01C4 state 429) */,
|
||||
/* pos 01c4: 429 */ /* terminal 255 */ 0xFF,
|
||||
/* 1 */ 0x01 /* (to 0x01C6 state 430) */,
|
||||
/* pos 01c6: 430 */ /* terminal 203 */ 0xCB,
|
||||
/* terminal 204 */ 0xCC,
|
||||
/* pos 01c8: 438 */ /* terminal 210 */ 0xD2,
|
||||
/* terminal 213 */ 0xD5,
|
||||
/* pos 01ca: 440 */ /* 0 */ 0x01 /* (to 0x01CC state 441) */,
|
||||
/* 1 */ 0x14 /* (to 0x01F2 state 494) */,
|
||||
/* pos 01cc: 441 */ /* 0 */ 0x01 /* (to 0x01CE state 442) */,
|
||||
/* 1 */ 0x09 /* (to 0x01DE state 461) */,
|
||||
/* pos 01ce: 442 */ /* 0 */ 0x01 /* (to 0x01D0 state 443) */,
|
||||
/* 1 */ 0x02 /* (to 0x01D2 state 447) */,
|
||||
/* pos 01d0: 443 */ /* terminal 211 */ 0xD3,
|
||||
/* terminal 212 */ 0xD4,
|
||||
/* pos 01d2: 447 */ /* terminal 214 */ 0xD6,
|
||||
/* terminal 221 */ 0xDD,
|
||||
/* pos 01d4: 449 */ /* terminal 215 */ 0xD7,
|
||||
/* terminal 225 */ 0xE1,
|
||||
/* pos 01d6: 451 */ /* 0 */ 0x01 /* (to 0x01D8 state 452) */,
|
||||
/* 1 */ 0x07 /* (to 0x01E4 state 469) */,
|
||||
/* pos 01d8: 452 */ /* terminal 216 */ 0xD8,
|
||||
/* terminal 217 */ 0xD9,
|
||||
/* pos 01da: 455 */ /* 0 */ 0x01 /* (to 0x01DC state 456) */,
|
||||
/* 1 */ 0x09 /* (to 0x01EC state 484) */,
|
||||
/* pos 01dc: 456 */ /* terminal 218 */ 0xDA,
|
||||
/* terminal 219 */ 0xDB,
|
||||
/* pos 01de: 461 */ /* 0 */ 0x01 /* (to 0x01E0 state 462) */,
|
||||
/* 1 */ 0x08 /* (to 0x01EE state 488) */,
|
||||
/* pos 01e0: 462 */ /* terminal 222 */ 0xDE,
|
||||
/* terminal 223 */ 0xDF,
|
||||
/* pos 01e2: 465 */ /* terminal 224 */ 0xE0,
|
||||
/* terminal 226 */ 0xE2,
|
||||
/* pos 01e4: 469 */ /* terminal 227 */ 0xE3,
|
||||
/* terminal 229 */ 0xE5,
|
||||
/* pos 01e6: 475 */ /* terminal 232 */ 0xE8,
|
||||
/* terminal 233 */ 0xE9,
|
||||
/* pos 01e8: 478 */ /* terminal 234 */ 0xEA,
|
||||
/* terminal 235 */ 0xEB,
|
||||
/* pos 01ea: 481 */ /* terminal 236 */ 0xEC,
|
||||
/* terminal 237 */ 0xED,
|
||||
/* pos 01ec: 484 */ /* terminal 238 */ 0xEE,
|
||||
/* terminal 240 */ 0xF0,
|
||||
/* pos 01ee: 488 */ /* terminal 241 */ 0xF1,
|
||||
/* terminal 244 */ 0xF4,
|
||||
/* pos 01f0: 490 */ /* terminal 242 */ 0xF2,
|
||||
/* terminal 243 */ 0xF3,
|
||||
/* pos 01f2: 494 */ /* 0 */ 0x01 /* (to 0x01F4 state 495) */,
|
||||
/* 1 */ 0x04 /* (to 0x01FA state 503) */,
|
||||
/* pos 01f4: 495 */ /* 0 */ 0x01 /* (to 0x01F6 state 496) */,
|
||||
/* 1 */ 0x02 /* (to 0x01F8 state 499) */,
|
||||
/* pos 01f6: 496 */ /* terminal 245 */ 0xF5,
|
||||
/* terminal 246 */ 0xF6,
|
||||
/* pos 01f8: 499 */ /* terminal 247 */ 0xF7,
|
||||
/* terminal 248 */ 0xF8,
|
||||
/* pos 01fa: 503 */ /* 0 */ 0x01 /* (to 0x01FC state 504) */,
|
||||
/* 1 */ 0x02 /* (to 0x01FE state 507) */,
|
||||
/* pos 01fc: 504 */ /* terminal 250 */ 0xFA,
|
||||
/* terminal 251 */ 0xFB,
|
||||
/* pos 01fe: 507 */ /* terminal 252 */ 0xFC,
|
||||
/* terminal 253 */ 0xFD,
|
||||
/* total size 512 bytes, biggest jump 200/256, fails=0 */
|
||||
};
|
||||
|
||||
static unsigned char lextable_terms[] = {
|
||||
|
||||
0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x03, 0x00,
|
||||
0x34, 0x0f, 0x43, 0x03, 0xf1, 0x3c, 0xfc, 0x3c,
|
||||
0x0f, 0x30, 0x37, 0xf7, 0x0f, 0xc3, 0xcf, 0x03,
|
||||
0x3c, 0xfc, 0xc0, 0xf3, 0xf0, 0x3c, 0xfc, 0xf0,
|
||||
0xcf, 0xfc, 0xcc, 0xff, 0xfc, 0x0d, 0x34, 0xcc,
|
||||
0xcf, 0x33, 0xf0, 0x33, 0x0c, 0x3f, 0xc3, 0x3f,
|
||||
0xcc, 0x30, 0xfc, 0xcf, 0x3c, 0xf0, 0x0c, 0xcf,
|
||||
0xd0, 0x03, 0x3f, 0x33, 0xff, 0xff, 0xc3, 0xf3,
|
||||
};
|
||||
|
||||
/* state that points to 0x100 for disambiguation with 0x0 */
|
||||
#define HUFTABLE_0x100_PREV 118
|
||||
516
Kinc/Sources/kinc/libs/roles/h2/minihuf.c
Normal file
516
Kinc/Sources/kinc/libs/roles/h2/minihuf.c
Normal file
@ -0,0 +1,516 @@
|
||||
/*
|
||||
* minilex.c
|
||||
*
|
||||
* High efficiency lexical state parser
|
||||
*
|
||||
* Copyright (C)2011-2014 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Licensed under MIT
|
||||
*
|
||||
* Usage: gcc minihuf.c -o minihuf && ./minihuf > huftable.h
|
||||
*
|
||||
* Run it twice to test parsing on the generated table on stderr
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LWS_ARRAY_SIZE(n) (sizeof(n) / sizeof(n[0]))
|
||||
|
||||
struct huf {
|
||||
unsigned int code;
|
||||
unsigned char len;
|
||||
};
|
||||
|
||||
static struct huf huf_literal[] = {
|
||||
/* 0x00 */ { 0x1ff8, 13 },
|
||||
/* 0x01 */ { 0x7fffd8, 23 },
|
||||
/* 0x02 */ { 0xfffffe2, 28 },
|
||||
/* 0x03 */ { 0xfffffe3, 28 },
|
||||
/* 0x04 */ { 0xfffffe4, 28 },
|
||||
/* 0x05 */ { 0xfffffe5, 28 },
|
||||
/* 0x06 */ { 0xfffffe6, 28 },
|
||||
/* 0x07 */ { 0xfffffe7, 28 },
|
||||
/* 0x08 */ { 0xfffffe8, 28 },
|
||||
/* 0x09 */ { 0xffffea, 24 },
|
||||
/* 0x0a */ { 0x3ffffffc, 30 },
|
||||
/* 0x0b */ { 0xfffffe9, 28 },
|
||||
|
||||
/* 0x0c */ { 0xfffffea, 28 },
|
||||
/* 0x0d */ { 0x3ffffffd, 30 },
|
||||
/* 0x0e */ { 0xfffffeb, 28 },
|
||||
/* 0x0f */ { 0xfffffec, 28 },
|
||||
/* 0x10 */ { 0xfffffed, 28 },
|
||||
/* 0x11 */ { 0xfffffee, 28 },
|
||||
/* 0x12 */ { 0xfffffef, 28 },
|
||||
/* 0x13 */ { 0xffffff0, 28 },
|
||||
/* 0x14 */ { 0xffffff1, 28 },
|
||||
/* 0x15 */ { 0xffffff2, 28 },
|
||||
/* 0x16 */ { 0x3ffffffe, 30 },
|
||||
/* 0x17 */ { 0xffffff3, 28 },
|
||||
/* 0x18 */ { 0xffffff4, 28 },
|
||||
/* 0x19 */ { 0xffffff5, 28 },
|
||||
/* 0x1a */ { 0xffffff6, 28 },
|
||||
/* 0x1b */ { 0xffffff7, 28 },
|
||||
/* 0x1c */ { 0xffffff8, 28 },
|
||||
/* 0x1d */ { 0xffffff9, 28 },
|
||||
/* 0x1e */ { 0xffffffa, 28 },
|
||||
/* 0x1f */ { 0xffffffb, 28 },
|
||||
/* 0x20 */ { 0x14, 6 },
|
||||
/* 0x21 */ { 0x3f8, 10 },
|
||||
/* 0x22 */ { 0x3f9, 10 },
|
||||
/* 0x23 */ { 0xffa, 12 },
|
||||
/* 0x24 */ { 0x1ff9, 13 },
|
||||
/* 0x25 */ { 0x15, 6 },
|
||||
/* 0x26 */ { 0xf8, 8 },
|
||||
/* 0x27 */ { 0x7fa, 11 },
|
||||
/* 0x28 */ { 0x3fa, 10 },
|
||||
/* 0x29 */ { 0x3fb, 10 },
|
||||
/* 0x2a */ { 0xf9, 8 },
|
||||
/* 0x2b */ { 0x7fb, 11 },
|
||||
/* 0x2c */ { 0xfa, 8 },
|
||||
/* 0x2d */ { 0x16, 6 },
|
||||
/* 0x2e */ { 0x17, 6 },
|
||||
/* 0x2f */ { 0x18, 6 },
|
||||
/* 0x30 */ { 0x0, 5 },
|
||||
/* 0x31 */ { 0x1, 5 },
|
||||
/* 0x32 */ { 0x2, 5 },
|
||||
/* 0x33 */ { 0x19, 6 },
|
||||
/* 0x34 */ { 0x1a, 6 },
|
||||
/* 0x35 */ { 0x1b, 6 },
|
||||
/* 0x36 */ { 0x1c, 6 },
|
||||
/* 0x37 */ { 0x1d, 6 },
|
||||
/* 0x38 */ { 0x1e, 6 },
|
||||
/* 0x39 */ { 0x1f, 6 },
|
||||
/* 0x3a */ { 0x5c, 7 },
|
||||
/* 0x3b */ { 0xfb, 8 },
|
||||
|
||||
/* 0x3c */ { 0x7ffc, 15 },
|
||||
/* 0x3d */ { 0x20, 6 },
|
||||
/* 0x3e */ { 0xffb, 12 },
|
||||
/* 0x3f */ { 0x3fc, 10 },
|
||||
/* 0x40 */ { 0x1ffa, 13 },
|
||||
/* 0x41 */ { 0x21, 6 },
|
||||
/* 0x42 */ { 0x5d, 7 },
|
||||
/* 0x43 */ { 0x5e, 7 },
|
||||
/* 0x44 */ { 0x5f, 7 },
|
||||
/* 0x45 */ { 0x60, 7 },
|
||||
/* 0x46 */ { 0x61, 7 },
|
||||
/* 0x47 */ { 0x62, 7 },
|
||||
/* 0x48 */ { 0x63, 7 },
|
||||
/* 0x49 */ { 0x64, 7 },
|
||||
/* 0x4a */ { 0x65, 7 },
|
||||
/* 0x4b */ { 0x66, 7 },
|
||||
/* 0x4c */ { 0x67, 7 },
|
||||
/* 0x4d */ { 0x68, 7 },
|
||||
/* 0x4e */ { 0x69, 7 },
|
||||
/* 0x4f */ { 0x6a, 7 },
|
||||
/* 0x50 */ { 0x6b, 7 },
|
||||
/* 0x51 */ { 0x6c, 7 },
|
||||
/* 0x52 */ { 0x6d, 7 },
|
||||
/* 0x53 */ { 0x6e, 7 },
|
||||
/* 0x54 */ { 0x6f, 7 },
|
||||
/* 0x55 */ { 0x70, 7 },
|
||||
/* 0x56 */ { 0x71, 7 },
|
||||
/* 0x57 */ { 0x72, 7 },
|
||||
/* 0x58 */ { 0xfc, 8 },
|
||||
/* 0x59 */ { 0x73, 7 },
|
||||
/* 0x5a */ { 0xfd, 8 },
|
||||
/* 0x5b */ { 0x1ffb, 13 },
|
||||
/* 0x5c */ { 0x7fff0, 19 },
|
||||
/* 0x5d */ { 0x1ffc, 13 },
|
||||
/* 0x5e */ { 0x3ffc, 14 },
|
||||
/* 0x5f */ { 0x22, 6 },
|
||||
/* 0x60 */ { 0x7ffd, 15 },
|
||||
/* 0x61 */ { 0x3, 5 },
|
||||
/* 0x62 */ { 0x23, 6 },
|
||||
/* 0x63 */ { 0x4, 5 },
|
||||
/* 0x64 */ { 0x24, 6 },
|
||||
/* 0x65 */ { 0x5, 5 },
|
||||
/* 0x66 */ { 0x25, 6 },
|
||||
/* 0x67 */ { 0x26, 6 },
|
||||
/* 0x68 */ { 0x27, 6 },
|
||||
/* 0x69 */ { 0x6, 5 },
|
||||
/* 0x6a */ { 0x74, 7 },
|
||||
/* 0x6b */ { 0x75, 7 },
|
||||
|
||||
|
||||
/* 0x6c */ { 0x28, 6 },
|
||||
/* 0x6d */ { 0x29, 6 },
|
||||
/* 0x6e */ { 0x2a, 6 },
|
||||
/* 0x6f */ { 0x7, 5 },
|
||||
/* 0x70 */ { 0x2b, 6 },
|
||||
/* 0x71 */ { 0x76, 7 },
|
||||
/* 0x72 */ { 0x2c, 6 },
|
||||
/* 0x73 */ { 0x8, 5 },
|
||||
/* 0x74 */ { 0x9, 5 },
|
||||
/* 0x75 */ { 0x2d, 6 },
|
||||
/* 0x76 */ { 0x77, 7 },
|
||||
/* 0x77 */ { 0x78, 7 },
|
||||
/* 0x78 */ { 0x79, 7 },
|
||||
/* 0x79 */ { 0x7a, 7 },
|
||||
/* 0x7a */ { 0x7b, 7 },
|
||||
/* 0x7b */ { 0x7ffe, 15 },
|
||||
/* 0x7c */ { 0x7fc, 11 },
|
||||
/* 0x7d */ { 0x3ffd, 14 },
|
||||
/* 0x7e */ { 0x1ffd, 13 },
|
||||
/* 0x7f */ { 0xffffffc, 28 },
|
||||
/* 0x80 */ { 0xfffe6, 20 },
|
||||
/* 0x81 */ { 0x3fffd2, 22 },
|
||||
/* 0x82 */ { 0xfffe7, 20 },
|
||||
/* 0x83 */ { 0xfffe8, 20 },
|
||||
/* 0x84 */ { 0x3fffd3, 22 },
|
||||
/* 0x85 */ { 0x3fffd4, 22 },
|
||||
/* 0x86 */ { 0x3fffd5, 22 },
|
||||
/* 0x87 */ { 0x7fffd9, 23 },
|
||||
/* 0x88 */ { 0x3fffd6, 22 },
|
||||
/* 0x89 */ { 0x7fffda, 23 },
|
||||
/* 0x8a */ { 0x7fffdb, 23 },
|
||||
/* 0x8b */ { 0x7fffdc, 23 },
|
||||
/* 0x8c */ { 0x7fffdd, 23 },
|
||||
/* 0x8d */ { 0x7fffde, 23 },
|
||||
/* 0x8e */ { 0xffffeb, 24 },
|
||||
/* 0x8f */ { 0x7fffdf, 23 },
|
||||
/* 0x90 */ { 0xffffec, 24 },
|
||||
/* 0x91 */ { 0xffffed, 24 },
|
||||
/* 0x92 */ { 0x3fffd7, 22 },
|
||||
/* 0x93 */ { 0x7fffe0, 23 },
|
||||
/* 0x94 */ { 0xffffee, 24 },
|
||||
/* 0x95 */ { 0x7fffe1, 23 },
|
||||
/* 0x96 */ { 0x7fffe2, 23 },
|
||||
/* 0x97 */ { 0x7fffe3, 23 },
|
||||
/* 0x98 */ { 0x7fffe4, 23 },
|
||||
/* 0x99 */ { 0x1fffdc, 21 },
|
||||
/* 0x9a */ { 0x3fffd8, 22 },
|
||||
/* 0x9b */ { 0x7fffe5, 23 },
|
||||
|
||||
/* 0x9c */ { 0x3fffd9, 22 },
|
||||
/* 0x9d */ { 0x7fffe6, 23 },
|
||||
/* 0x9e */ { 0x7fffe7, 23 },
|
||||
/* 0x9f */ { 0xffffef, 24 },
|
||||
/* 0xa0 */ { 0x3fffda, 22 },
|
||||
/* 0xa1 */ { 0x1fffdd, 21 },
|
||||
/* 0xa2 */ { 0xfffe9, 20 },
|
||||
/* 0xa3 */ { 0x3fffdb, 22 },
|
||||
/* 0xa4 */ { 0x3fffdc, 22 },
|
||||
/* 0xa5 */ { 0x7fffe8, 23 },
|
||||
/* 0xa6 */ { 0x7fffe9, 23 },
|
||||
/* 0xa7 */ { 0x1fffde, 21 },
|
||||
/* 0xa8 */ { 0x7fffea, 23 },
|
||||
/* 0xa9 */ { 0x3fffdd, 22 },
|
||||
/* 0xaa */ { 0x3fffde, 22 },
|
||||
/* 0xab */ { 0xfffff0, 24 },
|
||||
/* 0xac */ { 0x1fffdf, 21 },
|
||||
/* 0xad */ { 0x3fffdf, 22 },
|
||||
/* 0xae */ { 0x7fffeb, 23 },
|
||||
/* 0xaf */ { 0x7fffec, 23 },
|
||||
/* 0xb0 */ { 0x1fffe0, 21 },
|
||||
/* 0xb1 */ { 0x1fffe1, 21 },
|
||||
/* 0xb2 */ { 0x3fffe0, 22 },
|
||||
/* 0xb3 */ { 0x1fffe2, 21 },
|
||||
/* 0xb4 */ { 0x7fffed, 23 },
|
||||
/* 0xb5 */ { 0x3fffe1, 22 },
|
||||
/* 0xb6 */ { 0x7fffee, 23 },
|
||||
/* 0xb7 */ { 0x7fffef, 23 },
|
||||
/* 0xb8 */ { 0xfffea, 20 },
|
||||
/* 0xb9 */ { 0x3fffe2, 22 },
|
||||
/* 0xba */ { 0x3fffe3, 22 },
|
||||
/* 0xbb */ { 0x3fffe4, 22 },
|
||||
/* 0xbc */ { 0x7ffff0, 23 },
|
||||
/* 0xbd */ { 0x3fffe5, 22 },
|
||||
/* 0xbe */ { 0x3fffe6, 22 },
|
||||
/* 0xbf */ { 0x7ffff1, 23 },
|
||||
/* 0xc0 */ { 0x3ffffe0, 26 },
|
||||
/* 0xc1 */ { 0x3ffffe1, 26 },
|
||||
/* 0xc2 */ { 0xfffeb, 20 },
|
||||
/* 0xc3 */ { 0x7fff1, 19 },
|
||||
/* 0xc4 */ { 0x3fffe7, 22 },
|
||||
/* 0xc5 */ { 0x7ffff2, 23 },
|
||||
/* 0xc6 */ { 0x3fffe8, 22 },
|
||||
/* 0xc7 */ { 0x1ffffec, 25 },
|
||||
/* 0xc8 */ { 0x3ffffe2, 26 },
|
||||
/* 0xc9 */ { 0x3ffffe3, 26 },
|
||||
/* 0xca */ { 0x3ffffe4, 26 },
|
||||
/* 0xcb */ { 0x7ffffde, 27 },
|
||||
|
||||
/* 0xcc */ { 0x7ffffdf, 27 },
|
||||
/* 0xcd */ { 0x3ffffe5, 26 },
|
||||
/* 0xce */ { 0xfffff1, 24 },
|
||||
/* 0xcf */ { 0x1ffffed, 25 },
|
||||
/* 0xd0 */ { 0x7fff2, 19 },
|
||||
/* 0xd1 */ { 0x1fffe3, 21 },
|
||||
/* 0xd2 */ { 0x3ffffe6, 26 },
|
||||
/* 0xd3 */ { 0x7ffffe0, 27 },
|
||||
/* 0xd4 */ { 0x7ffffe1, 27 },
|
||||
/* 0xd5 */ { 0x3ffffe7, 26 },
|
||||
/* 0xd6 */ { 0x7ffffe2, 27 },
|
||||
/* 0xd7 */ { 0xfffff2, 24 },
|
||||
/* 0xd8 */ { 0x1fffe4, 21 },
|
||||
/* 0xd9 */ { 0x1fffe5, 21 },
|
||||
/* 0xda */ { 0x3ffffe8, 26 },
|
||||
/* 0xdb */ { 0x3ffffe9, 26 },
|
||||
/* 0xdc */ { 0xffffffd, 28 },
|
||||
/* 0xdd */ { 0x7ffffe3, 27 },
|
||||
/* 0xde */ { 0x7ffffe4, 27 },
|
||||
/* 0xdf */ { 0x7ffffe5, 27 },
|
||||
/* 0xe0 */ { 0xfffec, 20 },
|
||||
/* 0xe1 */ { 0xfffff3, 24 },
|
||||
/* 0xe2 */ { 0xfffed, 20 },
|
||||
/* 0xe3 */ { 0x1fffe6, 21 },
|
||||
/* 0xe4 */ { 0x3fffe9, 22 },
|
||||
/* 0xe5 */ { 0x1fffe7, 21 },
|
||||
/* 0xe6 */ { 0x1fffe8, 21 },
|
||||
/* 0xe7 */ { 0x7ffff3, 23 },
|
||||
/* 0xe8 */ { 0x3fffea, 22 },
|
||||
/* 0xe9 */ { 0x3fffeb, 22 },
|
||||
/* 0xea */ { 0x1ffffee, 25 },
|
||||
/* 0xeb */ { 0x1ffffef, 25 },
|
||||
/* 0xec */ { 0xfffff4, 24 },
|
||||
/* 0xed */ { 0xfffff5, 24 },
|
||||
/* 0xee */ { 0x3ffffea, 26 },
|
||||
/* 0xef */ { 0x7ffff4, 23 },
|
||||
/* 0xf0 */ { 0x3ffffeb, 26 },
|
||||
/* 0xf1 */ { 0x7ffffe6, 27 },
|
||||
/* 0xf2 */ { 0x3ffffec, 26 },
|
||||
/* 0xf3 */ { 0x3ffffed, 26 },
|
||||
/* 0xf4 */ { 0x7ffffe7, 27 },
|
||||
/* 0xf5 */ { 0x7ffffe8, 27 },
|
||||
/* 0xf6 */ { 0x7ffffe9, 27 },
|
||||
/* 0xf7 */ { 0x7ffffea, 27 },
|
||||
/* 0xf8 */ { 0x7ffffeb, 27 },
|
||||
/* 0xf9 */ { 0xffffffe, 28 },
|
||||
/* 0xfa */ { 0x7ffffec, 27 },
|
||||
/* 0xfb */ { 0x7ffffed, 27 },
|
||||
|
||||
/* 0xfc */ { 0x7ffffee, 27 },
|
||||
/* 0xfd */ { 0x7ffffef, 27 },
|
||||
/* 0xfe */ { 0x7fffff0, 27 },
|
||||
/* 0xff */ { 0x3ffffee, 26 },
|
||||
/* 0x100 */ { 0x3fffffff, 30 },
|
||||
};
|
||||
|
||||
int code_bit(int idx, int bit)
|
||||
{
|
||||
if (bit < huf_literal[idx].len)
|
||||
return !!(huf_literal[idx].code & (1 << (huf_literal[idx].len - 1 - bit)));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#include "huftable.h"
|
||||
|
||||
#define PARALLEL 2
|
||||
|
||||
struct state {
|
||||
int terminal;
|
||||
int state[PARALLEL];
|
||||
int bytepos;
|
||||
|
||||
int real_pos;
|
||||
};
|
||||
|
||||
struct state state[2000];
|
||||
unsigned char terms[2000];
|
||||
int next = 1;
|
||||
|
||||
int lextable_decode(int pos, char c)
|
||||
{
|
||||
int q = pos + !!c;
|
||||
|
||||
if (lextable_terms[q >> 3] & (1 << (q & 7))) /* terminal */
|
||||
return lextable[q] | 0x8000;
|
||||
|
||||
return pos + (lextable[q] << 1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int n = 0;
|
||||
int m = 0;
|
||||
int prev;
|
||||
char c;
|
||||
int walk;
|
||||
int saw;
|
||||
int y;
|
||||
int j;
|
||||
int pos = 0;
|
||||
int biggest = 0;
|
||||
int fails = 0;
|
||||
|
||||
m = 0;
|
||||
while (m < LWS_ARRAY_SIZE(state)) {
|
||||
for (j = 0; j < PARALLEL; j++) {
|
||||
state[m].state[j] = 0xffff;
|
||||
state[m].terminal = 0;
|
||||
}
|
||||
m++;
|
||||
}
|
||||
|
||||
while (n < LWS_ARRAY_SIZE(huf_literal)) {
|
||||
|
||||
m = 0;
|
||||
walk = 0;
|
||||
prev = 0;
|
||||
|
||||
while (m < huf_literal[n].len) {
|
||||
|
||||
saw = 0;
|
||||
if (state[walk].state[code_bit(n, m)] != 0xffff) {
|
||||
/* exists -- go forward */
|
||||
walk = state[walk].state[code_bit(n, m)];
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* something we didn't see before */
|
||||
|
||||
state[walk].state[code_bit(n, m)] = next;
|
||||
walk = next++;
|
||||
again:
|
||||
m++;
|
||||
}
|
||||
|
||||
state[walk].terminal = n++;
|
||||
state[walk].state[0] = 0; /* terminal marker */
|
||||
}
|
||||
|
||||
walk = 0;
|
||||
for (n = 0; n < next; n++) {
|
||||
state[n].bytepos = walk;
|
||||
walk += (2 * 2);
|
||||
}
|
||||
|
||||
/* compute everyone's position first */
|
||||
|
||||
pos = 0;
|
||||
walk = 0;
|
||||
for (n = 0; n < next; n++) {
|
||||
|
||||
state[n].real_pos = pos;
|
||||
|
||||
if (state[n].state[0]) /* nonterminal */
|
||||
pos += 2;
|
||||
|
||||
walk ++;
|
||||
}
|
||||
|
||||
fprintf(stdout, "static unsigned char lextable[] = {\n");
|
||||
|
||||
#define TERMINAL_MASK 0x8000
|
||||
|
||||
walk = 0;
|
||||
pos = 0;
|
||||
|
||||
for (n = 0; n < next; n++) {
|
||||
for (m = 0; m < 2; m++) {
|
||||
saw = state[n].state[m];
|
||||
|
||||
if (saw == 0) { // c is a terminal then
|
||||
m = 2;
|
||||
continue;
|
||||
}
|
||||
if (!m)
|
||||
fprintf(stdout, "/* pos %04x: %3d */ ",
|
||||
state[n].real_pos, n);
|
||||
else
|
||||
fprintf(stdout, " ");
|
||||
|
||||
if (saw == 0xffff) {
|
||||
fprintf(stdout,
|
||||
" 0xff, 0xff, /* 0 = fail */\n ");
|
||||
pos ++; /* fail */
|
||||
fails++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state[saw].state[0] == 0) { /* points to terminal */
|
||||
fprintf(stdout, " /* terminal %d */ 0x%02X,\n",
|
||||
state[saw].terminal,
|
||||
state[saw].terminal & 0xff);
|
||||
terms[(state[n].real_pos + m) >> 3] |=
|
||||
1 << ((state[n].real_pos + m) & 7);
|
||||
pos++;
|
||||
walk++;
|
||||
continue;
|
||||
}
|
||||
|
||||
j = (state[saw].real_pos - q) >> 1;
|
||||
|
||||
if (j > biggest)
|
||||
biggest = j;
|
||||
|
||||
if (j > 0xffff) {
|
||||
fprintf(stderr,
|
||||
"Jump > 64K bytes ahead (%d to %d)\n",
|
||||
state[n].real_pos, state[saw].real_pos);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stdout, " /* %d */ 0x%02X "
|
||||
"/* (to 0x%04X state %3d) */,\n",
|
||||
m,
|
||||
j & 0xff,
|
||||
state[saw].real_pos, saw);
|
||||
pos++;
|
||||
|
||||
walk++;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stdout, "/* total size %d bytes, biggest jump %d/256, fails=%d */\n};\n"
|
||||
"\n static unsigned char lextable_terms[] = {\n",
|
||||
pos, biggest, fails);
|
||||
|
||||
for (n = 0; n < (walk + 7) / 8; n++) {
|
||||
if (!(n & 7))
|
||||
fprintf(stdout, "\n\t");
|
||||
fprintf(stdout, "0x%02x, ", terms[n]);
|
||||
}
|
||||
fprintf(stdout, "\n};\n");
|
||||
|
||||
/*
|
||||
* Try to parse every legal input string
|
||||
*/
|
||||
|
||||
for (n = 0; n < LWS_ARRAY_SIZE(huf_literal); n++) {
|
||||
walk = 0;
|
||||
m = 0;
|
||||
y = -1;
|
||||
|
||||
fprintf(stderr, " trying %d\n", n);
|
||||
|
||||
while (m < huf_literal[n].len) {
|
||||
prev = walk;
|
||||
walk = lextable_decode(walk, code_bit(n, m));
|
||||
|
||||
if (walk == 0xffff) {
|
||||
fprintf(stderr, "failed\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (walk & 0x8000) {
|
||||
y = walk & 0x7fff;
|
||||
if (y == 0 && m == 29) {
|
||||
y |= 0x100;
|
||||
fprintf(stdout,
|
||||
"\n/* state that points to "
|
||||
"0x100 for disambiguation with "
|
||||
"0x0 */\n"
|
||||
"#define HUFTABLE_0x100_PREV "
|
||||
"%d\n", prev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
m++;
|
||||
}
|
||||
|
||||
if (y != n) {
|
||||
fprintf(stderr, "decode failed %d got %d (0x%x)\n", n, y, y);
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "All decode OK\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
1426
Kinc/Sources/kinc/libs/roles/h2/ops-h2.c
Normal file
1426
Kinc/Sources/kinc/libs/roles/h2/ops-h2.c
Normal file
File diff suppressed because it is too large
Load Diff
384
Kinc/Sources/kinc/libs/roles/h2/private-lib-roles-h2.h
Normal file
384
Kinc/Sources/kinc/libs/roles/h2/private-lib-roles-h2.h
Normal file
@ -0,0 +1,384 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
extern const struct lws_role_ops role_ops_h2;
|
||||
#define lwsi_role_h2(wsi) (wsi->role_ops == &role_ops_h2)
|
||||
|
||||
struct http2_settings {
|
||||
uint32_t s[H2SET_COUNT];
|
||||
};
|
||||
|
||||
struct lws_vhost_role_h2 {
|
||||
struct http2_settings set;
|
||||
};
|
||||
|
||||
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
|
||||
*/
|
||||
};
|
||||
|
||||
void
|
||||
lws_h2_state(struct lws *wsi, enum lws_h2_states s);
|
||||
|
||||
#define LWS_H2_STREAM_ID_MASTER 0
|
||||
#define LWS_H2_SETTINGS_LEN 6
|
||||
#define LWS_H2_FLAG_SETTINGS_ACK 1
|
||||
|
||||
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_PING,
|
||||
LWS_H2_PPS_PONG,
|
||||
LWS_H2_PPS_GOAWAY,
|
||||
LWS_H2_PPS_RST_STREAM,
|
||||
LWS_H2_PPS_UPDATE_WINDOW,
|
||||
LWS_H2_PPS_SETTINGS_INITIAL_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;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 our_set;
|
||||
struct http2_settings peer_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 */
|
||||
|
||||
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;
|
||||
unsigned int sent_preface: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;
|
||||
|
||||
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 {
|
||||
|
||||
struct lws_h2_netconn *h2n; /* malloc'd for root net conn */
|
||||
|
||||
char *pending_status_body;
|
||||
|
||||
uint8_t h2_state; /* RFC7540 state of the connection */
|
||||
|
||||
uint8_t END_STREAM:1;
|
||||
uint8_t END_HEADERS:1;
|
||||
uint8_t send_END_STREAM:1;
|
||||
uint8_t long_poll:1;
|
||||
uint8_t initialized:1;
|
||||
};
|
||||
|
||||
#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->mux.parent_wsi)
|
||||
|
||||
int
|
||||
lws_h2_rst_stream(struct lws *wsi, uint32_t err, const char *reason);
|
||||
struct lws * lws_h2_get_nth_child(struct lws *wsi, int n);
|
||||
void lws_h2_init(struct lws *wsi);
|
||||
int
|
||||
lws_h2_settings(struct lws *nwsi, struct http2_settings *settings,
|
||||
unsigned char *buf, int len);
|
||||
int
|
||||
lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
|
||||
lws_filepos_t *inused);
|
||||
int
|
||||
lws_h2_do_pps_send(struct lws *wsi);
|
||||
int
|
||||
lws_h2_frame_write(struct lws *wsi, int type, int flags, unsigned int sid,
|
||||
unsigned int len, unsigned char *buf);
|
||||
struct lws *
|
||||
lws_wsi_mux_from_id(struct lws *wsi, unsigned int sid);
|
||||
int
|
||||
lws_hpack_interpret(struct lws *wsi, unsigned char c);
|
||||
int
|
||||
lws_add_http2_header_by_name(struct lws *wsi,
|
||||
const unsigned char *name,
|
||||
const unsigned char *value, int length,
|
||||
unsigned char **p, unsigned char *end);
|
||||
int
|
||||
lws_add_http2_header_by_token(struct lws *wsi,
|
||||
enum lws_token_indexes token,
|
||||
const unsigned char *value, int length,
|
||||
unsigned char **p, unsigned char *end);
|
||||
int
|
||||
lws_add_http2_header_status(struct lws *wsi,
|
||||
unsigned int code, unsigned char **p,
|
||||
unsigned char *end);
|
||||
void
|
||||
lws_hpack_destroy_dynamic_header(struct lws *wsi);
|
||||
int
|
||||
lws_hpack_dynamic_size(struct lws *wsi, int size);
|
||||
int
|
||||
lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason);
|
||||
int
|
||||
lws_h2_tx_cr_get(struct lws *wsi);
|
||||
void
|
||||
lws_h2_tx_cr_consume(struct lws *wsi, int consumed);
|
||||
int
|
||||
lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h);
|
||||
void
|
||||
lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pss);
|
||||
|
||||
extern const struct http2_settings lws_h2_defaults;
|
||||
int
|
||||
lws_h2_ws_handshake(struct lws *wsi);
|
||||
int lws_h2_issue_preface(struct lws *wsi);
|
||||
int
|
||||
lws_h2_client_handshake(struct lws *wsi);
|
||||
struct lws *
|
||||
lws_wsi_h2_adopt(struct lws *parent_wsi, struct lws *wsi);
|
||||
int
|
||||
lws_handle_POLLOUT_event_h2(struct lws *wsi);
|
||||
int
|
||||
lws_read_h2(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
|
||||
struct lws_h2_protocol_send *
|
||||
lws_h2_new_pps(enum lws_h2_protocol_send_type type);
|
||||
96
Kinc/Sources/kinc/libs/roles/http/CMakeLists.txt
Normal file
96
Kinc/Sources/kinc/libs/roles/http/CMakeLists.txt
Normal file
@ -0,0 +1,96 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(. ./compression)
|
||||
|
||||
list(APPEND SOURCES
|
||||
roles/http/header.c
|
||||
roles/http/date.c
|
||||
roles/http/parsers.c)
|
||||
|
||||
if (NOT LWS_WITHOUT_SERVER)
|
||||
list(APPEND SOURCES
|
||||
roles/http/server/server.c
|
||||
roles/http/server/lws-spa.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_CACHE_NSCOOKIEJAR AND LWS_WITH_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
roles/http/cookie.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_HTTP_PROXY AND LWS_WITH_HUBBUB)
|
||||
list(APPEND SOURCES
|
||||
roles/http/server/rewrite.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_ACCESS_LOG)
|
||||
list(APPEND SOURCES
|
||||
roles/http/server/access-log.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_HTTP_STREAM_COMPRESSION)
|
||||
list(APPEND SOURCES
|
||||
roles/http/compression/stream.c
|
||||
roles/http/compression/deflate/deflate.c)
|
||||
|
||||
if (LWS_WITH_HTTP_BROTLI)
|
||||
list(APPEND SOURCES
|
||||
roles/http/compression/brotli/brotli.c)
|
||||
list(APPEND LIB_LIST brotlienc brotlidec brotlidec)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_LEJP_CONF AND LWS_WITH_NETWORK AND NOT LWS_PLAT_OPTEE)
|
||||
list(APPEND SOURCES
|
||||
roles/http/server/lejp-conf.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_RANGES)
|
||||
list(APPEND SOURCES
|
||||
roles/http/server/ranges.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_ZIP_FOPS)
|
||||
if (LWS_WITH_ZLIB)
|
||||
list(APPEND SOURCES
|
||||
roles/http/server/fops-zip.c)
|
||||
else()
|
||||
message(FATAL_ERROR "Pre-zipped file support (LWS_WITH_ZIP_FOPS) requires ZLIB (LWS_WITH_ZLIB)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
2097
Kinc/Sources/kinc/libs/roles/http/client/client-http.c
Normal file
2097
Kinc/Sources/kinc/libs/roles/http/client/client-http.c
Normal file
File diff suppressed because it is too large
Load Diff
17
Kinc/Sources/kinc/libs/roles/http/compression/README.md
Normal file
17
Kinc/Sources/kinc/libs/roles/http/compression/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
HTTP compression
|
||||
----------------
|
||||
|
||||
This directory contains generic compression transforms that can be applied to
|
||||
specifically HTTP content streams, after the header, be it h1 or h2.
|
||||
|
||||
The compression transforms expose an "ops" type struct and a compressor name
|
||||
as used by `content-encoding`... the ops struct definition can be found in
|
||||
./private-lib-roles-http-compression.h.
|
||||
|
||||
Because the compression transform depends on being able to send on its output
|
||||
before it can process new input, the transform adds a new kind of buflist
|
||||
`wsi->buflist_comp` that represents pre-compression transform data
|
||||
("input data" from the perspective of the compression transform) that was
|
||||
delivered to be processed but couldn't be accepted.
|
||||
|
||||
Currently, zlib 'deflate' and brotli 'br' are supported on the server side.
|
||||
124
Kinc/Sources/kinc/libs/roles/http/compression/brotli/brotli.c
Normal file
124
Kinc/Sources/kinc/libs/roles/http/compression/brotli/brotli.c
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
static int
|
||||
lcs_init_compression_brotli(lws_comp_ctx_t *ctx, int decomp)
|
||||
{
|
||||
ctx->is_decompression = (unsigned char)!!decomp;
|
||||
|
||||
if (!decomp) {
|
||||
ctx->u.br_en = BrotliEncoderCreateInstance(NULL, NULL, NULL);
|
||||
if (ctx->u.br_en) {
|
||||
BrotliEncoderSetParameter(ctx->u.br_en,
|
||||
BROTLI_PARAM_MODE, BROTLI_MODE_TEXT);
|
||||
BrotliEncoderSetParameter(ctx->u.br_en,
|
||||
BROTLI_PARAM_QUALITY, BROTLI_MIN_QUALITY);
|
||||
}
|
||||
}
|
||||
else
|
||||
ctx->u.br_de = BrotliDecoderCreateInstance(NULL, NULL, NULL);
|
||||
|
||||
return !ctx->u.br_de;
|
||||
}
|
||||
|
||||
static int
|
||||
lcs_process_brotli(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,
|
||||
void *out, size_t *olen_oused)
|
||||
{
|
||||
size_t a_in, a_out, t_out;
|
||||
const uint8_t *n_in;
|
||||
uint8_t *n_out;
|
||||
int n;
|
||||
|
||||
n_in = (void *)in;
|
||||
a_in = *ilen_iused;
|
||||
a_out = *olen_oused;
|
||||
n_out = out;
|
||||
t_out = 0;
|
||||
|
||||
if (!ctx->is_decompression) {
|
||||
|
||||
if (!a_in && !BrotliEncoderHasMoreOutput(ctx->u.br_en)) {
|
||||
*olen_oused = 0;
|
||||
|
||||
goto bail;
|
||||
}
|
||||
|
||||
n = BROTLI_OPERATION_PROCESS;
|
||||
if (!ctx->buflist_comp && ctx->final_on_input_side)
|
||||
n = BROTLI_OPERATION_FINISH;
|
||||
|
||||
if (BrotliEncoderCompressStream(ctx->u.br_en, n, &a_in, &n_in,
|
||||
&a_out, &n_out, &t_out) ==
|
||||
BROTLI_FALSE) {
|
||||
lwsl_err("brotli encode failed\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->may_have_more = !a_out;
|
||||
|
||||
} else {
|
||||
n = BrotliDecoderDecompressStream(ctx->u.br_de, &a_in, &n_in,
|
||||
&a_out, &n_out, &t_out);
|
||||
|
||||
switch (n) {
|
||||
case BROTLI_DECODER_RESULT_ERROR:
|
||||
lwsl_err("brotli decoder error\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*ilen_iused -= a_in;
|
||||
*olen_oused -= a_out;
|
||||
|
||||
bail:
|
||||
if (!ctx->is_decompression)
|
||||
return BrotliEncoderIsFinished(ctx->u.br_en);
|
||||
else
|
||||
return BrotliDecoderIsFinished(ctx->u.br_de);
|
||||
}
|
||||
|
||||
static void
|
||||
lcs_destroy_brotli(lws_comp_ctx_t *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
if (!(*ctx).is_decompression)
|
||||
BrotliEncoderDestroyInstance((*ctx).u.br_en);
|
||||
else
|
||||
BrotliDecoderDestroyInstance((*ctx).u.br_de);
|
||||
|
||||
(*ctx).u.generic_ctx_ptr = NULL;
|
||||
}
|
||||
|
||||
struct lws_compression_support lcs_brotli = {
|
||||
/* .encoding_name */ "br",
|
||||
/* .init_compression */ lcs_init_compression_brotli,
|
||||
/* .process */ lcs_process_brotli,
|
||||
/* .destroy */ lcs_destroy_brotli,
|
||||
};
|
||||
113
Kinc/Sources/kinc/libs/roles/http/compression/deflate/deflate.c
Normal file
113
Kinc/Sources/kinc/libs/roles/http/compression/deflate/deflate.c
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
static int
|
||||
lcs_init_compression_deflate(lws_comp_ctx_t *ctx, int decomp)
|
||||
{
|
||||
int n;
|
||||
|
||||
ctx->is_decompression = !!decomp;
|
||||
ctx->u.deflate = lws_malloc(sizeof(*ctx->u.deflate), __func__);
|
||||
|
||||
if (!ctx->u.deflate)
|
||||
return 2;
|
||||
|
||||
memset(ctx->u.deflate, 0, sizeof(*ctx->u.deflate));
|
||||
|
||||
if (!decomp &&
|
||||
(n = deflateInit2(ctx->u.deflate, 1, Z_DEFLATED, -15, 8,
|
||||
Z_DEFAULT_STRATEGY)) != Z_OK) {
|
||||
lwsl_err("deflate init failed: %d\n", n);
|
||||
lws_free_set_NULL(ctx->u.deflate);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (decomp &&
|
||||
inflateInit2(ctx->u.deflate, 16 + 15) != Z_OK) {
|
||||
lws_free_set_NULL(ctx->u.deflate);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lcs_process_deflate(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,
|
||||
void *out, size_t *olen_oused)
|
||||
{
|
||||
size_t olen_oused_in = *olen_oused;
|
||||
int n;
|
||||
|
||||
ctx->u.deflate->next_in = (void *)in;
|
||||
ctx->u.deflate->avail_in = (unsigned int)*ilen_iused;
|
||||
|
||||
ctx->u.deflate->next_out = out;
|
||||
ctx->u.deflate->avail_out = (unsigned int)*olen_oused;
|
||||
|
||||
if (!ctx->is_decompression)
|
||||
n = deflate(ctx->u.deflate, Z_SYNC_FLUSH);
|
||||
else
|
||||
n = inflate(ctx->u.deflate, Z_SYNC_FLUSH);
|
||||
|
||||
switch (n) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_STREAM_ERROR:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
lwsl_err("zlib error inflate %d\n", n);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ilen_iused -= ctx->u.deflate->avail_in;
|
||||
*olen_oused -= ctx->u.deflate->avail_out;
|
||||
|
||||
/* it's ambiguous with zlib... */
|
||||
ctx->may_have_more = (*olen_oused == olen_oused_in);
|
||||
|
||||
return n == Z_STREAM_END;
|
||||
}
|
||||
|
||||
static void
|
||||
lcs_destroy_deflate(lws_comp_ctx_t *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
if (!(*ctx).is_decompression)
|
||||
deflateEnd((*ctx).u.deflate);
|
||||
else
|
||||
inflateEnd((*ctx).u.deflate);
|
||||
|
||||
lws_free_set_NULL(ctx->u.deflate);
|
||||
}
|
||||
|
||||
struct lws_compression_support lcs_deflate = {
|
||||
/* .encoding_name */ "deflate",
|
||||
/* .init_compression */ lcs_init_compression_deflate,
|
||||
/* .process */ lcs_process_deflate,
|
||||
/* .destroy */ lcs_destroy_deflate,
|
||||
};
|
||||
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* This is included from private-lib-core.h if LWS_WITH_HTTP_STREAM_COMPRESSION
|
||||
*/
|
||||
|
||||
#if defined(LWS_WITH_MINIZ)
|
||||
#include <miniz.h>
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
#if defined(LWS_WITH_HTTP_BROTLI)
|
||||
#include <brotli/encode.h>
|
||||
#include <brotli/decode.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* struct holding union of all the available compression methods' context data,
|
||||
* and state if it's compressing or decompressing
|
||||
*/
|
||||
|
||||
typedef struct lws_compression_ctx {
|
||||
union {
|
||||
|
||||
#if defined(LWS_WITH_HTTP_BROTLI)
|
||||
BrotliEncoderState *br_en;
|
||||
BrotliDecoderState *br_de;
|
||||
#endif
|
||||
z_stream *deflate;
|
||||
void *generic_ctx_ptr;
|
||||
} u;
|
||||
|
||||
struct lws_buflist *buflist_comp;
|
||||
|
||||
unsigned int is_decompression:1;
|
||||
unsigned int final_on_input_side:1;
|
||||
unsigned int may_have_more:1;
|
||||
unsigned int chunking:1;
|
||||
} lws_comp_ctx_t;
|
||||
|
||||
/* generic structure defining the interface to a compression method */
|
||||
|
||||
struct lws_compression_support {
|
||||
/** compression name as used by, eg, content-ecoding */
|
||||
const char *encoding_name;
|
||||
/** create a compression context for the compression method, or NULL */
|
||||
int (*init_compression)(lws_comp_ctx_t *ctx, int decomp);
|
||||
/** pass data into the context to be processed */
|
||||
int (*process)(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,
|
||||
void *out, size_t *olen_oused);
|
||||
/** destroy the de/compression context */
|
||||
void (*destroy)(lws_comp_ctx_t *ctx);
|
||||
};
|
||||
|
||||
extern struct lws_compression_support lcs_deflate;
|
||||
extern struct lws_compression_support lcs_brotli;
|
||||
|
||||
int
|
||||
lws_http_compression_validate(struct lws *wsi);
|
||||
|
||||
int
|
||||
lws_http_compression_transform(struct lws *wsi, unsigned char *buf,
|
||||
size_t len, enum lws_write_protocol *wp,
|
||||
unsigned char **outbuf, size_t *olen_oused);
|
||||
|
||||
void
|
||||
lws_http_compression_destroy(struct lws *wsi);
|
||||
227
Kinc/Sources/kinc/libs/roles/http/compression/stream.c
Normal file
227
Kinc/Sources/kinc/libs/roles/http/compression/stream.c
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
/* compression methods listed in order of preference */
|
||||
|
||||
struct lws_compression_support *lcs_available[] = {
|
||||
#if defined(LWS_WITH_HTTP_BROTLI)
|
||||
&lcs_brotli,
|
||||
#endif
|
||||
&lcs_deflate,
|
||||
};
|
||||
|
||||
/* compute acceptable compression encodings while we still have an ah */
|
||||
|
||||
int
|
||||
lws_http_compression_validate(struct lws *wsi)
|
||||
{
|
||||
const char *a;
|
||||
size_t n;
|
||||
|
||||
wsi->http.comp_accept_mask = 0;
|
||||
|
||||
if (!wsi->http.ah || !lwsi_role_server(wsi))
|
||||
return 0;
|
||||
|
||||
a = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING);
|
||||
if (!a)
|
||||
return 0;
|
||||
|
||||
for (n = 0; n < LWS_ARRAY_SIZE(lcs_available); n++)
|
||||
if (strstr(a, lcs_available[n]->encoding_name))
|
||||
wsi->http.comp_accept_mask = (uint8_t)(wsi->http.comp_accept_mask | (1 << n));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_http_compression_apply(struct lws *wsi, const char *name,
|
||||
unsigned char **p, unsigned char *end, char decomp)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
for (n = 0; n < LWS_ARRAY_SIZE(lcs_available); n++) {
|
||||
/* if name is non-NULL, choose only that compression method */
|
||||
if (name && strcmp(lcs_available[n]->encoding_name, name))
|
||||
continue;
|
||||
/*
|
||||
* If we're the server, confirm that the client told us he could
|
||||
* handle this kind of compression transform...
|
||||
*/
|
||||
if (!decomp && !(wsi->http.comp_accept_mask & (1 << n)))
|
||||
continue;
|
||||
|
||||
/* let's go with this one then... */
|
||||
break;
|
||||
}
|
||||
|
||||
if (n == LWS_ARRAY_SIZE(lcs_available))
|
||||
return 1;
|
||||
|
||||
lcs_available[n]->init_compression(&wsi->http.comp_ctx, decomp);
|
||||
if (!wsi->http.comp_ctx.u.generic_ctx_ptr) {
|
||||
lwsl_err("%s: init_compression %d failed\n", __func__, (int)n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wsi->http.lcs = lcs_available[n];
|
||||
wsi->http.comp_ctx.may_have_more = 0;
|
||||
wsi->http.comp_ctx.final_on_input_side = 0;
|
||||
wsi->http.comp_ctx.chunking = 0;
|
||||
wsi->http.comp_ctx.is_decompression = !!decomp;
|
||||
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING,
|
||||
(unsigned char *)lcs_available[n]->encoding_name,
|
||||
(int)strlen(lcs_available[n]->encoding_name), p, end))
|
||||
return -1;
|
||||
|
||||
lwsl_info("%s: %s: applied %s content-encoding\n", __func__,
|
||||
lws_wsi_tag(wsi), lcs_available[n]->encoding_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_http_compression_destroy(struct lws *wsi)
|
||||
{
|
||||
if (!wsi->http.lcs || !wsi->http.comp_ctx.u.generic_ctx_ptr)
|
||||
return;
|
||||
|
||||
wsi->http.lcs->destroy(&wsi->http.comp_ctx);
|
||||
|
||||
wsi->http.lcs = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This manages the compression transform independent of h1 or h2.
|
||||
*
|
||||
* wsi->buflist_comp stashes pre-transform input that was not yet compressed
|
||||
*/
|
||||
|
||||
int
|
||||
lws_http_compression_transform(struct lws *wsi, unsigned char *buf,
|
||||
size_t len, enum lws_write_protocol *wp,
|
||||
unsigned char **outbuf, size_t *olen_oused)
|
||||
{
|
||||
size_t ilen_iused = len;
|
||||
int n, use = 0, wp1f = (*wp) & 0x1f;
|
||||
lws_comp_ctx_t *ctx = &wsi->http.comp_ctx;
|
||||
|
||||
ctx->may_have_more = 0;
|
||||
|
||||
if (!wsi->http.lcs ||
|
||||
(wp1f != LWS_WRITE_HTTP && wp1f != LWS_WRITE_HTTP_FINAL)) {
|
||||
*outbuf = buf;
|
||||
*olen_oused = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (wp1f == LWS_WRITE_HTTP_FINAL) {
|
||||
/*
|
||||
* ...we may get a large buffer that represents the final input
|
||||
* buffer, but it may form multiple frames after being
|
||||
* tranformed by compression; only the last of those is actually
|
||||
* the final frame on the output stream.
|
||||
*
|
||||
* Note that we have received the FINAL input, and downgrade it
|
||||
* to a non-final for now.
|
||||
*/
|
||||
ctx->final_on_input_side = 1;
|
||||
*wp = (unsigned int)(LWS_WRITE_HTTP | ((*wp) & ~0x1fu));
|
||||
}
|
||||
|
||||
if (ctx->buflist_comp) {
|
||||
/*
|
||||
* we can't send this new stuff when we have old stuff
|
||||
* buffered and not compressed yet. Add it to the tail
|
||||
* and switch to trying to process the head.
|
||||
*/
|
||||
if (buf && len) {
|
||||
if (lws_buflist_append_segment(
|
||||
&ctx->buflist_comp, buf, len) < 0)
|
||||
return -1;
|
||||
lwsl_debug("%s: %s: adding %d to comp buflist\n",
|
||||
__func__, lws_wsi_tag(wsi), (int)len);
|
||||
}
|
||||
|
||||
len = lws_buflist_next_segment_len(&ctx->buflist_comp, &buf);
|
||||
ilen_iused = len;
|
||||
use = 1;
|
||||
lwsl_debug("%s: %s: trying comp buflist %d\n", __func__,
|
||||
lws_wsi_tag(wsi), (int)len);
|
||||
}
|
||||
|
||||
if (!buf && ilen_iused)
|
||||
return 0;
|
||||
|
||||
lwsl_debug("%s: %s: pre-process: ilen_iused %d, olen_oused %d\n",
|
||||
__func__, lws_wsi_tag(wsi), (int)ilen_iused, (int)*olen_oused);
|
||||
|
||||
n = wsi->http.lcs->process(ctx, buf, &ilen_iused, *outbuf, olen_oused);
|
||||
|
||||
if (n && n != 1) {
|
||||
lwsl_err("%s: problem with compression\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ctx->may_have_more && ctx->final_on_input_side)
|
||||
|
||||
*wp = (unsigned int)(LWS_WRITE_HTTP_FINAL | ((*wp) & ~0x1fu));
|
||||
|
||||
lwsl_debug("%s: %s: more %d, ilen_iused %d\n", __func__, lws_wsi_tag(wsi),
|
||||
ctx->may_have_more, (int)ilen_iused);
|
||||
|
||||
if (use && ilen_iused) {
|
||||
/*
|
||||
* we were flushing stuff from the buflist head... account for
|
||||
* however much actually got processed by the compression
|
||||
* transform
|
||||
*/
|
||||
lws_buflist_use_segment(&ctx->buflist_comp, ilen_iused);
|
||||
lwsl_debug("%s: %s: marking %d of comp buflist as used "
|
||||
"(ctx->buflist_comp %p)\n", __func__,
|
||||
lws_wsi_tag(wsi), (int)len, ctx->buflist_comp);
|
||||
}
|
||||
|
||||
if (!use && ilen_iused != len) {
|
||||
/*
|
||||
* ...we were sending stuff from the caller directly and not
|
||||
* all of it got processed... stash on the buflist tail
|
||||
*/
|
||||
if (lws_buflist_append_segment(&ctx->buflist_comp,
|
||||
buf + ilen_iused, len - ilen_iused) < 0)
|
||||
return -1;
|
||||
|
||||
lwsl_debug("%s: buffering %d unused comp input\n", __func__,
|
||||
(int)(len - ilen_iused));
|
||||
}
|
||||
if (ctx->buflist_comp || ctx->may_have_more)
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
735
Kinc/Sources/kinc/libs/roles/http/cookie.c
Normal file
735
Kinc/Sources/kinc/libs/roles/http/cookie.c
Normal file
@ -0,0 +1,735 @@
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include "private-lib-core.h"
|
||||
|
||||
//#define LWS_COOKIE_DEBUG
|
||||
|
||||
#if defined(LWS_COOKIE_DEBUG)
|
||||
#define lwsl_cookie lwsl_notice
|
||||
#else
|
||||
#define lwsl_cookie lwsl_debug
|
||||
#endif
|
||||
|
||||
#define LWS_COOKIE_MAX_CACHE_NAME_LEN 128
|
||||
|
||||
#define lws_tolower(_c) (((_c) >= 'A' && (_c) <= 'Z') ? \
|
||||
(char)((_c) + 'a' - 'A') : \
|
||||
(char)(_c))
|
||||
|
||||
#define LWS_COOKIE_NSC_FORMAT "%.*s\t"\
|
||||
"%s\t"\
|
||||
"%.*s\t"\
|
||||
"%s\t"\
|
||||
"%llu\t"\
|
||||
"%.*s\t"\
|
||||
"%.*s"
|
||||
|
||||
static const char *const mon = "janfebmaraprnayjunjulaugsepoctnovdec";
|
||||
|
||||
enum lws_cookie_nsc_f {
|
||||
LWSC_NSC_DOMAIN,
|
||||
LWSC_NSC_HOSTONLY,
|
||||
LWSC_NSC_PATH,
|
||||
LWSC_NSC_SECURE,
|
||||
LWSC_NSC_EXPIRES,
|
||||
LWSC_NSC_NAME,
|
||||
LWSC_NSC_VALUE,
|
||||
|
||||
LWSC_NSC_COUNT,
|
||||
};
|
||||
|
||||
enum lws_cookie_elements {
|
||||
CE_DOMAIN,
|
||||
CE_PATH,
|
||||
CE_EXPIRES,
|
||||
CE_MAXAGE,
|
||||
CE_NAME,
|
||||
CE_VALUE,
|
||||
|
||||
CE_HOSTONLY, /* these are bool, NULL = 0, non-NULL = 1 */
|
||||
CE_SECURE,
|
||||
|
||||
CE_COUNT
|
||||
};
|
||||
|
||||
struct lws_cookie {
|
||||
const char *f[CE_COUNT];
|
||||
size_t l[CE_COUNT];
|
||||
|
||||
unsigned int httponly:1;
|
||||
};
|
||||
|
||||
static int
|
||||
lws_cookie_parse_date(const char *d, size_t len, time_t *t)
|
||||
{
|
||||
struct tm date;
|
||||
int offset = 0, i;
|
||||
|
||||
memset(&date, 0, sizeof(date));
|
||||
|
||||
while (len) {
|
||||
if (isalnum((int)*d)) {
|
||||
offset++;
|
||||
goto next;
|
||||
}
|
||||
switch (offset) {
|
||||
case 2:
|
||||
if (*d == ':' && len >= 6) {
|
||||
date.tm_hour = atoi(d - 2);
|
||||
if (date.tm_hour < 0 || date.tm_hour > 23)
|
||||
return -1;
|
||||
date.tm_min = atoi(d + 1);
|
||||
if (date.tm_min < 0 || date.tm_min > 60)
|
||||
return -1;
|
||||
date.tm_sec = atoi(d + 4);
|
||||
if (date.tm_sec < 0 || date.tm_sec > 61)
|
||||
/* leap second */
|
||||
return -1;
|
||||
|
||||
d += 6;
|
||||
len -= 6;
|
||||
offset = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!date.tm_mday) {
|
||||
date.tm_mday = atoi(d - 2);
|
||||
if (date.tm_mday < 1 || date.tm_mday > 31)
|
||||
return -1;
|
||||
goto next2;
|
||||
}
|
||||
|
||||
if (!date.tm_year) {
|
||||
date.tm_year = atoi(d - 2);
|
||||
if (date.tm_year < 0 || date.tm_year > 99)
|
||||
return -1;
|
||||
if (date.tm_year < 70)
|
||||
date.tm_year += 100;
|
||||
}
|
||||
goto next2;
|
||||
|
||||
case 3:
|
||||
for (i = 0; i < 36; i += 3) {
|
||||
if (lws_tolower(*(d - 3)) == mon[i] &&
|
||||
lws_tolower(*(d - 2)) == mon[i + 1] &&
|
||||
lws_tolower(*(d - 1)) == mon[i + 2]) {
|
||||
date.tm_mon = i / 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
goto next2;
|
||||
|
||||
case 4:
|
||||
if (!date.tm_year) {
|
||||
date.tm_year = atoi(d - 4);
|
||||
if (date.tm_year < 1601)
|
||||
return -1;
|
||||
date.tm_year -= 1900;
|
||||
}
|
||||
goto next2;
|
||||
|
||||
default:
|
||||
goto next2;
|
||||
}
|
||||
|
||||
next2:
|
||||
offset = 0;
|
||||
next:
|
||||
d++;
|
||||
len--;
|
||||
}
|
||||
|
||||
*t = mktime(&date);
|
||||
|
||||
if (*t < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
lws_cookie_rm_sws(const char **buf_p, size_t *len_p)
|
||||
{
|
||||
const char *buf;
|
||||
size_t len;
|
||||
|
||||
if (!buf_p || !*buf_p || !len_p || !*len_p) {
|
||||
lwsl_err("%s: false parameter\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
buf = *buf_p;
|
||||
len = *len_p;
|
||||
while (buf[0] == ' ' && len > 0) {
|
||||
buf++;
|
||||
len--;
|
||||
}
|
||||
while (buf[len - 1] == ' ' && len > 0)
|
||||
len--;
|
||||
|
||||
*buf_p = buf;
|
||||
*len_p = len;
|
||||
}
|
||||
|
||||
static int
|
||||
is_iprefix(const char *h, size_t hl, const char *n, size_t nl)
|
||||
{
|
||||
if (!h || !n || nl > hl)
|
||||
return 0;
|
||||
|
||||
while (nl) {
|
||||
nl--;
|
||||
if (lws_tolower(h[nl]) != lws_tolower(n[nl]))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_cookie_compile_cache_name(char *buf, size_t buf_len, struct lws_cookie *c)
|
||||
{
|
||||
if (!buf || !c->f[CE_DOMAIN] || !c->f[CE_PATH] || !c->f[CE_NAME] ||
|
||||
c->l[CE_DOMAIN] + c->l[CE_PATH] + c->l[CE_NAME] + 6 > buf_len)
|
||||
return -1;
|
||||
|
||||
memcpy(buf, c->f[CE_DOMAIN], c->l[CE_DOMAIN]);
|
||||
buf += c->l[CE_DOMAIN];
|
||||
*buf++ = '|';
|
||||
|
||||
memcpy(buf, c->f[CE_PATH], c->l[CE_PATH]);
|
||||
buf += c->l[CE_PATH];
|
||||
*buf++ = '|';
|
||||
|
||||
memcpy(buf, c->f[CE_NAME], c->l[CE_NAME]);
|
||||
buf += c->l[CE_NAME];
|
||||
*buf = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_cookie_parse_nsc(struct lws_cookie *c, const char *b, size_t l)
|
||||
{
|
||||
enum lws_cookie_nsc_f state = LWSC_NSC_DOMAIN;
|
||||
size_t n = 0;
|
||||
|
||||
if (!c || !b || l < 13)
|
||||
return -1;
|
||||
|
||||
memset(c, 0, sizeof(*c));
|
||||
lwsl_cookie("%s: parsing (%.*s) \n", __func__, (int)l, b);
|
||||
|
||||
while (l) {
|
||||
l--;
|
||||
if (b[n] != '\t' && l) {
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
switch (state) {
|
||||
case LWSC_NSC_DOMAIN:
|
||||
c->f[CE_DOMAIN] = b;
|
||||
c->l[CE_DOMAIN] = n;
|
||||
break;
|
||||
case LWSC_NSC_PATH:
|
||||
c->f[CE_PATH] = b;
|
||||
c->l[CE_PATH] = n;
|
||||
break;
|
||||
case LWSC_NSC_EXPIRES:
|
||||
c->f[CE_EXPIRES] = b;
|
||||
c->l[CE_EXPIRES] = n;
|
||||
break;
|
||||
case LWSC_NSC_NAME:
|
||||
c->f[CE_NAME] = b;
|
||||
c->l[CE_NAME] = n;
|
||||
break;
|
||||
|
||||
case LWSC_NSC_HOSTONLY:
|
||||
if (b[0] == 'T') {
|
||||
c->f[CE_HOSTONLY] = b;
|
||||
c->l[CE_HOSTONLY] = 1;
|
||||
}
|
||||
break;
|
||||
case LWSC_NSC_SECURE:
|
||||
if (b[0] == 'T') {
|
||||
c->f[CE_SECURE] = b;
|
||||
c->l[CE_SECURE] = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWSC_NSC_VALUE:
|
||||
c->f[CE_VALUE] = b;
|
||||
c->l[CE_VALUE] = n + 1;
|
||||
|
||||
for (n = 0; n < LWS_ARRAY_SIZE(c->f); n++)
|
||||
lwsl_cookie("%s: %d: %.*s\n", __func__,
|
||||
(int)n, (int)c->l[n], c->f[n]);
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
b += n + 1;
|
||||
n = 0;
|
||||
state++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_cookie_write_nsc(struct lws *wsi, struct lws_cookie *c)
|
||||
{
|
||||
char cache_name[LWS_COOKIE_MAX_CACHE_NAME_LEN];
|
||||
const char *ads, *path;
|
||||
struct lws_cache_ttl_lru *l1;
|
||||
struct client_info_stash *stash;
|
||||
char *cookie_string = NULL, *dl;
|
||||
/* 6 tabs + 20 for max time_t + 2 * TRUE/FALSE + null */
|
||||
size_t size = 6 + 20 + 10 + 1;
|
||||
time_t expires = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!wsi || !c)
|
||||
return -1;
|
||||
|
||||
l1 = wsi->a.context->l1;
|
||||
if (!l1 || !wsi->a.context->nsc)
|
||||
return -1;
|
||||
|
||||
stash = wsi->stash ? wsi->stash : lws_get_network_wsi(wsi)->stash;
|
||||
if (stash) {
|
||||
ads = stash->cis[CIS_ADDRESS];
|
||||
path = stash->cis[CIS_PATH];
|
||||
} else {
|
||||
ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
|
||||
path = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI);
|
||||
}
|
||||
if (!ads || !path)
|
||||
return -1;
|
||||
|
||||
if (!c->f[CE_NAME] || !c->f[CE_VALUE]) {
|
||||
lwsl_err("%s: malformed c\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!c->f[CE_EXPIRES]) {
|
||||
/*
|
||||
* Currently we just take the approach to reject session cookies
|
||||
*/
|
||||
lwsl_warn("%s: reject session cookies\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!c->f[CE_DOMAIN]) {
|
||||
c->f[CE_HOSTONLY] = "T";
|
||||
c->l[CE_HOSTONLY] = 1;
|
||||
c->f[CE_DOMAIN] = ads;
|
||||
c->l[CE_DOMAIN] = strlen(ads);
|
||||
}
|
||||
|
||||
if (!c->f[CE_PATH]) {
|
||||
c->f[CE_PATH] = path;
|
||||
c->l[CE_PATH] = strlen(path);
|
||||
dl = memchr(c->f[CE_PATH], '?', c->l[CE_PATH]);
|
||||
if (dl)
|
||||
c->l[CE_PATH] = (size_t)(dl - c->f[CE_PATH]);
|
||||
}
|
||||
|
||||
if (lws_cookie_compile_cache_name(cache_name, sizeof(cache_name), c))
|
||||
return -1;
|
||||
|
||||
if (c->f[CE_EXPIRES] &&
|
||||
lws_cookie_parse_date(c->f[CE_EXPIRES], c->l[CE_EXPIRES], &expires)) {
|
||||
lwsl_err("%s: can't parse date %.*s\n", __func__,
|
||||
(int)c->l[CE_EXPIRES], c->f[CE_EXPIRES]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size += c->l[CE_NAME] + c->l[CE_VALUE] + c->l[CE_DOMAIN] + c->l[CE_PATH];
|
||||
cookie_string = (char *)lws_malloc(size, __func__);
|
||||
if (!cookie_string) {
|
||||
lwsl_err("%s: OOM\n",__func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
lws_snprintf(cookie_string, size, LWS_COOKIE_NSC_FORMAT,
|
||||
(int)c->l[CE_DOMAIN], c->f[CE_DOMAIN],
|
||||
c->f[CE_HOSTONLY] ? "TRUE" : "FALSE",
|
||||
(int)c->l[CE_PATH], c->f[CE_PATH],
|
||||
c->f[CE_SECURE] ? "TRUE" : "FALSE",
|
||||
(unsigned long long)expires,
|
||||
(int)c->l[CE_NAME], c->f[CE_NAME],
|
||||
(int)c->l[CE_VALUE], c->f[CE_VALUE]);
|
||||
|
||||
lwsl_cookie("%s: name %s\n", __func__, cache_name);
|
||||
lwsl_cookie("%s: c %s\n", __func__, cookie_string);
|
||||
|
||||
if (lws_cache_write_through(l1, cache_name,
|
||||
(const uint8_t *)cookie_string,
|
||||
strlen(cookie_string),
|
||||
(lws_usec_t)((unsigned long long)expires *
|
||||
(lws_usec_t)LWS_US_PER_SEC), NULL)) {
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#if defined(LWS_COOKIE_DEBUG)
|
||||
char *po;
|
||||
if (lws_cache_item_get(l1, cache_name, (const void **)&po, &size) ||
|
||||
size != strlen(cookie_string) || memcmp(po, cookie_string, size)) {
|
||||
lwsl_err("%s: L1 '%s' missing\n", __func__, cache_name);
|
||||
}
|
||||
|
||||
if (lws_cache_item_get(wsi->a.context->nsc, cache_name,
|
||||
(const void **)&po, &size) ||
|
||||
size != strlen(cookie_string) ||
|
||||
memcmp(po, cookie_string, size)) {
|
||||
lwsl_err("%s: NSC '%s' missing, size %llu, po %s\n", __func__,
|
||||
cache_name, (unsigned long long)size, po);
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
lws_free(cookie_string);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_cookie_attach_cookies(struct lws *wsi, char *buf, char *end)
|
||||
{
|
||||
const char *domain, *path, *dl_domain, *dl_path, *po;
|
||||
char cache_name[LWS_COOKIE_MAX_CACHE_NAME_LEN];
|
||||
size_t domain_len, path_len, size, ret = 0;
|
||||
struct lws_cache_ttl_lru *l1;
|
||||
struct client_info_stash *stash;
|
||||
lws_cache_results_t cr;
|
||||
struct lws_cookie c;
|
||||
int hostdomain = 1;
|
||||
char *p, *p1;
|
||||
|
||||
if (!wsi)
|
||||
return -1;
|
||||
|
||||
stash = wsi->stash ? wsi->stash : lws_get_network_wsi(wsi)->stash;
|
||||
if (!stash || !stash->cis[CIS_ADDRESS] ||
|
||||
!stash->cis[CIS_PATH])
|
||||
return -1;
|
||||
|
||||
l1 = wsi->a.context->l1;
|
||||
if (!l1 || !wsi->a.context->nsc){
|
||||
lwsl_err("%s:no cookiejar\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
|
||||
domain = stash->cis[CIS_ADDRESS];
|
||||
path = stash->cis[CIS_PATH];
|
||||
|
||||
if (!domain || !path)
|
||||
return -1;
|
||||
|
||||
path_len = strlen(path);
|
||||
|
||||
/* remove query string if exist */
|
||||
dl_path = memchr(path, '?', path_len);
|
||||
if (dl_path)
|
||||
path_len = lws_ptr_diff_size_t(dl_path, path);
|
||||
|
||||
/* remove last slash if exist */
|
||||
if (path_len != 1 && path[path_len - 1] == '/')
|
||||
path_len--;
|
||||
|
||||
if (!path_len)
|
||||
return -1;
|
||||
|
||||
lwsl_cookie("%s: path %.*s len %d\n", __func__, (int)path_len, path, (int)path_len);
|
||||
|
||||
/* when dest buf is not provided, we only return size of cookie string */
|
||||
if (!buf || !end)
|
||||
p = NULL;
|
||||
else
|
||||
p = buf;
|
||||
|
||||
/* iterate through domain and path levels to find matching cookies */
|
||||
dl_domain = domain;
|
||||
while (dl_domain) {
|
||||
domain_len = strlen(domain);
|
||||
dl_domain = memchr(domain, '.', domain_len);
|
||||
/* don't match top level domain */
|
||||
if (!dl_domain)
|
||||
break;
|
||||
|
||||
if (domain_len + path_len + 6 > sizeof(cache_name))
|
||||
return -1;
|
||||
|
||||
/* compile key string "[domain]|[path]|*"" */
|
||||
p1 = cache_name;
|
||||
memcpy(p1, domain, domain_len);
|
||||
p1 += domain_len;
|
||||
*p1 = '|';
|
||||
p1++;
|
||||
memcpy(p1, path, path_len);
|
||||
p1 += path_len;
|
||||
*p1 = '|';
|
||||
p1++;
|
||||
*p1 = '*';
|
||||
p1++;
|
||||
*p1 = '\0';
|
||||
|
||||
lwsl_cookie("%s: looking for %s\n", __func__, cache_name);
|
||||
|
||||
if (!lws_cache_lookup(l1, cache_name,
|
||||
(const void **)&cr.ptr, &cr.size)) {
|
||||
|
||||
while (!lws_cache_results_walk(&cr)) {
|
||||
lwsl_cookie(" %s (%d)\n", (const char *)cr.tag,
|
||||
(int)cr.payload_len);
|
||||
|
||||
if (lws_cache_item_get(l1, (const char *)cr.tag,
|
||||
(const void **)&po, &size) ||
|
||||
lws_cookie_parse_nsc(&c, po, size)) {
|
||||
lwsl_err("%s: failed to get c '%s'\n",
|
||||
__func__, cr.tag);
|
||||
break;
|
||||
}
|
||||
|
||||
if (c.f[CE_HOSTONLY] && !hostdomain){
|
||||
lwsl_cookie("%s: not sending this\n",
|
||||
__func__);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p) {
|
||||
if (ret) {
|
||||
*p = ';';
|
||||
p++;
|
||||
*p = ' ';
|
||||
p++;
|
||||
}
|
||||
|
||||
memcpy(p, c.f[CE_NAME], c.l[CE_NAME]);
|
||||
p += c.l[CE_NAME];
|
||||
*p = '=';
|
||||
p++;
|
||||
memcpy(p, c.f[CE_VALUE], c.l[CE_VALUE]);
|
||||
p += c.l[CE_VALUE];
|
||||
}
|
||||
|
||||
if (ret)
|
||||
ret += 2;
|
||||
ret += c.l[CE_NAME] + 1 + c.l[CE_VALUE];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
domain = dl_domain + 1;
|
||||
hostdomain = 0;
|
||||
}
|
||||
|
||||
lwsl_notice("%s: c len (%d)\n", __func__, (int)ret);
|
||||
|
||||
return (int)ret;
|
||||
}
|
||||
|
||||
static struct {
|
||||
const char *const name;
|
||||
uint8_t len;
|
||||
} cft[] = {
|
||||
{ "domain=", 7 },
|
||||
{ "path=", 5 },
|
||||
{ "expires=", 8 },
|
||||
{ "max-age=", 8 },
|
||||
{ "httponly", 8 },
|
||||
{ "secure", 6 }
|
||||
};
|
||||
|
||||
int
|
||||
lws_parse_set_cookie(struct lws *wsi)
|
||||
{
|
||||
char *tk_head, *tk_end, *buf_head, *buf_end, *cookiep, *dl;
|
||||
struct lws_cache_ttl_lru *l1;
|
||||
struct lws_cookie c;
|
||||
size_t fl;
|
||||
int f, n;
|
||||
|
||||
if (!wsi)
|
||||
return -1;
|
||||
|
||||
l1 = wsi->a.context->l1;
|
||||
if (!l1)
|
||||
return -1;
|
||||
|
||||
f = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_SET_COOKIE];
|
||||
|
||||
while (f) {
|
||||
cookiep = wsi->http.ah->data + wsi->http.ah->frags[f].offset;
|
||||
fl = wsi->http.ah->frags[f].len;
|
||||
f = wsi->http.ah->frags[f].nfrag;
|
||||
|
||||
if (!cookiep || !fl)
|
||||
continue;
|
||||
|
||||
#if defined(LWS_COOKIE_DEBUG)
|
||||
lwsl_notice("%s:parsing: %.*s\n", __func__, (int)fl, cookiep);
|
||||
#endif
|
||||
|
||||
buf_head = cookiep;
|
||||
buf_end = cookiep + fl - 1;
|
||||
memset(&c, 0, sizeof(struct lws_cookie));
|
||||
|
||||
do {
|
||||
tk_head = buf_head;
|
||||
tk_end = memchr(buf_head, ';',
|
||||
(size_t)(buf_end - buf_head + 1));
|
||||
if (!tk_end) {
|
||||
tk_end = buf_end;
|
||||
buf_head = buf_end;
|
||||
} else {
|
||||
buf_head = tk_end + 1;
|
||||
tk_end--;
|
||||
}
|
||||
|
||||
if (c.f[CE_NAME])
|
||||
goto parse_av;
|
||||
|
||||
/*
|
||||
* find name value, remove leading trailing
|
||||
* WS and DQ for value
|
||||
*/
|
||||
|
||||
dl = memchr(tk_head, '=', lws_ptr_diff_size_t(tk_end,
|
||||
tk_head + 1));
|
||||
if (!dl || dl == tk_head)
|
||||
return -1;
|
||||
|
||||
c.f[CE_NAME] = tk_head;
|
||||
c.l[CE_NAME] = lws_ptr_diff_size_t(dl, tk_head);
|
||||
lws_cookie_rm_sws(&c.f[CE_NAME], &c.l[CE_NAME]);
|
||||
|
||||
if (!c.l[CE_NAME])
|
||||
return -1;
|
||||
|
||||
lwsl_cookie("%s: c name l %d v:%.*s\n", __func__,
|
||||
(int)c.l[CE_NAME],
|
||||
(int)c.l[CE_NAME], c.f[CE_NAME]);
|
||||
c.f[CE_VALUE] = dl + 1;
|
||||
c.l[CE_VALUE] = lws_ptr_diff_size_t(tk_end,
|
||||
c.f[CE_VALUE]) + 1;
|
||||
|
||||
lws_cookie_rm_sws(&c.f[CE_VALUE], &c.l[CE_VALUE]);
|
||||
if (c.l[CE_VALUE] >= 2 && c.f[CE_VALUE][0] == '\"') {
|
||||
c.f[CE_VALUE]++;
|
||||
c.l[CE_VALUE] -= 2;
|
||||
}
|
||||
lwsl_cookie("%s: c value l %d v:%.*s\n", __func__,
|
||||
(int)c.l[CE_VALUE], (int)c.l[CE_VALUE],
|
||||
c.f[CE_VALUE]);
|
||||
continue;
|
||||
|
||||
parse_av:
|
||||
while (*tk_head == ' ') {
|
||||
if (tk_head == tk_end)
|
||||
return -1;
|
||||
|
||||
tk_head++;
|
||||
}
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(cft); n++) {
|
||||
if (lws_tolower(*tk_head) != cft[n].name[0])
|
||||
continue;
|
||||
|
||||
if (!is_iprefix(tk_head,
|
||||
lws_ptr_diff_size_t(tk_end,
|
||||
tk_head) + 1,
|
||||
cft[n].name, cft[n].len))
|
||||
continue;
|
||||
|
||||
if (n == 4 || n == 5) {
|
||||
c.f[n] = "T";
|
||||
c.l[n] = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
c.f[n] = tk_head + cft[n].len;
|
||||
c.l[n] = lws_ptr_diff_size_t(tk_end, c.f[n]) + 1;
|
||||
lws_cookie_rm_sws(&c.f[n], &c.l[n]);
|
||||
|
||||
if (n == CE_DOMAIN && c.l[0] &&
|
||||
c.f[n][0] == '.'){
|
||||
c.f[n]++;
|
||||
c.l[n]--;
|
||||
}
|
||||
|
||||
lwsl_cookie("%s: %s l %d v:%.*s\n", __func__,
|
||||
cft[n].name, (int)c.l[n],
|
||||
(int)c.l[n], c.f[n]);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (tk_end != buf_end);
|
||||
|
||||
if (lws_cookie_write_nsc(wsi, &c))
|
||||
lwsl_err("%s:failed to write nsc\n", __func__);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_cookie_send_cookies(struct lws *wsi, char **pp, char *end)
|
||||
{
|
||||
char *p;
|
||||
int size;
|
||||
|
||||
if (!wsi || !pp || !(*pp) || !end)
|
||||
return -1;
|
||||
|
||||
size = lws_cookie_attach_cookies(wsi, NULL, NULL);
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
if (size < 0) {
|
||||
lwsl_err("%s:failed to get cookie string size\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lwsl_notice("%s: size %d\n", __func__, size);
|
||||
|
||||
#if defined(LWS_COOKIE_DEBUG)
|
||||
char *p_dbg = *pp;
|
||||
#endif
|
||||
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COOKIE, NULL, size,
|
||||
(unsigned char **)pp, (unsigned char *)end))
|
||||
return -1;
|
||||
|
||||
#if defined(LWS_COOKIE_DEBUG)
|
||||
lwsl_notice("%s: dummy copy (%.*s) \n", __func__, (int)(*pp - p_dbg), p_dbg);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (lws_wsi_is_h2(wsi))
|
||||
p = *pp - size;
|
||||
else
|
||||
#endif
|
||||
p = *pp - size - 2;
|
||||
|
||||
if (lws_cookie_attach_cookies(wsi, p, p + size) <= 0) {
|
||||
lwsl_err("%s:failed to attach cookies\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(LWS_COOKIE_DEBUG)
|
||||
lwsl_notice("%s: real copy (%.*s) total len %d\n", __func__, (int)(*pp - p_dbg), p_dbg, (int)(*pp - p_dbg));
|
||||
lwsl_hexdump_notice(p_dbg, (size_t)(*pp - p_dbg));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
225
Kinc/Sources/kinc/libs/roles/http/date.c
Normal file
225
Kinc/Sources/kinc/libs/roles/http/date.c
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* RFC7231 date string generation and parsing
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
/*
|
||||
* To avoid needless pointers, we encode these in one string using the fact
|
||||
* they're 3 chars each to index it
|
||||
*/
|
||||
|
||||
static const char *const s =
|
||||
"JanFebMarAprMayJunJulAugSepOctNovDecMonTueWedThuFriSatSun";
|
||||
|
||||
static int
|
||||
lws_http_date_render(char *buf, size_t len, const struct tm *tm)
|
||||
{
|
||||
const char *w = s + 36 + (3 * tm->tm_wday), *m = s + (3 * tm->tm_mon);
|
||||
|
||||
if (len < 29)
|
||||
return -1;
|
||||
|
||||
lws_snprintf(buf, len, "%c%c%c, %02d %c%c%c %d %02d:%02d:%02d GMT",
|
||||
w[0], w[1], w[2], tm->tm_mday, m[0], m[1], m[2],
|
||||
1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
lws_http_date_render_from_unix(char *buf, size_t len, const time_t *t)
|
||||
{
|
||||
#if defined(LWS_HAVE_GMTIME_R)
|
||||
struct tm tmp;
|
||||
struct tm *tm = gmtime_r(t, &tmp);
|
||||
#else
|
||||
struct tm *tm = gmtime(t);
|
||||
#endif
|
||||
if (!tm)
|
||||
return -1;
|
||||
|
||||
if (lws_http_date_render(buf, len, tm))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_http_date_parse(const char *b, size_t len, struct tm *tm)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (len < 29)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* We reject anything that isn't a properly-formatted RFC7231 date, eg
|
||||
*
|
||||
* Tue, 15 Nov 1994 08:12:31 GMT
|
||||
*/
|
||||
|
||||
if (b[3] != ',' || b[4] != ' ' || b[7] != ' ' || b[11] != ' ' ||
|
||||
b[16] != ' ' || b[19] != ':' || b[22] != ':' || b[25] != ' ' ||
|
||||
b[26] != 'G' || b[27] != 'M' || b[28] != 'T')
|
||||
return -1;
|
||||
|
||||
memset(tm, 0, sizeof(*tm));
|
||||
|
||||
for (n = 36; n < 57; n += 3)
|
||||
if (b[0] == s[n] && b[1] == s[n + 1] && b[2] == s[n + 2])
|
||||
break;
|
||||
else
|
||||
tm->tm_wday++;
|
||||
|
||||
if (n == 57)
|
||||
return -1;
|
||||
|
||||
for (n = 0; n < 36; n += 3)
|
||||
if (b[8] == s[n] && b[9] == s[n + 1] && b[10] == s[n + 2])
|
||||
break;
|
||||
else
|
||||
tm->tm_mon++;
|
||||
|
||||
if (n == 36)
|
||||
return -1;
|
||||
|
||||
tm->tm_mday = atoi(b + 5);
|
||||
n = atoi(b + 12);
|
||||
if (n < 1900)
|
||||
return -1;
|
||||
tm->tm_year = n - 1900;
|
||||
|
||||
n = atoi(b + 17);
|
||||
if (n < 0 || n > 23)
|
||||
return -1;
|
||||
tm->tm_hour = n;
|
||||
|
||||
n = atoi(b + 20);
|
||||
if (n < 0 || n > 60)
|
||||
return -1;
|
||||
tm->tm_min = n;
|
||||
|
||||
n = atoi(b + 23);
|
||||
if (n < 0 || n > 61) /* leap second */
|
||||
return -1;
|
||||
tm->tm_sec = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_http_date_parse_unix(const char *b, size_t len, time_t *t)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
if (lws_http_date_parse(b, len, &tm))
|
||||
return -1;
|
||||
|
||||
#if defined(WIN32)
|
||||
*t = _mkgmtime(&tm);
|
||||
#else
|
||||
#if defined(LWS_HAVE_TIMEGM)
|
||||
*t = timegm(&tm);
|
||||
#else
|
||||
/* this is a poor fallback since it uses localtime zone */
|
||||
*t = mktime(&tm);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return (int)(*t == -1 ? -1 : 0);
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
|
||||
int
|
||||
lws_http_check_retry_after(struct lws *wsi, lws_usec_t *us_interval_in_out)
|
||||
{
|
||||
size_t len = (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_RETRY_AFTER);
|
||||
char *p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_RETRY_AFTER);
|
||||
lws_usec_t u;
|
||||
time_t t, td;
|
||||
|
||||
if (!p)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* There are two arg styles for RETRY_AFTER specified in RFC7231 7.1.3,
|
||||
* either a full absolute second-resolution date/time, or an integer
|
||||
* interval
|
||||
*
|
||||
* Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
|
||||
* Retry-After: 120
|
||||
*/
|
||||
|
||||
if (len < 9)
|
||||
u = ((lws_usec_t)(time_t)atoi(p)) * LWS_USEC_PER_SEC;
|
||||
else {
|
||||
|
||||
if (lws_http_date_parse_unix(p, len, &t))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* If possible, look for DATE from the server as well, so we
|
||||
* can calculate the interval it thinks it is giving us,
|
||||
* eliminating problems from server - client clock skew
|
||||
*/
|
||||
|
||||
time(&td);
|
||||
len = (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_DATE);
|
||||
if (len) {
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_DATE);
|
||||
/* if this fails, it leaves td as client time */
|
||||
(void)lws_http_date_parse_unix(p, len, &td);
|
||||
}
|
||||
|
||||
if (td >= t)
|
||||
/*
|
||||
* if he's effectively giving us a 0 or negative
|
||||
* interval, just ignore the whole thing and keep the
|
||||
* incoming interval
|
||||
*/
|
||||
return 1;
|
||||
|
||||
u = ((lws_usec_t)(t - td)) * LWS_USEC_PER_SEC;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are only willing to increase the incoming interval, not
|
||||
* decrease it
|
||||
*/
|
||||
|
||||
if (u < *us_interval_in_out)
|
||||
/* keep the incoming interval */
|
||||
return 1;
|
||||
|
||||
/* use the computed interval */
|
||||
*us_interval_in_out = u;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
666
Kinc/Sources/kinc/libs/roles/http/header.c
Normal file
666
Kinc/Sources/kinc/libs/roles/http/header.c
Normal file
@ -0,0 +1,666 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
#include "lextable-strings.h"
|
||||
|
||||
|
||||
const unsigned char *
|
||||
lws_token_to_string(enum lws_token_indexes token)
|
||||
{
|
||||
if ((unsigned int)token >= LWS_ARRAY_SIZE(set))
|
||||
return NULL;
|
||||
|
||||
return (unsigned char *)set[token];
|
||||
}
|
||||
|
||||
/*
|
||||
* Return http header index if one matches slen chars of s, or -1
|
||||
*/
|
||||
|
||||
int
|
||||
lws_http_string_to_known_header(const char *s, size_t slen)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(set); n++)
|
||||
if (!strncmp(set[n], s, slen))
|
||||
return n;
|
||||
|
||||
return LWS_HTTP_NO_KNOWN_HEADER;
|
||||
}
|
||||
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
int
|
||||
lws_wsi_is_h2(struct lws *wsi)
|
||||
{
|
||||
return wsi->upgraded_to_http2 ||
|
||||
wsi->mux_substream ||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
wsi->client_mux_substream ||
|
||||
#endif
|
||||
lwsi_role_h2(wsi) ||
|
||||
lwsi_role_h2_ENCAPSULATION(wsi);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
|
||||
const unsigned char *value, int length,
|
||||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (lws_wsi_is_h2(wsi))
|
||||
return lws_add_http2_header_by_name(wsi, name,
|
||||
value, length, p, end);
|
||||
#else
|
||||
(void)wsi;
|
||||
#endif
|
||||
if (name) {
|
||||
char has_colon = 0;
|
||||
while (*p < end && *name) {
|
||||
has_colon = has_colon || *name == ':';
|
||||
*((*p)++) = *name++;
|
||||
}
|
||||
if (*p + (has_colon ? 1 : 2) >= end)
|
||||
return 1;
|
||||
if (!has_colon)
|
||||
*((*p)++) = ':';
|
||||
*((*p)++) = ' ';
|
||||
}
|
||||
if (*p + length + 3 >= end)
|
||||
return 1;
|
||||
|
||||
if (value)
|
||||
memcpy(*p, value, (unsigned int)length);
|
||||
*p += length;
|
||||
*((*p)++) = '\x0d';
|
||||
*((*p)++) = '\x0a';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
|
||||
unsigned char *end)
|
||||
{
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (lws_wsi_is_h2(wsi))
|
||||
return 0;
|
||||
#else
|
||||
(void)wsi;
|
||||
#endif
|
||||
if ((lws_intptr_t)(end - *p) < 3)
|
||||
return 1;
|
||||
*((*p)++) = '\x0d';
|
||||
*((*p)++) = '\x0a';
|
||||
|
||||
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, (unsigned int)len, LWS_WRITE_HTTP_HEADERS) != len)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
|
||||
const unsigned char *value, int length,
|
||||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
const unsigned char *name;
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (lws_wsi_is_h2(wsi))
|
||||
return lws_add_http2_header_by_token(wsi, token, value,
|
||||
length, p, end);
|
||||
#endif
|
||||
name = lws_token_to_string(token);
|
||||
if (!name)
|
||||
return 1;
|
||||
|
||||
return lws_add_http_header_by_name(wsi, name, value, length, p, end);
|
||||
}
|
||||
|
||||
int
|
||||
lws_add_http_header_content_length(struct lws *wsi,
|
||||
lws_filepos_t content_length,
|
||||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
char b[24];
|
||||
int n;
|
||||
|
||||
n = lws_snprintf(b, sizeof(b) - 1, "%llu", (unsigned long long)content_length);
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
|
||||
(unsigned char *)b, n, p, end))
|
||||
return 1;
|
||||
wsi->http.tx_content_length = content_length;
|
||||
wsi->http.tx_content_remain = content_length;
|
||||
|
||||
lwsl_info("%s: %s: tx_content_length/remain %llu\n", __func__,
|
||||
lws_wsi_tag(wsi), (unsigned long long)content_length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
|
||||
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)
|
||||
{
|
||||
const char *ka[] = { "close", "keep-alive" };
|
||||
int types[] = { HTTP_CONNECTION_CLOSE, HTTP_CONNECTION_KEEP_ALIVE },
|
||||
t = 0;
|
||||
|
||||
if (lws_add_http_header_status(wsi, code, p, end))
|
||||
return 1;
|
||||
|
||||
if (content_type &&
|
||||
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 defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
||||
if (!wsi->http.lcs && content_type &&
|
||||
(!strncmp(content_type, "text/", 5) ||
|
||||
!strcmp(content_type, "application/javascript") ||
|
||||
!strcmp(content_type, "image/svg+xml")))
|
||||
lws_http_compression_apply(wsi, NULL, p, end, 0);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* if we decided to compress it, we don't know the content length...
|
||||
* the compressed data will go out chunked on h1
|
||||
*/
|
||||
if (
|
||||
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
||||
!wsi->http.lcs &&
|
||||
#endif
|
||||
content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN) {
|
||||
if (lws_add_http_header_content_length(wsi, content_len,
|
||||
p, end))
|
||||
return 1;
|
||||
} else {
|
||||
/* there was no length... it normally means CONNECTION_CLOSE */
|
||||
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
||||
|
||||
if (!wsi->mux_substream && wsi->http.lcs) {
|
||||
/* so...
|
||||
* - h1 connection
|
||||
* - http compression transform active
|
||||
* - did not send content length
|
||||
*
|
||||
* then mark as chunked...
|
||||
*/
|
||||
wsi->http.comp_ctx.chunking = 1;
|
||||
if (lws_add_http_header_by_token(wsi,
|
||||
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
|
||||
(unsigned char *)"chunked", 7, p, end))
|
||||
return -1;
|
||||
|
||||
/* ... but h1 compression is chunked, if active we can
|
||||
* still pipeline
|
||||
*/
|
||||
if (wsi->http.lcs &&
|
||||
wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE)
|
||||
t = 1;
|
||||
}
|
||||
#endif
|
||||
if (!wsi->mux_substream) {
|
||||
if (lws_add_http_header_by_token(wsi,
|
||||
WSI_TOKEN_CONNECTION,
|
||||
(unsigned char *)ka[t],
|
||||
(int)strlen(ka[t]), p, end))
|
||||
return 1;
|
||||
|
||||
wsi->http.conn_type = (enum http_conn_type)types[t];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const err400[] = {
|
||||
"Bad Request",
|
||||
"Unauthorized",
|
||||
"Payment Required",
|
||||
"Forbidden",
|
||||
"Not Found",
|
||||
"Method Not Allowed",
|
||||
"Not Acceptable",
|
||||
"Proxy Auth Required",
|
||||
"Request Timeout",
|
||||
"Conflict",
|
||||
"Gone",
|
||||
"Length Required",
|
||||
"Precondition Failed",
|
||||
"Request Entity Too Large",
|
||||
"Request URI too Long",
|
||||
"Unsupported Media Type",
|
||||
"Requested Range Not Satisfiable",
|
||||
"Expectation Failed"
|
||||
};
|
||||
|
||||
static const char * const err500[] = {
|
||||
"Internal Server Error",
|
||||
"Not Implemented",
|
||||
"Bad Gateway",
|
||||
"Service Unavailable",
|
||||
"Gateway Timeout",
|
||||
"HTTP Version Not Supported"
|
||||
};
|
||||
|
||||
/* security best practices from Mozilla Observatory */
|
||||
|
||||
static const
|
||||
struct lws_protocol_vhost_options pvo_hsbph[] = {{
|
||||
NULL, NULL, "referrer-policy:", "no-referrer"
|
||||
}, {
|
||||
&pvo_hsbph[0], NULL, "x-frame-options:", "deny"
|
||||
}, {
|
||||
&pvo_hsbph[1], NULL, "x-xss-protection:", "1; mode=block"
|
||||
}, {
|
||||
&pvo_hsbph[2], NULL, "x-content-type-options:", "nosniff"
|
||||
}, {
|
||||
&pvo_hsbph[3], NULL, "content-security-policy:",
|
||||
"default-src 'none'; img-src 'self' data: ; "
|
||||
"script-src 'self'; font-src 'self'; "
|
||||
"style-src 'self'; connect-src 'self' ws: wss:; "
|
||||
"frame-ancestors 'none'; base-uri 'none';"
|
||||
"form-action 'self';"
|
||||
}};
|
||||
|
||||
int
|
||||
lws_add_http_header_status(struct lws *wsi, unsigned int _code,
|
||||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
static const char * const hver[] = {
|
||||
"HTTP/1.0", "HTTP/1.1", "HTTP/2"
|
||||
};
|
||||
const struct lws_protocol_vhost_options *headers;
|
||||
unsigned int code = _code & LWSAHH_CODE_MASK;
|
||||
const char *description = "", *p1;
|
||||
unsigned char code_and_desc[60];
|
||||
int n;
|
||||
|
||||
wsi->http.response_code = code;
|
||||
#ifdef LWS_WITH_ACCESS_LOG
|
||||
wsi->http.access_log.response = (int)code;
|
||||
#endif
|
||||
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (lws_wsi_is_h2(wsi)) {
|
||||
n = lws_add_http2_header_status(wsi, code, p, end);
|
||||
if (n)
|
||||
return n;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (code >= 400 && code < (400 + LWS_ARRAY_SIZE(err400)))
|
||||
description = err400[code - 400];
|
||||
if (code >= 500 && code < (500 + LWS_ARRAY_SIZE(err500)))
|
||||
description = err500[code - 500];
|
||||
|
||||
if (code == 100)
|
||||
description = "Continue";
|
||||
if (code == 200)
|
||||
description = "OK";
|
||||
if (code == 304)
|
||||
description = "Not Modified";
|
||||
else
|
||||
if (code >= 300 && code < 400)
|
||||
description = "Redirect";
|
||||
|
||||
if (wsi->http.request_version < LWS_ARRAY_SIZE(hver))
|
||||
p1 = hver[wsi->http.request_version];
|
||||
else
|
||||
p1 = hver[0];
|
||||
|
||||
n = lws_snprintf((char *)code_and_desc,
|
||||
sizeof(code_and_desc) - 1, "%s %u %s",
|
||||
p1, code, description);
|
||||
|
||||
if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p,
|
||||
end))
|
||||
return 1;
|
||||
}
|
||||
|
||||
headers = wsi->a.vhost->headers;
|
||||
while (headers) {
|
||||
if (lws_add_http_header_by_name(wsi,
|
||||
(const unsigned char *)headers->name,
|
||||
(unsigned char *)headers->value,
|
||||
(int)strlen(headers->value), p, end))
|
||||
return 1;
|
||||
|
||||
headers = headers->next;
|
||||
}
|
||||
|
||||
if (wsi->a.vhost->options &
|
||||
LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE) {
|
||||
headers = &pvo_hsbph[LWS_ARRAY_SIZE(pvo_hsbph) - 1];
|
||||
while (headers) {
|
||||
if (lws_add_http_header_by_name(wsi,
|
||||
(const unsigned char *)headers->name,
|
||||
(unsigned char *)headers->value,
|
||||
(int)strlen(headers->value), p, end))
|
||||
return 1;
|
||||
|
||||
headers = headers->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (wsi->a.context->server_string &&
|
||||
!(_code & LWSAHH_FLAG_NO_SERVER_NAME)) {
|
||||
assert(wsi->a.context->server_string_len > 0);
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER,
|
||||
(unsigned char *)wsi->a.context->server_string,
|
||||
wsi->a.context->server_string_len, p, end))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (wsi->a.vhost->options & LWS_SERVER_OPTION_STS)
|
||||
if (lws_add_http_header_by_name(wsi, (unsigned char *)
|
||||
"Strict-Transport-Security:",
|
||||
(unsigned char *)"max-age=15768000 ; "
|
||||
"includeSubDomains", 36, p, end))
|
||||
return 1;
|
||||
|
||||
if (*p >= (end - 2)) {
|
||||
lwsl_err("%s: reached end of buffer\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_return_http_status(struct lws *wsi, unsigned int code,
|
||||
const char *html_body)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
unsigned char *p = pt->serv_buf + LWS_PRE;
|
||||
unsigned char *start = p;
|
||||
unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
|
||||
char *body = (char *)start + context->pt_serv_buf_size - 512;
|
||||
int n = 0, m = 0, len;
|
||||
char slen[20];
|
||||
|
||||
if (!wsi->a.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->a.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->a.vhost->http.error_document_404,
|
||||
(int)strlen(wsi->a.vhost->http.error_document_404),
|
||||
&p, end) > 0)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
/* if the redirect failed, just do a simple status */
|
||||
p = start;
|
||||
|
||||
if (!html_body)
|
||||
html_body = "";
|
||||
|
||||
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 *)"text/html", 9,
|
||||
&p, end))
|
||||
return 1;
|
||||
|
||||
len = lws_snprintf(body, 510, "<html><head>"
|
||||
"<meta charset=utf-8 http-equiv=\"Content-Language\" "
|
||||
"content=\"en\"/>"
|
||||
"<link rel=\"stylesheet\" type=\"text/css\" "
|
||||
"href=\"/error.css\"/>"
|
||||
"</head><body><h1>%u</h1>%s</body></html>", code, html_body);
|
||||
|
||||
|
||||
n = lws_snprintf(slen, 12, "%d", len);
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
|
||||
(unsigned char *)slen, n, &p, end))
|
||||
return 1;
|
||||
|
||||
if (lws_finalize_http_header(wsi, &p, end))
|
||||
return 1;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (wsi->mux_substream) {
|
||||
|
||||
/*
|
||||
* for HTTP/2, the headers must be sent separately, since they
|
||||
* go out in their own frame. That puts us in a bind that
|
||||
* we won't always be able to get away with two lws_write()s in
|
||||
* sequence, since the first may use up the writability due to
|
||||
* the pipe being choked or SSL_WANT_.
|
||||
*
|
||||
* However we do need to send the human-readable body, and the
|
||||
* END_STREAM.
|
||||
*
|
||||
* Solve it by writing the headers now...
|
||||
*/
|
||||
m = lws_write(wsi, start, lws_ptr_diff_size_t(p, start),
|
||||
LWS_WRITE_HTTP_HEADERS);
|
||||
if (m != lws_ptr_diff(p, start))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* ... but stash the body and send it as a priority next
|
||||
* handle_POLLOUT
|
||||
*/
|
||||
wsi->http.tx_content_length = (unsigned int)len;
|
||||
wsi->http.tx_content_remain = (unsigned int)len;
|
||||
|
||||
wsi->h2.pending_status_body = lws_malloc((unsigned int)len + LWS_PRE + 1,
|
||||
"pending status body");
|
||||
if (!wsi->h2.pending_status_body)
|
||||
return -1;
|
||||
|
||||
strcpy(wsi->h2.pending_status_body + LWS_PRE, body);
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return 0;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/*
|
||||
* for http/1, we can just append the body after the finalized
|
||||
* headers and send it all in one go.
|
||||
*/
|
||||
|
||||
n = lws_ptr_diff(p, start) + len;
|
||||
memcpy(p, body, (unsigned int)len);
|
||||
m = lws_write(wsi, start, (unsigned int)n, LWS_WRITE_HTTP);
|
||||
if (m != n)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return m != n;
|
||||
}
|
||||
|
||||
int
|
||||
lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
|
||||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
unsigned char *start = *p;
|
||||
|
||||
if (lws_add_http_header_status(wsi, (unsigned int)code, p, end))
|
||||
return -1;
|
||||
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION, loc, len,
|
||||
p, end))
|
||||
return -1;
|
||||
/*
|
||||
* if we're going with http/1.1 and keepalive, we have to give fake
|
||||
* content metadata so the client knows we completed the transaction and
|
||||
* it can do the redirect...
|
||||
*/
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
|
||||
(unsigned char *)"text/html", 9, p,
|
||||
end))
|
||||
return -1;
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
|
||||
(unsigned char *)"0", 1, p, end))
|
||||
return -1;
|
||||
|
||||
if (lws_finalize_http_header(wsi, p, end))
|
||||
return -1;
|
||||
|
||||
return lws_write(wsi, start, lws_ptr_diff_size_t(*p, start),
|
||||
LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
||||
int
|
||||
lws_http_compression_apply(struct lws *wsi, const char *name,
|
||||
unsigned char **p, unsigned char *end, char decomp)
|
||||
{
|
||||
(void)wsi;
|
||||
(void)name;
|
||||
(void)p;
|
||||
(void)end;
|
||||
(void)decomp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
lws_http_headers_detach(struct lws *wsi)
|
||||
{
|
||||
return lws_header_table_detach(wsi, 0);
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
|
||||
void
|
||||
lws_sul_http_ah_lifecheck(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
struct allocated_headers *ah;
|
||||
struct lws_context_per_thread *pt = lws_container_of(sul,
|
||||
struct lws_context_per_thread, sul_ah_lifecheck);
|
||||
struct lws *wsi;
|
||||
time_t now;
|
||||
int m;
|
||||
|
||||
now = time(NULL);
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
|
||||
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->a.vhost &&
|
||||
(now - ah->assigned) <
|
||||
ah->wsi->a.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("%s: ah excessive hold: wsi %p\n"
|
||||
" peer address: %s\n"
|
||||
" ah pos %lu\n", __func__, lws_wsi_tag(wsi),
|
||||
buf, (unsigned long)ah->pos);
|
||||
buf[0] = '\0';
|
||||
m = 0;
|
||||
do {
|
||||
c = lws_token_to_string((enum lws_token_indexes)m);
|
||||
if (!c)
|
||||
break;
|
||||
if (!(*c))
|
||||
break;
|
||||
|
||||
len = lws_hdr_total_length(wsi, (enum lws_token_indexes)m);
|
||||
if (!len || len > (int)sizeof(buf) - 1) {
|
||||
m++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lws_hdr_copy(wsi, buf, sizeof buf, (enum lws_token_indexes)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 */
|
||||
|
||||
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"excessive ah");
|
||||
|
||||
ah = pt->http.ah_list;
|
||||
}
|
||||
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
#endif
|
||||
136
Kinc/Sources/kinc/libs/roles/http/lextable-strings.h
Normal file
136
Kinc/Sources/kinc/libs/roles/http/lextable-strings.h
Normal file
@ -0,0 +1,136 @@
|
||||
/* set of parsable strings -- ALL LOWER CASE */
|
||||
|
||||
static const char * const set[] = {
|
||||
"get ",
|
||||
"post ",
|
||||
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"options ",
|
||||
#endif
|
||||
"host:",
|
||||
"connection:",
|
||||
"upgrade:",
|
||||
"origin:",
|
||||
#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"sec-websocket-draft:",
|
||||
#endif
|
||||
"\x0d\x0a",
|
||||
|
||||
#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"sec-websocket-extensions:",
|
||||
"sec-websocket-key1:",
|
||||
"sec-websocket-key2:",
|
||||
"sec-websocket-protocol:",
|
||||
|
||||
"sec-websocket-accept:",
|
||||
"sec-websocket-nonce:",
|
||||
#endif
|
||||
"http/1.1 ",
|
||||
#if defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"http2-settings:",
|
||||
#endif
|
||||
|
||||
"accept:",
|
||||
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"access-control-request-headers:",
|
||||
#endif
|
||||
"if-modified-since:",
|
||||
"if-none-match:",
|
||||
"accept-encoding:",
|
||||
"accept-language:",
|
||||
"pragma:",
|
||||
"cache-control:",
|
||||
"authorization:",
|
||||
"cookie:",
|
||||
"content-length:",
|
||||
"content-type:",
|
||||
"date:",
|
||||
"range:",
|
||||
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"referer:",
|
||||
#endif
|
||||
#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"sec-websocket-key:",
|
||||
"sec-websocket-version:",
|
||||
"sec-websocket-origin:",
|
||||
#endif
|
||||
#if defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
":authority",
|
||||
":method",
|
||||
":path",
|
||||
":scheme",
|
||||
":status",
|
||||
#endif
|
||||
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"accept-charset:",
|
||||
#endif
|
||||
"accept-ranges:",
|
||||
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"access-control-allow-origin:",
|
||||
#endif
|
||||
"age:",
|
||||
"allow:",
|
||||
"content-disposition:",
|
||||
"content-encoding:",
|
||||
"content-language:",
|
||||
"content-location:",
|
||||
"content-range:",
|
||||
"etag:",
|
||||
"expect:",
|
||||
"expires:",
|
||||
"from:",
|
||||
"if-match:",
|
||||
"if-range:",
|
||||
"if-unmodified-since:",
|
||||
"last-modified:",
|
||||
"link:",
|
||||
"location:",
|
||||
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"max-forwards:",
|
||||
"proxy-authenticate:",
|
||||
"proxy-authorization:",
|
||||
#endif
|
||||
"refresh:",
|
||||
"retry-after:",
|
||||
"server:",
|
||||
"set-cookie:",
|
||||
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"strict-transport-security:",
|
||||
#endif
|
||||
"transfer-encoding:",
|
||||
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"user-agent:",
|
||||
"vary:",
|
||||
"via:",
|
||||
"www-authenticate:",
|
||||
#endif
|
||||
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"patch",
|
||||
"put",
|
||||
"delete",
|
||||
#endif
|
||||
|
||||
"uri-args", /* fake header used for uri-only storage */
|
||||
|
||||
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"proxy ",
|
||||
"x-real-ip:",
|
||||
#endif
|
||||
"http/1.0 ",
|
||||
|
||||
"x-forwarded-for:",
|
||||
"connect ",
|
||||
"head ",
|
||||
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
"te:", /* http/2 wants it to reject it */
|
||||
"replay-nonce:", /* ACME */
|
||||
#endif
|
||||
#if defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
|
||||
":protocol", /* defined in mcmanus-httpbis-h2-ws-02 */
|
||||
#endif
|
||||
|
||||
"x-auth-token:",
|
||||
"x-amzn-dss-signature:",
|
||||
|
||||
"", /* not matchable */
|
||||
|
||||
};
|
||||
6896
Kinc/Sources/kinc/libs/roles/http/lextable.h
Normal file
6896
Kinc/Sources/kinc/libs/roles/http/lextable.h
Normal file
File diff suppressed because it is too large
Load Diff
500
Kinc/Sources/kinc/libs/roles/http/minilex.c
Normal file
500
Kinc/Sources/kinc/libs/roles/http/minilex.c
Normal file
@ -0,0 +1,500 @@
|
||||
/*
|
||||
* minilex.c
|
||||
*
|
||||
* High efficiency lexical state parser
|
||||
*
|
||||
* Copyright (C)2011-2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Licensed under MIT
|
||||
*
|
||||
* Usage: gcc minilex.c -o minilex && ./minilex > lextable.h
|
||||
*
|
||||
* Run it twice to test parsing on the generated table on stderr
|
||||
*
|
||||
* Whoo this got a bit complicated by lws-buildtime deselection of some
|
||||
* headers optionally. There are 3 x vars, UNCOMMON, WS, H2 so we make
|
||||
* eight copies of the lextable selected by the appropriate #if defined()
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* get all the strings */
|
||||
|
||||
#define LWS_ROLE_WS 1
|
||||
#define LWS_WITH_HTTP_UNCOMMON_HEADERS 1
|
||||
#define LWS_ROLE_H2 1
|
||||
|
||||
#include "lextable-strings.h"
|
||||
|
||||
#undef LWS_ROLE_WS
|
||||
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
|
||||
#undef LWS_ROLE_H2
|
||||
|
||||
/* bitfield for the 8 versions as to which strings exist... index layout
|
||||
*
|
||||
* b0 b1 b2
|
||||
* 0 =
|
||||
* 1 = uncommon
|
||||
* 2 = ws
|
||||
* 3 = uncommon ws
|
||||
* 4 = h2
|
||||
* 5 = uncommon h2
|
||||
* 6 = ws h2
|
||||
* 7 = uncommon ws h2
|
||||
*/
|
||||
|
||||
unsigned char filter_array[] = {
|
||||
0xff, /* get */
|
||||
0xff, /* post */
|
||||
0xaa, /* options */
|
||||
0xff, /* host */
|
||||
0xff, /* connection */
|
||||
0xff, /* upgrade */
|
||||
0xff, /* origin */
|
||||
0xcc, /* sec-ws-draft */
|
||||
0xff, /* crlf */
|
||||
0xcc, /* sec-ws-ext */
|
||||
0xcc, /* sec-ws-key1 */
|
||||
0xcc, /* sec-ws-key2 */
|
||||
0xcc, /* sec-ws-protocol */
|
||||
0xcc, /* sec-ws-accept */
|
||||
0xcc, /* sec-ws-nonce */
|
||||
0xff, /* http/1.1 */
|
||||
0xf0, /* http2-settings */
|
||||
0xff, /* accept */
|
||||
0xaa, /* access-control-req-hdrs */
|
||||
0xff, /* if-modified-since */
|
||||
0xff, /* if-none-match */
|
||||
0xff, /* accept-encoding */
|
||||
0xff, /* accept-language */
|
||||
0xff, /* pragma */
|
||||
0xff, /* cache-control */
|
||||
0xff, /* authorization */
|
||||
0xff, /* cookie */
|
||||
0xff, /* content-length */
|
||||
0xff, /* content-type */
|
||||
0xff, /* date */
|
||||
0xff, /* range */
|
||||
0xfa, /* referer */
|
||||
0xcc, /* sec-ws-key */
|
||||
0xcc, /* sec-ws-version */
|
||||
0xcc, /* sec-sc-origin */
|
||||
0xf0, /* authority */
|
||||
0xf0, /* method */
|
||||
0xf0, /* path */
|
||||
0xf0, /* scheme */
|
||||
0xf0, /* status */
|
||||
0xfa, /* accept-charset */
|
||||
0xff, /* accept-ranges */
|
||||
0xfa, /* access-control-allow-origin */
|
||||
0xff, /* age */
|
||||
0xff, /* allow */
|
||||
0xff, /* content-disposition */
|
||||
0xff, /* content-encoding */
|
||||
0xff, /* content-language */
|
||||
0xff, /* content-location */
|
||||
0xff, /* content-range */
|
||||
0xff, /* etag */
|
||||
0xff, /* expect */
|
||||
0xff, /* expires */
|
||||
0xff, /* from */
|
||||
0xff, /* if-match */
|
||||
0xff, /* if-range */
|
||||
0xff, /* if-unmodified-since */
|
||||
0xff, /* last-modified */
|
||||
0xff, /* link */
|
||||
0xff, /* location */
|
||||
0xfa, /* max-forwards */
|
||||
0xfa, /* proxy-authenticate */
|
||||
0xfa, /* proxy-authorization */
|
||||
0xff, /* refresh */
|
||||
0xff, /* retry-after */
|
||||
0xff, /* server */
|
||||
0xff, /* set-cookie */
|
||||
0xfa, /* strict-transport-security */
|
||||
0xff, /* transfer-encoding */
|
||||
0xfa, /* user-agent */
|
||||
0xfa, /* vary */
|
||||
0xfa, /* via */
|
||||
0xfa, /* www-authenticate */
|
||||
0xaa, /* patch */
|
||||
0xaa, /* put */
|
||||
0xaa, /* delete */
|
||||
0xff, /* uri-args */
|
||||
0xaa, /* proxy */
|
||||
0xaa, /* x-real-ip */
|
||||
0xff, /* http/1.0 */
|
||||
0xff, /* x-forwarded-for */
|
||||
0xff, /* connect */
|
||||
0xff, /* head */
|
||||
0xfa, /* te */
|
||||
0xfa, /* replay-nonce */
|
||||
0xf0, /* protocol */
|
||||
0xff, /* x-auth-token */
|
||||
0xff /* not matchable */
|
||||
};
|
||||
|
||||
static unsigned char lws_header_implies_psuedoheader_map[] = {
|
||||
0x07, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00 /* <-64 */,
|
||||
0x0e /* <- 72 */, 0x24 /* <- 80 */, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/*
|
||||
* b7 = 0 = 1-byte seq
|
||||
* 0x08 = fail
|
||||
* 2-byte seq
|
||||
* 0x00 - 0x07, then terminal as given in 2nd byte
|
||||
3-byte seq
|
||||
* no match: go fwd 3 byte, match: jump fwd by amt in +1/+2 bytes
|
||||
* = 1 = 1-byte seq
|
||||
* no match: die, match go fwd 1 byte
|
||||
*/
|
||||
|
||||
unsigned char lextable[][2000] = {
|
||||
{
|
||||
#include "lextable.h"
|
||||
},
|
||||
#define LWS_WITH_HTTP_UNCOMMON_HEADERS
|
||||
{
|
||||
#include "lextable.h"
|
||||
},
|
||||
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
|
||||
#define LWS_ROLE_WS 1
|
||||
{
|
||||
#include "lextable.h"
|
||||
},
|
||||
#define LWS_WITH_HTTP_UNCOMMON_HEADERS
|
||||
{
|
||||
#include "lextable.h"
|
||||
},
|
||||
#undef LWS_ROLE_WS
|
||||
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
|
||||
#define LWS_ROLE_H2 1
|
||||
{
|
||||
#include "lextable.h"
|
||||
},
|
||||
#define LWS_WITH_HTTP_UNCOMMON_HEADERS
|
||||
{
|
||||
#include "lextable.h"
|
||||
},
|
||||
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
|
||||
#define LWS_ROLE_WS 1
|
||||
{
|
||||
#include "lextable.h"
|
||||
},
|
||||
#define LWS_WITH_HTTP_UNCOMMON_HEADERS 1
|
||||
{
|
||||
#include "lextable.h"
|
||||
},
|
||||
};
|
||||
|
||||
#define PARALLEL 30
|
||||
|
||||
struct state {
|
||||
char c[PARALLEL];
|
||||
int state[PARALLEL];
|
||||
int count;
|
||||
int bytepos;
|
||||
|
||||
int real_pos;
|
||||
};
|
||||
|
||||
static unsigned char pseudomap[8][16];
|
||||
|
||||
struct state state[1000];
|
||||
int next = 1;
|
||||
|
||||
#define FAIL_CHAR 0x08
|
||||
|
||||
int lextable_decode(int version, int pos, char c)
|
||||
{
|
||||
while (1) {
|
||||
if (lextable[version][pos] & (1 << 7)) { /* 1-byte, fail on mismatch */
|
||||
if ((lextable[version][pos] & 0x7f) != c)
|
||||
return -1;
|
||||
/* fall thru */
|
||||
pos++;
|
||||
if (lextable[version][pos] == FAIL_CHAR)
|
||||
return -1;
|
||||
return pos;
|
||||
} else { /* b7 = 0, end or 3-byte */
|
||||
if (lextable[version][pos] < FAIL_CHAR) /* terminal marker */
|
||||
return pos;
|
||||
|
||||
if (lextable[version][pos] == c) /* goto */
|
||||
return pos + (lextable[version][pos + 1]) +
|
||||
(lextable[version][pos + 2] << 8);
|
||||
/* fall thru goto */
|
||||
pos += 3;
|
||||
/* continue */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int issue(int version)
|
||||
{
|
||||
const char *rset[200];
|
||||
int n = 0;
|
||||
int m;
|
||||
int prev;
|
||||
int walk;
|
||||
int saw;
|
||||
int y;
|
||||
int j;
|
||||
int pos = 0;
|
||||
|
||||
int setmembers = 0;
|
||||
|
||||
memset(rset, 0, sizeof(rset));
|
||||
|
||||
if (version == 7)
|
||||
printf("#if defined(LWS_HTTP_HEADERS_ALL) || (%cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
|
||||
"%cdefined(LWS_ROLE_WS) && "
|
||||
"%cdefined(LWS_ROLE_H2))\n", version & 1 ? ' ' : '!',
|
||||
version & 2 ? ' ' : '!', version & 4 ? ' ' : '!');
|
||||
else
|
||||
printf("#if !defined(LWS_HTTP_HEADERS_ALL) && %cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
|
||||
"%cdefined(LWS_ROLE_WS) && "
|
||||
"%cdefined(LWS_ROLE_H2)\n", version & 1 ? ' ' : '!',
|
||||
version & 2 ? ' ' : '!', version & 4 ? ' ' : '!');
|
||||
|
||||
/*
|
||||
* let's create version's view of the set of strings
|
||||
*/
|
||||
|
||||
for (n = 0; n < sizeof(set) / sizeof(set[0]); n++)
|
||||
if (filter_array[n] & (1 << version)) {
|
||||
printf("\t/* %d: %d: %s */\n", setmembers, n, set[n]);
|
||||
if (lws_header_implies_psuedoheader_map[n >> 3] & (1 << (n & 7)))
|
||||
pseudomap[version][(setmembers >> 3)] |= 1 << (setmembers & 7);
|
||||
rset[setmembers++] = set[n];
|
||||
}
|
||||
|
||||
n = 0;
|
||||
while (n < setmembers) {
|
||||
|
||||
m = 0;
|
||||
walk = 0;
|
||||
prev = 0;
|
||||
|
||||
if (rset[n][0] == '\0') {
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
|
||||
while (rset[n][m]) {
|
||||
|
||||
saw = 0;
|
||||
for (y = 0; y < state[walk].count; y++)
|
||||
if (state[walk].c[y] == rset[n][m]) {
|
||||
/* exists -- go forward */
|
||||
walk = state[walk].state[y];
|
||||
saw = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (saw)
|
||||
goto again;
|
||||
|
||||
/* something we didn't see before */
|
||||
|
||||
state[walk].c[state[walk].count] = rset[n][m];
|
||||
|
||||
state[walk].state[state[walk].count] = next;
|
||||
state[walk].count++;
|
||||
walk = next++;
|
||||
again:
|
||||
m++;
|
||||
}
|
||||
|
||||
state[walk].c[0] = n++;
|
||||
state[walk].state[0] = 0; /* terminal marker */
|
||||
state[walk].count = 1;
|
||||
}
|
||||
|
||||
walk = 0;
|
||||
for (n = 0; n < next; n++) {
|
||||
state[n].bytepos = walk;
|
||||
walk += (2 * state[n].count);
|
||||
}
|
||||
|
||||
/* compute everyone's position first */
|
||||
|
||||
pos = 0;
|
||||
walk = 0;
|
||||
for (n = 0; n < next; n++) {
|
||||
|
||||
state[n].real_pos = pos;
|
||||
|
||||
for (m = 0; m < state[n].count; m++) {
|
||||
|
||||
if (state[n].state[m] == 0)
|
||||
pos += 2; /* terminal marker */
|
||||
else { /* c is a character */
|
||||
if ((state[state[n].state[m]].bytepos -
|
||||
walk) == 2)
|
||||
pos++;
|
||||
else {
|
||||
pos += 3;
|
||||
if (m == state[n].count - 1)
|
||||
pos++; /* fail */
|
||||
}
|
||||
}
|
||||
walk += 2;
|
||||
}
|
||||
}
|
||||
|
||||
walk = 0;
|
||||
pos = 0;
|
||||
for (n = 0; n < next; n++) {
|
||||
for (m = 0; m < state[n].count; m++) {
|
||||
|
||||
if (!m)
|
||||
fprintf(stdout, "/* pos %04x: %3d */ ",
|
||||
state[n].real_pos, n);
|
||||
else
|
||||
fprintf(stdout, " ");
|
||||
|
||||
y = state[n].c[m];
|
||||
saw = state[n].state[m];
|
||||
|
||||
if (saw == 0) { // c is a terminal then
|
||||
|
||||
if (y > 0x7ff) {
|
||||
fprintf(stderr, "terminal too big\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
fprintf(stdout, " 0x%02X, 0x%02X "
|
||||
" "
|
||||
"/* - terminal marker %2d - */,\n",
|
||||
y >> 8, y & 0xff, y & 0x7f);
|
||||
pos += 2;
|
||||
walk += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* c is a character */
|
||||
|
||||
prev = y &0x7f;
|
||||
if (prev < 32 || prev > 126)
|
||||
prev = '.';
|
||||
|
||||
|
||||
if ((state[saw].bytepos - walk) == 2) {
|
||||
fprintf(stdout, " 0x%02X /* '%c' -> */,\n",
|
||||
y | 0x80, prev);
|
||||
pos++;
|
||||
walk += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
j = state[saw].real_pos - pos;
|
||||
|
||||
if (j > 0xffff) {
|
||||
fprintf(stderr,
|
||||
"Jump > 64K bytes ahead (%d to %d)\n",
|
||||
state[n].real_pos, state[saw].real_pos);
|
||||
return 1;
|
||||
}
|
||||
fprintf(stdout, " 0x%02X /* '%c' */, 0x%02X, 0x%02X "
|
||||
"/* (to 0x%04X state %3d) */,\n",
|
||||
y, prev,
|
||||
j & 0xff, j >> 8,
|
||||
state[saw].real_pos, saw);
|
||||
pos += 3;
|
||||
|
||||
if (m == state[n].count - 1) {
|
||||
fprintf(stdout,
|
||||
" 0x%02X, /* fail */\n",
|
||||
FAIL_CHAR);
|
||||
pos++; /* fail */
|
||||
}
|
||||
|
||||
walk += 2;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stdout, "/* total size %d bytes */\n", pos);
|
||||
|
||||
printf("#endif\n\n");
|
||||
|
||||
/*
|
||||
* Try to parse every legal input string
|
||||
*/
|
||||
|
||||
for (n = 0; n < setmembers; n++) {
|
||||
walk = 0;
|
||||
m = 0;
|
||||
y = -1;
|
||||
|
||||
if (rset[n][0] == '\0')
|
||||
continue;
|
||||
|
||||
fprintf(stderr, " trying %d '%s'\n", n, rset[n]);
|
||||
|
||||
while (rset[n][m]) {
|
||||
walk = lextable_decode(version, walk, rset[n][m]);
|
||||
if (walk < 0) {
|
||||
fprintf(stderr, "failed\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (lextable[version][walk] < FAIL_CHAR) {
|
||||
y = (lextable[version][walk] << 8) +
|
||||
lextable[version][walk + 1];
|
||||
break;
|
||||
}
|
||||
m++;
|
||||
}
|
||||
|
||||
if (y != n) {
|
||||
fprintf(stderr, "decode failed %d\n", y);
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "All decode OK\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int m, n;
|
||||
|
||||
for (n = 0; n < 8; n++) {
|
||||
issue(n);
|
||||
}
|
||||
|
||||
printf("\n/*\n");
|
||||
|
||||
for (n = 0; n < 8; n++) {
|
||||
|
||||
if (n == 7)
|
||||
printf("#if defined(LWS_HTTP_HEADERS_ALL) || (%cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
|
||||
"%cdefined(LWS_ROLE_WS) && "
|
||||
"%cdefined(LWS_ROLE_H2))\n", n & 1 ? ' ' : '!',
|
||||
n & 2 ? ' ' : '!', n & 4 ? ' ' : '!');
|
||||
else
|
||||
printf("#if !defined(LWS_HTTP_HEADERS_ALL) && %cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
|
||||
"%cdefined(LWS_ROLE_WS) && "
|
||||
"%cdefined(LWS_ROLE_H2)\n", n & 1 ? ' ' : '!',
|
||||
n & 2 ? ' ' : '!', n & 4 ? ' ' : '!');
|
||||
|
||||
printf("static uint8_t lws_header_implies_psuedoheader_map[] = {\n\t");
|
||||
|
||||
for (m = 0; m < sizeof(pseudomap[n]); m++)
|
||||
printf("0x%02x,", pseudomap[n][m]);
|
||||
|
||||
printf("\n};\n");
|
||||
|
||||
printf("#endif\n");
|
||||
}
|
||||
|
||||
printf("*/\n");
|
||||
|
||||
fprintf(stderr, "did all the variants\n");
|
||||
}
|
||||
1726
Kinc/Sources/kinc/libs/roles/http/parsers.c
Normal file
1726
Kinc/Sources/kinc/libs/roles/http/parsers.c
Normal file
File diff suppressed because it is too large
Load Diff
355
Kinc/Sources/kinc/libs/roles/http/private-lib-roles-http.h
Normal file
355
Kinc/Sources/kinc/libs/roles/http/private-lib-roles-http.h
Normal file
@ -0,0 +1,355 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* This is included from private-lib-core.h if either H1 or H2 roles are
|
||||
* enabled
|
||||
*/
|
||||
|
||||
#if defined(LWS_WITH_HUBBUB)
|
||||
#include <hubbub/hubbub.h>
|
||||
#include <hubbub/parser.h>
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
||||
#include "private-lib-roles-http-compression.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_conn_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_PLAT_FREERTOS)
|
||||
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
|
||||
|
||||
#define LWS_HTTP_NO_KNOWN_HEADER 0xff
|
||||
|
||||
/*
|
||||
* 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_CLIENT)
|
||||
char initial_handshake_hash_base64[30];
|
||||
#endif
|
||||
int hdr_token_idx;
|
||||
|
||||
ah_data_idx_t pos;
|
||||
ah_data_idx_t http_response;
|
||||
ah_data_idx_t current_token_limit;
|
||||
ah_data_idx_t unk_pos; /* to undo speculative unknown header */
|
||||
|
||||
#if defined(LWS_WITH_CUSTOM_HEADERS)
|
||||
ah_data_idx_t unk_value_pos;
|
||||
|
||||
ah_data_idx_t unk_ll_head;
|
||||
ah_data_idx_t unk_ll_tail;
|
||||
#endif
|
||||
|
||||
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_HUBBUB)
|
||||
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 {
|
||||
#if defined(LWS_CLIENT_HTTP_PROXYING)
|
||||
char http_proxy_address[128];
|
||||
#endif
|
||||
const struct lws_http_mount *mount_list;
|
||||
const char *error_document_404;
|
||||
#if defined(LWS_CLIENT_HTTP_PROXYING)
|
||||
unsigned int http_proxy_port;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef LWS_WITH_ACCESS_LOG
|
||||
struct lws_access_log {
|
||||
char *header_log;
|
||||
char *user_agent;
|
||||
char *referrer;
|
||||
unsigned long sent;
|
||||
int response;
|
||||
};
|
||||
#endif
|
||||
|
||||
#define LWS_HTTP_CHUNK_HDR_MAX_SIZE (6 + 2) /* 6 hex digits and then CRLF */
|
||||
#define LWS_HTTP_CHUNK_TRL_MAX_SIZE (2 + 5) /* CRLF, then maybe 0 CRLF CRLF */
|
||||
|
||||
struct _lws_http_mode_related {
|
||||
struct lws *new_wsi_list;
|
||||
|
||||
unsigned char *pending_return_headers;
|
||||
size_t pending_return_headers_len;
|
||||
size_t prh_content_length;
|
||||
|
||||
#if defined(LWS_WITH_HTTP_PROXY)
|
||||
struct lws_rewrite *rw;
|
||||
struct lws_buflist *buflist_post_body;
|
||||
#endif
|
||||
struct allocated_headers *ah;
|
||||
struct lws *ah_wait_list;
|
||||
|
||||
unsigned long writeable_len;
|
||||
|
||||
#if defined(LWS_WITH_FILE_OPS)
|
||||
lws_filepos_t filepos;
|
||||
lws_filepos_t filelen;
|
||||
lws_fop_fd_t fop_fd;
|
||||
#endif
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
char multipart_boundary[16];
|
||||
#endif
|
||||
#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
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
unsigned int response_code;
|
||||
#endif
|
||||
#ifdef LWS_WITH_CGI
|
||||
struct lws_cgi *cgi; /* wsi being cgi stream have one of these */
|
||||
#endif
|
||||
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
||||
struct lws_compression_support *lcs;
|
||||
lws_comp_ctx_t comp_ctx;
|
||||
unsigned char comp_accept_mask;
|
||||
#endif
|
||||
|
||||
enum http_version request_version;
|
||||
enum http_conn_type conn_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;
|
||||
unsigned int proxy_clientside:1;
|
||||
unsigned int proxy_parent_chunked:1;
|
||||
#endif
|
||||
unsigned int deferred_transaction_completed:1;
|
||||
unsigned int content_length_explicitly_zero:1;
|
||||
unsigned int content_length_given:1;
|
||||
unsigned int did_stream_close:1;
|
||||
unsigned int multipart:1;
|
||||
unsigned int cgi_transaction_complete:1;
|
||||
unsigned int multipart_issue_boundary:1;
|
||||
|
||||
char auth_username[64];
|
||||
char auth_password[64];
|
||||
char *digest_auth_hdr;
|
||||
};
|
||||
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
enum lws_chunk_parser {
|
||||
ELCP_HEX,
|
||||
ELCP_CR,
|
||||
ELCP_CONTENT,
|
||||
ELCP_POST_CR,
|
||||
ELCP_POST_LF,
|
||||
ELCP_TRAILER_CR,
|
||||
ELCP_TRAILER_LF
|
||||
};
|
||||
#endif
|
||||
|
||||
enum lws_parse_urldecode_results {
|
||||
LPUR_CONTINUE,
|
||||
LPUR_SWALLOW,
|
||||
LPUR_FORBID,
|
||||
LPUR_EXCESSIVE,
|
||||
};
|
||||
|
||||
enum lws_check_basic_auth_results {
|
||||
LCBA_CONTINUE,
|
||||
LCBA_FAILED_AUTH,
|
||||
LCBA_END_TRANSACTION,
|
||||
};
|
||||
|
||||
enum lws_check_basic_auth_results
|
||||
lws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file, unsigned int auth_mode);
|
||||
|
||||
int
|
||||
lws_unauthorised_basic_auth(struct lws *wsi);
|
||||
|
||||
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);
|
||||
|
||||
int
|
||||
lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit,
|
||||
char *uri_ptr, char ws);
|
||||
|
||||
void
|
||||
lws_sul_http_ah_lifecheck(lws_sorted_usec_list_t *sul);
|
||||
|
||||
uint8_t *
|
||||
lws_http_multipart_headers(struct lws *wsi, uint8_t *p);
|
||||
|
||||
int
|
||||
lws_http_string_to_known_header(const char *s, size_t slen);
|
||||
|
||||
int
|
||||
lws_http_date_render_from_unix(char *buf, size_t len, const time_t *t);
|
||||
|
||||
int
|
||||
lws_http_date_parse_unix(const char *b, size_t len, time_t *t);
|
||||
|
||||
enum {
|
||||
CCTLS_RETURN_ERROR = -1,
|
||||
CCTLS_RETURN_DONE = 0,
|
||||
CCTLS_RETURN_RETRY = 1,
|
||||
};
|
||||
|
||||
int
|
||||
lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1);
|
||||
209
Kinc/Sources/kinc/libs/roles/http/server/access-log.c
Normal file
209
Kinc/Sources/kinc/libs/roles/http/server/access-log.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.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 uri_len, int meth)
|
||||
{
|
||||
char da[64], uri[256], ta[64];
|
||||
time_t t = time(NULL);
|
||||
struct lws *nwsi;
|
||||
const char *me;
|
||||
int l = 256, m;
|
||||
struct tm *ptm = NULL;
|
||||
#if defined(LWS_HAVE_LOCALTIME_R)
|
||||
struct tm tm;
|
||||
#endif
|
||||
|
||||
if (!wsi->a.vhost)
|
||||
return;
|
||||
|
||||
/* only worry about preparing it if we store it */
|
||||
if (wsi->a.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((unsigned int)l, "access log");
|
||||
if (!wsi->http.access_log.header_log)
|
||||
return;
|
||||
|
||||
#if defined(LWS_HAVE_LOCALTIME_R)
|
||||
ptm = localtime_r(&t, &tm);
|
||||
#else
|
||||
ptm = localtime(&t);
|
||||
#endif
|
||||
if (ptm)
|
||||
strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", ptm);
|
||||
else
|
||||
strcpy(da, "01/Jan/1970:00:00:00 +0000");
|
||||
|
||||
#if defined(LWS_ROLE_H2)
|
||||
if (wsi->mux_substream)
|
||||
me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
|
||||
else
|
||||
#endif
|
||||
me = method_names[meth];
|
||||
|
||||
if (!me)
|
||||
me = "(null)";
|
||||
|
||||
m = uri_len;
|
||||
if (m > (int)sizeof(uri) - 1)
|
||||
m = sizeof(uri) - 1;
|
||||
|
||||
strncpy(uri, uri_ptr, (unsigned int)m);
|
||||
uri[m] = '\0';
|
||||
|
||||
nwsi = lws_get_network_wsi(wsi);
|
||||
|
||||
if (nwsi->sa46_peer.sa4.sin_family)
|
||||
lws_sa46_write_numeric_address(&nwsi->sa46_peer, ta, sizeof(ta));
|
||||
else
|
||||
strncpy(ta, "unknown", sizeof(ta));
|
||||
|
||||
lws_snprintf(wsi->http.access_log.header_log, (size_t)l,
|
||||
"%s - - [%s] \"%s %s %s\"",
|
||||
ta, da, me, uri, hver[wsi->http.request_version]);
|
||||
|
||||
//lwsl_notice("%s\n", wsi->http.access_log.header_log);
|
||||
|
||||
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT);
|
||||
if (l) {
|
||||
wsi->http.access_log.user_agent =
|
||||
lws_malloc((unsigned int)l + 5, "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;
|
||||
}
|
||||
wsi->http.access_log.user_agent[0] = '\0';
|
||||
|
||||
if (lws_hdr_copy(wsi, wsi->http.access_log.user_agent, l + 4,
|
||||
WSI_TOKEN_HTTP_USER_AGENT) >= 0)
|
||||
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((unsigned int)l + 5, "referrer");
|
||||
if (!wsi->http.access_log.referrer) {
|
||||
lwsl_err("OOM getting referrer\n");
|
||||
lws_free_set_NULL(wsi->http.access_log.user_agent);
|
||||
lws_free_set_NULL(wsi->http.access_log.header_log);
|
||||
return;
|
||||
}
|
||||
wsi->http.access_log.referrer[0] = '\0';
|
||||
if (lws_hdr_copy(wsi, wsi->http.access_log.referrer,
|
||||
l + 4, WSI_TOKEN_HTTP_REFERER) >= 0)
|
||||
|
||||
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->a.vhost)
|
||||
return 0;
|
||||
|
||||
if (wsi->a.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 - (unsigned int)l) {
|
||||
p[sizeof(ass) - 6 - (unsigned int)l] = '\0';
|
||||
l--;
|
||||
}
|
||||
l += lws_snprintf(ass + (unsigned int)l, sizeof(ass) - 1 - (unsigned int)l, "\" \"%s\"\n", p);
|
||||
|
||||
ass[sizeof(ass) - 1] = '\0';
|
||||
|
||||
if ((int)write(wsi->a.vhost->log_fd, ass, (size_t)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;
|
||||
}
|
||||
|
||||
657
Kinc/Sources/kinc/libs/roles/http/server/fops-zip.c
Normal file
657
Kinc/Sources/kinc/libs/roles/http/server/fops-zip.c
Normal file
@ -0,0 +1,657 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Original code used in this source file:
|
||||
*
|
||||
* https://github.com/PerBothner/DomTerm.git @912add15f3d0aec
|
||||
*
|
||||
* ./lws-term/io.c
|
||||
* ./lws-term/junzip.c
|
||||
*
|
||||
* Copyright (C) 2017 Per Bothner <per@bothner.com>
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* ( copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Somewhat rewritten by AG
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
#if defined(LWS_WITH_MINIZ)
|
||||
#include <miniz.h>
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This code works with zip format containers which may have files compressed
|
||||
* with gzip deflate (type 8) or store uncompressed (type 0).
|
||||
*
|
||||
* Linux zip produces such zipfiles by default, eg
|
||||
*
|
||||
* $ zip ../myzip.zip file1 file2 file3
|
||||
*/
|
||||
|
||||
#define ZIP_COMPRESSION_METHOD_STORE 0
|
||||
#define ZIP_COMPRESSION_METHOD_DEFLATE 8
|
||||
|
||||
typedef struct {
|
||||
lws_filepos_t filename_start;
|
||||
uint32_t crc32;
|
||||
uint32_t comp_size;
|
||||
uint32_t uncomp_size;
|
||||
uint32_t offset;
|
||||
uint32_t mod_time;
|
||||
uint16_t filename_len;
|
||||
uint16_t extra;
|
||||
uint16_t method;
|
||||
uint16_t file_com_len;
|
||||
} lws_fops_zip_hdr_t;
|
||||
|
||||
typedef struct {
|
||||
struct lws_fop_fd fop_fd; /* MUST BE FIRST logical fop_fd into
|
||||
* file inside zip: fops_zip fops */
|
||||
lws_fop_fd_t zip_fop_fd; /* logical fop fd on to zip file
|
||||
* itself: using platform fops */
|
||||
lws_fops_zip_hdr_t hdr;
|
||||
z_stream inflate;
|
||||
lws_filepos_t content_start;
|
||||
lws_filepos_t exp_uncomp_pos;
|
||||
union {
|
||||
uint8_t trailer8[8];
|
||||
uint32_t trailer32[2];
|
||||
} u;
|
||||
uint8_t rbuf[128]; /* decompression chunk size */
|
||||
int entry_count;
|
||||
|
||||
unsigned int decompress:1; /* 0 = direct from file */
|
||||
unsigned int add_gzip_container:1;
|
||||
} *lws_fops_zip_t;
|
||||
|
||||
struct lws_plat_file_ops fops_zip;
|
||||
#define fop_fd_to_priv(FD) ((lws_fops_zip_t)(FD))
|
||||
|
||||
static const uint8_t hd[] = { 31, 139, 8, 0, 0, 0, 0, 0, 0, 3 };
|
||||
|
||||
enum {
|
||||
ZC_SIGNATURE = 0,
|
||||
ZC_VERSION_MADE_BY = 4,
|
||||
ZC_VERSION_NEEDED_TO_EXTRACT = 6,
|
||||
ZC_GENERAL_PURPOSE_BIT_FLAG = 8,
|
||||
ZC_COMPRESSION_METHOD = 10,
|
||||
ZC_LAST_MOD_FILE_TIME = 12,
|
||||
ZC_LAST_MOD_FILE_DATE = 14,
|
||||
ZC_CRC32 = 16,
|
||||
ZC_COMPRESSED_SIZE = 20,
|
||||
ZC_UNCOMPRESSED_SIZE = 24,
|
||||
ZC_FILE_NAME_LENGTH = 28,
|
||||
ZC_EXTRA_FIELD_LENGTH = 30,
|
||||
|
||||
ZC_FILE_COMMENT_LENGTH = 32,
|
||||
ZC_DISK_NUMBER_START = 34,
|
||||
ZC_INTERNAL_FILE_ATTRIBUTES = 36,
|
||||
ZC_EXTERNAL_FILE_ATTRIBUTES = 38,
|
||||
ZC_REL_OFFSET_LOCAL_HEADER = 42,
|
||||
ZC_DIRECTORY_LENGTH = 46,
|
||||
|
||||
ZE_SIGNATURE_OFFSET = 0,
|
||||
ZE_DESK_NUMBER = 4,
|
||||
ZE_CENTRAL_DIRECTORY_DISK_NUMBER = 6,
|
||||
ZE_NUM_ENTRIES_THIS_DISK = 8,
|
||||
ZE_NUM_ENTRIES = 10,
|
||||
ZE_CENTRAL_DIRECTORY_SIZE = 12,
|
||||
ZE_CENTRAL_DIR_OFFSET = 16,
|
||||
ZE_ZIP_COMMENT_LENGTH = 20,
|
||||
ZE_DIRECTORY_LENGTH = 22,
|
||||
|
||||
ZL_REL_OFFSET_CONTENT = 28,
|
||||
ZL_HEADER_LENGTH = 30,
|
||||
|
||||
LWS_FZ_ERR_SEEK_END_RECORD = 1,
|
||||
LWS_FZ_ERR_READ_END_RECORD,
|
||||
LWS_FZ_ERR_END_RECORD_MAGIC,
|
||||
LWS_FZ_ERR_END_RECORD_SANITY,
|
||||
LWS_FZ_ERR_CENTRAL_SEEK,
|
||||
LWS_FZ_ERR_CENTRAL_READ,
|
||||
LWS_FZ_ERR_CENTRAL_SANITY,
|
||||
LWS_FZ_ERR_NAME_TOO_LONG,
|
||||
LWS_FZ_ERR_NAME_SEEK,
|
||||
LWS_FZ_ERR_NAME_READ,
|
||||
LWS_FZ_ERR_CONTENT_SANITY,
|
||||
LWS_FZ_ERR_CONTENT_SEEK,
|
||||
LWS_FZ_ERR_SCAN_SEEK,
|
||||
LWS_FZ_ERR_NOT_FOUND,
|
||||
LWS_FZ_ERR_ZLIB_INIT,
|
||||
LWS_FZ_ERR_READ_CONTENT,
|
||||
LWS_FZ_ERR_SEEK_COMPRESSED,
|
||||
};
|
||||
|
||||
#define eff_size(_priv) (_priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE ? \
|
||||
_priv->hdr.uncomp_size : _priv->hdr.comp_size)
|
||||
|
||||
static uint16_t
|
||||
get_u16(void *p)
|
||||
{
|
||||
const uint8_t *c = (const uint8_t *)p;
|
||||
|
||||
return (uint16_t)((c[0] | (c[1] << 8)));
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
get_u32(void *p)
|
||||
{
|
||||
const uint8_t *c = (const uint8_t *)p;
|
||||
|
||||
return (uint32_t)((c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)));
|
||||
}
|
||||
|
||||
int
|
||||
lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
|
||||
{
|
||||
lws_filepos_t amount;
|
||||
uint8_t buf[96];
|
||||
int i;
|
||||
|
||||
if (lws_vfs_file_seek_end(priv->zip_fop_fd, -ZE_DIRECTORY_LENGTH) < 0)
|
||||
return LWS_FZ_ERR_SEEK_END_RECORD;
|
||||
|
||||
if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
|
||||
ZE_DIRECTORY_LENGTH))
|
||||
return LWS_FZ_ERR_READ_END_RECORD;
|
||||
|
||||
if (amount != ZE_DIRECTORY_LENGTH)
|
||||
return LWS_FZ_ERR_READ_END_RECORD;
|
||||
|
||||
/*
|
||||
* We require the zip to have the last record right at the end
|
||||
* Linux zip always does this if no zip comment.
|
||||
*/
|
||||
if (buf[0] != 'P' || buf[1] != 'K' || buf[2] != 5 || buf[3] != 6)
|
||||
return LWS_FZ_ERR_END_RECORD_MAGIC;
|
||||
|
||||
i = get_u16(buf + ZE_NUM_ENTRIES);
|
||||
|
||||
if (get_u16(buf + ZE_DESK_NUMBER) ||
|
||||
get_u16(buf + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) ||
|
||||
i != get_u16(buf + ZE_NUM_ENTRIES_THIS_DISK))
|
||||
return LWS_FZ_ERR_END_RECORD_SANITY;
|
||||
|
||||
/* end record is OK... look for our file in the central dir */
|
||||
|
||||
if (lws_vfs_file_seek_set(priv->zip_fop_fd,
|
||||
get_u32(buf + ZE_CENTRAL_DIR_OFFSET)) < 0)
|
||||
return LWS_FZ_ERR_CENTRAL_SEEK;
|
||||
|
||||
while (i--) {
|
||||
priv->content_start = lws_vfs_tell(priv->zip_fop_fd);
|
||||
|
||||
if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
|
||||
ZC_DIRECTORY_LENGTH))
|
||||
return LWS_FZ_ERR_CENTRAL_READ;
|
||||
|
||||
if (amount != ZC_DIRECTORY_LENGTH)
|
||||
return LWS_FZ_ERR_CENTRAL_READ;
|
||||
|
||||
if (get_u32(buf + ZC_SIGNATURE) != 0x02014B50)
|
||||
return LWS_FZ_ERR_CENTRAL_SANITY;
|
||||
|
||||
lwsl_debug("cstart 0x%lx\n", (unsigned long)priv->content_start);
|
||||
|
||||
priv->hdr.filename_len = get_u16(buf + ZC_FILE_NAME_LENGTH);
|
||||
priv->hdr.extra = get_u16(buf + ZC_EXTRA_FIELD_LENGTH);
|
||||
priv->hdr.filename_start = lws_vfs_tell(priv->zip_fop_fd);
|
||||
|
||||
priv->hdr.method = get_u16(buf + ZC_COMPRESSION_METHOD);
|
||||
priv->hdr.crc32 = get_u32(buf + ZC_CRC32);
|
||||
priv->hdr.comp_size = get_u32(buf + ZC_COMPRESSED_SIZE);
|
||||
priv->hdr.uncomp_size = get_u32(buf + ZC_UNCOMPRESSED_SIZE);
|
||||
priv->hdr.offset = get_u32(buf + ZC_REL_OFFSET_LOCAL_HEADER);
|
||||
priv->hdr.mod_time = get_u32(buf + ZC_LAST_MOD_FILE_TIME);
|
||||
priv->hdr.file_com_len = get_u16(buf + ZC_FILE_COMMENT_LENGTH);
|
||||
|
||||
if (priv->hdr.filename_len != len)
|
||||
goto next;
|
||||
|
||||
if (len >= (int)sizeof(buf) - 1)
|
||||
return LWS_FZ_ERR_NAME_TOO_LONG;
|
||||
|
||||
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
|
||||
&amount, buf, (unsigned int)len))
|
||||
return LWS_FZ_ERR_NAME_READ;
|
||||
if ((int)amount != len)
|
||||
return LWS_FZ_ERR_NAME_READ;
|
||||
|
||||
buf[len] = '\0';
|
||||
lwsl_debug("check %s vs %s\n", buf, name);
|
||||
|
||||
if (strcmp((const char *)buf, name))
|
||||
goto next;
|
||||
|
||||
/* we found a match */
|
||||
if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->hdr.offset) < 0)
|
||||
return LWS_FZ_ERR_NAME_SEEK;
|
||||
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
|
||||
&amount, buf,
|
||||
ZL_HEADER_LENGTH))
|
||||
return LWS_FZ_ERR_NAME_READ;
|
||||
if (amount != ZL_HEADER_LENGTH)
|
||||
return LWS_FZ_ERR_NAME_READ;
|
||||
|
||||
priv->content_start = priv->hdr.offset +
|
||||
ZL_HEADER_LENGTH +
|
||||
priv->hdr.filename_len +
|
||||
get_u16(buf + ZL_REL_OFFSET_CONTENT);
|
||||
|
||||
lwsl_debug("content supposed to start at 0x%lx\n",
|
||||
(unsigned long)priv->content_start);
|
||||
|
||||
if (priv->content_start > priv->zip_fop_fd->len)
|
||||
return LWS_FZ_ERR_CONTENT_SANITY;
|
||||
|
||||
if (lws_vfs_file_seek_set(priv->zip_fop_fd,
|
||||
(lws_fileofs_t)priv->content_start) < 0)
|
||||
return LWS_FZ_ERR_CONTENT_SEEK;
|
||||
|
||||
/* we are aligned at the start of the content */
|
||||
|
||||
priv->exp_uncomp_pos = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
next:
|
||||
if (i && lws_vfs_file_seek_set(priv->zip_fop_fd,
|
||||
(lws_fileofs_t)priv->content_start +
|
||||
(ZC_DIRECTORY_LENGTH +
|
||||
priv->hdr.filename_len +
|
||||
priv->hdr.extra +
|
||||
priv->hdr.file_com_len)) < 0)
|
||||
return LWS_FZ_ERR_SCAN_SEEK;
|
||||
}
|
||||
|
||||
return LWS_FZ_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_fops_zip_reset_inflate(lws_fops_zip_t priv)
|
||||
{
|
||||
if (priv->decompress)
|
||||
inflateEnd(&priv->inflate);
|
||||
|
||||
priv->inflate.zalloc = Z_NULL;
|
||||
priv->inflate.zfree = Z_NULL;
|
||||
priv->inflate.opaque = Z_NULL;
|
||||
priv->inflate.avail_in = 0;
|
||||
priv->inflate.next_in = Z_NULL;
|
||||
|
||||
if (inflateInit2(&priv->inflate, -MAX_WBITS) != Z_OK) {
|
||||
lwsl_err("inflate init failed\n");
|
||||
return LWS_FZ_ERR_ZLIB_INIT;
|
||||
}
|
||||
|
||||
if (lws_vfs_file_seek_set(priv->zip_fop_fd, (lws_fileofs_t)priv->content_start) < 0)
|
||||
return LWS_FZ_ERR_CONTENT_SEEK;
|
||||
|
||||
priv->exp_uncomp_pos = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lws_fop_fd_t
|
||||
lws_fops_zip_open(const struct lws_plat_file_ops *fops_own,
|
||||
const struct lws_plat_file_ops *fops, const char *vfs_path,
|
||||
const char *vpath, lws_fop_flags_t *flags)
|
||||
{
|
||||
lws_fop_flags_t local_flags = 0;
|
||||
lws_fops_zip_t priv;
|
||||
char rp[192];
|
||||
int m;
|
||||
|
||||
/*
|
||||
* vpath points at the / after the fops signature in vfs_path, eg
|
||||
* with a vfs_path "/var/www/docs/manual.zip/index.html", vpath
|
||||
* will come pointing at "/index.html"
|
||||
*/
|
||||
|
||||
priv = lws_zalloc(sizeof(*priv), "fops_zip priv");
|
||||
if (!priv)
|
||||
return NULL;
|
||||
|
||||
priv->fop_fd.fops = &fops_zip;
|
||||
|
||||
m = sizeof(rp) - 1;
|
||||
if ((vpath - vfs_path - 1) < m)
|
||||
m = lws_ptr_diff(vpath, vfs_path) - 1;
|
||||
lws_strncpy(rp, vfs_path, (unsigned int)m + 1);
|
||||
|
||||
/* open the zip file itself using the incoming fops, not fops_zip */
|
||||
|
||||
priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops_own, fops, rp, NULL, &local_flags);
|
||||
if (!priv->zip_fop_fd) {
|
||||
lwsl_err("%s: unable to open zip %s\n", __func__, rp);
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
if (*vpath == '/')
|
||||
vpath++;
|
||||
|
||||
m = lws_fops_zip_scan(priv, vpath, (int)strlen(vpath));
|
||||
if (m) {
|
||||
lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/* the directory metadata tells us modification time, so pass it on */
|
||||
priv->fop_fd.mod_time = priv->hdr.mod_time;
|
||||
*flags |= LWS_FOP_FLAG_MOD_TIME_VALID | LWS_FOP_FLAG_VIRTUAL;
|
||||
priv->fop_fd.flags = *flags;
|
||||
|
||||
/* The zip fop_fd is left pointing at the start of the content.
|
||||
*
|
||||
* 1) Content could be uncompressed (STORE), and we can always serve
|
||||
* that directly
|
||||
*
|
||||
* 2) Content could be compressed (GZIP), and the client can handle
|
||||
* receiving GZIP... we can wrap it in a GZIP header and trailer
|
||||
* and serve the content part directly. The flag indicating we
|
||||
* are providing GZIP directly is set so lws will send the right
|
||||
* headers.
|
||||
*
|
||||
* 3) Content could be compressed (GZIP) but the client can't handle
|
||||
* receiving GZIP... we can decompress it and serve as it is
|
||||
* inflated piecemeal.
|
||||
*
|
||||
* 4) Content may be compressed some unknown way... fail
|
||||
*
|
||||
*/
|
||||
if (priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE) {
|
||||
/*
|
||||
* it is stored uncompressed, leave it indicated as
|
||||
* uncompressed, and just serve it from inside the
|
||||
* zip with no gzip container;
|
||||
*/
|
||||
|
||||
lwsl_info("direct zip serving (stored)\n");
|
||||
|
||||
priv->fop_fd.len = priv->hdr.uncomp_size;
|
||||
|
||||
return &priv->fop_fd;
|
||||
}
|
||||
|
||||
if ((*flags & LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP) &&
|
||||
priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
|
||||
|
||||
/*
|
||||
* We can serve the gzipped file contents directly as gzip
|
||||
* from inside the zip container; client says it is OK.
|
||||
*
|
||||
* To convert to standalone gzip, we have to add a 10-byte
|
||||
* constant header and a variable 8-byte trailer around the
|
||||
* content.
|
||||
*
|
||||
* The 8-byte trailer is prepared now and held in the priv.
|
||||
*/
|
||||
|
||||
lwsl_info("direct zip serving (gzipped)\n");
|
||||
|
||||
priv->fop_fd.len = sizeof(hd) + priv->hdr.comp_size +
|
||||
sizeof(priv->u);
|
||||
|
||||
if (lws_is_be()) {
|
||||
uint8_t *p = priv->u.trailer8;
|
||||
|
||||
*p++ = (uint8_t)priv->hdr.crc32;
|
||||
*p++ = (uint8_t)(priv->hdr.crc32 >> 8);
|
||||
*p++ = (uint8_t)(priv->hdr.crc32 >> 16);
|
||||
*p++ = (uint8_t)(priv->hdr.crc32 >> 24);
|
||||
*p++ = (uint8_t)priv->hdr.uncomp_size;
|
||||
*p++ = (uint8_t)(priv->hdr.uncomp_size >> 8);
|
||||
*p++ = (uint8_t)(priv->hdr.uncomp_size >> 16);
|
||||
*p = (uint8_t)(priv->hdr.uncomp_size >> 24);
|
||||
} else {
|
||||
priv->u.trailer32[0] = priv->hdr.crc32;
|
||||
priv->u.trailer32[1] = priv->hdr.uncomp_size;
|
||||
}
|
||||
|
||||
*flags |= LWS_FOP_FLAG_COMPR_IS_GZIP;
|
||||
priv->fop_fd.flags = *flags;
|
||||
priv->add_gzip_container = 1;
|
||||
|
||||
return &priv->fop_fd;
|
||||
}
|
||||
|
||||
if (priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
|
||||
|
||||
/* we must decompress it to serve it */
|
||||
|
||||
lwsl_info("decompressed zip serving\n");
|
||||
|
||||
priv->fop_fd.len = priv->hdr.uncomp_size;
|
||||
|
||||
if (lws_fops_zip_reset_inflate(priv)) {
|
||||
lwsl_err("inflate init failed\n");
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
priv->decompress = 1;
|
||||
|
||||
return &priv->fop_fd;
|
||||
}
|
||||
|
||||
/* we can't handle it ... */
|
||||
|
||||
lwsl_err("zipped file %s compressed in unknown way (%d)\n", vfs_path,
|
||||
priv->hdr.method);
|
||||
|
||||
bail2:
|
||||
lws_vfs_file_close(&priv->zip_fop_fd);
|
||||
bail1:
|
||||
free(priv);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ie, we are closing the fop_fd for the file inside the gzip */
|
||||
|
||||
static int
|
||||
lws_fops_zip_close(lws_fop_fd_t *fd)
|
||||
{
|
||||
lws_fops_zip_t priv = fop_fd_to_priv(*fd);
|
||||
|
||||
if (priv->decompress)
|
||||
inflateEnd(&priv->inflate);
|
||||
|
||||
lws_vfs_file_close(&priv->zip_fop_fd); /* close the gzip fop_fd */
|
||||
|
||||
free(priv);
|
||||
*fd = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lws_fileofs_t
|
||||
lws_fops_zip_seek_cur(lws_fop_fd_t fd, lws_fileofs_t offset_from_cur_pos)
|
||||
{
|
||||
fd->pos = (lws_filepos_t)((lws_fileofs_t)fd->pos + offset_from_cur_pos);
|
||||
|
||||
return (lws_fileofs_t)fd->pos;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_fops_zip_read(lws_fop_fd_t fd, lws_filepos_t *amount, uint8_t *buf,
|
||||
lws_filepos_t len)
|
||||
{
|
||||
lws_fops_zip_t priv = fop_fd_to_priv(fd);
|
||||
lws_filepos_t ramount, rlen, cur = lws_vfs_tell(fd);
|
||||
int ret;
|
||||
|
||||
if (priv->decompress) {
|
||||
|
||||
if (priv->exp_uncomp_pos != fd->pos) {
|
||||
/*
|
||||
* there has been a seek in the uncompressed fop_fd
|
||||
* we have to restart the decompression and loop eating
|
||||
* the decompressed data up to the seek point
|
||||
*/
|
||||
lwsl_info("seek in decompressed\n");
|
||||
|
||||
lws_fops_zip_reset_inflate(priv);
|
||||
|
||||
while (priv->exp_uncomp_pos != fd->pos) {
|
||||
rlen = len;
|
||||
if (rlen > fd->pos - priv->exp_uncomp_pos)
|
||||
rlen = fd->pos - priv->exp_uncomp_pos;
|
||||
if (lws_fops_zip_read(fd, amount, buf, rlen))
|
||||
return LWS_FZ_ERR_SEEK_COMPRESSED;
|
||||
}
|
||||
*amount = 0;
|
||||
}
|
||||
|
||||
priv->inflate.avail_out = (unsigned int)len;
|
||||
priv->inflate.next_out = buf;
|
||||
|
||||
spin:
|
||||
if (!priv->inflate.avail_in) {
|
||||
rlen = sizeof(priv->rbuf);
|
||||
if (rlen > eff_size(priv) - (cur - priv->content_start))
|
||||
rlen = eff_size(priv) - (cur - priv->content_start);
|
||||
|
||||
if (priv->zip_fop_fd->fops->LWS_FOP_READ(
|
||||
priv->zip_fop_fd, &ramount, priv->rbuf,
|
||||
rlen))
|
||||
return LWS_FZ_ERR_READ_CONTENT;
|
||||
|
||||
cur += ramount;
|
||||
|
||||
priv->inflate.avail_in = (unsigned int)ramount;
|
||||
priv->inflate.next_in = priv->rbuf;
|
||||
}
|
||||
|
||||
ret = inflate(&priv->inflate, Z_NO_FLUSH);
|
||||
if (ret == Z_STREAM_ERROR)
|
||||
return ret;
|
||||
|
||||
switch (ret) {
|
||||
case Z_NEED_DICT:
|
||||
ret = Z_DATA_ERROR;
|
||||
/* fallthru */
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!priv->inflate.avail_in && priv->inflate.avail_out &&
|
||||
cur != priv->content_start + priv->hdr.comp_size)
|
||||
goto spin;
|
||||
|
||||
*amount = len - priv->inflate.avail_out;
|
||||
|
||||
priv->exp_uncomp_pos += *amount;
|
||||
fd->pos += *amount;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (priv->add_gzip_container) {
|
||||
|
||||
lwsl_info("%s: gzip + container\n", __func__);
|
||||
*amount = 0;
|
||||
|
||||
/* place the canned header at the start */
|
||||
|
||||
if (len && fd->pos < sizeof(hd)) {
|
||||
rlen = sizeof(hd) - fd->pos;
|
||||
if (rlen > len)
|
||||
rlen = len;
|
||||
/* provide stuff from canned header */
|
||||
memcpy(buf, hd + fd->pos, (size_t)rlen);
|
||||
fd->pos += rlen;
|
||||
buf += rlen;
|
||||
len -= rlen;
|
||||
*amount += rlen;
|
||||
}
|
||||
|
||||
/* serve gzipped data direct from zipfile */
|
||||
|
||||
if (len && fd->pos >= sizeof(hd) &&
|
||||
fd->pos < priv->hdr.comp_size + sizeof(hd)) {
|
||||
|
||||
rlen = priv->hdr.comp_size - (priv->zip_fop_fd->pos -
|
||||
priv->content_start);
|
||||
if (rlen > len)
|
||||
rlen = len;
|
||||
|
||||
if (rlen &&
|
||||
priv->zip_fop_fd->pos < (priv->hdr.comp_size +
|
||||
priv->content_start)) {
|
||||
if (lws_vfs_file_read(priv->zip_fop_fd,
|
||||
&ramount, buf, rlen))
|
||||
return LWS_FZ_ERR_READ_CONTENT;
|
||||
*amount += ramount;
|
||||
fd->pos += ramount; // virtual pos
|
||||
buf += ramount;
|
||||
len -= ramount;
|
||||
}
|
||||
}
|
||||
|
||||
/* place the prepared trailer at the end */
|
||||
|
||||
if (len && fd->pos >= priv->hdr.comp_size + sizeof(hd) &&
|
||||
fd->pos < priv->hdr.comp_size + sizeof(hd) +
|
||||
sizeof(priv->u)) {
|
||||
cur = fd->pos - priv->hdr.comp_size - sizeof(hd);
|
||||
rlen = sizeof(priv->u) - cur;
|
||||
if (rlen > len)
|
||||
rlen = len;
|
||||
|
||||
memcpy(buf, priv->u.trailer8 + cur, (size_t)rlen);
|
||||
|
||||
*amount += rlen;
|
||||
fd->pos += rlen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
lwsl_info("%s: store\n", __func__);
|
||||
|
||||
if (len > eff_size(priv) - cur)
|
||||
len = eff_size(priv) - cur;
|
||||
|
||||
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
|
||||
amount, buf, len))
|
||||
return LWS_FZ_ERR_READ_CONTENT;
|
||||
|
||||
fd->pos += *amount;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_plat_file_ops fops_zip = {
|
||||
lws_fops_zip_open,
|
||||
lws_fops_zip_close,
|
||||
lws_fops_zip_seek_cur,
|
||||
lws_fops_zip_read,
|
||||
NULL,
|
||||
{ { ".zip/", 5 }, { ".jar/", 5 }, { ".war/", 5 } },
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
1112
Kinc/Sources/kinc/libs/roles/http/server/lejp-conf.c
Normal file
1112
Kinc/Sources/kinc/libs/roles/http/server/lejp-conf.c
Normal file
File diff suppressed because it is too large
Load Diff
725
Kinc/Sources/kinc/libs/roles/http/server/lws-spa.c
Normal file
725
Kinc/Sources/kinc/libs/roles/http/server/lws-spa.c
Normal file
@ -0,0 +1,725 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
#define LWS_MAX_ELEM_NAME 32
|
||||
|
||||
enum urldecode_stateful {
|
||||
US_NAME,
|
||||
US_IDLE,
|
||||
US_PC1,
|
||||
US_PC2,
|
||||
|
||||
MT_LOOK_BOUND_IN,
|
||||
MT_HNAME,
|
||||
MT_DISP,
|
||||
MT_TYPE,
|
||||
MT_IGNORE1,
|
||||
MT_IGNORE2,
|
||||
MT_IGNORE3,
|
||||
MT_COMPLETED,
|
||||
};
|
||||
|
||||
static struct mp_hdr {
|
||||
const char * const hdr;
|
||||
uint8_t hdr_len;
|
||||
} mp_hdrs[] = {
|
||||
{ "content-disposition: ", 21 },
|
||||
{ "content-type: ", 14 },
|
||||
{ "\x0d\x0a", 2 }
|
||||
};
|
||||
|
||||
struct lws_spa;
|
||||
|
||||
typedef int (*lws_urldecode_stateful_cb)(struct lws_spa *spa,
|
||||
const char *name, char **buf, int len, int final);
|
||||
|
||||
struct lws_urldecode_stateful {
|
||||
char *out;
|
||||
struct lws_spa *data;
|
||||
struct lws *wsi;
|
||||
char name[LWS_MAX_ELEM_NAME];
|
||||
char temp[LWS_MAX_ELEM_NAME];
|
||||
char content_type[32];
|
||||
char content_disp[32];
|
||||
char content_disp_filename[256];
|
||||
char mime_boundary[128];
|
||||
int out_len;
|
||||
int pos;
|
||||
int hdr_idx;
|
||||
int mp;
|
||||
int sum;
|
||||
|
||||
uint8_t matchable;
|
||||
|
||||
uint8_t multipart_form_data:1;
|
||||
uint8_t inside_quote:1;
|
||||
uint8_t subname:1;
|
||||
uint8_t boundary_real_crlf:1;
|
||||
|
||||
enum urldecode_stateful state;
|
||||
|
||||
lws_urldecode_stateful_cb output;
|
||||
};
|
||||
|
||||
struct lws_spa {
|
||||
struct lws_urldecode_stateful *s;
|
||||
lws_spa_create_info_t i;
|
||||
int *param_length;
|
||||
char finalized;
|
||||
char **params;
|
||||
char *storage;
|
||||
char *end;
|
||||
};
|
||||
|
||||
static struct lws_urldecode_stateful *
|
||||
lws_urldecode_s_create(struct lws_spa *spa, struct lws *wsi, char *out,
|
||||
int out_len, lws_urldecode_stateful_cb output)
|
||||
{
|
||||
struct lws_urldecode_stateful *s;
|
||||
char buf[205], *p;
|
||||
int m = 0;
|
||||
|
||||
if (spa->i.ac)
|
||||
s = lwsac_use_zero(spa->i.ac, sizeof(*s), spa->i.ac_chunk_size);
|
||||
else
|
||||
s = lws_zalloc(sizeof(*s), "stateful urldecode");
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
s->out = out;
|
||||
s->out_len = out_len;
|
||||
s->output = output;
|
||||
s->pos = 0;
|
||||
s->sum = 0;
|
||||
s->mp = 0;
|
||||
s->state = US_NAME;
|
||||
s->name[0] = '\0';
|
||||
s->data = spa;
|
||||
s->wsi = wsi;
|
||||
|
||||
if (lws_hdr_copy(wsi, buf, sizeof(buf),
|
||||
WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) {
|
||||
/* multipart/form-data;
|
||||
* boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
|
||||
|
||||
if (!strncmp(buf, "multipart/form-data", 19) ||
|
||||
!strncmp(buf, "multipart/related", 17)) {
|
||||
s->multipart_form_data = 1;
|
||||
s->state = MT_LOOK_BOUND_IN;
|
||||
s->mp = 2;
|
||||
p = strstr(buf, "boundary=");
|
||||
if (p) {
|
||||
p += 9;
|
||||
s->mime_boundary[m++] = '\x0d';
|
||||
s->mime_boundary[m++] = '\x0a';
|
||||
s->mime_boundary[m++] = '-';
|
||||
s->mime_boundary[m++] = '-';
|
||||
if (*p == '\"')
|
||||
p++;
|
||||
while (m < (int)sizeof(s->mime_boundary) - 1 &&
|
||||
*p && *p != ' ' && *p != ';' && *p != '\"')
|
||||
s->mime_boundary[m++] = *p++;
|
||||
s->mime_boundary[m] = '\0';
|
||||
|
||||
// lwsl_notice("boundary '%s'\n", s->mime_boundary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_urldecode_s_process(struct lws_urldecode_stateful *s, const char *in,
|
||||
int len)
|
||||
{
|
||||
int n, hit;
|
||||
char c;
|
||||
|
||||
while (len--) {
|
||||
if (s->pos == s->out_len - s->mp - 1) {
|
||||
if (s->output(s->data, s->name, &s->out, s->pos,
|
||||
LWS_UFS_CONTENT))
|
||||
return -1;
|
||||
|
||||
s->pos = 0;
|
||||
}
|
||||
|
||||
switch (s->state) {
|
||||
|
||||
/* states for url arg style */
|
||||
|
||||
case US_NAME:
|
||||
s->inside_quote = 0;
|
||||
if (*in == '=') {
|
||||
s->name[s->pos] = '\0';
|
||||
s->pos = 0;
|
||||
s->state = US_IDLE;
|
||||
in++;
|
||||
continue;
|
||||
}
|
||||
if (*in == '&') {
|
||||
s->name[s->pos] = '\0';
|
||||
if (s->output(s->data, s->name, &s->out,
|
||||
s->pos, LWS_UFS_FINAL_CONTENT))
|
||||
return -1;
|
||||
s->pos = 0;
|
||||
s->state = US_IDLE;
|
||||
in++;
|
||||
continue;
|
||||
}
|
||||
if (s->pos >= (int)sizeof(s->name) - 1) {
|
||||
lwsl_hexdump_notice(s->name, (size_t)s->pos);
|
||||
lwsl_notice("Name too long...\n");
|
||||
return -1;
|
||||
}
|
||||
s->name[s->pos++] = *in++;
|
||||
break;
|
||||
case US_IDLE:
|
||||
if (*in == '%') {
|
||||
s->state++;
|
||||
in++;
|
||||
continue;
|
||||
}
|
||||
if (*in == '&') {
|
||||
s->out[s->pos] = '\0';
|
||||
if (s->output(s->data, s->name, &s->out,
|
||||
s->pos, LWS_UFS_FINAL_CONTENT))
|
||||
return -1;
|
||||
s->pos = 0;
|
||||
s->state = US_NAME;
|
||||
in++;
|
||||
continue;
|
||||
}
|
||||
if (*in == '+') {
|
||||
in++;
|
||||
s->out[s->pos++] = ' ';
|
||||
continue;
|
||||
}
|
||||
s->out[s->pos++] = *in++;
|
||||
break;
|
||||
case US_PC1:
|
||||
n = char_to_hex(*in);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
in++;
|
||||
s->sum = n << 4;
|
||||
s->state++;
|
||||
break;
|
||||
|
||||
case US_PC2:
|
||||
n = char_to_hex(*in);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
in++;
|
||||
s->out[s->pos++] = (char)(s->sum | n);
|
||||
s->state = US_IDLE;
|
||||
break;
|
||||
|
||||
|
||||
/* states for multipart / mime style */
|
||||
|
||||
case MT_LOOK_BOUND_IN:
|
||||
retry_as_first:
|
||||
if (*in == s->mime_boundary[s->mp] &&
|
||||
s->mime_boundary[s->mp]) {
|
||||
in++;
|
||||
s->mp++;
|
||||
if (!s->mime_boundary[s->mp]) {
|
||||
s->mp = 0;
|
||||
s->state = MT_IGNORE1;
|
||||
|
||||
if (s->output(s->data, s->name,
|
||||
&s->out, s->pos,
|
||||
LWS_UFS_FINAL_CONTENT))
|
||||
return -1;
|
||||
|
||||
s->pos = 0;
|
||||
|
||||
s->content_disp[0] = '\0';
|
||||
s->name[0] = '\0';
|
||||
s->content_disp_filename[0] = '\0';
|
||||
s->boundary_real_crlf = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (s->mp) {
|
||||
n = 0;
|
||||
if (!s->boundary_real_crlf)
|
||||
n = 2;
|
||||
if (s->mp >= n) {
|
||||
memcpy(s->out + s->pos,
|
||||
s->mime_boundary + n,
|
||||
(unsigned int)(s->mp - n));
|
||||
s->pos += s->mp;
|
||||
s->mp = 0;
|
||||
goto retry_as_first;
|
||||
}
|
||||
}
|
||||
|
||||
s->out[s->pos++] = *in;
|
||||
in++;
|
||||
s->mp = 0;
|
||||
break;
|
||||
|
||||
case MT_HNAME:
|
||||
c =*in;
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
c = (char)(c + 'a' - 'A');
|
||||
if (!s->mp)
|
||||
/* initially, any of them might match */
|
||||
s->matchable = (1 << LWS_ARRAY_SIZE(mp_hdrs)) - 1;
|
||||
|
||||
hit = -1;
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(mp_hdrs); n++) {
|
||||
|
||||
if (!(s->matchable & (1 << n)))
|
||||
continue;
|
||||
/* this guy is still in contention... */
|
||||
|
||||
if (s->mp >= mp_hdrs[n].hdr_len) {
|
||||
/* he went past the end of it */
|
||||
s->matchable &= (uint8_t)~(1 << n);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c != mp_hdrs[n].hdr[s->mp]) {
|
||||
/* mismatched a char */
|
||||
s->matchable &= (uint8_t)~(1 << n);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s->mp + 1 == mp_hdrs[n].hdr_len) {
|
||||
/* we have a winner... */
|
||||
hit = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
in++;
|
||||
if (hit == -1 && !s->matchable) {
|
||||
/* We ruled them all out */
|
||||
s->state = MT_IGNORE1;
|
||||
s->mp = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
s->mp++;
|
||||
if (hit < 0)
|
||||
continue;
|
||||
|
||||
/* we matched the one in hit */
|
||||
|
||||
s->mp = 0;
|
||||
s->temp[0] = '\0';
|
||||
s->subname = 0;
|
||||
|
||||
if (hit == 2)
|
||||
s->state = MT_LOOK_BOUND_IN;
|
||||
else
|
||||
s->state += (unsigned int)hit + 1u;
|
||||
break;
|
||||
|
||||
case MT_DISP:
|
||||
/* form-data; name="file"; filename="t.txt" */
|
||||
|
||||
if (*in == '\x0d') {
|
||||
if (s->content_disp_filename[0])
|
||||
if (s->output(s->data, s->name,
|
||||
&s->out, s->pos,
|
||||
LWS_UFS_OPEN))
|
||||
return -1;
|
||||
s->state = MT_IGNORE2;
|
||||
goto done;
|
||||
}
|
||||
if (*in == ';') {
|
||||
s->subname = 1;
|
||||
s->temp[0] = '\0';
|
||||
s->mp = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (*in == '\"') {
|
||||
s->inside_quote = !!((s->inside_quote ^ 1) & 1);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (s->subname) {
|
||||
if (*in == '=') {
|
||||
s->temp[s->mp] = '\0';
|
||||
s->subname = 0;
|
||||
s->mp = 0;
|
||||
goto done;
|
||||
}
|
||||
if (s->mp < (int)sizeof(s->temp) - 1 &&
|
||||
(*in != ' ' || s->inside_quote))
|
||||
s->temp[s->mp++] = *in;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!s->temp[0]) {
|
||||
if (s->mp < (int)sizeof(s->content_disp) - 1)
|
||||
s->content_disp[s->mp++] = *in;
|
||||
if (s->mp < (int)sizeof(s->content_disp))
|
||||
s->content_disp[s->mp] = '\0';
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!strcmp(s->temp, "name")) {
|
||||
if (s->mp < (int)sizeof(s->name) - 1)
|
||||
s->name[s->mp++] = *in;
|
||||
else
|
||||
s->mp = (int)sizeof(s->name) - 1;
|
||||
s->name[s->mp] = '\0';
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!strcmp(s->temp, "filename")) {
|
||||
if (s->mp < (int)sizeof(s->content_disp_filename) - 1)
|
||||
s->content_disp_filename[s->mp++] = *in;
|
||||
s->content_disp_filename[s->mp] = '\0';
|
||||
goto done;
|
||||
}
|
||||
done:
|
||||
in++;
|
||||
break;
|
||||
|
||||
case MT_TYPE:
|
||||
if (*in == '\x0d')
|
||||
s->state = MT_IGNORE2;
|
||||
else {
|
||||
if (s->mp < (int)sizeof(s->content_type) - 1)
|
||||
s->content_type[s->mp++] = *in;
|
||||
s->content_type[s->mp] = '\0';
|
||||
}
|
||||
in++;
|
||||
break;
|
||||
|
||||
case MT_IGNORE1:
|
||||
if (*in == '\x0d')
|
||||
s->state = MT_IGNORE2;
|
||||
if (*in == '-')
|
||||
s->state = MT_IGNORE3;
|
||||
in++;
|
||||
break;
|
||||
|
||||
case MT_IGNORE2:
|
||||
s->mp = 0;
|
||||
if (*in == '\x0a')
|
||||
s->state = MT_HNAME;
|
||||
in++;
|
||||
break;
|
||||
|
||||
case MT_IGNORE3:
|
||||
if (*in == '\x0d')
|
||||
s->state = MT_IGNORE2;
|
||||
if (*in == '-') {
|
||||
s->state = MT_COMPLETED;
|
||||
s->wsi->http.rx_content_remain = 0;
|
||||
}
|
||||
in++;
|
||||
break;
|
||||
case MT_COMPLETED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_urldecode_s_destroy(struct lws_spa *spa, struct lws_urldecode_stateful *s)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (s->state != US_IDLE)
|
||||
ret = -1;
|
||||
|
||||
if (!ret)
|
||||
if (s->output(s->data, s->name, &s->out, s->pos,
|
||||
LWS_UFS_FINAL_CONTENT))
|
||||
ret = -1;
|
||||
|
||||
if (s->output(s->data, s->name, NULL, 0, LWS_UFS_CLOSE))
|
||||
return -1;
|
||||
|
||||
if (!spa->i.ac)
|
||||
lws_free(s);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_urldecode_spa_lookup(struct lws_spa *spa, const char *name)
|
||||
{
|
||||
const char * const *pp = spa->i.param_names;
|
||||
int n;
|
||||
|
||||
for (n = 0; n < spa->i.count_params; n++) {
|
||||
if (!*pp && !spa->i.param_names_stride && spa->i.ac) {
|
||||
unsigned int len = (unsigned int)strlen(name);
|
||||
char **ptr = (char**)spa->i.param_names;
|
||||
|
||||
/* Use NULLs at end of list to dynamically create
|
||||
* unknown entries */
|
||||
|
||||
ptr[n] = lwsac_use(spa->i.ac, len + 1, spa->i.ac_chunk_size);
|
||||
if (!ptr[n])
|
||||
return -1;
|
||||
|
||||
memcpy(ptr[n], name, len);
|
||||
ptr[n][len] = '\0';
|
||||
|
||||
return n;
|
||||
}
|
||||
if (*pp && !strcmp(*pp, name))
|
||||
return n;
|
||||
|
||||
if (spa->i.param_names_stride)
|
||||
pp = (const char * const *)(((char *)pp) + spa->i.param_names_stride);
|
||||
else
|
||||
pp++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_urldecode_spa_cb(struct lws_spa *spa, const char *name, char **buf, int len,
|
||||
int final)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (final == LWS_UFS_CLOSE || spa->s->content_disp_filename[0]) {
|
||||
if (spa->i.opt_cb) {
|
||||
n = spa->i.opt_cb(spa->i.opt_data, name,
|
||||
spa->s->content_disp_filename,
|
||||
buf ? *buf : NULL, len, (enum lws_spa_fileupload_states)final);
|
||||
|
||||
if (n < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
n = lws_urldecode_spa_lookup(spa, name);
|
||||
if (n == -1 || !len) /* unrecognized */
|
||||
return 0;
|
||||
|
||||
if (!spa->i.ac) {
|
||||
if (!spa->params[n])
|
||||
spa->params[n] = *buf;
|
||||
|
||||
if ((*buf) + len >= spa->end) {
|
||||
lwsl_info("%s: exceeded storage\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* move it on inside storage */
|
||||
(*buf) += len;
|
||||
*((*buf)++) = '\0';
|
||||
|
||||
spa->s->out_len -= len + 1;
|
||||
} else {
|
||||
spa->params[n] = lwsac_use(spa->i.ac, (unsigned int)len + 1,
|
||||
spa->i.ac_chunk_size);
|
||||
if (!spa->params[n])
|
||||
return -1;
|
||||
|
||||
memcpy(spa->params[n], *buf, (unsigned int)len);
|
||||
spa->params[n][len] = '\0';
|
||||
}
|
||||
|
||||
spa->param_length[n] += len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_spa *
|
||||
lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *i)
|
||||
{
|
||||
struct lws_spa *spa;
|
||||
|
||||
if (i->ac)
|
||||
spa = lwsac_use_zero(i->ac, sizeof(*spa), i->ac_chunk_size);
|
||||
else
|
||||
spa = lws_zalloc(sizeof(*spa), "spa");
|
||||
|
||||
if (!spa)
|
||||
return NULL;
|
||||
|
||||
spa->i = *i;
|
||||
if (!spa->i.max_storage)
|
||||
spa->i.max_storage = 512;
|
||||
|
||||
if (i->ac)
|
||||
spa->storage = lwsac_use(i->ac, (unsigned int)spa->i.max_storage,
|
||||
i->ac_chunk_size);
|
||||
else
|
||||
spa->storage = lws_malloc((unsigned int)spa->i.max_storage, "spa");
|
||||
|
||||
if (!spa->storage)
|
||||
goto bail2;
|
||||
|
||||
spa->end = spa->storage + spa->i.max_storage - 1;
|
||||
|
||||
if (i->count_params) {
|
||||
if (i->ac)
|
||||
spa->params = lwsac_use_zero(i->ac,
|
||||
sizeof(char *) * (unsigned int)i->count_params, i->ac_chunk_size);
|
||||
else
|
||||
spa->params = lws_zalloc(sizeof(char *) * (unsigned int)i->count_params,
|
||||
"spa params");
|
||||
if (!spa->params)
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
spa->s = lws_urldecode_s_create(spa, wsi, spa->storage, spa->i.max_storage,
|
||||
lws_urldecode_spa_cb);
|
||||
if (!spa->s)
|
||||
goto bail4;
|
||||
|
||||
if (i->count_params) {
|
||||
if (i->ac)
|
||||
spa->param_length = lwsac_use_zero(i->ac,
|
||||
sizeof(int) * (unsigned int)i->count_params, i->ac_chunk_size);
|
||||
else
|
||||
spa->param_length = lws_zalloc(sizeof(int) * (unsigned int)i->count_params,
|
||||
"spa param len");
|
||||
if (!spa->param_length)
|
||||
goto bail5;
|
||||
}
|
||||
|
||||
// lwsl_notice("%s: Created SPA %p\n", __func__, spa);
|
||||
|
||||
return spa;
|
||||
|
||||
bail5:
|
||||
lws_urldecode_s_destroy(spa, spa->s);
|
||||
bail4:
|
||||
if (!i->ac)
|
||||
lws_free(spa->params);
|
||||
bail3:
|
||||
if (!i->ac)
|
||||
lws_free(spa->storage);
|
||||
bail2:
|
||||
if (!i->ac)
|
||||
lws_free(spa);
|
||||
|
||||
if (i->ac)
|
||||
lwsac_free(i->ac);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct lws_spa *
|
||||
lws_spa_create(struct lws *wsi, const char * const *param_names,
|
||||
int count_params, int max_storage,
|
||||
lws_spa_fileupload_cb opt_cb, void *opt_data)
|
||||
{
|
||||
lws_spa_create_info_t i;
|
||||
|
||||
memset(&i, 0, sizeof(i));
|
||||
i.count_params = count_params;
|
||||
i.max_storage = max_storage;
|
||||
i.opt_cb = opt_cb;
|
||||
i.opt_data = opt_data;
|
||||
i.param_names = param_names;
|
||||
|
||||
return lws_spa_create_via_info(wsi, &i);
|
||||
}
|
||||
|
||||
int
|
||||
lws_spa_process(struct lws_spa *spa, const char *in, int len)
|
||||
{
|
||||
if (!spa) {
|
||||
lwsl_err("%s: NULL spa\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
/* we reject any junk after the last part arrived and we finalized */
|
||||
if (spa->finalized)
|
||||
return 0;
|
||||
|
||||
return lws_urldecode_s_process(spa->s, in, len);
|
||||
}
|
||||
|
||||
int
|
||||
lws_spa_get_length(struct lws_spa *spa, int n)
|
||||
{
|
||||
if (n >= spa->i.count_params)
|
||||
return 0;
|
||||
|
||||
return spa->param_length[n];
|
||||
}
|
||||
|
||||
const char *
|
||||
lws_spa_get_string(struct lws_spa *spa, int n)
|
||||
{
|
||||
if (n >= spa->i.count_params)
|
||||
return NULL;
|
||||
|
||||
return spa->params[n];
|
||||
}
|
||||
|
||||
int
|
||||
lws_spa_finalize(struct lws_spa *spa)
|
||||
{
|
||||
if (!spa)
|
||||
return 0;
|
||||
|
||||
if (spa->s) {
|
||||
lws_urldecode_s_destroy(spa, spa->s);
|
||||
spa->s = NULL;
|
||||
}
|
||||
|
||||
spa->finalized = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_spa_destroy(struct lws_spa *spa)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
lwsl_info("%s: destroy spa %p\n", __func__, spa);
|
||||
|
||||
if (spa->s)
|
||||
lws_urldecode_s_destroy(spa, spa->s);
|
||||
|
||||
if (spa->i.ac)
|
||||
lwsac_free(spa->i.ac);
|
||||
else {
|
||||
lws_free(spa->param_length);
|
||||
lws_free(spa->params);
|
||||
lws_free(spa->storage);
|
||||
lws_free(spa);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
214
Kinc/Sources/kinc/libs/roles/http/server/ranges.c
Normal file
214
Kinc/Sources/kinc/libs/roles/http/server/ranges.c
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.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=";
|
||||
|
||||
while (1) {
|
||||
|
||||
char 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 = (unsigned long long)(((unsigned long long)rp->start * 10) + (unsigned long long)(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 = (unsigned long long)(((unsigned long long)rp->end * 10) + (unsigned long long)(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;
|
||||
}
|
||||
55
Kinc/Sources/kinc/libs/roles/http/server/rewrite.c
Normal file
55
Kinc/Sources/kinc/libs/roles/http/server/rewrite.c
Normal file
@ -0,0 +1,55 @@
|
||||
#include "private-lib-core.h"
|
||||
|
||||
#if defined(LWS_WITH_HUBBUB)
|
||||
|
||||
struct lws_rewrite *
|
||||
lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from,
|
||||
const char *to)
|
||||
{
|
||||
struct lws_rewrite *r = lws_malloc(sizeof(*r), "rewrite");
|
||||
|
||||
if (!r) {
|
||||
lwsl_err("OOM\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (hubbub_parser_create("UTF-8", false, &r->parser) != HUBBUB_OK) {
|
||||
lws_free(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
r->from = from;
|
||||
r->from_len = strlen(from);
|
||||
r->to = to;
|
||||
r->to_len = strlen(to);
|
||||
r->params.token_handler.handler = cb;
|
||||
r->wsi = wsi;
|
||||
r->params.token_handler.pw = (void *)r;
|
||||
if (hubbub_parser_setopt(r->parser, HUBBUB_PARSER_TOKEN_HANDLER,
|
||||
&r->params) != HUBBUB_OK) {
|
||||
lws_free(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
lws_rewrite_parse(struct lws_rewrite *r,
|
||||
const unsigned char *in, int in_len)
|
||||
{
|
||||
if (r && hubbub_parser_parse_chunk(r->parser, in, in_len) != HUBBUB_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_rewrite_destroy(struct lws_rewrite *r)
|
||||
{
|
||||
hubbub_parser_destroy(r->parser);
|
||||
lws_free(r);
|
||||
}
|
||||
|
||||
#endif
|
||||
3511
Kinc/Sources/kinc/libs/roles/http/server/server.c
Normal file
3511
Kinc/Sources/kinc/libs/roles/http/server/server.c
Normal file
File diff suppressed because it is too large
Load Diff
42
Kinc/Sources/kinc/libs/roles/listen/CMakeLists.txt
Normal file
42
Kinc/Sources/kinc/libs/roles/listen/CMakeLists.txt
Normal file
@ -0,0 +1,42 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
list(APPEND SOURCES
|
||||
roles/listen/ops-listen.c)
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
|
||||
220
Kinc/Sources/kinc/libs/roles/listen/ops-listen.c
Normal file
220
Kinc/Sources/kinc/libs/roles/listen/ops-listen.c
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.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->a.context;
|
||||
struct lws_filter_network_conn_args filt;
|
||||
lws_sock_file_fd_type fd;
|
||||
|
||||
memset(&filt, 0, sizeof(filt));
|
||||
|
||||
/* if our vhost is going down, ignore it */
|
||||
|
||||
if (wsi->a.vhost->being_destroyed)
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
/* 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;
|
||||
int opts = LWS_ADOPT_SOCKET | LWS_ADOPT_ALLOW_SSL;
|
||||
|
||||
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->a.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... */
|
||||
|
||||
filt.clilen = sizeof(filt.cli_addr);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
filt.accept_fd = accept((int)pollfd->fd,
|
||||
(struct sockaddr *)&filt.cli_addr,
|
||||
&filt.clilen);
|
||||
if (filt.accept_fd == LWS_SOCK_INVALID) {
|
||||
if (LWS_ERRNO == LWS_EAGAIN ||
|
||||
LWS_ERRNO == LWS_EWOULDBLOCK) {
|
||||
break;
|
||||
}
|
||||
lwsl_err("accept: errno %d\n", LWS_ERRNO);
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
if (context->being_destroyed) {
|
||||
compatible_close(filt.accept_fd);
|
||||
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
lws_plat_set_socket_options(wsi->a.vhost, filt.accept_fd, 0);
|
||||
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
lwsl_debug("accepted new conn port %u on fd=%d\n",
|
||||
((filt.cli_addr.ss_family == AF_INET6) ?
|
||||
ntohs(((struct sockaddr_in6 *) &filt.cli_addr)->sin6_port) :
|
||||
ntohs(((struct sockaddr_in *) &filt.cli_addr)->sin_port)),
|
||||
filt.accept_fd);
|
||||
#else
|
||||
{
|
||||
struct sockaddr_in sain;
|
||||
|
||||
memcpy(&sain, &filt.cli_addr, sizeof(sain));
|
||||
lwsl_debug("accepted new conn port %u on fd=%d\n",
|
||||
ntohs(sain.sin_port),
|
||||
filt.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->a.vhost->protocols[0].callback)(wsi,
|
||||
LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
|
||||
(void *)&filt,
|
||||
(void *)(lws_intptr_t)filt.accept_fd, 0)) {
|
||||
lwsl_debug("Callback denied net connection\n");
|
||||
compatible_close(filt.accept_fd);
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
if (!(wsi->a.vhost->options &
|
||||
LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG))
|
||||
opts |= LWS_ADOPT_HTTP;
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (!wsi->a.vhost->tls.use_ssl)
|
||||
#endif
|
||||
opts &= ~LWS_ADOPT_ALLOW_SSL;
|
||||
|
||||
fd.sockfd = filt.accept_fd;
|
||||
cwsi = lws_adopt_descriptor_vhost(wsi->a.vhost, (lws_adoption_type)opts, fd,
|
||||
wsi->a.vhost->listen_accept_protocol, NULL);
|
||||
if (!cwsi) {
|
||||
lwsl_info("%s: vh %s: adopt failed\n", __func__,
|
||||
wsi->a.vhost->name);
|
||||
|
||||
/* already closed cleanly as necessary */
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
}
|
||||
/*
|
||||
if (lws_server_socket_service_ssl(cwsi, accept_fd, 1)) {
|
||||
lws_close_free_wsi(cwsi, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"listen svc fail");
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
}
|
||||
|
||||
lwsl_info("%s: new %s: wsistate 0x%lx, role_ops %s\n",
|
||||
__func__, lws_wsi_tag(cwsi), (unsigned long)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;
|
||||
}
|
||||
|
||||
static const lws_rops_t rops_table_listen[] = {
|
||||
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_listen },
|
||||
/* 2 */ { .handle_POLLOUT = rops_handle_POLLOUT_listen },
|
||||
};
|
||||
|
||||
const struct lws_role_ops role_ops_listen = {
|
||||
/* role name */ "listen",
|
||||
/* alpn id */ NULL,
|
||||
|
||||
/* rops_table */ rops_table_listen,
|
||||
/* rops_idx */ {
|
||||
/* LWS_ROPS_check_upgrades */
|
||||
/* LWS_ROPS_pt_init_destroy */ 0x00,
|
||||
/* LWS_ROPS_init_vhost */
|
||||
/* LWS_ROPS_destroy_vhost */ 0x00,
|
||||
/* LWS_ROPS_service_flag_pending */
|
||||
/* LWS_ROPS_handle_POLLIN */ 0x01,
|
||||
/* LWS_ROPS_handle_POLLOUT */
|
||||
/* LWS_ROPS_perform_user_POLLOUT */ 0x20,
|
||||
/* LWS_ROPS_callback_on_writable */
|
||||
/* LWS_ROPS_tx_credit */ 0x00,
|
||||
/* LWS_ROPS_write_role_protocol */
|
||||
/* LWS_ROPS_encapsulation_parent */ 0x00,
|
||||
/* LWS_ROPS_alpn_negotiated */
|
||||
/* LWS_ROPS_close_via_role_protocol */ 0x00,
|
||||
/* LWS_ROPS_close_role */
|
||||
/* LWS_ROPS_close_kill_connection */ 0x00,
|
||||
/* LWS_ROPS_destroy_role */
|
||||
/* LWS_ROPS_adoption_bind */ 0x00,
|
||||
/* LWS_ROPS_client_bind */
|
||||
/* LWS_ROPS_issue_keepalive */ 0x00,
|
||||
},
|
||||
|
||||
/* adoption_cb clnt, srv */ { 0, 0 },
|
||||
/* rx_cb clnt, srv */ { 0, 0 },
|
||||
/* writeable cb clnt, srv */ { 0, 0 },
|
||||
/* close cb clnt, srv */ { 0, 0 },
|
||||
/* protocol_bind_cb c,s */ { 0, 0 },
|
||||
/* protocol_unbind_cb c,s */ { 0, 0 },
|
||||
/* file_handle */ 0,
|
||||
};
|
||||
48
Kinc/Sources/kinc/libs/roles/mqtt/CMakeLists.txt
Normal file
48
Kinc/Sources/kinc/libs/roles/mqtt/CMakeLists.txt
Normal file
@ -0,0 +1,48 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
if (LWS_WITH_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
roles/mqtt/mqtt.c
|
||||
roles/mqtt/ops-mqtt.c
|
||||
roles/mqtt/primitives.c
|
||||
roles/mqtt/client/client-mqtt.c
|
||||
roles/mqtt/client/client-mqtt-handshake.c
|
||||
)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
200
Kinc/Sources/kinc/libs/roles/mqtt/client/client-mqtt-handshake.c
Normal file
200
Kinc/Sources/kinc/libs/roles/mqtt/client/client-mqtt-handshake.c
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
* Sakthi Kannan <saktr@amazon.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
#define MQTT_CONNECT_MSG_BASE_LEN (12)
|
||||
|
||||
struct lws *
|
||||
lws_mqtt_client_send_connect(struct lws *wsi)
|
||||
{
|
||||
/* static int */
|
||||
/* lws_mqttc_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget) */
|
||||
const lws_mqttc_t *c = &wsi->mqtt->client;
|
||||
uint8_t b[256 + LWS_PRE], *start = b + LWS_PRE, *p = start;
|
||||
unsigned int len = MQTT_CONNECT_MSG_BASE_LEN;
|
||||
|
||||
switch (lwsi_state(wsi)) {
|
||||
case LRS_MQTTC_IDLE:
|
||||
/*
|
||||
* Transport connected - this is our chance to do the
|
||||
* protocol connect action.
|
||||
*/
|
||||
|
||||
/* 1. Fixed Headers */
|
||||
if (lws_mqtt_fill_fixed_header(p++, LMQCP_CTOS_CONNECT, 0, 0, 0)) {
|
||||
lwsl_err("%s: Failled to fill fixed header\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2. Remaining length - Add the lengths of client ID,
|
||||
* username and password and their length fields if
|
||||
* the respective flags are set.
|
||||
*/
|
||||
len += c->id->len;
|
||||
if (c->conn_flags & LMQCFT_USERNAME && c->username) {
|
||||
len = len + (unsigned int)c->username->len + 2;
|
||||
if (c->conn_flags & LMQCFT_PASSWORD)
|
||||
len += (unsigned int)(c->password ? c->password->len : 0) + 2u;
|
||||
}
|
||||
if (c->conn_flags & LMQCFT_WILL_FLAG && c->will.topic) {
|
||||
len = len + (unsigned int)c->will.topic->len + 2;
|
||||
len += (c->will.message ? c->will.message->len : 0) + 2u;
|
||||
}
|
||||
p += lws_mqtt_vbi_encode(len, p);
|
||||
|
||||
/*
|
||||
* 3. Variable Header - Protocol name & level, Connect
|
||||
* flags and keep alive time (in secs).
|
||||
*/
|
||||
lws_ser_wu16be(p, 4); /* Length of protocol name */
|
||||
p += 2;
|
||||
*p++ = 'M';
|
||||
*p++ = 'Q';
|
||||
*p++ = 'T';
|
||||
*p++ = 'T';
|
||||
*p++ = MQTT_VER_3_1_1;
|
||||
*p++ = (uint8_t)c->conn_flags;
|
||||
lws_ser_wu16be(p, c->keep_alive_secs);
|
||||
p += 2;
|
||||
|
||||
/*
|
||||
* 4. Payload - Client ID, Will topic & message,
|
||||
* Username & password.
|
||||
*/
|
||||
if (lws_mqtt_str_is_not_empty(c->id)) {
|
||||
lws_ser_wu16be(p, c->id->len);
|
||||
p += 2;
|
||||
memcpy(p, c->id->buf, c->id->len);
|
||||
p += c->id->len;
|
||||
} else {
|
||||
/*
|
||||
* If the Client supplies a zero-byte
|
||||
* ClientId, the Client MUST also set
|
||||
* CleanSession to 1 [MQTT-3.1.3-7].
|
||||
*/
|
||||
if (!(c->conn_flags & LMQCFT_CLEAN_START)) {
|
||||
lwsl_err("%s: Empty client ID needs a clean start\n",
|
||||
__func__);
|
||||
return NULL;
|
||||
}
|
||||
*p++ = 0;
|
||||
}
|
||||
|
||||
if (c->conn_flags & LMQCFT_WILL_FLAG) {
|
||||
if (lws_mqtt_str_is_not_empty(c->will.topic)) {
|
||||
lws_ser_wu16be(p, c->will.topic->len);
|
||||
p += 2;
|
||||
memcpy(p, c->will.topic->buf, c->will.topic->len);
|
||||
p += c->will.topic->len;
|
||||
if (lws_mqtt_str_is_not_empty(c->will.message)) {
|
||||
lws_ser_wu16be(p, c->will.message->len);
|
||||
p += 2;
|
||||
memcpy(p, c->will.message->buf,
|
||||
c->will.message->len);
|
||||
p += c->will.message->len;
|
||||
} else {
|
||||
lws_ser_wu16be(p, 0);
|
||||
p += 2;
|
||||
}
|
||||
} else {
|
||||
lwsl_err("%s: Missing Will Topic\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (c->conn_flags & LMQCFT_USERNAME) {
|
||||
/*
|
||||
* Detailed sanity check on the username and
|
||||
* password strings.
|
||||
*/
|
||||
if (lws_mqtt_str_is_not_empty(c->username)) {
|
||||
lws_ser_wu16be(p, c->username->len);
|
||||
p += 2;
|
||||
memcpy(p, c->username->buf, c->username->len);
|
||||
p += c->username->len;
|
||||
} else {
|
||||
lwsl_err("%s: Empty / missing Username!\n",
|
||||
__func__);
|
||||
return NULL;
|
||||
}
|
||||
if (c->conn_flags & LMQCFT_PASSWORD) {
|
||||
if (lws_mqtt_str_is_not_empty(c->password)) {
|
||||
lws_ser_wu16be(p, c->password->len);
|
||||
p += 2;
|
||||
memcpy(p, c->password->buf,
|
||||
c->password->len);
|
||||
p += c->password->len;
|
||||
} else {
|
||||
lws_ser_wu16be(p, 0);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
} else if (c->conn_flags & LMQCFT_PASSWORD) {
|
||||
lwsl_err("%s: Unsupported - Password without username\n",
|
||||
__func__);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
lwsl_err("%s: unexpected state %d\n", __func__, lwsi_state(wsi));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform the actual write
|
||||
*/
|
||||
if (lws_write(wsi, (unsigned char *)&b[LWS_PRE], lws_ptr_diff_size_t(p, start),
|
||||
LWS_WRITE_BINARY) != lws_ptr_diff(p, start)) {
|
||||
lwsl_notice("%s: write failed\n", __func__);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return wsi;
|
||||
}
|
||||
|
||||
struct lws *
|
||||
lws_mqtt_client_send_disconnect(struct lws *wsi)
|
||||
{
|
||||
uint8_t b[256 + LWS_PRE], *start = b + LWS_PRE, *p = start;
|
||||
|
||||
/* 1. Fixed Headers */
|
||||
if (lws_mqtt_fill_fixed_header(p++, LMQCP_DISCONNECT, 0, 0, 0))
|
||||
{
|
||||
lwsl_err("%s: Failled to fill fixed header\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
*p++ = 0;
|
||||
if (lws_write(wsi, (unsigned char *)&b[LWS_PRE], lws_ptr_diff_size_t(p, start),
|
||||
LWS_WRITE_BINARY) != lws_ptr_diff(p, start)) {
|
||||
lwsl_err("%s: write failed\n", __func__);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return wsi;
|
||||
}
|
||||
375
Kinc/Sources/kinc/libs/roles/mqtt/client/client-mqtt.c
Normal file
375
Kinc/Sources/kinc/libs/roles/mqtt/client/client-mqtt.c
Normal file
@ -0,0 +1,375 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* You can leave buf NULL, if so it will be allocated on the heap once the
|
||||
* actual length is known. nf should be 0, it will be set at allocation time.
|
||||
*
|
||||
* Or you can ensure no allocation and use an external buffer by setting buf
|
||||
* and lim. But buf must be in the ep context somehow, since it may have to
|
||||
* survive returns to the event loop unchanged. Set nf to 0 in this case.
|
||||
*
|
||||
* Or you can set buf to an externally allocated buffer, in which case you may
|
||||
* set nf so it will be freed when the string is "freed".
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
/* #include "lws-mqtt.h" */
|
||||
/* 3.1.3.1-5: MUST allow... that contain only the characters... */
|
||||
|
||||
static const uint8_t *code = (const uint8_t *)
|
||||
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
static int
|
||||
lws_mqtt_generate_id(struct lws* wsi, lws_mqtt_str_t **ms, const char *client_id)
|
||||
{
|
||||
struct lws_context *context = wsi->a.context;
|
||||
uint16_t ran[24]; /* 16-bit so wrap bias from %62 diluted by ~1000 */
|
||||
size_t n, len;
|
||||
uint8_t *buf;
|
||||
|
||||
if (client_id)
|
||||
len = strlen(client_id);
|
||||
else
|
||||
len = LWS_MQTT_RANDOM_CIDLEN;
|
||||
|
||||
*ms = lws_mqtt_str_create((uint16_t)(len + 1));
|
||||
if (!*ms)
|
||||
return 1;
|
||||
|
||||
buf = lws_mqtt_str_next(*ms, NULL);
|
||||
|
||||
if (client_id) {
|
||||
lws_strnncpy((char *)buf, client_id, len, len + 1);
|
||||
lwsl_notice("%s: User space provided a client ID '%s'\n",
|
||||
__func__, (const char *)buf);
|
||||
} else {
|
||||
lwsl_notice("%s: generating random client id\n", __func__);
|
||||
n = len * sizeof(ran[0]);
|
||||
if (lws_get_random(context, ran, n) != n) {
|
||||
lws_mqtt_str_free(ms);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (n = 0; n < len; n++)
|
||||
buf[n] = code[ran[n] % 62];
|
||||
buf[len] = '\0';
|
||||
}
|
||||
|
||||
if (lws_mqtt_str_advance(*ms, (uint16_t)len)) {
|
||||
lws_mqtt_str_free(ms);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_read_mqtt(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
|
||||
{
|
||||
lws_mqttc_t *c = &wsi->mqtt->client;
|
||||
|
||||
return _lws_mqtt_rx_parser(wsi, &c->par, buf, (size_t)len);
|
||||
}
|
||||
|
||||
int
|
||||
lws_create_client_mqtt_object(const struct lws_client_connect_info *i,
|
||||
struct lws *wsi)
|
||||
{
|
||||
lws_mqttc_t *c;
|
||||
const lws_mqtt_client_connect_param_t *cp = i->mqtt_cp;
|
||||
|
||||
/* allocate the ws struct for the wsi */
|
||||
wsi->mqtt = lws_zalloc(sizeof(*wsi->mqtt), "client mqtt struct");
|
||||
if (!wsi->mqtt)
|
||||
goto oom;
|
||||
|
||||
wsi->mqtt->wsi = wsi;
|
||||
c = &wsi->mqtt->client;
|
||||
|
||||
if (lws_mqtt_generate_id(wsi, &c->id, cp->client_id)) {
|
||||
lwsl_err("%s: Error generating client ID\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
lwsl_info("%s: using client id '%.*s'\n", __func__, c->id->len,
|
||||
(const char *)c->id->buf);
|
||||
|
||||
if (cp->clean_start || !(cp->client_id &&
|
||||
cp->client_id[0]))
|
||||
c->conn_flags = LMQCFT_CLEAN_START;
|
||||
if (cp->client_id_nofree)
|
||||
c->conn_flags |= LMQCFT_CLIENT_ID_NOFREE;
|
||||
if (cp->username_nofree)
|
||||
c->conn_flags |= LMQCFT_USERNAME_NOFREE;
|
||||
if (cp->password_nofree)
|
||||
c->conn_flags |= LMQCFT_PASSWORD_NOFREE;
|
||||
|
||||
if (!(c->conn_flags & LMQCFT_CLIENT_ID_NOFREE))
|
||||
lws_free((void *)cp->client_id);
|
||||
|
||||
c->keep_alive_secs = cp->keep_alive;
|
||||
c->aws_iot = cp->aws_iot;
|
||||
|
||||
if (cp->will_param.topic &&
|
||||
*cp->will_param.topic) {
|
||||
c->will.topic = lws_mqtt_str_create_cstr_dup(
|
||||
cp->will_param.topic, 0);
|
||||
if (!c->will.topic)
|
||||
goto oom1;
|
||||
c->conn_flags |= LMQCFT_WILL_FLAG;
|
||||
if (cp->will_param.message) {
|
||||
c->will.message = lws_mqtt_str_create_cstr_dup(
|
||||
cp->will_param.message, 0);
|
||||
if (!c->will.message)
|
||||
goto oom2;
|
||||
}
|
||||
c->conn_flags = (uint16_t)(unsigned int)(c->conn_flags | ((cp->will_param.qos << 3) & LMQCFT_WILL_QOS_MASK));
|
||||
c->conn_flags |= (uint16_t)((!!cp->will_param.retain) * LMQCFT_WILL_RETAIN);
|
||||
}
|
||||
|
||||
if (cp->username &&
|
||||
*cp->username) {
|
||||
c->username = lws_mqtt_str_create_cstr_dup(cp->username, 0);
|
||||
if (!c->username)
|
||||
goto oom3;
|
||||
c->conn_flags |= LMQCFT_USERNAME;
|
||||
if (!(c->conn_flags & LMQCFT_USERNAME_NOFREE))
|
||||
lws_free((void *)cp->username);
|
||||
if (cp->password) {
|
||||
c->password =
|
||||
lws_mqtt_str_create_cstr_dup(cp->password, 0);
|
||||
if (!c->password)
|
||||
goto oom4;
|
||||
c->conn_flags |= LMQCFT_PASSWORD;
|
||||
if (!(c->conn_flags & LMQCFT_PASSWORD_NOFREE))
|
||||
lws_free((void *)cp->password);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
oom4:
|
||||
lws_mqtt_str_free(&c->username);
|
||||
oom3:
|
||||
lws_mqtt_str_free(&c->will.message);
|
||||
oom2:
|
||||
lws_mqtt_str_free(&c->will.topic);
|
||||
oom1:
|
||||
lws_mqtt_str_free(&c->id);
|
||||
oom:
|
||||
lwsl_err("%s: OOM!\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
lws_mqtt_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
|
||||
struct lws *wsi_conn)
|
||||
{
|
||||
struct lws_context *context = wsi->a.context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
int n = 0, m = 0;
|
||||
struct lws_tokens ebuf;
|
||||
int buffered = 0;
|
||||
int pending = 0;
|
||||
#if defined(LWS_WITH_TLS)
|
||||
char erbuf[128];
|
||||
#endif
|
||||
const char *cce = NULL;
|
||||
|
||||
switch (lwsi_state(wsi)) {
|
||||
#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:
|
||||
|
||||
switch (lws_socks5c_handle_state(wsi, pollfd, &cce)) {
|
||||
case LW5CHS_RET_RET0:
|
||||
return 0;
|
||||
case LW5CHS_RET_BAIL3:
|
||||
goto bail3;
|
||||
case LW5CHS_RET_STARTHS:
|
||||
|
||||
/*
|
||||
* Now we got the socks5 connection, we need to go down
|
||||
* the tls path on it if that's what we want
|
||||
*/
|
||||
|
||||
if (!(wsi->tls.use_ssl & LCCSCF_USE_SSL))
|
||||
goto start_ws_handshake;
|
||||
|
||||
switch (lws_client_create_tls(wsi, &cce, 0)) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
return 0;
|
||||
default:
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case LRS_WAITING_DNS:
|
||||
/*
|
||||
* we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
|
||||
* timeout protection set in client-handshake.c
|
||||
*/
|
||||
if (!lws_client_connect_2_dnsreq(wsi)) {
|
||||
/* closed */
|
||||
lwsl_client("closed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* either still pending connection, or changed mode */
|
||||
return 0;
|
||||
|
||||
case LRS_WAITING_CONNECT:
|
||||
|
||||
/*
|
||||
* we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
|
||||
* timeout protection set in client-handshake.c
|
||||
*/
|
||||
if (pollfd->revents & LWS_POLLOUT)
|
||||
lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL);
|
||||
break;
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
case LRS_WAITING_SSL:
|
||||
|
||||
if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
|
||||
n = lws_ssl_client_connect2(wsi, erbuf, sizeof(erbuf));
|
||||
if (!n)
|
||||
return 0;
|
||||
if (n < 0) {
|
||||
cce = erbuf;
|
||||
goto bail3;
|
||||
}
|
||||
} else
|
||||
wsi->tls.ssl = NULL;
|
||||
#endif /* LWS_WITH_TLS */
|
||||
|
||||
/* fallthru */
|
||||
|
||||
#if defined(LWS_WITH_SOCKS5)
|
||||
start_ws_handshake:
|
||||
#endif
|
||||
lwsi_set_state(wsi, LRS_MQTTC_IDLE);
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
|
||||
(int)context->timeout_secs);
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LRS_MQTTC_IDLE:
|
||||
/*
|
||||
* we should be ready to send out MQTT CONNECT
|
||||
*/
|
||||
lwsl_info("%s: %s: Transport established, send out CONNECT\n",
|
||||
__func__, lws_wsi_tag(wsi));
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
|
||||
return -1;
|
||||
if (!lws_mqtt_client_send_connect(wsi)) {
|
||||
lwsl_err("%s: Unable to send MQTT CONNECT\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLIN))
|
||||
return -1;
|
||||
|
||||
lwsi_set_state(wsi, LRS_MQTTC_AWAIT_CONNACK);
|
||||
return 0;
|
||||
|
||||
case LRS_ESTABLISHED:
|
||||
case LRS_MQTTC_AWAIT_CONNACK:
|
||||
buffered = 0;
|
||||
ebuf.token = pt->serv_buf;
|
||||
ebuf.len = (int)wsi->a.context->pt_serv_buf_size;
|
||||
|
||||
if ((unsigned int)ebuf.len > wsi->a.context->pt_serv_buf_size)
|
||||
ebuf.len = (int)wsi->a.context->pt_serv_buf_size;
|
||||
|
||||
if ((int)pending > ebuf.len)
|
||||
pending = (char)ebuf.len;
|
||||
|
||||
ebuf.len = lws_ssl_capable_read(wsi, ebuf.token,
|
||||
(unsigned int)(pending ? pending :
|
||||
ebuf.len));
|
||||
switch (ebuf.len) {
|
||||
case 0:
|
||||
lwsl_info("%s: zero length read\n",
|
||||
__func__);
|
||||
goto fail;
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
lwsl_info("SSL Capable more service\n");
|
||||
return 0;
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
lwsl_info("%s: LWS_SSL_CAPABLE_ERROR\n",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ebuf.len < 0)
|
||||
n = -1;
|
||||
else
|
||||
n = lws_read_mqtt(wsi, ebuf.token, (unsigned int)ebuf.len);
|
||||
if (n < 0) {
|
||||
lwsl_err("%s: Parsing packet failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
m = ebuf.len - n;
|
||||
// lws_buflist_describe(&wsi->buflist, wsi, __func__);
|
||||
lwsl_debug("%s: consuming %d / %d\n", __func__, n, ebuf.len);
|
||||
if (lws_buflist_aware_finished_consuming(wsi, &ebuf, m,
|
||||
buffered,
|
||||
__func__))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
||||
#if defined(LWS_WITH_TLS) || defined(LWS_WITH_SOCKS5)
|
||||
bail3:
|
||||
#endif
|
||||
lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n");
|
||||
if (cce)
|
||||
lwsl_info("reason: %s\n", cce);
|
||||
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
|
||||
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cbail3");
|
||||
return -1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "mqtt svc fail");
|
||||
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
}
|
||||
2506
Kinc/Sources/kinc/libs/roles/mqtt/mqtt.c
Normal file
2506
Kinc/Sources/kinc/libs/roles/mqtt/mqtt.c
Normal file
File diff suppressed because it is too large
Load Diff
654
Kinc/Sources/kinc/libs/roles/mqtt/ops-mqtt.c
Normal file
654
Kinc/Sources/kinc/libs/roles/mqtt/ops-mqtt.c
Normal file
@ -0,0 +1,654 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_mqtt(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
unsigned int pending = 0;
|
||||
struct lws_tokens ebuf;
|
||||
int n = 0;
|
||||
char buffered = 0;
|
||||
|
||||
lwsl_debug("%s: wsistate 0x%x, %s pollout %d\n", __func__,
|
||||
(unsigned int)wsi->wsistate, wsi->a.protocol->name,
|
||||
pollfd->revents);
|
||||
|
||||
/*
|
||||
* After the CONNACK and nwsi establishment, the first logical
|
||||
* stream is migrated out of the nwsi to be child sid 1, and the
|
||||
* nwsi no longer has a wsi->mqtt of its own.
|
||||
*
|
||||
* RX events on the nwsi must be converted to events seen or not
|
||||
* seen by one or more child streams.
|
||||
*
|
||||
* SUBACK - reflected to child stream that asked for it
|
||||
* PUBACK - routed to child that did the related publish
|
||||
*/
|
||||
|
||||
ebuf.token = NULL;
|
||||
ebuf.len = 0;
|
||||
|
||||
if (lwsi_state(wsi) != LRS_ESTABLISHED) {
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
|
||||
if (lwsi_state(wsi) == LRS_WAITING_SSL &&
|
||||
((pollfd->revents & LWS_POLLOUT)) &&
|
||||
lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_info("failed at set pollfd\n");
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
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_mqtt_client_socket_service(wsi, pollfd, NULL);
|
||||
if (n)
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
#endif
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 3: buflist needs to be drained
|
||||
*/
|
||||
read:
|
||||
// lws_buflist_describe(&wsi->buflist, wsi, __func__);
|
||||
ebuf.len = (int)lws_buflist_next_segment_len(&wsi->buflist, &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))
|
||||
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)) {
|
||||
/*
|
||||
* 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 = pt->serv_buf;
|
||||
ebuf.len = (int)wsi->a.context->pt_serv_buf_size;
|
||||
|
||||
if ((unsigned int)ebuf.len > wsi->a.context->pt_serv_buf_size)
|
||||
ebuf.len = (int)wsi->a.context->pt_serv_buf_size;
|
||||
|
||||
if ((int)pending > ebuf.len)
|
||||
pending = (unsigned int)ebuf.len;
|
||||
|
||||
ebuf.len = lws_ssl_capable_read(wsi, ebuf.token,
|
||||
pending ? pending :
|
||||
(unsigned int)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;
|
||||
}
|
||||
|
||||
/*
|
||||
* coverity thinks ssl_capable_read() may read over
|
||||
* 2GB. Dissuade it...
|
||||
*/
|
||||
ebuf.len &= 0x7fffffff;
|
||||
}
|
||||
|
||||
drain:
|
||||
/* service incoming data */
|
||||
//lws_buflist_describe(&wsi->buflist, wsi, __func__);
|
||||
if (ebuf.len) {
|
||||
n = lws_read_mqtt(wsi, ebuf.token, (unsigned int)ebuf.len);
|
||||
if (n < 0) {
|
||||
lwsl_notice("%s: lws_read_mqtt returned %d\n",
|
||||
__func__, n);
|
||||
/* we closed wsi */
|
||||
goto fail;
|
||||
}
|
||||
// lws_buflist_describe(&wsi->buflist, wsi, __func__);
|
||||
lwsl_debug("%s: consuming %d / %d\n", __func__, n, ebuf.len);
|
||||
if (lws_buflist_aware_finished_consuming(wsi, &ebuf, ebuf.len,
|
||||
buffered, __func__))
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
ebuf.token = NULL;
|
||||
ebuf.len = 0;
|
||||
|
||||
pending = (unsigned int)lws_ssl_pending(wsi);
|
||||
if (pending) {
|
||||
pending = pending > wsi->a.context->pt_serv_buf_size ?
|
||||
wsi->a.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: %s flow buf: drained\n", __func__, lws_wsi_tag(wsi));
|
||||
/* having drained the rxflow buffer, can rearm POLLIN */
|
||||
#if !defined(LWS_WITH_SERVER)
|
||||
n =
|
||||
#endif
|
||||
__lws_rx_flow_control(wsi);
|
||||
/* n ignored, needed for NO_SERVER case */
|
||||
}
|
||||
|
||||
/* n = 0 */
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
fail:
|
||||
lwsl_err("%s: Failed, bailing\n", __func__);
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "mqtt svc fail");
|
||||
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
}
|
||||
|
||||
#if 0 /* defined(LWS_WITH_SERVER) */
|
||||
|
||||
static int
|
||||
rops_adoption_bind_mqtt(struct lws *wsi, int type, const char *vh_prot_name)
|
||||
{
|
||||
/* no http but socket... must be mqtt */
|
||||
if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) ||
|
||||
(type & _LWS_ADOPT_FINISH))
|
||||
return 0; /* no match */
|
||||
|
||||
lws_role_transition(wsi, 0, (type & LWS_ADOPT_ALLOW_SSL) ? LRS_SSL_INIT :
|
||||
LRS_ESTABLISHED, &role_ops_mqtt);
|
||||
|
||||
if (vh_prot_name)
|
||||
lws_bind_protocol(wsi, wsi->a.protocol, __func__);
|
||||
else
|
||||
/* this is the only time he will transition */
|
||||
lws_bind_protocol(wsi,
|
||||
&wsi->a.vhost->protocols[wsi->a.vhost->mqtt_protocol_index],
|
||||
__func__);
|
||||
|
||||
return 1; /* bound */
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
rops_client_bind_mqtt(struct lws *wsi, const struct lws_client_connect_info *i)
|
||||
{
|
||||
lwsl_debug("%s: i = %p\n", __func__, i);
|
||||
if (!i) {
|
||||
|
||||
/* finalize */
|
||||
|
||||
if (!wsi->user_space && wsi->stash->cis[CIS_METHOD])
|
||||
if (lws_ensure_user_space(wsi))
|
||||
return 1;
|
||||
|
||||
if (!wsi->stash->cis[CIS_METHOD] && !wsi->stash->cis[CIS_ALPN])
|
||||
wsi->stash->cis[CIS_ALPN] = "x-amzn-mqtt-ca";
|
||||
|
||||
/* if we went on the ah waiting list, it's ok, we can
|
||||
* wait.
|
||||
*
|
||||
* When we do get the ah, now or later, he will end up
|
||||
* at lws_http_client_connect_via_info2().
|
||||
*/
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
if (lws_header_table_attach(wsi, 0) < 0)
|
||||
/*
|
||||
* if we failed here, the connection is already closed
|
||||
* and freed.
|
||||
*/
|
||||
return -1;
|
||||
#else
|
||||
if (lws_header_table_attach(wsi, 0))
|
||||
return 0;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if a recognized mqtt method, bind to it */
|
||||
if (strcmp(i->method, "MQTT"))
|
||||
return 0; /* no match */
|
||||
|
||||
if (lws_create_client_mqtt_object(i, wsi))
|
||||
return 1;
|
||||
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
|
||||
&role_ops_mqtt);
|
||||
return 1; /* matched */
|
||||
}
|
||||
|
||||
static int
|
||||
rops_handle_POLLOUT_mqtt(struct lws *wsi)
|
||||
{
|
||||
struct lws **wsi2;
|
||||
|
||||
lwsl_debug("%s\n", __func__);
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
if (wsi->mqtt && wsi->mqtt->send_pingreq && !wsi->mqtt->inside_payload) {
|
||||
uint8_t buf[LWS_PRE + 2];
|
||||
|
||||
/*
|
||||
* We are swallowing this POLLOUT in order to send a PINGREQ
|
||||
* autonomously
|
||||
*/
|
||||
|
||||
wsi->mqtt->send_pingreq = 0;
|
||||
|
||||
lwsl_notice("%s: issuing PINGREQ\n", __func__);
|
||||
|
||||
buf[LWS_PRE] = LMQCP_CTOS_PINGREQ << 4;
|
||||
buf[LWS_PRE + 1] = 0;
|
||||
|
||||
if (lws_write(wsi, (uint8_t *)&buf[LWS_PRE], 2,
|
||||
LWS_WRITE_BINARY) != 2)
|
||||
return LWS_HP_RET_BAIL_DIE;
|
||||
|
||||
return LWS_HP_RET_BAIL_OK;
|
||||
}
|
||||
#endif
|
||||
if (wsi->mqtt && !wsi->mqtt->inside_payload &&
|
||||
(wsi->mqtt->send_pubrec || wsi->mqtt->send_pubrel ||
|
||||
wsi->mqtt->send_pubcomp)) {
|
||||
uint8_t buf[LWS_PRE + 4];
|
||||
/* Remaining len = 2 */
|
||||
buf[LWS_PRE + 1] = 2;
|
||||
if (wsi->mqtt->send_pubrec) {
|
||||
lwsl_notice("%s: issuing PUBREC for pkt id: %d\n",
|
||||
__func__, wsi->mqtt->peer_ack_pkt_id);
|
||||
buf[LWS_PRE] = LMQCP_PUBREC << 4 | 0x2;
|
||||
/* Packet ID */
|
||||
lws_ser_wu16be(&buf[LWS_PRE + 2],
|
||||
wsi->mqtt->peer_ack_pkt_id);
|
||||
wsi->mqtt->send_pubrec = 0;
|
||||
} else if (wsi->mqtt->send_pubrel) {
|
||||
lwsl_notice("%s: issuing PUBREL for pkt id: %d\n",
|
||||
__func__, wsi->mqtt->ack_pkt_id);
|
||||
buf[LWS_PRE] = LMQCP_PUBREL << 4 | 0x2;
|
||||
lws_ser_wu16be(&buf[LWS_PRE + 2],
|
||||
wsi->mqtt->ack_pkt_id);
|
||||
wsi->mqtt->send_pubrel = 0;
|
||||
} else {
|
||||
lwsl_notice("%s: issuing PUBCOMP for pkt id: %d\n",
|
||||
__func__, wsi->mqtt->peer_ack_pkt_id);
|
||||
buf[LWS_PRE] = LMQCP_PUBCOMP << 4 | 0x2;
|
||||
lws_ser_wu16be(&buf[LWS_PRE + 2],
|
||||
wsi->mqtt->peer_ack_pkt_id);
|
||||
wsi->mqtt->send_pubcomp = 0;
|
||||
}
|
||||
if (lws_write(wsi, (uint8_t *)&buf[LWS_PRE], 4,
|
||||
LWS_WRITE_BINARY) != 4)
|
||||
return LWS_HP_RET_BAIL_DIE;
|
||||
return LWS_HP_RET_BAIL_OK;
|
||||
}
|
||||
|
||||
wsi = lws_get_network_wsi(wsi);
|
||||
|
||||
wsi->mux.requested_POLLOUT = 0;
|
||||
|
||||
wsi2 = &wsi->mux.child_list;
|
||||
if (!*wsi2) {
|
||||
lwsl_debug("%s: no children\n", __func__);
|
||||
return LWS_HP_RET_DROP_POLLOUT;
|
||||
}
|
||||
|
||||
if (!wsi->mqtt)
|
||||
return LWS_HP_RET_BAIL_DIE;
|
||||
|
||||
lws_wsi_mux_dump_waiting_children(wsi);
|
||||
|
||||
do {
|
||||
struct lws *w, **wa;
|
||||
|
||||
wa = &(*wsi2)->mux.sibling_list;
|
||||
if (!(*wsi2)->mux.requested_POLLOUT)
|
||||
goto next_child;
|
||||
|
||||
if (!lwsi_state_can_handle_POLLOUT(wsi))
|
||||
goto next_child;
|
||||
|
||||
/*
|
||||
* If the nwsi is in the middle of a frame, we can only
|
||||
* continue to send that
|
||||
*/
|
||||
|
||||
if (wsi->mqtt->inside_payload && !(*wsi2)->mqtt->inside_payload)
|
||||
goto next_child;
|
||||
|
||||
/*
|
||||
* we're going to do writable callback for this child.
|
||||
* move him to be the last child
|
||||
*/
|
||||
w = lws_wsi_mux_move_child_to_tail(wsi2);
|
||||
if (!w) {
|
||||
wa = &wsi->mux.child_list;
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
lwsl_debug("%s: child %s (wsistate 0x%x)\n", __func__,
|
||||
lws_wsi_tag(w), (unsigned int)w->wsistate);
|
||||
|
||||
if (lwsi_state(wsi) == LRS_ESTABLISHED &&
|
||||
!wsi->mqtt->inside_payload &&
|
||||
wsi->mqtt->send_puback) {
|
||||
uint8_t buf[LWS_PRE + 4];
|
||||
lwsl_notice("%s: issuing PUBACK for pkt id: %d\n",
|
||||
__func__, wsi->mqtt->ack_pkt_id);
|
||||
|
||||
/* Fixed header */
|
||||
buf[LWS_PRE] = LMQCP_PUBACK << 4;
|
||||
/* Remaining len = 2 */
|
||||
buf[LWS_PRE + 1] = 2;
|
||||
/* Packet ID */
|
||||
lws_ser_wu16be(&buf[LWS_PRE + 2], wsi->mqtt->peer_ack_pkt_id);
|
||||
|
||||
if (lws_write(wsi, (uint8_t *)&buf[LWS_PRE], 4,
|
||||
LWS_WRITE_BINARY) != 4)
|
||||
return LWS_HP_RET_BAIL_DIE;
|
||||
|
||||
wsi->mqtt->send_puback = 0;
|
||||
w->mux.requested_POLLOUT = 1;
|
||||
|
||||
wa = &wsi->mux.child_list;
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
if (lws_callback_as_writeable(w)) {
|
||||
lwsl_notice("%s: Closing child %s\n", __func__, lws_wsi_tag(w));
|
||||
lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"mqtt pollout handle");
|
||||
wa = &wsi->mux.child_list;
|
||||
}
|
||||
|
||||
next_child:
|
||||
wsi2 = wa;
|
||||
} while (wsi2 && *wsi2 && !lws_send_pipe_choked(wsi));
|
||||
|
||||
// lws_wsi_mux_dump_waiting_children(wsi);
|
||||
|
||||
if (lws_wsi_mux_action_pending_writeable_reqs(wsi))
|
||||
return LWS_HP_RET_BAIL_DIE;
|
||||
|
||||
return LWS_HP_RET_BAIL_OK;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
static int
|
||||
rops_issue_keepalive_mqtt(struct lws *wsi, int isvalid)
|
||||
{
|
||||
struct lws *nwsi = lws_get_network_wsi(wsi);
|
||||
|
||||
if (isvalid) {
|
||||
_lws_validity_confirmed_role(nwsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
nwsi->mqtt->send_pingreq = 1;
|
||||
lws_callback_on_writable(nwsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
rops_close_role_mqtt(struct lws_context_per_thread *pt, struct lws *wsi)
|
||||
{
|
||||
struct lws *nwsi = lws_get_network_wsi(wsi);
|
||||
lws_mqtt_subs_t *s, *s1, *mysub;
|
||||
lws_mqttc_t *c;
|
||||
|
||||
if (!wsi->mqtt)
|
||||
return 0;
|
||||
|
||||
c = &wsi->mqtt->client;
|
||||
|
||||
lws_sul_cancel(&wsi->mqtt->sul_qos_puback_pubrec_wait);
|
||||
|
||||
lws_mqtt_str_free(&c->username);
|
||||
lws_mqtt_str_free(&c->password);
|
||||
lws_mqtt_str_free(&c->will.message);
|
||||
lws_mqtt_str_free(&c->will.topic);
|
||||
lws_mqtt_str_free(&c->id);
|
||||
|
||||
/* clean up any subscription allocations */
|
||||
|
||||
s = wsi->mqtt->subs_head;
|
||||
wsi->mqtt->subs_head = NULL;
|
||||
while (s) {
|
||||
s1 = s->next;
|
||||
/*
|
||||
* Account for children no longer using nwsi subscription
|
||||
*/
|
||||
mysub = lws_mqtt_find_sub(nwsi->mqtt, (const char *)&s[1]);
|
||||
// assert(mysub); /* if child subscribed, nwsi must feel the same */
|
||||
if (mysub) {
|
||||
assert(mysub->ref_count);
|
||||
mysub->ref_count--;
|
||||
}
|
||||
lws_free(s);
|
||||
s = s1;
|
||||
}
|
||||
|
||||
lws_mqtt_publish_param_t *pub =
|
||||
(lws_mqtt_publish_param_t *)
|
||||
wsi->mqtt->rx_cpkt_param;
|
||||
|
||||
if (pub)
|
||||
lws_free_set_NULL(pub->topic);
|
||||
|
||||
lws_free_set_NULL(wsi->mqtt->rx_cpkt_param);
|
||||
|
||||
lws_free_set_NULL(wsi->mqtt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_callback_on_writable_mqtt(struct lws *wsi)
|
||||
{
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
struct lws *network_wsi;
|
||||
#endif
|
||||
int already;
|
||||
|
||||
lwsl_debug("%s: %s (wsistate 0x%x)\n", __func__, lws_wsi_tag(wsi),
|
||||
(unsigned int)wsi->wsistate);
|
||||
|
||||
if (wsi->mux.requested_POLLOUT
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
&& !wsi->client_h2_alpn
|
||||
#endif
|
||||
) {
|
||||
lwsl_debug("already pending writable\n");
|
||||
return 1;
|
||||
}
|
||||
#if 0
|
||||
/* is this for DATA or for control messages? */
|
||||
if (wsi->upgraded_to_http2 && !wsi->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->h2.tx_cr);
|
||||
wsi->h2.skint = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
wsi->h2.skint = 0;
|
||||
#endif
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
network_wsi = lws_get_network_wsi(wsi);
|
||||
#endif
|
||||
already = lws_wsi_mux_mark_parents_needing_writeable(wsi);
|
||||
|
||||
/* for network action, act only on the network wsi */
|
||||
|
||||
if (already
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
&& !network_wsi->client_mux_substream
|
||||
#endif
|
||||
)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_close_kill_connection_mqtt(struct lws *wsi, enum lws_close_status reason)
|
||||
{
|
||||
lwsl_info(" %s, his parent %s: child list %p, siblings:\n",
|
||||
lws_wsi_tag(wsi),
|
||||
lws_wsi_tag(wsi->mux.parent_wsi), wsi->mux.child_list);
|
||||
//lws_wsi_mux_dump_children(wsi);
|
||||
|
||||
if (wsi->mux_substream
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
|| wsi->client_mux_substream
|
||||
#endif
|
||||
) {
|
||||
lwsl_info("closing %s: parent %s: first child %p\n",
|
||||
lws_wsi_tag(wsi),
|
||||
lws_wsi_tag(wsi->mux.parent_wsi),
|
||||
wsi->mux.child_list);
|
||||
|
||||
if (wsi->mux.child_list && lwsl_visible(LLL_INFO)) {
|
||||
lwsl_info(" parent %s: closing children: list:\n", lws_wsi_tag(wsi));
|
||||
lws_wsi_mux_dump_children(wsi);
|
||||
}
|
||||
|
||||
lws_wsi_mux_close_children(wsi, (int)reason);
|
||||
}
|
||||
|
||||
if ((
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
wsi->client_mux_substream ||
|
||||
#endif
|
||||
wsi->mux_substream) &&
|
||||
wsi->mux.parent_wsi) {
|
||||
lws_wsi_mux_sibling_disconnect(wsi);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const lws_rops_t rops_table_mqtt[] = {
|
||||
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_mqtt },
|
||||
/* 2 */ { .handle_POLLOUT = rops_handle_POLLOUT_mqtt },
|
||||
/* 3 */ { .callback_on_writable = rops_callback_on_writable_mqtt },
|
||||
/* 4 */ { .close_role = rops_close_role_mqtt },
|
||||
/* 5 */ { .close_kill_connection = rops_close_kill_connection_mqtt },
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
/* 6 */ { .client_bind = rops_client_bind_mqtt },
|
||||
/* 7 */ { .issue_keepalive = rops_issue_keepalive_mqtt },
|
||||
#endif
|
||||
};
|
||||
|
||||
struct lws_role_ops role_ops_mqtt = {
|
||||
/* role name */ "mqtt",
|
||||
/* alpn id */ "x-amzn-mqtt-ca", /* "mqtt/3.1.1" */
|
||||
|
||||
/* rops_table */ rops_table_mqtt,
|
||||
/* rops_idx */ {
|
||||
/* LWS_ROPS_check_upgrades */
|
||||
/* LWS_ROPS_pt_init_destroy */ 0x00,
|
||||
/* LWS_ROPS_init_vhost */
|
||||
/* LWS_ROPS_destroy_vhost */ 0x00,
|
||||
/* LWS_ROPS_service_flag_pending */
|
||||
/* LWS_ROPS_handle_POLLIN */ 0x01,
|
||||
/* LWS_ROPS_handle_POLLOUT */
|
||||
/* LWS_ROPS_perform_user_POLLOUT */ 0x20,
|
||||
/* LWS_ROPS_callback_on_writable */
|
||||
/* LWS_ROPS_tx_credit */ 0x30,
|
||||
/* LWS_ROPS_write_role_protocol */
|
||||
/* LWS_ROPS_encapsulation_parent */ 0x00,
|
||||
/* LWS_ROPS_alpn_negotiated */
|
||||
/* LWS_ROPS_close_via_role_protocol */ 0x00,
|
||||
/* LWS_ROPS_close_role */
|
||||
/* LWS_ROPS_close_kill_connection */ 0x45,
|
||||
/* LWS_ROPS_destroy_role */
|
||||
/* LWS_ROPS_adoption_bind */ 0x00,
|
||||
|
||||
/* LWS_ROPS_client_bind */
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
/* LWS_ROPS_issue_keepalive */ 0x67,
|
||||
#else
|
||||
/* LWS_ROPS_issue_keepalive */ 0x00,
|
||||
#endif
|
||||
},
|
||||
|
||||
.adoption_cb = { LWS_CALLBACK_MQTT_NEW_CLIENT_INSTANTIATED,
|
||||
LWS_CALLBACK_MQTT_NEW_CLIENT_INSTANTIATED },
|
||||
.rx_cb = { LWS_CALLBACK_MQTT_CLIENT_RX,
|
||||
LWS_CALLBACK_MQTT_CLIENT_RX },
|
||||
.writeable_cb = { LWS_CALLBACK_MQTT_CLIENT_WRITEABLE,
|
||||
LWS_CALLBACK_MQTT_CLIENT_WRITEABLE },
|
||||
.close_cb = { LWS_CALLBACK_MQTT_CLIENT_CLOSED,
|
||||
LWS_CALLBACK_MQTT_CLIENT_CLOSED },
|
||||
.protocol_bind_cb = { LWS_CALLBACK_MQTT_IDLE,
|
||||
LWS_CALLBACK_MQTT_IDLE },
|
||||
.protocol_unbind_cb = { LWS_CALLBACK_MQTT_DROP_PROTOCOL,
|
||||
LWS_CALLBACK_MQTT_DROP_PROTOCOL },
|
||||
.file_handle = 0,
|
||||
};
|
||||
324
Kinc/Sources/kinc/libs/roles/mqtt/primitives.c
Normal file
324
Kinc/Sources/kinc/libs/roles/mqtt/primitives.c
Normal file
@ -0,0 +1,324 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* MQTT v5
|
||||
*
|
||||
* http://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
/*
|
||||
* Encode is done into a buffer of at least 4 bytes space.
|
||||
*
|
||||
* Returns -1 for error, or number of bytes used
|
||||
*/
|
||||
|
||||
int
|
||||
lws_mqtt_vbi_encode(uint32_t value, void *buf)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)buf, b;
|
||||
|
||||
if (value > 0xfffffff) {
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
b = value & 0x7f;
|
||||
value >>= 7;
|
||||
if (value)
|
||||
*p++ = (0x80 | b);
|
||||
else
|
||||
*p++ = b;
|
||||
} while (value);
|
||||
|
||||
return lws_ptr_diff(p, (uint8_t *)buf);
|
||||
}
|
||||
|
||||
void
|
||||
lws_mqtt_vbi_init(lws_mqtt_vbi *vbi)
|
||||
{
|
||||
vbi->value = 0;
|
||||
vbi->consumed = 0;
|
||||
vbi->budget = 4;
|
||||
}
|
||||
|
||||
void
|
||||
lws_mqtt_2byte_init(lws_mqtt_vbi *vbi)
|
||||
{
|
||||
vbi->value = 0;
|
||||
vbi->consumed = 0;
|
||||
vbi->budget = 2;
|
||||
}
|
||||
|
||||
void
|
||||
lws_mqtt_4byte_init(lws_mqtt_vbi *vbi)
|
||||
{
|
||||
vbi->value = 0;
|
||||
vbi->consumed = 0;
|
||||
vbi->budget = 4;
|
||||
}
|
||||
|
||||
lws_mqtt_stateful_primitive_return_t
|
||||
lws_mqtt_vbi_r(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len)
|
||||
{
|
||||
uint8_t multiplier = 0;
|
||||
if (!vbi->budget) {
|
||||
lwsl_info("%s: bad vbi\n", __func__);
|
||||
|
||||
return LMSPR_FAILED_ALREADY_COMPLETED;
|
||||
}
|
||||
|
||||
while (*len && vbi->budget--) {
|
||||
uint8_t u = *((*in)++);
|
||||
|
||||
(*len)--;
|
||||
vbi->consumed++;
|
||||
vbi->value = vbi->value + (uint32_t)((u & 0x7f) << multiplier);
|
||||
multiplier = (uint8_t)(multiplier + 7);
|
||||
if (!(u & 0x80))
|
||||
return LMSPR_COMPLETED; /* finished */
|
||||
}
|
||||
|
||||
if (!vbi->budget) { /* should have ended on b7 = 0 and exited then... */
|
||||
lwsl_info("%s: bad vbi\n", __func__);
|
||||
|
||||
return LMSPR_FAILED_FORMAT;
|
||||
}
|
||||
|
||||
return LMSPR_NEED_MORE;
|
||||
}
|
||||
|
||||
lws_mqtt_stateful_primitive_return_t
|
||||
lws_mqtt_mb_parse(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len)
|
||||
{
|
||||
if (!vbi->budget)
|
||||
return LMSPR_FAILED_ALREADY_COMPLETED;
|
||||
|
||||
while (*len && vbi->budget--) {
|
||||
vbi->value = (vbi->value << 8) | *((*in)++);
|
||||
(*len)--;
|
||||
vbi->consumed++;
|
||||
}
|
||||
|
||||
return vbi->budget ? LMSPR_NEED_MORE : LMSPR_COMPLETED;
|
||||
}
|
||||
|
||||
/*
|
||||
* You can leave buf NULL, if so it will be allocated on the heap once the
|
||||
* actual length is known. nf should be 0, it will be set at allocation time.
|
||||
*
|
||||
* Or you can ensure no allocation and use an external buffer by setting buf
|
||||
* and lim. But buf must be in the ep context somehow, since it may have to
|
||||
* survive returns to the event loop unchanged. Set nf to 0 in this case.
|
||||
*
|
||||
* Or you can set buf to an externally allocated buffer, in which case you may
|
||||
* set nf so it will be freed when the string is "freed".
|
||||
*/
|
||||
|
||||
void
|
||||
lws_mqtt_str_init(lws_mqtt_str_t *s, uint8_t *buf, uint16_t lim, char nf)
|
||||
{
|
||||
s->len = 0; /* at COMPLETED, consumed count is s->len + 2 */
|
||||
s->pos = 0;
|
||||
s->buf = buf;
|
||||
s->limit = lim;
|
||||
s->len_valid = 0;
|
||||
s->needs_freeing = nf;
|
||||
}
|
||||
|
||||
lws_mqtt_str_t *
|
||||
lws_mqtt_str_create(uint16_t lim)
|
||||
{
|
||||
lws_mqtt_str_t *s = lws_malloc(sizeof(*s) + lim + 1, __func__);
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
s->len = 0;
|
||||
s->pos = 0;
|
||||
s->buf = (uint8_t *)&s[1];
|
||||
s->limit = lim;
|
||||
s->len_valid = 0;
|
||||
s->needs_freeing = 1;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
lws_mqtt_str_t *
|
||||
lws_mqtt_str_create_init(uint8_t *buf, uint16_t len, uint16_t lim)
|
||||
{
|
||||
lws_mqtt_str_t *s;
|
||||
|
||||
if (!lim)
|
||||
lim = len;
|
||||
|
||||
s = lws_mqtt_str_create(lim);
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
memcpy(s->buf, buf, len);
|
||||
s->len = len;
|
||||
s->len_valid = 1;
|
||||
s->pos = len;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
lws_mqtt_str_t *
|
||||
lws_mqtt_str_create_cstr_dup(const char *buf, uint16_t lim)
|
||||
{
|
||||
size_t len = strlen(buf);
|
||||
|
||||
if (!lim)
|
||||
lim = (uint16_t)len;
|
||||
|
||||
return lws_mqtt_str_create_init((uint8_t *)buf, (uint16_t)len, lim);
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
lws_mqtt_str_next(lws_mqtt_str_t *s, uint16_t *budget)
|
||||
{
|
||||
if (budget)
|
||||
*budget = (uint16_t)(s->limit - s->pos);
|
||||
|
||||
return &s->buf[s->pos];
|
||||
}
|
||||
|
||||
int
|
||||
lws_mqtt_str_advance(lws_mqtt_str_t *s, int n)
|
||||
{
|
||||
if (n > s->limit - s->pos) {
|
||||
lwsl_err("%s: attempted overflow %d vs %d\n", __func__,
|
||||
n, s->limit - s->pos);
|
||||
return 1;
|
||||
}
|
||||
|
||||
s->pos = (uint16_t)(s->pos + (uint16_t)n);
|
||||
s->len = (uint16_t)(s->len + (uint16_t)n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_mqtt_str_free(lws_mqtt_str_t **ps)
|
||||
{
|
||||
lws_mqtt_str_t *s = *ps;
|
||||
|
||||
if (!s || !s->needs_freeing)
|
||||
return;
|
||||
|
||||
/* buf may be independently allocated or allocated along with the
|
||||
* lws_mqtt_str_t at the end... if so the whole lws_mqtt_str_t is freed.
|
||||
*/
|
||||
|
||||
if (s->buf != (uint8_t *)&s[1])
|
||||
lws_free_set_NULL(s->buf);
|
||||
else
|
||||
lws_free_set_NULL(*ps);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses and allocates for lws_mqtt_str_t in a fragmentation-immune, but
|
||||
* efficient for bulk data way.
|
||||
*
|
||||
* Returns: LMSPR_NEED_MORE if needs more data,
|
||||
* LMSPR_COMPLETED if complete, <0 for error
|
||||
*
|
||||
* *len is reduced by, and *in is advanced by, the amount of data actually used,
|
||||
* except in error case
|
||||
*
|
||||
* lws_mqtt_str_free() must be called after calling this successfully
|
||||
* or not.
|
||||
*/
|
||||
lws_mqtt_stateful_primitive_return_t
|
||||
lws_mqtt_str_parse(lws_mqtt_str_t *s, const uint8_t **in, size_t *len)
|
||||
{
|
||||
const uint8_t *oin = *in;
|
||||
|
||||
/* handle the length + allocation if needed */
|
||||
while (*len && !s->len_valid && s->pos < 2) {
|
||||
s->len = (uint16_t)((s->len << 8) | *((*in)++));
|
||||
(*len)--;
|
||||
oin = *in;
|
||||
if (++s->pos == 2) {
|
||||
if (s->len > s->limit)
|
||||
return LMSPR_FAILED_OVERSIZE;
|
||||
|
||||
s->pos = 0;
|
||||
s->len_valid = 1;
|
||||
|
||||
if (!s->len) /* do not need to allocate */
|
||||
return LMSPR_COMPLETED;
|
||||
|
||||
if (!s->buf) {
|
||||
s->buf = lws_malloc(s->len, __func__);
|
||||
if (!s->buf)
|
||||
return LMSPR_FAILED_OOM;
|
||||
|
||||
s->needs_freeing = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* handle copying bulk data into allocation */
|
||||
if (s->len_valid && *len) {
|
||||
uint16_t span = (uint16_t)(s->len - s->pos);
|
||||
|
||||
if (span > *len)
|
||||
span = (uint16_t)*len;
|
||||
|
||||
memcpy(s->buf + s->pos, *in, span);
|
||||
*in += span;
|
||||
s->pos = (uint16_t)(s->pos + (uint16_t)span);
|
||||
}
|
||||
|
||||
*len -= (unsigned long)(*in - oin);
|
||||
|
||||
return s->buf && s->pos == s->len ? LMSPR_COMPLETED : LMSPR_NEED_MORE;
|
||||
}
|
||||
|
||||
int
|
||||
lws_mqtt_bindata_cmp(const lws_mqtt_str_t *bd1,
|
||||
const lws_mqtt_str_t *bd2)
|
||||
{
|
||||
if (bd1->len != bd2->len)
|
||||
return 1;
|
||||
|
||||
if (!!bd1->buf != !!bd2->buf)
|
||||
return 1;
|
||||
|
||||
if (!bd1->buf && !bd2->buf)
|
||||
return 0;
|
||||
|
||||
return memcmp(bd1->buf, bd2->buf, bd1->len);
|
||||
}
|
||||
|
||||
449
Kinc/Sources/kinc/libs/roles/mqtt/private-lib-roles-mqtt.h
Normal file
449
Kinc/Sources/kinc/libs/roles/mqtt/private-lib-roles-mqtt.h
Normal file
@ -0,0 +1,449 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _PRIVATE_LIB_ROLES_MQTT
|
||||
#define _PRIVATE_LIB_ROLES_MQTT 1
|
||||
|
||||
extern struct lws_role_ops role_ops_mqtt;
|
||||
|
||||
#define lwsi_role_mqtt(wsi) (wsi->role_ops == &role_ops_mqtt)
|
||||
|
||||
#define LWS_MQTT_MAX_CHILDREN 8 /* max child streams on same parent */
|
||||
|
||||
#define LMQCP_LUT_FLAG_RESERVED_FLAGS 0x10
|
||||
#define LMQCP_LUT_FLAG_PACKET_ID_NONE 0x00
|
||||
#define LMQCP_LUT_FLAG_PACKET_ID_HAS 0x20
|
||||
#define LMQCP_LUT_FLAG_PACKET_ID_QOS12 0x40
|
||||
#define LMQCP_LUT_FLAG_PACKET_ID_MASK 0x60
|
||||
#define LMQCP_LUT_FLAG_PAYLOAD 0x80 /* payload req (publish = opt)*/
|
||||
|
||||
#define lws_mqtt_str_is_not_empty(s) ( ((s)) && \
|
||||
((s))->len && \
|
||||
((s))->buf && \
|
||||
*((s))->buf )
|
||||
|
||||
#define LWS_MQTT_RESPONSE_TIMEOUT (3 * LWS_US_PER_SEC)
|
||||
#define LWS_MQTT_RETRY_CEILING (60 * LWS_US_PER_SEC)
|
||||
#define LWS_MQTT_MAX_PUBLISH_RETRY (3)
|
||||
|
||||
typedef enum {
|
||||
LMSPR_COMPLETED = 0,
|
||||
LMSPR_NEED_MORE = 1,
|
||||
|
||||
LMSPR_FAILED_OOM = -1,
|
||||
LMSPR_FAILED_OVERSIZE = -2,
|
||||
LMSPR_FAILED_FORMAT = -3,
|
||||
LMSPR_FAILED_ALREADY_COMPLETED = -4,
|
||||
} lws_mqtt_stateful_primitive_return_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t value;
|
||||
char budget;
|
||||
char consumed;
|
||||
} lws_mqtt_vbi;
|
||||
|
||||
/* works for vbi, 2-byte and 4-byte fixed length */
|
||||
static inline int
|
||||
lws_mqtt_mb_first(lws_mqtt_vbi *vbi) { return !vbi->consumed; }
|
||||
|
||||
int
|
||||
lws_mqtt_vbi_encode(uint32_t value, void *buf);
|
||||
|
||||
/*
|
||||
* Decode is done statefully on an arbitrary amount of input data (which may
|
||||
* be one byte). It's like this so it can continue seamlessly if a buffer ends
|
||||
* partway through the primitive, and the api matches the bulk binary data case.
|
||||
*
|
||||
* VBI decode:
|
||||
*
|
||||
* Initialize the lws_mqtt_vbi state by calling lws_mqtt_vbi_init() on it, then
|
||||
* feed lws_mqtt_vbi_r() bytes to decode.
|
||||
*
|
||||
* Returns <0 for error, LMSPR_COMPLETED if done (vbi->value is valid), or
|
||||
* LMSPR_NEED_MORE if more calls to lws_mqtt_vbi_r() with subsequent bytes
|
||||
* needed.
|
||||
*
|
||||
* *in and *len are updated accordingly.
|
||||
*
|
||||
* 2-byte and 4-byte decode:
|
||||
*
|
||||
* Initialize the lws_mqtt_vbi state by calling lws_mqtt_2byte_init() or
|
||||
* lws_mqtt_4byte_init() on it, then feed lws_mqtt_mb_parse() bytes
|
||||
* to decode.
|
||||
*
|
||||
* Returns <0 for error, LMSPR_COMPLETED if done (vbi->value is valid), or
|
||||
* LMSPR_NEED_MORE if more calls to lws_mqtt_mb_parse() with subsequent
|
||||
* bytes needed.
|
||||
*
|
||||
* *in and *len are updated accordingly.
|
||||
*/
|
||||
|
||||
void
|
||||
lws_mqtt_vbi_init(lws_mqtt_vbi *vbi);
|
||||
|
||||
void
|
||||
lws_mqtt_2byte_init(lws_mqtt_vbi *vbi);
|
||||
|
||||
void
|
||||
lws_mqtt_4byte_init(lws_mqtt_vbi *vbi);
|
||||
|
||||
lws_mqtt_stateful_primitive_return_t
|
||||
lws_mqtt_vbi_r(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len);
|
||||
|
||||
lws_mqtt_stateful_primitive_return_t
|
||||
lws_mqtt_mb_parse(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len);
|
||||
|
||||
struct lws_mqtt_str_st {
|
||||
uint8_t *buf;
|
||||
uint16_t len;
|
||||
|
||||
uint16_t limit; /* it's cheaper to add the state here than
|
||||
* the pointer to point to it elsewhere */
|
||||
uint16_t pos;
|
||||
char len_valid;
|
||||
char needs_freeing;
|
||||
};
|
||||
|
||||
static inline int
|
||||
lws_mqtt_str_first(struct lws_mqtt_str_st *s) { return !s->buf && !s->pos; }
|
||||
|
||||
|
||||
lws_mqtt_stateful_primitive_return_t
|
||||
lws_mqtt_str_parse(struct lws_mqtt_str_st *bd, const uint8_t **in, size_t *len);
|
||||
|
||||
typedef enum {
|
||||
LMQCPP_IDLE,
|
||||
|
||||
/* receive packet type part of fixed header took us out of idle... */
|
||||
LMQCPP_CONNECT_PACKET = LMQCP_CTOS_CONNECT << 4,
|
||||
LMQCPP_CONNECT_REMAINING_LEN_VBI,
|
||||
LMQCPP_CONNECT_VH_PNAME,
|
||||
LMQCPP_CONNECT_VH_PVERSION,
|
||||
LMQCPP_CONNECT_VH_FLAGS,
|
||||
LMQCPP_CONNECT_VH_KEEPALIVE,
|
||||
LMQCPP_CONNECT_VH_PROPERTIES_VBI_LEN,
|
||||
|
||||
LMQCPP_CONNACK_PACKET = LMQCP_STOC_CONNACK << 4,
|
||||
LMQCPP_CONNACK_VH_FLAGS,
|
||||
LMQCPP_CONNACK_VH_RETURN_CODE,
|
||||
|
||||
LMQCPP_PUBLISH_PACKET = LMQCP_PUBLISH << 4,
|
||||
LMQCPP_PUBLISH_REMAINING_LEN_VBI,
|
||||
LMQCPP_PUBLISH_VH_TOPIC,
|
||||
LMQCPP_PUBLISH_VH_PKT_ID,
|
||||
|
||||
LMQCPP_PUBACK_PACKET = LMQCP_PUBACK << 4,
|
||||
LMQCPP_PUBACK_VH_PKT_ID,
|
||||
LMQCPP_PUBACK_PROPERTIES_LEN_VBI,
|
||||
|
||||
LMQCPP_PUBREC_PACKET = LMQCP_PUBREC << 4,
|
||||
LMQCPP_PUBREC_VH_PKT_ID,
|
||||
|
||||
LMQCPP_PUBREL_PACKET = LMQCP_PUBREL << 4,
|
||||
LMQCPP_PUBREL_VH_PKT_ID,
|
||||
|
||||
LMQCPP_PUBCOMP_PACKET = LMQCP_PUBCOMP << 4,
|
||||
LMQCPP_PUBCOMP_VH_PKT_ID,
|
||||
|
||||
LMQCPP_SUBACK_PACKET = LMQCP_STOC_SUBACK << 4,
|
||||
LMQCPP_SUBACK_VH_PKT_ID,
|
||||
LMQCPP_SUBACK_PAYLOAD,
|
||||
|
||||
LMQCPP_UNSUBACK_PACKET = LMQCP_STOC_UNSUBACK << 4,
|
||||
LMQCPP_UNSUBACK_VH_PKT_ID,
|
||||
|
||||
LMQCPP_PINGRESP_ZERO = LMQCP_STOC_PINGRESP << 4,
|
||||
|
||||
LMQCPP_PAYLOAD,
|
||||
|
||||
LMQCPP_EAT_PROPERTIES_AND_COMPLETE,
|
||||
|
||||
LMQCPP_PROP_ID_VBI,
|
||||
|
||||
/* all possible property payloads */
|
||||
|
||||
/* 3.3.2.3.2 */
|
||||
LMQCPP_PROP_PAYLOAD_FORMAT_INDICATOR_1BYTE = 0x101,
|
||||
|
||||
LMQCPP_PROP_MSG_EXPIRY_INTERVAL_4BYTE = 0x102,
|
||||
|
||||
LMQCPP_PROP_CONTENT_TYPE_UTF8S = 0x103,
|
||||
|
||||
LMQCPP_PROP_RESPONSE_TOPIC_UTF8S = 0x108,
|
||||
|
||||
LMQCPP_PROP_CORRELATION_BINDATA = 0x109,
|
||||
|
||||
LMQCPP_PROP_SUBSCRIPTION_ID_VBI = 0x10b,
|
||||
|
||||
LMQCPP_PROP_SESSION_EXPIRY_INTERVAL_4BYTE = 0x111,
|
||||
|
||||
LMQCPP_PROP_ASSIGNED_CLIENTID_UTF8S = 0x112,
|
||||
|
||||
LMQCPP_PROP_SERVER_KEEPALIVE_2BYTE = 0x113,
|
||||
|
||||
LMQCPP_PROP_AUTH_METHOD_UTF8S = 0x115,
|
||||
|
||||
LMQCPP_PROP_AUTH_DATA_BINDATA = 0x116,
|
||||
|
||||
LMQCPP_PROP_REQUEST_PROBLEM_INFO_1BYTE = 0x117,
|
||||
|
||||
LMQCPP_PROP_WILL_DELAY_INTERVAL_4BYTE = 0x118,
|
||||
|
||||
LMQCPP_PROP_REQUEST_REPSONSE_INFO_1BYTE = 0x119,
|
||||
|
||||
LMQCPP_PROP_RESPONSE_INFO_UTF8S = 0x11a,
|
||||
|
||||
LMQCPP_PROP_SERVER_REFERENCE_UTF8S = 0x11c,
|
||||
|
||||
LMQCPP_PROP_REASON_STRING_UTF8S = 0x11f,
|
||||
|
||||
LMQCPP_PROP_RECEIVE_MAXIMUM_2BYTE = 0x121,
|
||||
|
||||
LMQCPP_PROP_TOPIC_MAXIMUM_2BYTE = 0x122,
|
||||
|
||||
LMQCPP_PROP_TOPIC_ALIAS_2BYTE = 0x123,
|
||||
|
||||
LMQCPP_PROP_MAXIMUM_QOS_1BYTE = 0x124,
|
||||
|
||||
LMQCPP_PROP_RETAIN_AVAILABLE_1BYTE = 0x125,
|
||||
|
||||
LMQCPP_PROP_USER_PROPERTY_NAME_UTF8S = 0x126,
|
||||
LMQCPP_PROP_USER_PROPERTY_VALUE_UTF8S = 0x226,
|
||||
|
||||
LMQCPP_PROP_MAXIMUM_PACKET_SIZE_4BYTE = 0x127,
|
||||
|
||||
LMQCPP_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE_1BYTE = 0x128,
|
||||
|
||||
LMQCPP_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE_1BYTE = 0x129,
|
||||
|
||||
LMQCPP_PROP_SHARED_SUBSCRIPTION_AVAILABLE_1BYTE = 0x12a,
|
||||
|
||||
} lws_mqtt_packet_parse_state_t;
|
||||
|
||||
/*
|
||||
* the states an MQTT connection can be in
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
LGSMQTT_UNKNOWN,
|
||||
LGSMQTT_IDLE,
|
||||
LGSMQTT_TRANSPORT_CONNECTED,
|
||||
|
||||
LGSMQTT_SENT_CONNECT,
|
||||
LGSMQTT_ESTABLISHED,
|
||||
|
||||
LGSMQTT_SENT_SUBSCRIBE,
|
||||
LGSMQTT_SUBSCRIBED,
|
||||
|
||||
} lwsgs_mqtt_states_t;
|
||||
|
||||
typedef struct lws_mqtt_parser_st {
|
||||
/* struct lws_mqtt_str_st s_content_type; */
|
||||
lws_mqtt_packet_parse_state_t state;
|
||||
lws_mqtt_vbi vbit;
|
||||
|
||||
lws_mqtt_reason_t reason;
|
||||
|
||||
lws_mqtt_str_t s_temp;
|
||||
|
||||
uint8_t fixed_seen[4];
|
||||
uint8_t props_seen[8];
|
||||
|
||||
uint8_t cpkt_flags;
|
||||
uint32_t cpkt_remlen;
|
||||
|
||||
uint32_t props_len;
|
||||
uint32_t consumed;
|
||||
uint32_t prop_id;
|
||||
uint32_t props_consumed;
|
||||
uint32_t payload_consumed;
|
||||
|
||||
uint16_t keepalive;
|
||||
uint16_t cpkt_id;
|
||||
uint32_t n;
|
||||
|
||||
uint8_t temp[32];
|
||||
uint8_t conn_rc;
|
||||
uint8_t payload_format;
|
||||
uint8_t packet_type_flags;
|
||||
uint8_t conn_protocol_version;
|
||||
uint8_t fixed;
|
||||
|
||||
uint8_t flag_pending_send_connack_close:1;
|
||||
uint8_t flag_pending_send_reason_close:1;
|
||||
uint8_t flag_prop_multi:1;
|
||||
uint8_t flag_server:1;
|
||||
|
||||
} lws_mqtt_parser_t;
|
||||
|
||||
typedef enum {
|
||||
LMVTR_VALID = 0,
|
||||
LMVTR_VALID_WILDCARD = 1,
|
||||
LMVTR_VALID_SHADOW = 2,
|
||||
|
||||
LMVTR_FAILED_OVERSIZE = -1,
|
||||
LMVTR_FAILED_WILDCARD_FORMAT = -2,
|
||||
LMVTR_FAILED_SHADOW_FORMAT = -3,
|
||||
} lws_mqtt_validate_topic_return_t;
|
||||
|
||||
typedef enum {
|
||||
LMMTR_TOPIC_NOMATCH = 0,
|
||||
LMMTR_TOPIC_MATCH = 1,
|
||||
|
||||
LMMTR_TOPIC_MATCH_ERROR = -1
|
||||
} lws_mqtt_match_topic_return_t;
|
||||
|
||||
typedef struct lws_mqtt_subs {
|
||||
struct lws_mqtt_subs *next;
|
||||
|
||||
uint8_t ref_count; /* number of children referencing */
|
||||
|
||||
/* Flags */
|
||||
uint8_t wildcard:1;
|
||||
uint8_t shadow:1;
|
||||
|
||||
/* subscription name + NUL overallocated here */
|
||||
char topic[];
|
||||
} lws_mqtt_subs_t;
|
||||
|
||||
typedef struct lws_mqtts {
|
||||
lws_mqtt_parser_t par;
|
||||
lwsgs_mqtt_states_t estate;
|
||||
struct lws_dll2 active_session_list_head;
|
||||
struct lws_dll2 limbo_session_list_head;
|
||||
} lws_mqtts_t;
|
||||
|
||||
typedef struct lws_mqttc {
|
||||
lws_mqtt_parser_t par;
|
||||
lwsgs_mqtt_states_t estate;
|
||||
struct lws_mqtt_str_st *id;
|
||||
struct lws_mqtt_str_st *username;
|
||||
struct lws_mqtt_str_st *password;
|
||||
struct {
|
||||
struct lws_mqtt_str_st *topic;
|
||||
struct lws_mqtt_str_st *message;
|
||||
lws_mqtt_qos_levels_t qos;
|
||||
uint8_t retain;
|
||||
} will;
|
||||
uint16_t keep_alive_secs;
|
||||
uint16_t conn_flags;
|
||||
uint8_t aws_iot;
|
||||
} lws_mqttc_t;
|
||||
|
||||
struct _lws_mqtt_related {
|
||||
lws_mqttc_t client;
|
||||
lws_sorted_usec_list_t sul_qos_puback_pubrec_wait; /* QoS1 puback or QoS2 pubrec wait TO */
|
||||
lws_sorted_usec_list_t sul_qos1_puback_wait; /* QoS1 puback wait TO */
|
||||
lws_sorted_usec_list_t sul_unsuback_wait; /* unsuback wait TO */
|
||||
lws_sorted_usec_list_t sul_qos2_pubrec_wait; /* QoS2 pubrec wait TO */
|
||||
lws_sorted_usec_list_t sul_shadow_wait; /* Device Shadow wait TO */
|
||||
struct lws *wsi; /**< so sul can use lws_container_of */
|
||||
lws_mqtt_subs_t *subs_head; /**< Linked-list of heap-allocated subscription objects */
|
||||
void *rx_cpkt_param;
|
||||
uint16_t pkt_id;
|
||||
uint16_t ack_pkt_id;
|
||||
uint16_t peer_ack_pkt_id;
|
||||
uint16_t sub_size;
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
uint8_t send_pingreq:1;
|
||||
uint8_t session_resumed:1;
|
||||
#endif
|
||||
uint8_t inside_payload:1;
|
||||
uint8_t inside_subscribe:1;
|
||||
uint8_t inside_unsubscribe:1;
|
||||
uint8_t inside_birth:1;
|
||||
uint8_t inside_resume_session:1;
|
||||
uint8_t send_puback:1;
|
||||
uint8_t send_pubrel:1;
|
||||
uint8_t send_pubrec:1;
|
||||
uint8_t send_pubcomp:1;
|
||||
uint8_t unacked_publish:1;
|
||||
uint8_t unacked_pubrel:1;
|
||||
|
||||
uint8_t done_subscribe:1;
|
||||
uint8_t done_birth:1;
|
||||
uint8_t inside_shadow:1;
|
||||
uint8_t done_shadow_subscribe:1;
|
||||
uint8_t send_shadow_unsubscribe:1;
|
||||
};
|
||||
|
||||
/*
|
||||
* New sessions are created by starting CONNECT. If the ClientID sent
|
||||
* by the client matches a different, extant session, then the
|
||||
* existing one is taken over and the new one created for duration of
|
||||
* CONNECT processing is destroyed.
|
||||
*
|
||||
* On the server side, bearing in mind multiple simultaneous,
|
||||
* fragmented CONNECTs may be interleaved ongoing, all state and
|
||||
* parsing temps for a session must live in the session object.
|
||||
*/
|
||||
|
||||
struct lws_mqtt_endpoint_st;
|
||||
|
||||
typedef struct lws_mqtts_session_st {
|
||||
struct lws_dll2 session_list;
|
||||
|
||||
} lws_mqtts_session_t;
|
||||
|
||||
#define ctl_pkt_type(x) (x->packet_type_flags >> 4)
|
||||
|
||||
|
||||
void
|
||||
lws_mqttc_state_transition(lws_mqttc_t *ep, lwsgs_mqtt_states_t s);
|
||||
|
||||
int
|
||||
_lws_mqtt_rx_parser(struct lws *wsi, lws_mqtt_parser_t *par,
|
||||
const uint8_t *buf, size_t len);
|
||||
|
||||
int
|
||||
lws_mqtt_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
|
||||
struct lws *wsi_conn);
|
||||
|
||||
int
|
||||
lws_create_client_mqtt_object(const struct lws_client_connect_info *i,
|
||||
struct lws *wsi);
|
||||
|
||||
struct lws *
|
||||
lws_mqtt_client_send_connect(struct lws *wsi);
|
||||
|
||||
struct lws *
|
||||
lws_mqtt_client_send_disconnect(struct lws *wsi);
|
||||
|
||||
int
|
||||
lws_mqtt_fill_fixed_header(uint8_t *p, lws_mqtt_control_packet_t ctrl_pkt_type,
|
||||
uint8_t dup, lws_mqtt_qos_levels_t qos,
|
||||
uint8_t retain);
|
||||
|
||||
struct lws *
|
||||
lws_wsi_mqtt_adopt(struct lws *parent_wsi, struct lws *wsi);
|
||||
|
||||
lws_mqtt_subs_t *
|
||||
lws_mqtt_find_sub(struct _lws_mqtt_related *mqtt, const char *topic);
|
||||
|
||||
lws_mqtt_match_topic_return_t
|
||||
lws_mqtt_is_topic_matched(const char* sub, const char* pub);
|
||||
|
||||
#endif /* _PRIVATE_LIB_ROLES_MQTT */
|
||||
|
||||
717
Kinc/Sources/kinc/libs/roles/netlink/ops-netlink.c
Normal file
717
Kinc/Sources/kinc/libs/roles/netlink/ops-netlink.c
Normal file
@ -0,0 +1,717 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* We mainly focus on the routing table / gateways because those are the
|
||||
* elements that decide if we can get on to the internet or not.
|
||||
*
|
||||
* We also need to understand the source addresses of possible outgoing routes,
|
||||
* and follow LINK down (ifconfig down) to clean up routes on the interface idx
|
||||
* going down that are not otherwise cleaned.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
//#define lwsl_netlink lwsl_notice
|
||||
#define lwsl_cx_netlink lwsl_cx_info
|
||||
#define lwsl_cx_netlink_debug lwsl_cx_debug
|
||||
|
||||
static void
|
||||
lws_netlink_coldplug_done_cb(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
struct lws_context *ctx = lws_container_of(sul, struct lws_context,
|
||||
sul_nl_coldplug);
|
||||
ctx->nl_initial_done = 1;
|
||||
#if defined(LWS_WITH_SYS_STATE)
|
||||
/* if nothing is there to intercept anything, go all the way */
|
||||
lws_state_transition_steps(&ctx->mgr_system, LWS_SYSTATE_OPERATIONAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_netlink(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
struct lws_context *cx = pt->context;
|
||||
uint8_t s[4096]
|
||||
#if defined(_DEBUG)
|
||||
, route_change = 0
|
||||
#endif
|
||||
#if defined(LWS_WITH_SYS_SMD)
|
||||
, gateway_change = 0
|
||||
#endif
|
||||
;
|
||||
struct sockaddr_nl nladdr;
|
||||
lws_route_t robj, *rou;
|
||||
struct nlmsghdr *h;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
unsigned int n, removed;
|
||||
char buf[72];
|
||||
|
||||
if (!(pollfd->revents & LWS_POLLIN))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
iov.iov_base = (void *)s;
|
||||
iov.iov_len = sizeof(s);
|
||||
|
||||
msg.msg_name = (void *)&(nladdr);
|
||||
msg.msg_namelen = sizeof(nladdr);
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
n = (unsigned int)recvmsg(wsi->desc.sockfd, &msg, 0);
|
||||
if ((int)n < 0) {
|
||||
lwsl_cx_notice(cx, "recvmsg failed");
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
// lwsl_hexdump_notice(s, (size_t)n);
|
||||
|
||||
h = (struct nlmsghdr *)s;
|
||||
|
||||
/* we can get a bunch of messages coalesced in one read*/
|
||||
|
||||
for ( ; NLMSG_OK(h, n); h = NLMSG_NEXT(h, n)) {
|
||||
struct ifaddrmsg *ifam;
|
||||
struct rtattr *ra;
|
||||
struct rtmsg *rm;
|
||||
#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG)
|
||||
struct ndmsg *nd;
|
||||
#endif
|
||||
unsigned int ra_len;
|
||||
uint8_t *p;
|
||||
|
||||
struct ifinfomsg *ifi;
|
||||
struct rtattr *attribute;
|
||||
unsigned int len;
|
||||
|
||||
lwsl_cx_netlink(cx, "RTM %d", h->nlmsg_type);
|
||||
|
||||
memset(&robj, 0, sizeof(robj));
|
||||
robj.if_idx = -1;
|
||||
robj.priority = -1;
|
||||
rm = (struct rtmsg *)NLMSG_DATA(h);
|
||||
|
||||
/*
|
||||
* We have to care about NEWLINK so we can understand when a
|
||||
* network interface went down, and clear the related routes.
|
||||
*
|
||||
* We don't get individual DELROUTEs for these.
|
||||
*/
|
||||
|
||||
switch (h->nlmsg_type) {
|
||||
case RTM_NEWLINK:
|
||||
|
||||
ifi = NLMSG_DATA(h);
|
||||
len = (unsigned int)(h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)));
|
||||
|
||||
/* loop over all attributes for the NEWLINK message */
|
||||
for (attribute = IFLA_RTA(ifi); RTA_OK(attribute, len);
|
||||
attribute = RTA_NEXT(attribute, len)) {
|
||||
lwsl_cx_netlink(cx, "if attr %d",
|
||||
(int)attribute->rta_type);
|
||||
switch(attribute->rta_type) {
|
||||
case IFLA_IFNAME:
|
||||
lwsl_cx_netlink(cx, "NETLINK ifidx %d : %s",
|
||||
ifi->ifi_index,
|
||||
(char *)RTA_DATA(attribute));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} /* switch */
|
||||
} /* for loop */
|
||||
|
||||
lwsl_cx_netlink(cx, "NEWLINK ifi_index %d, flags 0x%x",
|
||||
ifi->ifi_index, ifi->ifi_flags);
|
||||
|
||||
/*
|
||||
* Despite "New"link this is actually telling us there
|
||||
* is some change on the network interface IFF_ state
|
||||
*/
|
||||
|
||||
if (!(ifi->ifi_flags & IFF_UP)) {
|
||||
/*
|
||||
* Interface is down, so scrub all routes that
|
||||
* applied to it
|
||||
*/
|
||||
lwsl_cx_netlink(cx, "NEWLINK: ifdown %d",
|
||||
ifi->ifi_index);
|
||||
lws_pt_lock(pt, __func__);
|
||||
_lws_route_table_ifdown(pt, ifi->ifi_index);
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
continue; /* ie, not break, no second half */
|
||||
|
||||
case RTM_NEWADDR:
|
||||
case RTM_DELADDR:
|
||||
|
||||
ifam = (struct ifaddrmsg *)NLMSG_DATA(h);
|
||||
|
||||
robj.source_ads = 1;
|
||||
robj.dest_len = ifam->ifa_prefixlen;
|
||||
robj.if_idx = (int)ifam->ifa_index;
|
||||
robj.scope = ifam->ifa_scope;
|
||||
robj.ifa_flags = ifam->ifa_flags;
|
||||
robj.dest.sa4.sin_family = ifam->ifa_family;
|
||||
|
||||
/* address attributes */
|
||||
ra = (struct rtattr *)IFA_RTA(ifam);
|
||||
ra_len = (unsigned int)IFA_PAYLOAD(h);
|
||||
|
||||
lwsl_cx_netlink(cx, "%s",
|
||||
h->nlmsg_type == RTM_NEWADDR ? "NEWADDR" : "DELADDR");
|
||||
|
||||
// Parse attributes.
|
||||
for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) {
|
||||
//lwsl_cx_netlink_debug(cx, "%s: IFA %d\n", __func__, ra->rta_type);
|
||||
switch (ra->rta_type) {
|
||||
case IFA_LOCAL:
|
||||
// Local address
|
||||
lws_sa46_copy_address(&robj.src, RTA_DATA(ra), rm->rtm_family);
|
||||
robj.src_len = rm->rtm_src_len;
|
||||
lws_sa46_write_numeric_address(&robj.src, buf, sizeof(buf));
|
||||
lwsl_cx_netlink_debug(cx, "IFA_LOCAL: %s/%d", buf, robj.src_len);
|
||||
break;
|
||||
case IFA_ADDRESS:
|
||||
// Prefix address, not local interface.
|
||||
lws_sa46_copy_address(&robj.dest, RTA_DATA(ra), rm->rtm_family);
|
||||
robj.dest_len = rm->rtm_dst_len;
|
||||
lws_sa46_write_numeric_address(&robj.dest, buf, sizeof(buf));
|
||||
lwsl_cx_netlink_debug(cx, "IFA_ADDRESS: %s/%d", buf, robj.dest_len);
|
||||
break;
|
||||
case IFA_FLAGS:
|
||||
lwsl_cx_netlink_debug(cx, "IFA_FLAGS: 0x%x (not handled)",
|
||||
*(unsigned int*)RTA_DATA(ra));
|
||||
break;
|
||||
case IFA_BROADCAST:
|
||||
lwsl_cx_netlink_debug(cx, "IFA_BROADCAST (not handled)");
|
||||
break;
|
||||
case IFA_ANYCAST:
|
||||
lwsl_cx_netlink_debug(cx, "IFA_ANYCAST (not handled)");
|
||||
break;
|
||||
case IFA_CACHEINFO:
|
||||
lwsl_cx_netlink_debug(cx, "IFA_CACHEINFO (not handled)");
|
||||
break;
|
||||
case IFA_LABEL:
|
||||
strncpy(buf, RTA_DATA(ra), sizeof(buf));
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
lwsl_cx_netlink_debug(cx, "IFA_LABEL: %s (not used)", buf);
|
||||
break;
|
||||
default:
|
||||
lwsl_cx_netlink_debug(cx, "unknown IFA attr type %d", ra->rta_type);
|
||||
break;
|
||||
}
|
||||
//lwsl_cx_debug(cx, "rta payload length: %ld", RTA_PAYLOAD(ra));
|
||||
} /* for */
|
||||
|
||||
/*
|
||||
* almost nothing interesting within IFA_* attributes:
|
||||
* so skip it and goto to the second half
|
||||
*/
|
||||
goto second_half;
|
||||
|
||||
case RTM_NEWROUTE:
|
||||
case RTM_DELROUTE:
|
||||
|
||||
lwsl_cx_netlink(cx, "%s",
|
||||
h->nlmsg_type == RTM_NEWROUTE ?
|
||||
"NEWROUTE" : "DELROUTE");
|
||||
|
||||
/* route attributes */
|
||||
ra = (struct rtattr *)RTM_RTA(rm);
|
||||
ra_len = (unsigned int)RTM_PAYLOAD(h);
|
||||
break;
|
||||
|
||||
case RTM_DELNEIGH:
|
||||
case RTM_NEWNEIGH:
|
||||
lwsl_cx_netlink(cx, "%s", h->nlmsg_type ==
|
||||
RTM_NEWNEIGH ? "NEWNEIGH" :
|
||||
"DELNEIGH");
|
||||
#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG)
|
||||
nd = (struct ndmsg *)rm;
|
||||
lwsl_cx_netlink(cx, "fam %u, ifidx %u, flags 0x%x",
|
||||
nd->ndm_family, nd->ndm_ifindex,
|
||||
nd->ndm_flags);
|
||||
#endif
|
||||
ra = (struct rtattr *)RTM_RTA(rm);
|
||||
ra_len = (unsigned int)RTM_PAYLOAD(h);
|
||||
for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) {
|
||||
lwsl_cx_netlink(cx, "atr %d", ra->rta_type);
|
||||
switch (ra->rta_type) {
|
||||
case NDA_DST:
|
||||
lwsl_cx_netlink(cx, "dst len %d",
|
||||
ra->rta_len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
lws_pt_lock(pt, __func__);
|
||||
_lws_route_pt_close_unroutable(pt);
|
||||
lws_pt_unlock(pt);
|
||||
continue;
|
||||
|
||||
default:
|
||||
lwsl_cx_netlink(cx, "*** Unknown RTM_%d",
|
||||
h->nlmsg_type);
|
||||
continue;
|
||||
} /* switch */
|
||||
|
||||
robj.proto = rm->rtm_protocol;
|
||||
|
||||
// iterate over route attributes
|
||||
for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) {
|
||||
// lwsl_netlink("%s: atr %d\n", __func__, ra->rta_type);
|
||||
switch (ra->rta_type) {
|
||||
case RTA_PREFSRC: /* protocol ads: preferred src ads */
|
||||
case RTA_SRC:
|
||||
lws_sa46_copy_address(&robj.src, RTA_DATA(ra),
|
||||
rm->rtm_family);
|
||||
robj.src_len = rm->rtm_src_len;
|
||||
lws_sa46_write_numeric_address(&robj.src, buf, sizeof(buf));
|
||||
if (ra->rta_type == RTA_SRC)
|
||||
lwsl_cx_netlink_debug(cx, "RTA_SRC: %s/%d", buf, robj.src_len);
|
||||
else
|
||||
lwsl_cx_netlink_debug(cx, "RTA_PREFSRC: %s/%d", buf, robj.src_len);
|
||||
break;
|
||||
case RTA_DST:
|
||||
/* check if is local addr -> considering it as src addr too */
|
||||
if (rm->rtm_type == RTN_LOCAL &&
|
||||
((rm->rtm_family == AF_INET && rm->rtm_dst_len == 32) ||
|
||||
(rm->rtm_family == AF_INET6 && rm->rtm_dst_len == 128))) {
|
||||
lws_sa46_copy_address(&robj.src, RTA_DATA(ra),
|
||||
rm->rtm_family);
|
||||
lwsl_cx_netlink_debug(cx, "Local addr: RTA_DST -> added to RTA_SRC");
|
||||
}
|
||||
|
||||
lws_sa46_copy_address(&robj.dest, RTA_DATA(ra),
|
||||
rm->rtm_family);
|
||||
robj.dest_len = rm->rtm_dst_len;
|
||||
lws_sa46_write_numeric_address(&robj.dest, buf, sizeof(buf));
|
||||
lwsl_cx_netlink_debug(cx, "RTA_DST: %s/%d", buf, robj.dest_len);
|
||||
break;
|
||||
case RTA_GATEWAY:
|
||||
lws_sa46_copy_address(&robj.gateway, RTA_DATA(ra),
|
||||
rm->rtm_family);
|
||||
|
||||
lws_sa46_write_numeric_address(&robj.gateway, buf, sizeof(buf));
|
||||
lwsl_cx_netlink_debug(cx, "RTA_GATEWAY: %s", buf);
|
||||
#if defined(LWS_WITH_SYS_SMD)
|
||||
gateway_change = 1;
|
||||
#endif
|
||||
break;
|
||||
case RTA_IIF: /* int: input interface index */
|
||||
case RTA_OIF: /* int: output interface index */
|
||||
robj.if_idx = *(int *)RTA_DATA(ra);
|
||||
lwsl_cx_netlink_debug(cx, "RTA_IIF/RTA_OIF: %d", robj.if_idx);
|
||||
break;
|
||||
case RTA_PRIORITY: /* int: priority of route */
|
||||
p = RTA_DATA(ra);
|
||||
robj.priority = p[3] << 24 | p[2] << 16 |
|
||||
p[1] << 8 | p[0];
|
||||
lwsl_cx_netlink_debug(cx, "RTA_PRIORITY: %d", robj.priority);
|
||||
break;
|
||||
case RTA_CACHEINFO: /* struct rta_cacheinfo */
|
||||
lwsl_cx_netlink_debug(cx, "RTA_CACHEINFO (not handled)");
|
||||
break;
|
||||
#if defined(LWS_HAVE_RTA_PREF)
|
||||
case RTA_PREF: /* char: RFC4191 v6 router preference */
|
||||
lwsl_cx_netlink_debug(cx, "RTA_PREF (not handled)");
|
||||
break;
|
||||
#endif
|
||||
case RTA_TABLE: /* int */
|
||||
lwsl_cx_netlink_debug(cx, "RTA_TABLE (not handled)");
|
||||
break;
|
||||
|
||||
default:
|
||||
lwsl_cx_netlink_debug(cx, "unknown attr type %d", ra->rta_type);
|
||||
break;
|
||||
}
|
||||
//lwsl_cx_debug(cx, "rta payload length: %ld", RTA_PAYLOAD(ra));
|
||||
} /* for */
|
||||
|
||||
/*
|
||||
* the second half, once all the attributes were collected
|
||||
*/
|
||||
second_half:
|
||||
switch (h->nlmsg_type) {
|
||||
|
||||
case RTM_DELROUTE:
|
||||
/*
|
||||
* This will also take down wsi marked as using it
|
||||
*/
|
||||
lwsl_cx_netlink(cx, "DELROUTE: if_idx %d",
|
||||
robj.if_idx);
|
||||
lws_pt_lock(pt, __func__);
|
||||
_lws_route_remove(pt, &robj, 0);
|
||||
lws_pt_unlock(pt);
|
||||
goto inform;
|
||||
|
||||
case RTM_NEWROUTE:
|
||||
|
||||
/*
|
||||
* We don't want any routing debris like /32 or broadcast
|
||||
* in our routing table... we will collect source addresses
|
||||
* bound to interfaces via NEWADDR
|
||||
*/
|
||||
if (rm->rtm_type != RTN_UNICAST
|
||||
&& rm->rtm_type != RTN_LOCAL) {
|
||||
lwsl_cx_netlink(cx, "NEWROUTE: IGNORED (%s)",
|
||||
rm->rtm_type == RTN_BROADCAST ? "broadcast" :
|
||||
rm->rtm_type == RTN_ANYCAST ? "anycast" :
|
||||
rm->rtm_type == RTN_MULTICAST ? "multicast" :
|
||||
rm->rtm_type == RTN_UNREACHABLE ? "unreachable" :
|
||||
rm->rtm_type == RTN_NAT ? "nat" :
|
||||
rm->rtm_type == RTN_UNSPEC ? "unspecified" :
|
||||
"other");
|
||||
break;
|
||||
}
|
||||
|
||||
if (rm->rtm_flags & RTM_F_CLONED) {
|
||||
lwsl_cx_netlink(cx, "NEWROUTE: IGNORED (cloned)");
|
||||
break;
|
||||
}
|
||||
|
||||
lwsl_cx_netlink(cx, "NEWROUTE: ACCEPTED (if_idx %d)",
|
||||
robj.if_idx);
|
||||
|
||||
#if defined(_DEBUG)
|
||||
_lws_routing_entry_dump(cx, &robj);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 1. Allocate new route for linked-list.
|
||||
* (robj is on stack, do NOT use)
|
||||
*/
|
||||
rou = lws_malloc(sizeof(*rou), __func__);
|
||||
if (!rou) {
|
||||
lwsl_cx_err(cx, "oom");
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
*rou = robj;
|
||||
|
||||
// 2. Remove duplicates and add route (both under a lock).
|
||||
lws_pt_lock(pt, __func__);
|
||||
|
||||
/*
|
||||
* Is robj a dupe in the routing table already?
|
||||
*
|
||||
* match on pri ignore == set pri and skip
|
||||
* no match == add
|
||||
*
|
||||
* returns zero ALWAYS
|
||||
*
|
||||
* We could be adding a route to the same destination with
|
||||
* a higher or lower priority from a different source, so why
|
||||
* all existing routes? Only remove if its the same source and
|
||||
* destination, effectively a change in priority.
|
||||
*/
|
||||
_lws_route_remove(pt, &robj,
|
||||
LRR_MATCH_DST | LRR_MATCH_SRC | LRR_IGNORE_PRI);
|
||||
|
||||
/* add route to linked-list */
|
||||
rou->uidx = _lws_route_get_uidx(cx);
|
||||
lws_dll2_add_tail(&rou->list, &cx->routing_table);
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
lwsl_cx_netlink_debug(cx, "route list size %u",
|
||||
cx->routing_table.count);
|
||||
|
||||
/*
|
||||
* 3. Close anyything we cant reach anymore due to the removal.
|
||||
* (don't need to or want to do this under lock)
|
||||
*/
|
||||
_lws_route_pt_close_unroutable(pt);
|
||||
|
||||
inform:
|
||||
#if defined(_DEBUG)
|
||||
route_change = 1;
|
||||
#endif
|
||||
#if defined(LWS_WITH_SYS_SMD)
|
||||
/*
|
||||
* Reflect the route add / del event using SMD.
|
||||
* Participants interested can refer to the pt
|
||||
* routing table
|
||||
*/
|
||||
(void)lws_smd_msg_printf(cx, LWSSMDCL_NETWORK,
|
||||
"{\"rt\":\"%s\"}\n",
|
||||
(h->nlmsg_type == RTM_NEWROUTE) ?
|
||||
"add" : "del");
|
||||
#endif
|
||||
|
||||
break;
|
||||
|
||||
case RTM_DELADDR:
|
||||
lwsl_cx_notice(cx, "DELADDR");
|
||||
#if defined(_DEBUG)
|
||||
_lws_routing_entry_dump(cx, &robj);
|
||||
#endif
|
||||
lws_pt_lock(pt, __func__);
|
||||
removed = cx->routing_table.count;
|
||||
_lws_route_remove(pt, &robj, LRR_MATCH_SRC | LRR_IGNORE_PRI);
|
||||
removed -= cx->routing_table.count;
|
||||
lws_pt_unlock(pt);
|
||||
_lws_route_pt_close_unroutable(pt);
|
||||
if (removed > 0)
|
||||
goto inform;
|
||||
break;
|
||||
|
||||
case RTM_NEWADDR:
|
||||
lwsl_cx_netlink(cx, "NEWADDR (nothing to do)");
|
||||
#if defined(_DEBUG)
|
||||
_lws_routing_entry_dump(cx, &robj);
|
||||
#endif
|
||||
/*
|
||||
* An address alone does not provide new routes.
|
||||
* NEWADDR will happen when the DHCP lease is being
|
||||
* renewed, and will likely not change any routes.
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
// lwsl_info("%s: unknown msg type %d\n", __func__,
|
||||
// h->nlmsg_type);
|
||||
break;
|
||||
}
|
||||
} /* message iterator */
|
||||
|
||||
#if defined(LWS_WITH_SYS_SMD)
|
||||
if (gateway_change)
|
||||
/*
|
||||
* If a route with a gw was added or deleted, retrigger captive
|
||||
* portal detection if we have that
|
||||
*/
|
||||
(void)lws_smd_msg_printf(cx, LWSSMDCL_NETWORK,
|
||||
"{\"trigger\": \"cpdcheck\", "
|
||||
"\"src\":\"gw-change\"}");
|
||||
#endif
|
||||
|
||||
#if defined(_DEBUG)
|
||||
if (route_change) {
|
||||
lws_context_lock(cx, __func__);
|
||||
_lws_routing_table_dump(cx);
|
||||
lws_context_unlock(cx);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!cx->nl_initial_done &&
|
||||
pt == &cx->pt[0] &&
|
||||
cx->routing_table.count) {
|
||||
/*
|
||||
* While netlink info still coming, keep moving the timer for
|
||||
* calling it "done" to +100ms until after it stops coming
|
||||
*/
|
||||
lws_context_lock(cx, __func__);
|
||||
lws_sul_schedule(cx, 0, &cx->sul_nl_coldplug,
|
||||
lws_netlink_coldplug_done_cb,
|
||||
100 * LWS_US_PER_MS);
|
||||
lws_context_unlock(cx);
|
||||
}
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
struct nl_req_s {
|
||||
struct nlmsghdr hdr;
|
||||
struct rtmsg gen;
|
||||
};
|
||||
|
||||
int
|
||||
rops_pt_init_destroy_netlink(struct lws_context *context,
|
||||
const struct lws_context_creation_info *info,
|
||||
struct lws_context_per_thread *pt, int destroy)
|
||||
{
|
||||
struct sockaddr_nl sanl;
|
||||
struct nl_req_s req;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
struct lws *wsi;
|
||||
int n, ret = 1;
|
||||
|
||||
if (destroy) {
|
||||
|
||||
/*
|
||||
* pt netlink wsi closed + freed as part of pt's destroy
|
||||
* wsi mass close, just need to take down the routing table
|
||||
*/
|
||||
_lws_route_table_empty(pt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (context->netlink)
|
||||
return 0;
|
||||
|
||||
if (pt > &context->pt[0])
|
||||
/* we can only have one netlink socket */
|
||||
return 0;
|
||||
|
||||
lwsl_cx_info(context, "creating netlink skt");
|
||||
|
||||
/*
|
||||
* We want a netlink socket per pt as well
|
||||
*/
|
||||
|
||||
lws_context_lock(context, __func__);
|
||||
wsi = __lws_wsi_create_with_role(context, (int)(pt - &context->pt[0]),
|
||||
&role_ops_netlink, NULL);
|
||||
lws_context_unlock(context);
|
||||
if (!wsi)
|
||||
goto bail;
|
||||
|
||||
wsi->desc.sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (wsi->desc.sockfd == LWS_SOCK_INVALID) {
|
||||
lwsl_cx_err(context, "unable to open netlink");
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
lws_plat_set_nonblocking(wsi->desc.sockfd);
|
||||
|
||||
__lws_lc_tag(context, &context->lcg[LWSLCG_VHOST], &wsi->lc,
|
||||
"netlink");
|
||||
|
||||
memset(&sanl, 0, sizeof(sanl));
|
||||
sanl.nl_family = AF_NETLINK;
|
||||
sanl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
| RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR
|
||||
#endif
|
||||
;
|
||||
|
||||
if (lws_fi(&context->fic, "netlink_bind") ||
|
||||
bind(wsi->desc.sockfd, (struct sockaddr*)&sanl, sizeof(sanl)) < 0) {
|
||||
lwsl_cx_warn(context, "netlink bind failed");
|
||||
ret = 0; /* some systems deny access, just ignore */
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
context->netlink = wsi;
|
||||
if (lws_wsi_inject_to_loop(pt, wsi))
|
||||
goto bail2;
|
||||
|
||||
/* if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
|
||||
lwsl_err("%s: pollfd in fail\n", __func__);
|
||||
goto bail2;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
* Since we're starting the PT, ask to be sent all the existing routes.
|
||||
*
|
||||
* This requires CAP_ADMIN, or root... we do this early before dropping
|
||||
* privs
|
||||
*/
|
||||
|
||||
memset(&sanl, 0, sizeof(sanl));
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
sanl.nl_family = AF_NETLINK;
|
||||
|
||||
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.gen));
|
||||
req.hdr.nlmsg_type = RTM_GETROUTE;
|
||||
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
req.hdr.nlmsg_seq = 1;
|
||||
req.hdr.nlmsg_pid = (uint32_t)getpid();
|
||||
req.gen.rtm_family = AF_PACKET;
|
||||
req.gen.rtm_table = RT_TABLE_DEFAULT;
|
||||
|
||||
iov.iov_base = &req;
|
||||
iov.iov_len = req.hdr.nlmsg_len;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_name = &sanl;
|
||||
msg.msg_namelen = sizeof(sanl);
|
||||
|
||||
n = (int)sendmsg(wsi->desc.sockfd, (struct msghdr *)&msg, 0);
|
||||
if (n < 0) {
|
||||
lwsl_cx_notice(context, "rt dump req failed... permissions? errno %d",
|
||||
LWS_ERRNO);
|
||||
}
|
||||
|
||||
/*
|
||||
* Responses are going to come asynchronously, let's block moving
|
||||
* off state IFACE_COLDPLUG until we have had them. This is important
|
||||
* since if we don't hold there, when we do get the responses we may
|
||||
* cull any ongoing connections as unroutable otherwise
|
||||
*/
|
||||
|
||||
lwsl_cx_debug(context, "starting netlink coldplug wait");
|
||||
|
||||
return 0;
|
||||
|
||||
bail2:
|
||||
__lws_lc_untag(wsi->a.context, &wsi->lc);
|
||||
compatible_close(wsi->desc.sockfd);
|
||||
bail1:
|
||||
lws_free(wsi);
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const lws_rops_t rops_table_netlink[] = {
|
||||
/* 1 */ { .pt_init_destroy = rops_pt_init_destroy_netlink },
|
||||
/* 2 */ { .handle_POLLIN = rops_handle_POLLIN_netlink },
|
||||
};
|
||||
|
||||
const struct lws_role_ops role_ops_netlink = {
|
||||
/* role name */ "netlink",
|
||||
/* alpn id */ NULL,
|
||||
|
||||
/* rops_table */ rops_table_netlink,
|
||||
/* rops_idx */ {
|
||||
/* LWS_ROPS_check_upgrades */
|
||||
/* LWS_ROPS_pt_init_destroy */ 0x01,
|
||||
/* LWS_ROPS_init_vhost */
|
||||
/* LWS_ROPS_destroy_vhost */ 0x00,
|
||||
/* LWS_ROPS_service_flag_pending */
|
||||
/* LWS_ROPS_handle_POLLIN */ 0x02,
|
||||
/* LWS_ROPS_handle_POLLOUT */
|
||||
/* LWS_ROPS_perform_user_POLLOUT */ 0x00,
|
||||
/* LWS_ROPS_callback_on_writable */
|
||||
/* LWS_ROPS_tx_credit */ 0x00,
|
||||
/* LWS_ROPS_write_role_protocol */
|
||||
/* LWS_ROPS_encapsulation_parent */ 0x00,
|
||||
/* LWS_ROPS_alpn_negotiated */
|
||||
/* LWS_ROPS_close_via_role_protocol */ 0x00,
|
||||
/* LWS_ROPS_close_role */
|
||||
/* LWS_ROPS_close_kill_connection */ 0x00,
|
||||
/* LWS_ROPS_destroy_role */
|
||||
/* LWS_ROPS_adoption_bind */ 0x00,
|
||||
/* LWS_ROPS_client_bind */
|
||||
/* LWS_ROPS_issue_keepalive */ 0x00,
|
||||
},
|
||||
|
||||
/* adoption_cb clnt, srv */ { 0, 0 },
|
||||
/* rx_cb clnt, srv */ { 0, 0 },
|
||||
/* writeable cb clnt, srv */ { 0, 0 },
|
||||
/* close cb clnt, srv */ { 0, 0 },
|
||||
/* protocol_bind_cb c,s */ { 0, 0 },
|
||||
/* protocol_unbind_cb c,s */ { 0, 0 },
|
||||
/* file_handle */ 0,
|
||||
};
|
||||
163
Kinc/Sources/kinc/libs/roles/pipe/ops-pipe.c
Normal file
163
Kinc/Sources/kinc/libs/roles/pipe/ops-pipe.c
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_pipe(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
#if defined(LWS_HAVE_EVENTFD)
|
||||
eventfd_t value;
|
||||
int n;
|
||||
|
||||
n = eventfd_read(wsi->desc.sockfd, &value);
|
||||
if (n < 0) {
|
||||
lwsl_notice("%s: eventfd read %d bailed errno %d\n", __func__,
|
||||
wsi->desc.sockfd, LWS_ERRNO);
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
#elif !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 = (int)read(wsi->desc.sockfd, s, sizeof(s));
|
||||
(void)n;
|
||||
if (n < 0)
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
#elif defined(WIN32)
|
||||
char s[100];
|
||||
int n;
|
||||
|
||||
n = recv(wsi->desc.sockfd, s, sizeof(s), 0);
|
||||
if (n == SOCKET_ERROR)
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_THREADPOOL) && defined(LWS_HAVE_PTHREAD_H)
|
||||
/*
|
||||
* threadpools that need to call for on_writable callbacks do it by
|
||||
* marking the task as needing one for its wsi, then cancelling service.
|
||||
*
|
||||
* Each tsi will call this to perform the actual callback_on_writable
|
||||
* from the correct service thread context
|
||||
*/
|
||||
lws_threadpool_tsi_context(pt->context, pt->tid);
|
||||
#endif
|
||||
|
||||
#if LWS_MAX_SMP > 1
|
||||
|
||||
/*
|
||||
* Other pts need to take care of their own wsi bound to a vhost that
|
||||
* is going down
|
||||
*/
|
||||
|
||||
if (pt->context->owner_vh_being_destroyed.head) {
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
|
||||
pt->context->owner_vh_being_destroyed.head) {
|
||||
struct lws_vhost *v =
|
||||
lws_container_of(d, struct lws_vhost,
|
||||
vh_being_destroyed_list);
|
||||
|
||||
lws_vhost_lock(v); /* -------------- vh { */
|
||||
__lws_vhost_destroy_pt_wsi_dieback_start(v);
|
||||
lws_vhost_unlock(v); /* } vh -------------- */
|
||||
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
lws_dll2_foreach_safe(&pt->ss_owner, NULL, lws_ss_cancel_notify_dll);
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) && defined(LWS_WITH_CLIENT)
|
||||
lws_dll2_foreach_safe(&pt->ss_client_owner, NULL, lws_sspc_cancel_notify_dll);
|
||||
#endif
|
||||
#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(pt, 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;
|
||||
}
|
||||
|
||||
static const lws_rops_t rops_table_pipe[] = {
|
||||
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_pipe },
|
||||
};
|
||||
|
||||
|
||||
const struct lws_role_ops role_ops_pipe = {
|
||||
/* role name */ "pipe",
|
||||
/* alpn id */ NULL,
|
||||
|
||||
/* rops_table */ rops_table_pipe,
|
||||
/* rops_idx */ {
|
||||
/* LWS_ROPS_check_upgrades */
|
||||
/* LWS_ROPS_pt_init_destroy */ 0x00,
|
||||
/* LWS_ROPS_init_vhost */
|
||||
/* LWS_ROPS_destroy_vhost */ 0x00,
|
||||
/* LWS_ROPS_service_flag_pending */
|
||||
/* LWS_ROPS_handle_POLLIN */ 0x01,
|
||||
/* LWS_ROPS_handle_POLLOUT */
|
||||
/* LWS_ROPS_perform_user_POLLOUT */ 0x00,
|
||||
/* LWS_ROPS_callback_on_writable */
|
||||
/* LWS_ROPS_tx_credit */ 0x00,
|
||||
/* LWS_ROPS_write_role_protocol */
|
||||
/* LWS_ROPS_encapsulation_parent */ 0x00,
|
||||
/* LWS_ROPS_alpn_negotiated */
|
||||
/* LWS_ROPS_close_via_role_protocol */ 0x00,
|
||||
/* LWS_ROPS_close_role */
|
||||
/* LWS_ROPS_close_kill_connection */ 0x00,
|
||||
/* LWS_ROPS_destroy_role */
|
||||
/* LWS_ROPS_adoption_bind */ 0x00,
|
||||
/* LWS_ROPS_client_bind */
|
||||
/* LWS_ROPS_issue_keepalive */ 0x00,
|
||||
},
|
||||
|
||||
/* adoption_cb clnt, srv */ { 0, 0 },
|
||||
/* rx_cb clnt, srv */ { 0, 0 },
|
||||
/* writeable cb clnt, srv */ { 0, 0 },
|
||||
/* close cb clnt, srv */ { 0, 0 },
|
||||
/* protocol_bind_cb c,s */ { 0, 0 },
|
||||
/* protocol_unbind_cb c,s */ { 0, 0 },
|
||||
#if defined(WIN32)
|
||||
/* file_handle (no, UDP) */ 0,
|
||||
#else
|
||||
/* file_handle */ 1,
|
||||
#endif
|
||||
};
|
||||
438
Kinc/Sources/kinc/libs/roles/private-lib-roles.h
Normal file
438
Kinc/Sources/kinc/libs/roles/private-lib-roles.h
Normal file
@ -0,0 +1,438 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
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 & (unsigned int)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_DNS = LWSIFS_NOT_EST | 1,
|
||||
LRS_WAITING_CONNECT = LWSIFS_NOT_EST | 2,
|
||||
|
||||
/* Phase 2: establishing intermediaries on top of transport */
|
||||
|
||||
LRS_WAITING_PROXY_REPLY = LWSIFS_NOT_EST | 3,
|
||||
LRS_WAITING_SSL = LWSIFS_NOT_EST | 4,
|
||||
LRS_WAITING_SOCKS_GREETING_REPLY = LWSIFS_NOT_EST | 5,
|
||||
LRS_WAITING_SOCKS_CONNECT_REPLY = LWSIFS_NOT_EST | 6,
|
||||
LRS_WAITING_SOCKS_AUTH_REPLY = LWSIFS_NOT_EST | 7,
|
||||
|
||||
/* Phase 3: establishing tls tunnel */
|
||||
|
||||
LRS_SSL_INIT = LWSIFS_NOT_EST | 8,
|
||||
LRS_SSL_ACK_PENDING = LWSIFS_NOT_EST | 9,
|
||||
LRS_PRE_WS_SERVING_ACCEPT = LWSIFS_NOT_EST | 10,
|
||||
|
||||
/* Phase 4: connected */
|
||||
|
||||
LRS_WAITING_SERVER_REPLY = LWSIFS_NOT_EST | 11,
|
||||
LRS_H2_AWAIT_PREFACE = LWSIFS_NOT_EST | 12,
|
||||
LRS_H2_AWAIT_SETTINGS = LWSIFS_NOT_EST |
|
||||
LWSIFS_POCB | 13,
|
||||
LRS_MQTTC_IDLE = LWSIFS_POCB | 33,
|
||||
LRS_MQTTC_AWAIT_CONNACK = 34,
|
||||
|
||||
/* Phase 5: protocol logically established */
|
||||
|
||||
LRS_H2_CLIENT_SEND_SETTINGS = LWSIFS_POCB | 14,
|
||||
LRS_H2_WAITING_TO_SEND_HEADERS = LWSIFS_POCB | 15,
|
||||
LRS_DEFERRING_ACTION = LWSIFS_POCB | 16,
|
||||
LRS_IDLING = 17,
|
||||
LRS_H1C_ISSUE_HANDSHAKE = 18,
|
||||
LRS_H1C_ISSUE_HANDSHAKE2 = 19,
|
||||
LRS_ISSUE_HTTP_BODY = 20,
|
||||
LRS_ISSUING_FILE = 21,
|
||||
LRS_HEADERS = 22,
|
||||
LRS_BODY = 23,
|
||||
LRS_DISCARD_BODY = 24,
|
||||
LRS_ESTABLISHED = LWSIFS_POCB | 25,
|
||||
|
||||
/* 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 | 26,
|
||||
|
||||
/* Phase 6: finishing */
|
||||
|
||||
LRS_WAITING_TO_SEND_CLOSE = LWSIFS_POCB | 27,
|
||||
LRS_RETURNED_CLOSE = LWSIFS_POCB | 28,
|
||||
LRS_AWAITING_CLOSE_ACK = LWSIFS_POCB | 29,
|
||||
LRS_FLUSHING_BEFORE_CLOSE = LWSIFS_POCB | 30,
|
||||
LRS_SHUTDOWN = 31,
|
||||
|
||||
/* Phase 7: dead */
|
||||
|
||||
LRS_DEAD_SOCKET = 32,
|
||||
|
||||
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 & (lws_wsi_state_t)(~LRS_MASK)) | lrs
|
||||
#else
|
||||
void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs);
|
||||
#endif
|
||||
|
||||
#define _LWS_ADOPT_FINISH (1 << 24)
|
||||
|
||||
/*
|
||||
* Internal role-specific ops
|
||||
*
|
||||
* Many roles are sparsely filled with callbacks, rather than has 20 x
|
||||
* function pointers in the ops struct, let's have a 20 nybble array telling us
|
||||
* if the pointer doesn't exist, or its offset in a smaller "just pointers that
|
||||
* exist" array.
|
||||
*
|
||||
* We can support up to 15 valid pointers in the role that way and only have to
|
||||
* provide pointers that exist for that role, at the cost of a 10-byte nybble
|
||||
* table.
|
||||
*
|
||||
* For x86_64, a set 196 byte allocation becomes 60 + 8 bytes per defined ptr,
|
||||
* where the ops table is sparse this is a considable .rodata saving, for 32-bit
|
||||
* 52 + 4 bytes per defined ptr accounting for padding.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
typedef int (*lws_rops_check_upgrades_t)(struct lws *wsi);
|
||||
/* role-specific context init during context creation */
|
||||
typedef int (*lws_rops_pt_init_destroy_t)(struct lws_context *context,
|
||||
const struct lws_context_creation_info *info,
|
||||
struct lws_context_per_thread *pt, int destroy);
|
||||
/* role-specific per-vhost init during vhost creation */
|
||||
typedef int (*lws_rops_init_vhost_t)(struct lws_vhost *vh,
|
||||
const struct lws_context_creation_info *info);
|
||||
/* role-specific per-vhost destructor during vhost destroy */
|
||||
typedef int (*lws_rops_destroy_vhost_t)(struct lws_vhost *vh);
|
||||
/* chance for the role to force POLLIN without network activity */
|
||||
typedef int (*lws_rops_service_flag_pending_t)(struct lws_context *context,
|
||||
int tsi);
|
||||
/* an fd using this role has POLLIN signalled */
|
||||
typedef int (*lws_rops_handle_POLLIN_t)(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 */
|
||||
typedef int (*lws_rops_handle_POLLOUT_t)(struct lws *wsi);
|
||||
/* perform user pollout */
|
||||
typedef int (*lws_rops_perform_user_POLLOUT_t)(struct lws *wsi);
|
||||
/* do effective callback on writeable */
|
||||
typedef int (*lws_rops_callback_on_writable_t)(struct lws *wsi);
|
||||
/* connection-specific tx credit in bytes */
|
||||
typedef int (*lws_rops_tx_credit_t)(struct lws *wsi, char peer_to_us, int add);
|
||||
/* role-specific write formatting */
|
||||
typedef int (*lws_rops_write_role_protocol_t)(struct lws *wsi,
|
||||
unsigned char *buf, size_t len,
|
||||
enum lws_write_protocol *wp);
|
||||
|
||||
/* get encapsulation parent */
|
||||
typedef struct lws * (*lws_rops_encapsulation_parent_t)(struct lws *wsi);
|
||||
|
||||
/* role-specific destructor */
|
||||
typedef int (*lws_rops_alpn_negotiated_t)(struct lws *wsi, const char *alpn);
|
||||
|
||||
/* chance for the role to handle close in the protocol */
|
||||
typedef int (*lws_rops_close_via_role_protocol_t)(struct lws *wsi,
|
||||
enum lws_close_status reason);
|
||||
/* role-specific close processing */
|
||||
typedef int (*lws_rops_close_role_t)(struct lws_context_per_thread *pt,
|
||||
struct lws *wsi);
|
||||
/* role-specific connection close processing */
|
||||
typedef int (*lws_rops_close_kill_connection_t)(struct lws *wsi,
|
||||
enum lws_close_status reason);
|
||||
/* role-specific destructor */
|
||||
typedef int (*lws_rops_destroy_role_t)(struct lws *wsi);
|
||||
|
||||
/* role-specific socket-adopt */
|
||||
typedef int (*lws_rops_adoption_bind_t)(struct lws *wsi, int type,
|
||||
const char *prot);
|
||||
/* role-specific client-bind:
|
||||
* ret 1 = bound, 0 = not bound, -1 = fail out
|
||||
* i may be NULL, indicating client_bind is being called after
|
||||
* a successful bind earlier, to finalize the binding. In that
|
||||
* case ret 0 = OK, 1 = fail, wsi needs freeing, -1 = fail, wsi freed */
|
||||
typedef int (*lws_rops_client_bind_t)(struct lws *wsi,
|
||||
const struct lws_client_connect_info *i);
|
||||
/* isvalid = 0: request a role-specific keepalive (PING etc)
|
||||
* = 1: reset any related validity timer */
|
||||
typedef int (*lws_rops_issue_keepalive_t)(struct lws *wsi, int isvalid);
|
||||
|
||||
#define LWS_COUNT_ROLE_OPS 20
|
||||
|
||||
typedef union lws_rops {
|
||||
lws_rops_check_upgrades_t check_upgrades;
|
||||
lws_rops_pt_init_destroy_t pt_init_destroy;
|
||||
lws_rops_init_vhost_t init_vhost;
|
||||
lws_rops_destroy_vhost_t destroy_vhost;
|
||||
lws_rops_service_flag_pending_t service_flag_pending;
|
||||
lws_rops_handle_POLLIN_t handle_POLLIN;
|
||||
lws_rops_handle_POLLOUT_t handle_POLLOUT;
|
||||
lws_rops_perform_user_POLLOUT_t perform_user_POLLOUT;
|
||||
lws_rops_callback_on_writable_t callback_on_writable;
|
||||
lws_rops_tx_credit_t tx_credit;
|
||||
lws_rops_write_role_protocol_t write_role_protocol;
|
||||
lws_rops_encapsulation_parent_t encapsulation_parent;
|
||||
lws_rops_alpn_negotiated_t alpn_negotiated;
|
||||
lws_rops_close_via_role_protocol_t close_via_role_protocol;
|
||||
lws_rops_close_role_t close_role;
|
||||
lws_rops_close_kill_connection_t close_kill_connection;
|
||||
lws_rops_destroy_role_t destroy_role;
|
||||
lws_rops_adoption_bind_t adoption_bind;
|
||||
lws_rops_client_bind_t client_bind;
|
||||
lws_rops_issue_keepalive_t issue_keepalive;
|
||||
} lws_rops_t;
|
||||
|
||||
typedef enum {
|
||||
LWS_ROPS_check_upgrades,
|
||||
LWS_ROPS_pt_init_destroy,
|
||||
LWS_ROPS_init_vhost,
|
||||
LWS_ROPS_destroy_vhost,
|
||||
LWS_ROPS_service_flag_pending,
|
||||
LWS_ROPS_handle_POLLIN,
|
||||
LWS_ROPS_handle_POLLOUT,
|
||||
LWS_ROPS_perform_user_POLLOUT,
|
||||
LWS_ROPS_callback_on_writable,
|
||||
LWS_ROPS_tx_credit,
|
||||
LWS_ROPS_write_role_protocol,
|
||||
LWS_ROPS_encapsulation_parent,
|
||||
LWS_ROPS_alpn_negotiated,
|
||||
LWS_ROPS_close_via_role_protocol,
|
||||
LWS_ROPS_close_role,
|
||||
LWS_ROPS_close_kill_connection,
|
||||
LWS_ROPS_destroy_role,
|
||||
LWS_ROPS_adoption_bind,
|
||||
LWS_ROPS_client_bind,
|
||||
LWS_ROPS_issue_keepalive,
|
||||
} lws_rops_func_idx_t;
|
||||
|
||||
struct lws_context_per_thread;
|
||||
|
||||
struct lws_role_ops {
|
||||
const char *name;
|
||||
const char *alpn;
|
||||
|
||||
const lws_rops_t *rops_table;
|
||||
/**< the occupied role ops func ptrs */
|
||||
uint8_t rops_idx[(LWS_COUNT_ROLE_OPS + 1) / 2];
|
||||
/**< translates role index into .rops[] offset */
|
||||
|
||||
/*
|
||||
* the callback reasons for adoption for client, server
|
||||
* (just client applies if no concept of client or server)
|
||||
*/
|
||||
uint8_t adoption_cb[2];
|
||||
/*
|
||||
* the callback reasons for adoption for client, server
|
||||
* (just client applies if no concept of client or server)
|
||||
*/
|
||||
uint8_t rx_cb[2];
|
||||
/*
|
||||
* the callback reasons for WRITEABLE for client, server
|
||||
* (just client applies if no concept of client or server)
|
||||
*/
|
||||
uint8_t writeable_cb[2];
|
||||
/*
|
||||
* the callback reasons for CLOSE for client, server
|
||||
* (just client applies if no concept of client or server)
|
||||
*/
|
||||
uint8_t close_cb[2];
|
||||
/*
|
||||
* the callback reasons for protocol bind for client, server
|
||||
* (just client applies if no concept of client or server)
|
||||
*/
|
||||
uint8_t protocol_bind_cb[2];
|
||||
/*
|
||||
* the callback reasons for protocol unbind for client, server
|
||||
* (just client applies if no concept of client or server)
|
||||
*/
|
||||
uint8_t protocol_unbind_cb[2];
|
||||
|
||||
uint8_t file_handle:1;
|
||||
/* role operates on files not sockets */
|
||||
};
|
||||
|
||||
#define lws_rops_fidx(_rops, fidx) \
|
||||
((fidx & 1) ? (_rops)->rops_idx[fidx / 2] & 0xf : \
|
||||
(_rops)->rops_idx[fidx / 2] >> 4)
|
||||
|
||||
#define lws_rops_func_fidx(_rops, fidx) \
|
||||
((_rops)->rops_table[lws_rops_fidx(_rops, fidx) - 1])
|
||||
|
||||
/* core roles */
|
||||
extern const struct lws_role_ops role_ops_raw_skt, role_ops_raw_file,
|
||||
role_ops_listen, role_ops_pipe,
|
||||
role_ops_netlink;
|
||||
|
||||
/* bring in role private declarations */
|
||||
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
#include "private-lib-roles-http.h"
|
||||
#else
|
||||
#define lwsi_role_http(wsi) (0)
|
||||
#endif
|
||||
|
||||
#if defined(LWS_ROLE_H1)
|
||||
#include "private-lib-roles-h1.h"
|
||||
#else
|
||||
#define lwsi_role_h1(wsi) (0)
|
||||
#endif
|
||||
|
||||
#if defined(LWS_ROLE_H2)
|
||||
#include "private-lib-roles-h2.h"
|
||||
#else
|
||||
#define lwsi_role_h2(wsi) (0)
|
||||
#endif
|
||||
|
||||
#if defined(LWS_ROLE_WS)
|
||||
#include "private-lib-roles-ws.h"
|
||||
#else
|
||||
#define lwsi_role_ws(wsi) (0)
|
||||
#endif
|
||||
|
||||
#if defined(LWS_ROLE_CGI)
|
||||
#include "private-lib-roles-cgi.h"
|
||||
#else
|
||||
#define lwsi_role_cgi(wsi) (0)
|
||||
#endif
|
||||
|
||||
#if defined(LWS_ROLE_DBUS)
|
||||
#include "private-lib-roles-dbus.h"
|
||||
#else
|
||||
#define lwsi_role_dbus(wsi) (0)
|
||||
#endif
|
||||
|
||||
#if defined(LWS_ROLE_RAW_PROXY)
|
||||
#include "private-lib-roles-raw-proxy.h"
|
||||
#else
|
||||
#define lwsi_role_raw_proxy(wsi) (0)
|
||||
#endif
|
||||
|
||||
#if defined(LWS_ROLE_MQTT)
|
||||
#include "mqtt/private-lib-roles-mqtt.h"
|
||||
#else
|
||||
#define lwsi_role_mqtt(wsi) (0)
|
||||
#endif
|
||||
|
||||
enum {
|
||||
LWS_HP_RET_BAIL_OK,
|
||||
LWS_HP_RET_BAIL_DIE,
|
||||
LWS_HP_RET_USER_SERVICE,
|
||||
LWS_HP_RET_DROP_POLLOUT,
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
#define LWS_CONNECT_COMPLETION_GOOD (-99)
|
||||
|
||||
int
|
||||
lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot);
|
||||
|
||||
struct lws *
|
||||
lws_client_connect_4_established(struct lws *wsi, struct lws *wsi_piggyback,
|
||||
ssize_t plen);
|
||||
|
||||
struct lws *
|
||||
lws_client_connect_3_connect(struct lws *wsi, const char *ads,
|
||||
const struct addrinfo *result, int n, void *opaque);
|
||||
40
Kinc/Sources/kinc/libs/roles/raw-file/CMakeLists.txt
Normal file
40
Kinc/Sources/kinc/libs/roles/raw-file/CMakeLists.txt
Normal file
@ -0,0 +1,40 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
list(APPEND SOURCES roles/raw-file/ops-raw-file.c)
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
127
Kinc/Sources/kinc/libs/roles/raw-file/ops-raw-file.c
Normal file
127
Kinc/Sources/kinc/libs/roles/raw-file/ops-raw-file.c
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
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) {
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_wsi_info(wsi, "failed at set pollfd");
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
}
|
||||
n = lws_callback_as_writeable(wsi);
|
||||
if (n)
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
if (pollfd->revents & LWS_POLLIN) {
|
||||
if (user_callback_handle_rxflow(wsi->a.protocol->callback,
|
||||
wsi, LWS_CALLBACK_RAW_RX_FILE,
|
||||
wsi->user_space, NULL, 0)) {
|
||||
lwsl_wsi_debug(wsi, "raw rx callback closed it");
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
}
|
||||
|
||||
if (pollfd->revents & LWS_POLLHUP)
|
||||
if (!(pollfd->revents & LWS_POLLIN))
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_adoption_bind_raw_file(struct lws *wsi, int type, const char *vh_prot_name)
|
||||
{
|
||||
/* no socket or http: it can only be a raw file */
|
||||
if ((type & LWS_ADOPT_HTTP) || (type & LWS_ADOPT_SOCKET) ||
|
||||
(type & _LWS_ADOPT_FINISH))
|
||||
return 0; /* no match */
|
||||
|
||||
lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_raw_file);
|
||||
|
||||
if (!vh_prot_name) {
|
||||
if (wsi->a.vhost->default_protocol_index >=
|
||||
wsi->a.vhost->count_protocols)
|
||||
return 0;
|
||||
|
||||
wsi->a.protocol = &wsi->a.vhost->protocols[
|
||||
wsi->a.vhost->default_protocol_index];
|
||||
}
|
||||
|
||||
return 1; /* bound */
|
||||
}
|
||||
|
||||
static const lws_rops_t rops_table_raw_file[] = {
|
||||
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_raw_file },
|
||||
/* 2 */ { .adoption_bind = rops_adoption_bind_raw_file },
|
||||
};
|
||||
|
||||
const struct lws_role_ops role_ops_raw_file = {
|
||||
/* role name */ "raw-file",
|
||||
/* alpn id */ NULL,
|
||||
|
||||
/* rops_table */ rops_table_raw_file,
|
||||
/* rops_idx */ {
|
||||
/* LWS_ROPS_check_upgrades */
|
||||
/* LWS_ROPS_pt_init_destroy */ 0x00,
|
||||
/* LWS_ROPS_init_vhost */
|
||||
/* LWS_ROPS_destroy_vhost */ 0x00,
|
||||
/* LWS_ROPS_service_flag_pending */
|
||||
/* LWS_ROPS_handle_POLLIN */ 0x01,
|
||||
/* LWS_ROPS_handle_POLLOUT */
|
||||
/* LWS_ROPS_perform_user_POLLOUT */ 0x00,
|
||||
/* LWS_ROPS_callback_on_writable */
|
||||
/* LWS_ROPS_tx_credit */ 0x00,
|
||||
/* LWS_ROPS_write_role_protocol */
|
||||
/* LWS_ROPS_encapsulation_parent */ 0x00,
|
||||
/* LWS_ROPS_alpn_negotiated */
|
||||
/* LWS_ROPS_close_via_role_protocol */ 0x00,
|
||||
/* LWS_ROPS_close_role */
|
||||
/* LWS_ROPS_close_kill_connection */ 0x00,
|
||||
/* LWS_ROPS_destroy_role */
|
||||
/* LWS_ROPS_adoption_bind */ 0x02,
|
||||
/* LWS_ROPS_client_bind */
|
||||
/* LWS_ROPS_issue_keepalive */ 0x00,
|
||||
},
|
||||
|
||||
/* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_ADOPT_FILE,
|
||||
LWS_CALLBACK_RAW_ADOPT_FILE },
|
||||
/* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX_FILE,
|
||||
LWS_CALLBACK_RAW_RX_FILE },
|
||||
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE,
|
||||
LWS_CALLBACK_RAW_WRITEABLE_FILE},
|
||||
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE,
|
||||
LWS_CALLBACK_RAW_CLOSE_FILE},
|
||||
/* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL,
|
||||
LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL },
|
||||
/* protocol_unbind cb c, srv */ { LWS_CALLBACK_RAW_FILE_DROP_PROTOCOL,
|
||||
LWS_CALLBACK_RAW_FILE_DROP_PROTOCOL },
|
||||
/* file_handle */ 1,
|
||||
};
|
||||
42
Kinc/Sources/kinc/libs/roles/raw-proxy/CMakeLists.txt
Normal file
42
Kinc/Sources/kinc/libs/roles/raw-proxy/CMakeLists.txt
Normal file
@ -0,0 +1,42 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
list(APPEND SOURCES
|
||||
roles/raw-proxy/ops-raw-proxy.c)
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
|
||||
246
Kinc/Sources/kinc/libs/roles/raw-proxy/ops-raw-proxy.c
Normal file
246
Kinc/Sources/kinc/libs/roles/raw-proxy/ops-raw-proxy.c
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_raw_proxy(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 (lws_has_buffered_out(wsi)) {
|
||||
if (!(pollfd->revents & LWS_POLLOUT))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
/* drain the output buflist */
|
||||
if (lws_issue_raw(wsi, NULL, 0) < 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 (lwsi_state(wsi) == LRS_WAITING_CONNECT)
|
||||
goto try_pollout;
|
||||
|
||||
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))) {
|
||||
|
||||
ebuf.token = NULL;
|
||||
ebuf.len = 0;
|
||||
buffered = lws_buflist_aware_read(pt, wsi, &ebuf, 1, __func__);
|
||||
if (buffered < 0)
|
||||
goto fail;
|
||||
|
||||
switch (ebuf.len) {
|
||||
case 0:
|
||||
lwsl_info("%s: read 0 len\n", __func__);
|
||||
wsi->seen_zero_length_recv = 1;
|
||||
if (lws_change_pollfd(wsi, LWS_POLLIN, 0))
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* 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->a.protocol->callback,
|
||||
wsi, lwsi_role_client(wsi) ?
|
||||
LWS_CALLBACK_RAW_PROXY_CLI_RX :
|
||||
LWS_CALLBACK_RAW_PROXY_SRV_RX,
|
||||
wsi->user_space, ebuf.token,
|
||||
(size_t)ebuf.len);
|
||||
if (n < 0) {
|
||||
lwsl_info("LWS_CALLBACK_RAW_PROXY_*_RX fail\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (lws_buflist_aware_finished_consuming(wsi, &ebuf, ebuf.len,
|
||||
buffered, __func__))
|
||||
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:
|
||||
|
||||
if (!(pollfd->revents & LWS_POLLOUT))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
if (lws_handle_POLLOUT_event(wsi, pollfd)) {
|
||||
lwsl_debug("POLLOUT event closed it\n");
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
if (lws_http_client_socket_service(wsi, pollfd))
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
#endif
|
||||
|
||||
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_adoption_bind_raw_proxy(struct lws *wsi, int type,
|
||||
const char *vh_prot_name)
|
||||
{
|
||||
/* no http but socket... must be raw skt */
|
||||
if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) ||
|
||||
(!(type & LWS_ADOPT_FLAG_RAW_PROXY)) || (type & _LWS_ADOPT_FINISH))
|
||||
return 0; /* no match */
|
||||
|
||||
#if defined(LWS_WITH_UDP)
|
||||
if (type & LWS_ADOPT_FLAG_UDP)
|
||||
/*
|
||||
* these can be >128 bytes, so just alloc for UDP
|
||||
*/
|
||||
wsi->udp = lws_malloc(sizeof(*wsi->udp), "udp struct");
|
||||
#endif
|
||||
|
||||
lws_role_transition(wsi, LWSIFR_SERVER, (type & LWS_ADOPT_ALLOW_SSL) ?
|
||||
LRS_SSL_INIT : LRS_ESTABLISHED,
|
||||
&role_ops_raw_proxy);
|
||||
|
||||
if (vh_prot_name)
|
||||
lws_bind_protocol(wsi, wsi->a.protocol, __func__);
|
||||
else
|
||||
/* this is the only time he will transition */
|
||||
lws_bind_protocol(wsi,
|
||||
&wsi->a.vhost->protocols[wsi->a.vhost->raw_protocol_index],
|
||||
__func__);
|
||||
|
||||
return 1; /* bound */
|
||||
}
|
||||
|
||||
static int
|
||||
rops_client_bind_raw_proxy(struct lws *wsi,
|
||||
const struct lws_client_connect_info *i)
|
||||
{
|
||||
if (!i) {
|
||||
|
||||
/* finalize */
|
||||
|
||||
if (!wsi->user_space && wsi->stash->cis[CIS_METHOD])
|
||||
if (lws_ensure_user_space(wsi))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we are a fallback if nothing else matched */
|
||||
|
||||
if (i->local_protocol_name && !strcmp(i->local_protocol_name, "raw-proxy"))
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
|
||||
&role_ops_raw_proxy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_handle_POLLOUT_raw_proxy(struct lws *wsi)
|
||||
{
|
||||
if (lwsi_state(wsi) == LRS_ESTABLISHED)
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
|
||||
if (lwsi_role_client(wsi))
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
|
||||
return LWS_HP_RET_BAIL_OK;
|
||||
}
|
||||
|
||||
static const lws_rops_t rops_table_raw_proxy[] = {
|
||||
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_raw_proxy },
|
||||
/* 2 */ { .handle_POLLOUT = rops_handle_POLLOUT_raw_proxy },
|
||||
/* 3 */ { .adoption_bind = rops_adoption_bind_raw_proxy },
|
||||
/* 4 */ { .client_bind = rops_client_bind_raw_proxy },
|
||||
};
|
||||
|
||||
|
||||
const struct lws_role_ops role_ops_raw_proxy = {
|
||||
/* role name */ "raw-proxy",
|
||||
/* alpn id */ NULL,
|
||||
|
||||
/* rops_table */ rops_table_raw_proxy,
|
||||
/* rops_idx */ {
|
||||
/* LWS_ROPS_check_upgrades */
|
||||
/* LWS_ROPS_pt_init_destroy */ 0x00,
|
||||
/* LWS_ROPS_init_vhost */
|
||||
/* LWS_ROPS_destroy_vhost */ 0x00,
|
||||
/* LWS_ROPS_service_flag_pending */
|
||||
/* LWS_ROPS_handle_POLLIN */ 0x01,
|
||||
/* LWS_ROPS_handle_POLLOUT */
|
||||
/* LWS_ROPS_perform_user_POLLOUT */ 0x20,
|
||||
/* LWS_ROPS_callback_on_writable */
|
||||
/* LWS_ROPS_tx_credit */ 0x00,
|
||||
/* LWS_ROPS_write_role_protocol */
|
||||
/* LWS_ROPS_encapsulation_parent */ 0x00,
|
||||
/* LWS_ROPS_alpn_negotiated */
|
||||
/* LWS_ROPS_close_via_role_protocol */ 0x00,
|
||||
/* LWS_ROPS_close_role */
|
||||
/* LWS_ROPS_close_kill_connection */ 0x00,
|
||||
/* LWS_ROPS_destroy_role */
|
||||
/* LWS_ROPS_adoption_bind */ 0x03,
|
||||
/* LWS_ROPS_client_bind */
|
||||
/* LWS_ROPS_issue_keepalive */ 0x40,
|
||||
},
|
||||
|
||||
/* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_ADOPT,
|
||||
LWS_CALLBACK_RAW_PROXY_SRV_ADOPT },
|
||||
/* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_RX,
|
||||
LWS_CALLBACK_RAW_PROXY_SRV_RX },
|
||||
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE,
|
||||
LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE, },
|
||||
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_CLOSE,
|
||||
LWS_CALLBACK_RAW_PROXY_SRV_CLOSE },
|
||||
/* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_BIND_PROTOCOL,
|
||||
LWS_CALLBACK_RAW_PROXY_SRV_BIND_PROTOCOL },
|
||||
/* protocol_unbind cb c, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_DROP_PROTOCOL,
|
||||
LWS_CALLBACK_RAW_PROXY_SRV_DROP_PROTOCOL },
|
||||
/* file_handle */ 0,
|
||||
};
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* This is included from private-lib-core.h if LWS_ROLE_RAW_PROXY
|
||||
*/
|
||||
|
||||
extern const struct lws_role_ops role_ops_raw_proxy;
|
||||
|
||||
#define lwsi_role_raw_proxy(wsi) (wsi->role_ops == &role_ops_raw_proxy)
|
||||
|
||||
#if 0
|
||||
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;
|
||||
};
|
||||
|
||||
struct _lws_raw_proxy_related {
|
||||
struct lws *wsi_onward;
|
||||
};
|
||||
#endif
|
||||
46
Kinc/Sources/kinc/libs/roles/raw-skt/CMakeLists.txt
Normal file
46
Kinc/Sources/kinc/libs/roles/raw-skt/CMakeLists.txt
Normal file
@ -0,0 +1,46 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
list(APPEND SOURCES
|
||||
roles/raw-skt/ops-raw-skt.c)
|
||||
|
||||
if (LWS_WITH_ABSTRACT)
|
||||
list(APPEND SOURCES
|
||||
abstract/transports/raw-skt.c)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
404
Kinc/Sources/kinc/libs/roles/raw-skt/ops-raw-skt.c
Normal file
404
Kinc/Sources/kinc/libs/roles/raw-skt/ops-raw-skt.c
Normal file
@ -0,0 +1,404 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
static int
|
||||
lws_raw_skt_connect(struct lws *wsi)
|
||||
{
|
||||
int n;
|
||||
#if defined(LWS_WITH_TLS)
|
||||
const char *cce = NULL;
|
||||
char ccebuf[128];
|
||||
|
||||
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
switch (lws_client_create_tls(wsi, &cce, 1)) {
|
||||
#else
|
||||
switch (lws_client_create_tls(wsi, &cce, 0)) {
|
||||
#endif
|
||||
case CCTLS_RETURN_ERROR:
|
||||
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
|
||||
return -1;
|
||||
case CCTLS_RETURN_RETRY:
|
||||
return 0;
|
||||
case CCTLS_RETURN_DONE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
|
||||
n = lws_ssl_client_connect2(wsi, ccebuf, sizeof(ccebuf));
|
||||
if (n < 0) {
|
||||
lws_inform_client_conn_fail(wsi, (void *)ccebuf,
|
||||
strlen(ccebuf));
|
||||
|
||||
return -1;
|
||||
}
|
||||
if (n != 1)
|
||||
return 0; /* wait */
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!wsi->hdr_parsing_completed) {
|
||||
n = user_callback_handle_rxflow(wsi->a.protocol->callback,
|
||||
wsi, wsi->role_ops->adoption_cb[lwsi_role_server(wsi)],
|
||||
wsi->user_space, NULL, 0);
|
||||
if (n) {
|
||||
lws_inform_client_conn_fail(wsi, (void *)"user", 4);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
||||
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
||||
|
||||
return 1; /* success */
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_raw_skt(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
#if defined(LWS_WITH_SOCKS5)
|
||||
const char *cce = NULL;
|
||||
#endif
|
||||
struct lws_tokens ebuf;
|
||||
int n = 0, buffered = 0;
|
||||
|
||||
/* pending truncated sends have uber priority */
|
||||
|
||||
if (lws_has_buffered_out(wsi)) {
|
||||
if (!(pollfd->revents & LWS_POLLOUT))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
/* drain the output buflist */
|
||||
if (lws_issue_raw(wsi, NULL, 0) < 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 defined(LWS_WITH_SERVER)
|
||||
if (!lwsi_role_client(wsi) && lwsi_state(wsi) != LRS_ESTABLISHED) {
|
||||
|
||||
lwsl_wsi_debug(wsi, "wsistate 0x%x\n", (int)wsi->wsistate);
|
||||
|
||||
if (lwsi_state(wsi) != LRS_SSL_INIT)
|
||||
if (lws_server_socket_service_ssl(wsi,
|
||||
LWS_SOCK_INVALID,
|
||||
!!(pollfd->revents & pollfd->events & LWS_POLLIN)))
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((pollfd->revents & pollfd->events & LWS_POLLIN) &&
|
||||
!(wsi->favoured_pollin &&
|
||||
(pollfd->revents & pollfd->events & LWS_POLLOUT))) {
|
||||
|
||||
lwsl_wsi_debug(wsi, "POLLIN: state 0x%x", lwsi_state(wsi));
|
||||
|
||||
switch (lwsi_state(wsi)) {
|
||||
|
||||
/* any tunnel has to have been established... */
|
||||
case LRS_SSL_ACK_PENDING:
|
||||
goto nope;
|
||||
/* we are actually connected */
|
||||
case LRS_WAITING_CONNECT:
|
||||
goto nope;
|
||||
|
||||
case LRS_WAITING_SSL:
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
n = lws_raw_skt_connect(wsi);
|
||||
if (n < 0)
|
||||
goto fail;
|
||||
#endif
|
||||
break;
|
||||
|
||||
#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:
|
||||
|
||||
switch (lws_socks5c_handle_state(wsi, pollfd, &cce)) {
|
||||
case LW5CHS_RET_RET0:
|
||||
goto nope;
|
||||
case LW5CHS_RET_BAIL3:
|
||||
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
|
||||
goto fail;
|
||||
case LW5CHS_RET_STARTHS:
|
||||
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
||||
lws_client_connect_4_established(wsi, NULL, 0);
|
||||
|
||||
/*
|
||||
* Now we got the socks5 connection, we need to
|
||||
* go down the tls path on it now if that's what
|
||||
* we want
|
||||
*/
|
||||
goto post_rx;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
goto post_rx;
|
||||
#endif
|
||||
default:
|
||||
ebuf.token = NULL;
|
||||
ebuf.len = (int) wsi->a.protocol->rx_buffer_size;
|
||||
|
||||
buffered = lws_buflist_aware_read(pt, wsi, &ebuf, 1, __func__);
|
||||
switch (ebuf.len) {
|
||||
case 0:
|
||||
if (wsi->unix_skt)
|
||||
break;
|
||||
lwsl_wsi_info(wsi, "read 0 len");
|
||||
wsi->seen_zero_length_recv = 1;
|
||||
if (lws_change_pollfd(wsi, LWS_POLLIN, 0))
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_UDP)
|
||||
if (lws_fi(&wsi->fic, "udp_rx_loss")) {
|
||||
n = ebuf.len;
|
||||
goto post_rx;
|
||||
}
|
||||
#endif
|
||||
|
||||
n = user_callback_handle_rxflow(wsi->a.protocol->callback,
|
||||
wsi, LWS_CALLBACK_RAW_RX,
|
||||
wsi->user_space, ebuf.token,
|
||||
(unsigned int)ebuf.len);
|
||||
#if defined(LWS_WITH_UDP) || defined(LWS_WITH_SOCKS5)
|
||||
post_rx:
|
||||
#endif
|
||||
if (n < 0) {
|
||||
lwsl_wsi_info(wsi, "LWS_CALLBACK_RAW_RX_fail");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (lws_buflist_aware_finished_consuming(wsi, &ebuf, ebuf.len,
|
||||
buffered, __func__))
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
|
||||
goto try_pollout;
|
||||
}
|
||||
}
|
||||
nope:
|
||||
if (wsi->favoured_pollin &&
|
||||
(pollfd->revents & pollfd->events & LWS_POLLOUT))
|
||||
/* we balanced the last favouring of pollin */
|
||||
wsi->favoured_pollin = 0;
|
||||
|
||||
try_pollout:
|
||||
|
||||
if (!(pollfd->revents & LWS_POLLOUT))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
if (lwsi_state(wsi) == LRS_WAITING_CONNECT) {
|
||||
if (!lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL))
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
|
||||
if (lws_raw_skt_connect(wsi) < 0)
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lwsi_state(wsi) == LRS_WAITING_SSL)
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
/* one shot */
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
|
||||
goto fail;
|
||||
|
||||
/* clear back-to-back write detection */
|
||||
wsi->could_have_pending = 0;
|
||||
|
||||
n = user_callback_handle_rxflow(wsi->a.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;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
static int
|
||||
rops_adoption_bind_raw_skt(struct lws *wsi, int type, const char *vh_prot_name)
|
||||
{
|
||||
|
||||
// lwsl_notice("%s: bind type %d\n", __func__, type);
|
||||
|
||||
/* no http but socket... must be raw skt */
|
||||
if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) ||
|
||||
((type & _LWS_ADOPT_FINISH) && (!(type & LWS_ADOPT_FLAG_UDP))))
|
||||
return 0; /* no match */
|
||||
|
||||
#if defined(LWS_WITH_UDP)
|
||||
if ((type & LWS_ADOPT_FLAG_UDP) && !wsi->udp) {
|
||||
/*
|
||||
* these can be >128 bytes, so just alloc for UDP
|
||||
*/
|
||||
wsi->udp = lws_malloc(sizeof(*wsi->udp), "udp struct");
|
||||
if (!wsi->udp)
|
||||
return 0;
|
||||
memset(wsi->udp, 0, sizeof(*wsi->udp));
|
||||
}
|
||||
#endif
|
||||
|
||||
lws_role_transition(wsi, 0, (type & LWS_ADOPT_ALLOW_SSL) ? LRS_SSL_INIT :
|
||||
LRS_ESTABLISHED, &role_ops_raw_skt);
|
||||
|
||||
if (vh_prot_name)
|
||||
lws_bind_protocol(wsi, wsi->a.protocol, __func__);
|
||||
else
|
||||
/* this is the only time he will transition */
|
||||
lws_bind_protocol(wsi,
|
||||
&wsi->a.vhost->protocols[wsi->a.vhost->raw_protocol_index],
|
||||
__func__);
|
||||
|
||||
return 1; /* bound */
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
static int
|
||||
rops_client_bind_raw_skt(struct lws *wsi,
|
||||
const struct lws_client_connect_info *i)
|
||||
{
|
||||
if (!i) {
|
||||
|
||||
/* finalize */
|
||||
|
||||
if (!wsi->user_space && wsi->stash->cis[CIS_METHOD])
|
||||
if (lws_ensure_user_space(wsi))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we are a fallback if nothing else matched */
|
||||
|
||||
if (!i->local_protocol_name ||
|
||||
strcmp(i->local_protocol_name, "raw-proxy"))
|
||||
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
|
||||
&role_ops_raw_skt);
|
||||
|
||||
return 1; /* matched */
|
||||
}
|
||||
#endif
|
||||
|
||||
static const lws_rops_t rops_table_raw_skt[] = {
|
||||
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_raw_skt },
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
/* 2 */ { .adoption_bind = rops_adoption_bind_raw_skt },
|
||||
#else
|
||||
/* 2 */ { .adoption_bind = NULL },
|
||||
#endif
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
/* 3 */ { .client_bind = rops_client_bind_raw_skt },
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct lws_role_ops role_ops_raw_skt = {
|
||||
/* role name */ "raw-skt",
|
||||
/* alpn id */ NULL,
|
||||
|
||||
/* rops_table */ rops_table_raw_skt,
|
||||
/* rops_idx */ {
|
||||
/* LWS_ROPS_check_upgrades */
|
||||
/* LWS_ROPS_pt_init_destroy */ 0x00,
|
||||
/* LWS_ROPS_init_vhost */
|
||||
/* LWS_ROPS_destroy_vhost */ 0x00,
|
||||
/* LWS_ROPS_service_flag_pending */
|
||||
/* LWS_ROPS_handle_POLLIN */ 0x01,
|
||||
/* LWS_ROPS_handle_POLLOUT */
|
||||
/* LWS_ROPS_perform_user_POLLOUT */ 0x00,
|
||||
/* LWS_ROPS_callback_on_writable */
|
||||
/* LWS_ROPS_tx_credit */ 0x00,
|
||||
/* LWS_ROPS_write_role_protocol */
|
||||
/* LWS_ROPS_encapsulation_parent */ 0x00,
|
||||
/* LWS_ROPS_alpn_negotiated */
|
||||
/* LWS_ROPS_close_via_role_protocol */ 0x00,
|
||||
/* LWS_ROPS_close_role */
|
||||
/* LWS_ROPS_close_kill_connection */ 0x00,
|
||||
/* LWS_ROPS_destroy_role */
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
/* LWS_ROPS_adoption_bind */ 0x02,
|
||||
#else
|
||||
/* LWS_ROPS_adoption_bind */ 0x00,
|
||||
#endif
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
/* LWS_ROPS_client_bind */
|
||||
/* LWS_ROPS_issue_keepalive */ 0x30,
|
||||
#else
|
||||
/* LWS_ROPS_client_bind */
|
||||
/* LWS_ROPS_issue_keepalive */ 0x00,
|
||||
#endif
|
||||
},
|
||||
|
||||
/* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_CONNECTED,
|
||||
LWS_CALLBACK_RAW_ADOPT },
|
||||
/* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX,
|
||||
LWS_CALLBACK_RAW_RX },
|
||||
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE,
|
||||
LWS_CALLBACK_RAW_WRITEABLE},
|
||||
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE,
|
||||
LWS_CALLBACK_RAW_CLOSE },
|
||||
/* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL,
|
||||
LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL },
|
||||
/* protocol_unbind cb c, srv */ { LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL,
|
||||
LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL },
|
||||
/* file_handle */ 0,
|
||||
};
|
||||
61
Kinc/Sources/kinc/libs/roles/ws/CMakeLists.txt
Normal file
61
Kinc/Sources/kinc/libs/roles/ws/CMakeLists.txt
Normal file
@ -0,0 +1,61 @@
|
||||
#
|
||||
# libwebsockets - small server side websockets and web server implementation
|
||||
#
|
||||
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
#
|
||||
# The strategy is to only export to PARENT_SCOPE
|
||||
#
|
||||
# - changes to LIB_LIST
|
||||
# - changes to SOURCES
|
||||
# - includes via include_directories
|
||||
#
|
||||
# and keep everything else private
|
||||
|
||||
include_directories(.)
|
||||
|
||||
list(APPEND SOURCES
|
||||
roles/ws/ops-ws.c)
|
||||
|
||||
if (NOT LWS_WITHOUT_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
roles/ws/client-ws.c
|
||||
roles/ws/client-parser-ws.c)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITHOUT_SERVER)
|
||||
list(APPEND SOURCES
|
||||
roles/ws/server-ws.c)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITHOUT_EXTENSIONS)
|
||||
list(APPEND HDR_PRIVATE
|
||||
roles/ws/ext/extension-permessage-deflate.h)
|
||||
list(APPEND SOURCES
|
||||
roles/ws/ext/extension.c
|
||||
roles/ws/ext/extension-permessage-deflate.c)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Keep explicit parent scope exports at end
|
||||
#
|
||||
|
||||
exports_to_parent_scope()
|
||||
|
||||
696
Kinc/Sources/kinc/libs/roles/ws/client-parser-ws.c
Normal file
696
Kinc/Sources/kinc/libs/roles/ws/client-parser-ws.c
Normal file
@ -0,0 +1,696 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
/*
|
||||
* parsers.c: lws_ws_rx_sm() needs to be roughly kept in
|
||||
* sync with changes here, esp related to ext draining
|
||||
*/
|
||||
|
||||
int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
|
||||
{
|
||||
int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
|
||||
struct lws_ext_pm_deflate_rx_ebufs pmdrx;
|
||||
unsigned short close_code;
|
||||
unsigned char *pp;
|
||||
int handled, m, n;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
int rx_draining_ext = 0;
|
||||
#endif
|
||||
|
||||
pmdrx.eb_in.token = NULL;
|
||||
pmdrx.eb_in.len = 0;
|
||||
pmdrx.eb_out.token = NULL;
|
||||
pmdrx.eb_out.len = 0;
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
assert(!c);
|
||||
|
||||
lws_remove_wsi_from_draining_ext_list(wsi);
|
||||
rx_draining_ext = 1;
|
||||
lwsl_wsi_debug(wsi, "doing draining flow");
|
||||
|
||||
goto drain_extension;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (wsi->lws_rx_parse_state) {
|
||||
case LWS_RXPS_NEW:
|
||||
/* control frames (PING) may interrupt checkable sequences */
|
||||
wsi->ws->defeat_check_utf8 = 0;
|
||||
|
||||
switch (wsi->ws->ietf_spec_revision) {
|
||||
case 13:
|
||||
wsi->ws->opcode = c & 0xf;
|
||||
/* revisit if an extension wants them... */
|
||||
switch (wsi->ws->opcode) {
|
||||
case LWSWSOPC_TEXT_FRAME:
|
||||
wsi->ws->rsv_first_msg = (c & 0x70);
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/*
|
||||
* set the expectation that we will have to
|
||||
* fake up the zlib trailer to the inflator for
|
||||
* this frame
|
||||
*/
|
||||
wsi->ws->pmd_trailer_application = !!(c & 0x40);
|
||||
#endif
|
||||
wsi->ws->continuation_possible = 1;
|
||||
wsi->ws->check_utf8 = lws_check_opt(
|
||||
wsi->a.context->options,
|
||||
LWS_SERVER_OPTION_VALIDATE_UTF8);
|
||||
wsi->ws->utf8 = 0;
|
||||
wsi->ws->first_fragment = 1;
|
||||
break;
|
||||
case LWSWSOPC_BINARY_FRAME:
|
||||
wsi->ws->rsv_first_msg = (c & 0x70);
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/*
|
||||
* set the expectation that we will have to
|
||||
* fake up the zlib trailer to the inflator for
|
||||
* this frame
|
||||
*/
|
||||
wsi->ws->pmd_trailer_application = !!(c & 0x40);
|
||||
#endif
|
||||
wsi->ws->check_utf8 = 0;
|
||||
wsi->ws->continuation_possible = 1;
|
||||
wsi->ws->first_fragment = 1;
|
||||
break;
|
||||
case LWSWSOPC_CONTINUATION:
|
||||
if (!wsi->ws->continuation_possible) {
|
||||
lwsl_wsi_info(wsi, "disordered continuation");
|
||||
return -1;
|
||||
}
|
||||
wsi->ws->first_fragment = 0;
|
||||
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:
|
||||
if (wsi->ws->allow_unknown_opcode)
|
||||
break;
|
||||
lwsl_wsi_info(wsi, "illegal opcode");
|
||||
return -1;
|
||||
default:
|
||||
wsi->ws->defeat_check_utf8 = 1;
|
||||
break;
|
||||
}
|
||||
wsi->ws->rsv = (c & 0x70);
|
||||
/* revisit if an extension wants them... */
|
||||
if (
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
!wsi->ws->count_act_ext &&
|
||||
#endif
|
||||
wsi->ws->rsv && !wsi->ws->allow_reserved_bits) {
|
||||
lwsl_wsi_info(wsi, "illegal rsv bits set");
|
||||
return -1;
|
||||
}
|
||||
wsi->ws->final = !!((c >> 7) & 1);
|
||||
lwsl_wsi_ext(wsi, " This RX frame Final %d",
|
||||
wsi->ws->final);
|
||||
|
||||
if (wsi->ws->owed_a_fin &&
|
||||
(wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
|
||||
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
|
||||
lwsl_wsi_info(wsi, "hey you owed us a FIN");
|
||||
return -1;
|
||||
}
|
||||
if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) {
|
||||
wsi->ws->continuation_possible = 0;
|
||||
wsi->ws->owed_a_fin = 0;
|
||||
}
|
||||
|
||||
if ((wsi->ws->opcode & 8) && !wsi->ws->final) {
|
||||
lwsl_wsi_info(wsi, "control msg can't be fragmented");
|
||||
return -1;
|
||||
}
|
||||
if (!wsi->ws->final)
|
||||
wsi->ws->owed_a_fin = 1;
|
||||
|
||||
switch (wsi->ws->opcode) {
|
||||
case LWSWSOPC_TEXT_FRAME:
|
||||
case LWSWSOPC_BINARY_FRAME:
|
||||
wsi->ws->frame_is_binary = wsi->ws->opcode ==
|
||||
LWSWSOPC_BINARY_FRAME;
|
||||
break;
|
||||
}
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
lwsl_wsi_err(wsi, "unknown spec version %02d",
|
||||
wsi->ws->ietf_spec_revision);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN:
|
||||
|
||||
wsi->ws->this_frame_masked = !!(c & 0x80);
|
||||
if (wsi->ws->this_frame_masked)
|
||||
goto server_cannot_mask;
|
||||
|
||||
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 = (size_t)((unsigned int)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 {
|
||||
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;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN64_8:
|
||||
if (c & 0x80) {
|
||||
lwsl_wsi_warn(wsi, "b63 of length must be zero");
|
||||
/* 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 {
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
case LWS_RXPS_WS_FRAME_PAYLOAD:
|
||||
|
||||
assert(wsi->ws->rx_ubuf);
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->rx_draining_ext)
|
||||
goto drain_extension;
|
||||
#endif
|
||||
if (wsi->ws->this_frame_masked && !wsi->ws->all_zero_nonce)
|
||||
c ^= wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
|
||||
|
||||
/*
|
||||
* unmask and collect the payload body in
|
||||
* rx_ubuf_head + LWS_PRE
|
||||
*/
|
||||
|
||||
wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] = c;
|
||||
|
||||
if (--wsi->ws->rx_packet_length == 0) {
|
||||
/* spill because we have the whole frame */
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
lwsl_wsi_debug(wsi, "spilling as we have the whole frame");
|
||||
goto spill;
|
||||
}
|
||||
|
||||
/*
|
||||
* if there's no protocol max frame size given, we are
|
||||
* supposed to default to context->pt_serv_buf_size
|
||||
*/
|
||||
if (!wsi->a.protocol->rx_buffer_size &&
|
||||
wsi->ws->rx_ubuf_head != wsi->a.context->pt_serv_buf_size)
|
||||
break;
|
||||
|
||||
if (wsi->a.protocol->rx_buffer_size &&
|
||||
wsi->ws->rx_ubuf_head != wsi->a.protocol->rx_buffer_size)
|
||||
break;
|
||||
|
||||
/* spill because we filled our rx buffer */
|
||||
|
||||
lwsl_wsi_debug(wsi, "spilling as we filled our rx buffer");
|
||||
spill:
|
||||
|
||||
handled = 0;
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
switch (wsi->ws->opcode) {
|
||||
case LWSWSOPC_CLOSE:
|
||||
pp = &wsi->ws->rx_ubuf[LWS_PRE];
|
||||
if (lws_check_opt(wsi->a.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_wsi_parser(wsi, "seen server's close ack");
|
||||
return -1;
|
||||
}
|
||||
|
||||
lwsl_wsi_parser(wsi, "client sees server close len = %d",
|
||||
(int)wsi->ws->rx_ubuf_head);
|
||||
if (wsi->ws->rx_ubuf_head >= 2) {
|
||||
close_code = (unsigned short)((pp[0] << 8) | pp[1]);
|
||||
if (close_code < 1000 ||
|
||||
close_code == 1004 ||
|
||||
close_code == 1005 ||
|
||||
close_code == 1006 ||
|
||||
(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->a.protocol->callback, wsi,
|
||||
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
|
||||
wsi->user_space, pp,
|
||||
wsi->ws->rx_ubuf_head))
|
||||
return -1;
|
||||
|
||||
memcpy(wsi->ws->ping_payload_buf + LWS_PRE, pp,
|
||||
wsi->ws->rx_ubuf_head);
|
||||
wsi->ws->close_in_ping_buffer_len =
|
||||
(uint8_t)wsi->ws->rx_ubuf_head;
|
||||
|
||||
lwsl_wsi_info(wsi, "scheduling return close as ack");
|
||||
__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:
|
||||
lwsl_wsi_info(wsi, "received %d byte ping, sending pong",
|
||||
(int)wsi->ws->rx_ubuf_head);
|
||||
|
||||
/* he set a close reason on this guy, ignore PING */
|
||||
if (wsi->ws->close_in_ping_buffer_len)
|
||||
goto ping_drop;
|
||||
|
||||
if (wsi->ws->pong_pending_flag) {
|
||||
/*
|
||||
* there is already a pending pong payload
|
||||
* we should just log and drop
|
||||
*/
|
||||
lwsl_wsi_parser(wsi, "DROP PING since one pending");
|
||||
goto ping_drop;
|
||||
}
|
||||
|
||||
/* control packets can only be < 128 bytes long */
|
||||
if (wsi->ws->rx_ubuf_head > 128 - 3) {
|
||||
lwsl_wsi_parser(wsi, "DROP PING payload too large");
|
||||
goto ping_drop;
|
||||
}
|
||||
|
||||
/* stash the pong payload */
|
||||
memcpy(wsi->ws->pong_payload_buf + LWS_PRE,
|
||||
&wsi->ws->rx_ubuf[LWS_PRE],
|
||||
wsi->ws->rx_ubuf_head);
|
||||
|
||||
wsi->ws->pong_payload_len = (uint8_t)wsi->ws->rx_ubuf_head;
|
||||
wsi->ws->pong_pending_flag = 1;
|
||||
|
||||
/* get it sent as soon as possible */
|
||||
lws_callback_on_writable(wsi);
|
||||
ping_drop:
|
||||
wsi->ws->rx_ubuf_head = 0;
|
||||
handled = 1;
|
||||
break;
|
||||
|
||||
case LWSWSOPC_PONG:
|
||||
lwsl_wsi_info(wsi, "Received pong");
|
||||
lwsl_hexdump_wsi_debug(wsi, &wsi->ws->rx_ubuf[LWS_PRE],
|
||||
wsi->ws->rx_ubuf_head);
|
||||
|
||||
lws_validity_confirmed(wsi);
|
||||
/* issue it */
|
||||
callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
|
||||
break;
|
||||
|
||||
case LWSWSOPC_CONTINUATION:
|
||||
case LWSWSOPC_TEXT_FRAME:
|
||||
case LWSWSOPC_BINARY_FRAME:
|
||||
break;
|
||||
|
||||
default:
|
||||
/* not handled or failed */
|
||||
lwsl_wsi_ext(wsi, "Unhandled ext opc 0x%x", wsi->ws->opcode);
|
||||
wsi->ws->rx_ubuf_head = 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* No it's real payload, pass it up to the user callback.
|
||||
*
|
||||
* We have been statefully collecting it in the
|
||||
* LWS_RXPS_WS_FRAME_PAYLOAD clause above.
|
||||
*
|
||||
* It's nicely buffered with the pre-padding taken care of
|
||||
* so it can be sent straight out again using lws_write.
|
||||
*
|
||||
* However, now we have a chunk of it, we want to deal with it
|
||||
* all here. Since this may be input to permessage-deflate and
|
||||
* there are block limits on that for input and output, we may
|
||||
* need to iterate.
|
||||
*/
|
||||
if (handled)
|
||||
goto already_done;
|
||||
|
||||
pmdrx.eb_in.token = &wsi->ws->rx_ubuf[LWS_PRE];
|
||||
pmdrx.eb_in.len = (int)wsi->ws->rx_ubuf_head;
|
||||
|
||||
/* for the non-pm-deflate case */
|
||||
|
||||
pmdrx.eb_out = pmdrx.eb_in;
|
||||
|
||||
lwsl_wsi_debug(wsi, "starting disbursal of %d deframed rx",
|
||||
(int)wsi->ws->rx_ubuf_head);
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
drain_extension:
|
||||
#endif
|
||||
do {
|
||||
|
||||
// lwsl_wsi_notice("pmdrx.eb_in.len: %d",
|
||||
// (int)pmdrx.eb_in.len);
|
||||
|
||||
n = PMDR_DID_NOTHING;
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
lwsl_wsi_ext(wsi, "+++ passing %d %p to ext",
|
||||
pmdrx.eb_in.len, pmdrx.eb_in.token);
|
||||
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX,
|
||||
&pmdrx, 0);
|
||||
lwsl_wsi_ext(wsi, "Ext RX returned %d", n);
|
||||
if (n < 0) {
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
return -1;
|
||||
}
|
||||
if (n == PMDR_DID_NOTHING)
|
||||
/* ie, not PMDR_NOTHING_WE_SHOULD_DO */
|
||||
break;
|
||||
#endif
|
||||
lwsl_wsi_ext(wsi, "post inflate ebuf in len %d / out len %d",
|
||||
pmdrx.eb_in.len, pmdrx.eb_out.len);
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (rx_draining_ext && !pmdrx.eb_out.len) {
|
||||
lwsl_wsi_debug(wsi, " --- ending drain on 0 read result");
|
||||
goto already_done;
|
||||
}
|
||||
|
||||
if (n == PMDR_HAS_PENDING) { /* 1 means stuff to drain */
|
||||
/* extension had more... main loop will come back */
|
||||
lwsl_wsi_ext(wsi, "adding to draining ext list");
|
||||
lws_add_wsi_to_draining_ext_list(wsi);
|
||||
} else {
|
||||
lwsl_wsi_ext(wsi, "removing from draining ext list");
|
||||
lws_remove_wsi_from_draining_ext_list(wsi);
|
||||
}
|
||||
rx_draining_ext = wsi->ws->rx_draining_ext;
|
||||
#endif
|
||||
|
||||
if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
|
||||
|
||||
if (lws_check_utf8(&wsi->ws->utf8,
|
||||
pmdrx.eb_out.token,
|
||||
(unsigned int)pmdrx.eb_out.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
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/* if ext not negotiated, going to be UNKNOWN */
|
||||
&& (n == PMDR_EMPTY_FINAL || n == PMDR_UNKNOWN)
|
||||
#endif
|
||||
) {
|
||||
lwsl_wsi_info(wsi, "FINAL utf8 error");
|
||||
lws_close_reason(wsi,
|
||||
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
|
||||
(uint8_t *)"partial utf8", 12);
|
||||
utf8_fail:
|
||||
lwsl_wsi_info(wsi, "utf8 error");
|
||||
lwsl_hexdump_wsi_info(wsi, pmdrx.eb_out.token,
|
||||
(unsigned int)pmdrx.eb_out.len);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (pmdrx.eb_out.len < 0 &&
|
||||
callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
|
||||
goto already_done;
|
||||
|
||||
if (!pmdrx.eb_out.token)
|
||||
goto already_done;
|
||||
|
||||
pmdrx.eb_out.token[pmdrx.eb_out.len] = '\0';
|
||||
|
||||
if (!wsi->a.protocol->callback)
|
||||
goto already_done;
|
||||
|
||||
if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
|
||||
lwsl_wsi_info(wsi, "Client doing pong callback");
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (n == PMDR_HAS_PENDING)
|
||||
/* extension had more... main loop will come back
|
||||
* we want callback to be done with this set, if so,
|
||||
* because lws_is_final() hides it was final until the
|
||||
* last chunk
|
||||
*/
|
||||
lws_add_wsi_to_draining_ext_list(wsi);
|
||||
else
|
||||
lws_remove_wsi_from_draining_ext_list(wsi);
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
/* if pmd not enabled, in == out */
|
||||
|
||||
if (n == PMDR_DID_NOTHING
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
|| n == PMDR_NOTHING_WE_SHOULD_DO
|
||||
|| n == PMDR_UNKNOWN
|
||||
#endif
|
||||
)
|
||||
pmdrx.eb_in.len -= pmdrx.eb_out.len;
|
||||
|
||||
m = wsi->a.protocol->callback(wsi,
|
||||
(enum lws_callback_reasons)callback_action,
|
||||
wsi->user_space, pmdrx.eb_out.token,
|
||||
(unsigned int)pmdrx.eb_out.len);
|
||||
|
||||
wsi->ws->first_fragment = 0;
|
||||
|
||||
lwsl_wsi_debug(wsi, "bulk ws rx: inp used %d, output %d",
|
||||
(int)wsi->ws->rx_ubuf_head,
|
||||
(int)pmdrx.eb_out.len);
|
||||
|
||||
/* if user code wants to close, let caller know */
|
||||
if (m)
|
||||
return 1;
|
||||
|
||||
} while (pmdrx.eb_in.len
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
|| rx_draining_ext
|
||||
#endif
|
||||
);
|
||||
|
||||
already_done:
|
||||
wsi->ws->rx_ubuf_head = 0;
|
||||
break;
|
||||
default:
|
||||
lwsl_wsi_err(wsi, "client rx illegal state");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
illegal_ctl_length:
|
||||
lwsl_wsi_warn(wsi, "Control frame asking for extended length is illegal");
|
||||
|
||||
/* kill the connection */
|
||||
return -1;
|
||||
|
||||
server_cannot_mask:
|
||||
lws_close_reason(wsi,
|
||||
LWS_CLOSE_STATUS_PROTOCOL_ERR,
|
||||
(uint8_t *)"srv mask", 8);
|
||||
|
||||
lwsl_wsi_warn(wsi, "Server must not mask");
|
||||
|
||||
/* kill the connection */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
674
Kinc/Sources/kinc/libs/roles/ws/client-ws.c
Normal file
674
Kinc/Sources/kinc/libs/roles/ws/client-ws.c
Normal file
@ -0,0 +1,674 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.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 = (char)tolower((int)*s);
|
||||
#endif
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
lws_create_client_ws_object(const 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_wsi_notice(wsi, "OOM");
|
||||
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 = (uint8_t)v;
|
||||
|
||||
if (i->allow_reserved_bits)
|
||||
wsi->ws->allow_reserved_bits = 1;
|
||||
|
||||
if (i->allow_unknown_opcode)
|
||||
wsi->ws->allow_unknown_opcode = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
int
|
||||
lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
unsigned char *bufin = *buf;
|
||||
|
||||
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_wsi_debug(wsi, "hs client feels it has %d in", (int)len);
|
||||
|
||||
while (len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
lwsl_wsi_debug(wsi, "caching %ld", (long)len);
|
||||
/*
|
||||
* Since we cached the remaining available input, we
|
||||
* can say we "consumed" it.
|
||||
*
|
||||
* But what about the case where the available input
|
||||
* came out of the rxflow cache already? If we are
|
||||
* effectively "putting it back in the cache", we have
|
||||
* to place it at the cache head, not the tail as usual.
|
||||
*/
|
||||
if (lws_rxflow_cache(wsi, *buf, 0, len) ==
|
||||
LWSRXFC_TRIMMED) {
|
||||
/*
|
||||
* we dealt with it by trimming the existing
|
||||
* rxflow cache HEAD to account for what we used.
|
||||
*
|
||||
* indicate we didn't use anything to the caller
|
||||
* so he doesn't do any consumed processing
|
||||
*/
|
||||
lwsl_wsi_info(wsi, "trimming inside rxflow cache");
|
||||
*buf = bufin;
|
||||
} else
|
||||
*buf += len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
int m;
|
||||
|
||||
lwsl_wsi_info(wsi, "draining ext");
|
||||
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 by studying what
|
||||
* happened to *buf
|
||||
*/
|
||||
|
||||
if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
|
||||
lwsl_wsi_info(wsi, "client_rx_sm exited, DROPPING %d",
|
||||
(int)len);
|
||||
return -1;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
// lwsl_wsi_notice(wsi, "finished with %ld", (long)len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
char *
|
||||
lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1)
|
||||
{
|
||||
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
|
||||
*/
|
||||
if (lws_get_random(wsi->a.context, hash, 16) != 16) {
|
||||
lwsl_wsi_err(wsi, "Unable to read from random dev %s",
|
||||
SYSTEM_RANDOM_FILEPATH);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* coverity[tainted_scalar] */
|
||||
lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
|
||||
|
||||
p += sprintf(p, "Upgrade: websocket\x0d\x0a"
|
||||
"Connection: %sUpgrade\x0d\x0a"
|
||||
"Sec-WebSocket-Key: ", conn1);
|
||||
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->a.vhost->ws.extensions;
|
||||
while (ext && ext->callback) {
|
||||
|
||||
n = wsi->a.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, (unsigned int)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)
|
||||
{
|
||||
struct lws_context *context = wsi->a.context;
|
||||
struct lws_tokenize ts;
|
||||
int n, len, okay = 0;
|
||||
lws_tokenize_elem e;
|
||||
char *p, buf[64];
|
||||
const char *pc;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
struct lws_context_per_thread *pt = &wsi->a.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;
|
||||
int more = 1;
|
||||
char ignore;
|
||||
#endif
|
||||
|
||||
if (wsi->client_mux_substream) {/* !!! client ws-over-h2 not there yet */
|
||||
lwsl_wsi_warn(wsi, "client ws-over-h2 upgrade not supported yet");
|
||||
*cce = "HS: h2 / ws upgrade unsupported";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
if (wsi->http.ah->http_response == 401) {
|
||||
lwsl_wsi_warn(wsi, "got bad HTTP response '%ld'",
|
||||
(long)wsi->http.ah->http_response);
|
||||
*cce = "HS: ws upgrade unauthorized";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
if (wsi->http.ah->http_response != 101) {
|
||||
lwsl_wsi_warn(wsi, "got bad HTTP response '%ld'",
|
||||
(long)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_wsi_info(wsi, "no ACCEPT");
|
||||
*cce = "HS: ACCEPT missing";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
|
||||
if (!p) {
|
||||
lwsl_wsi_info(wsi, "no UPGRADE");
|
||||
*cce = "HS: UPGRADE missing";
|
||||
goto bail3;
|
||||
}
|
||||
strtolower(p);
|
||||
if (strcmp(p, "websocket")) {
|
||||
lwsl_wsi_warn(wsi, "got bad Upgrade header '%s'", p);
|
||||
*cce = "HS: Upgrade to something other than websocket";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
/* connection: must have "upgrade" */
|
||||
|
||||
lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST |
|
||||
LWS_TOKENIZE_F_MINUS_NONTERM);
|
||||
n = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_CONNECTION);
|
||||
if (n <= 0) /* won't fit, or absent */
|
||||
goto bad_conn_format;
|
||||
ts.len = (unsigned int)n;
|
||||
|
||||
do {
|
||||
e = lws_tokenize(&ts);
|
||||
switch (e) {
|
||||
case LWS_TOKZE_TOKEN:
|
||||
if (!strncasecmp(ts.token, "upgrade", ts.token_len))
|
||||
e = LWS_TOKZE_ENDED;
|
||||
break;
|
||||
|
||||
case LWS_TOKZE_DELIMITER:
|
||||
break;
|
||||
|
||||
default: /* includes ENDED found by the tokenizer itself */
|
||||
bad_conn_format:
|
||||
lwsl_wsi_info(wsi, "malformed connection '%s'", buf);
|
||||
*cce = "HS: UPGRADE malformed";
|
||||
goto bail3;
|
||||
}
|
||||
} while (e > 0);
|
||||
|
||||
pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
|
||||
#if defined(_DEBUG)
|
||||
if (!pc)
|
||||
lwsl_wsi_parser(wsi, "lws_client_int_s_hs: no protocol list");
|
||||
else
|
||||
lwsl_wsi_parser(wsi, "lws_client_int_s_hs: protocol list '%s'", pc);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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_wsi_info(wsi, "WSI_TOKEN_PROTOCOL is null");
|
||||
/*
|
||||
* no protocol name to work from, if we don't already have one
|
||||
* default to first protocol
|
||||
*/
|
||||
|
||||
if (wsi->a.protocol) {
|
||||
p = (char *)wsi->a.protocol->name;
|
||||
goto identify_protocol;
|
||||
}
|
||||
|
||||
/* no choice but to use the default protocol */
|
||||
|
||||
n = 0;
|
||||
wsi->a.protocol = &wsi->a.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, (unsigned int)len) &&
|
||||
(pc[len] == ',' || pc[len] == '\0')) {
|
||||
okay = 1;
|
||||
continue;
|
||||
}
|
||||
while (*pc && *pc++ != ',')
|
||||
;
|
||||
while (*pc == ' ')
|
||||
pc++;
|
||||
}
|
||||
|
||||
if (!okay) {
|
||||
lwsl_wsi_info(wsi, "got bad protocol %s", p);
|
||||
*cce = "HS: PROTOCOL malformed";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
identify_protocol:
|
||||
|
||||
#if defined(LWS_WITH_HTTP_PROXY)
|
||||
lws_strncpy(wsi->ws->actual_protocol, p,
|
||||
sizeof(wsi->ws->actual_protocol));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* identify the selected protocol struct and set it
|
||||
*/
|
||||
n = 0;
|
||||
/* keep client connection pre-bound protocol */
|
||||
if (!lwsi_role_client(wsi))
|
||||
wsi->a.protocol = NULL;
|
||||
|
||||
while (n < wsi->a.vhost->count_protocols) {
|
||||
if (!wsi->a.protocol &&
|
||||
strcmp(p, wsi->a.vhost->protocols[n].name) == 0) {
|
||||
wsi->a.protocol = &wsi->a.vhost->protocols[n];
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n == wsi->a.vhost->count_protocols) { /* no match */
|
||||
/* if server, that's already fatal */
|
||||
if (!lwsi_role_client(wsi)) {
|
||||
lwsl_wsi_info(wsi, "fail protocol %s", p);
|
||||
*cce = "HS: Cannot match protocol";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/* for client, find the index of our pre-bound protocol */
|
||||
|
||||
n = 0;
|
||||
while (wsi->a.vhost->protocols[n].callback) {
|
||||
if (wsi->a.protocol && strcmp(wsi->a.protocol->name,
|
||||
wsi->a.vhost->protocols[n].name) == 0) {
|
||||
wsi->a.protocol = &wsi->a.vhost->protocols[n];
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!wsi->a.vhost->protocols[n].callback) {
|
||||
if (wsi->a.protocol)
|
||||
lwsl_wsi_err(wsi, "Failed to match protocol %s",
|
||||
wsi->a.protocol->name);
|
||||
else
|
||||
lwsl_wsi_err(wsi, "No protocol on client");
|
||||
*cce = "ws protocol no match";
|
||||
goto bail2;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_wsi_debug(wsi, "Selected protocol %s", wsi->a.protocol ?
|
||||
wsi->a.protocol->name : "no pcol");
|
||||
|
||||
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_same_vh_protocol_insert(wsi, n);
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/* instantiate the accepted extensions */
|
||||
|
||||
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
|
||||
lwsl_wsi_ext(wsi, "no client extensions allowed by server");
|
||||
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, (int)context->pt_serv_buf_size,
|
||||
WSI_TOKEN_EXTENSIONS) < 0) {
|
||||
lwsl_wsi_warn(wsi, "ext list from server failed to copy");
|
||||
*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_wsi_notice(wsi, "checking client ext %s", ext_name);
|
||||
|
||||
n = 0;
|
||||
ext = wsi->a.vhost->ws.extensions;
|
||||
while (ext && ext->callback) {
|
||||
if (strcmp(ext_name, ext->name)) {
|
||||
ext++;
|
||||
continue;
|
||||
}
|
||||
|
||||
n = 1;
|
||||
lwsl_wsi_notice(wsi, "instantiating client ext %s", 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_wsi_info(wsi, " ext %s failed construction",
|
||||
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->a.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_wsi_err(wsi, "unable to parse user defaults '%s'",
|
||||
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_wsi_err(wsi, "unable to parse remote def '%s'", 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_wsi_err(wsi, "ext %s rejects server options %s",
|
||||
ext->name, a);
|
||||
*cce = "HS: EXT: Rejects server options";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
wsi->ws->count_act_ext++;
|
||||
|
||||
ext++;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
lwsl_wsi_warn(wsi, "Unknown ext '%s'!", 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_wsi_warn(wsi, "lws_client_int_s_hs: accept '%s' wrong vs '%s'", 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_wsi_err(wsi, "Problem allocating wsi user mem");
|
||||
*cce = "HS: OOM";
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/*
|
||||
* we seem to be good to go, give client last chance to check
|
||||
* headers and OK it
|
||||
*/
|
||||
if (wsi->a.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_validity_confirmed(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->a.protocol->rx_buffer_size;
|
||||
if (!n)
|
||||
n = (int)context->pt_serv_buf_size;
|
||||
n += LWS_PRE;
|
||||
wsi->ws->rx_ubuf = lws_malloc((unsigned int)n + 4 /* 0x0000ffff zlib */,
|
||||
"client frame buffer");
|
||||
if (!wsi->ws->rx_ubuf) {
|
||||
lwsl_wsi_err(wsi, "OOM allocating rx buffer %d", n);
|
||||
*cce = "HS: OOM";
|
||||
goto bail2;
|
||||
}
|
||||
wsi->ws->rx_ubuf_alloc = (unsigned int)n;
|
||||
|
||||
lwsl_wsi_debug(wsi, "handshake OK for protocol %s", wsi->a.protocol->name);
|
||||
|
||||
/* call him back to inform him he is up */
|
||||
|
||||
if (wsi->a.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;
|
||||
}
|
||||
@ -0,0 +1,559 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.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 = (int)wsi->a.context->pt_serv_buf_size;
|
||||
if (wsi->a.protocol->rx_buffer_size)
|
||||
n = (int)wsi->a.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] = (unsigned char)extra;
|
||||
lwsl_wsi_info(wsi, " Capping pmd rx to %d", 1 << extra);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char trail[] = { 0, 0, 0xff, 0xff };
|
||||
|
||||
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_ext_pm_deflate_rx_ebufs *pmdrx =
|
||||
(struct lws_ext_pm_deflate_rx_ebufs *)in;
|
||||
struct lws_ext_option_arg *oa;
|
||||
int n, ret = 0, was_fin = 0, m;
|
||||
unsigned int pen = 0;
|
||||
int penbits = 0;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_EXT_CB_NAMED_OPTION_SET:
|
||||
oa = in;
|
||||
if (!oa->option_name)
|
||||
break;
|
||||
lwsl_wsi_ext(wsi, "named option set: %s", oa->option_name);
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options);
|
||||
n++)
|
||||
if (!strcmp(lws_ext_pm_deflate_options[n].name,
|
||||
oa->option_name))
|
||||
break;
|
||||
|
||||
if (n == (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options))
|
||||
break;
|
||||
oa->option_index = n;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LWS_EXT_CB_OPTION_SET:
|
||||
oa = in;
|
||||
lwsl_wsi_ext(wsi, "option set: idx %d, %s, len %d",
|
||||
oa->option_index, oa->start, oa->len);
|
||||
if (oa->start)
|
||||
priv->args[oa->option_index] = (unsigned char)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 = (int)context->pt_serv_buf_size;
|
||||
if (wsi->a.protocol->rx_buffer_size)
|
||||
n = (int)wsi->a.protocol->rx_buffer_size;
|
||||
|
||||
if (n < 128) {
|
||||
lwsl_wsi_info(wsi, " permessage-deflate requires the protocol "
|
||||
"(%s) to have an RX buffer >= 128",
|
||||
wsi->a.protocol->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* fill in **user */
|
||||
priv = lws_zalloc(sizeof(*priv), "pmd priv");
|
||||
*((void **)user) = priv;
|
||||
lwsl_wsi_ext(wsi, "LWS_EXT_CB_*CONSTRUCT");
|
||||
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_wsi_ext(wsi, "LWS_EXT_CB_DESTROY");
|
||||
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:
|
||||
/*
|
||||
* ie, we are INFLATING
|
||||
*/
|
||||
lwsl_wsi_ext(wsi, " LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d",
|
||||
pmdrx->eb_in.len, priv->rx.avail_in);
|
||||
|
||||
/*
|
||||
* If this frame is not marked as compressed,
|
||||
* there is nothing we should do with it
|
||||
*/
|
||||
|
||||
if (!(wsi->ws->rsv_first_msg & 0x40) || (wsi->ws->opcode & 8))
|
||||
/*
|
||||
* This is a bit different than DID_NOTHING... we have
|
||||
* identified using ext-private bits in the packet, or
|
||||
* by it being a control fragment that we SHOULD not do
|
||||
* anything to it, parent should continue as if we
|
||||
* processed it
|
||||
*/
|
||||
return PMDR_NOTHING_WE_SHOULD_DO;
|
||||
|
||||
/*
|
||||
* we shouldn't come back in here if we already applied the
|
||||
* trailer for this compressed packet
|
||||
*/
|
||||
if (!wsi->ws->pmd_trailer_application)
|
||||
return PMDR_DID_NOTHING;
|
||||
|
||||
pmdrx->eb_out.len = 0;
|
||||
|
||||
lwsl_wsi_ext(wsi, "LWS_EXT_CB_PAYLOAD_RX: in %d, "
|
||||
"existing avail in %d, pkt fin: %d",
|
||||
pmdrx->eb_in.len, priv->rx.avail_in, wsi->ws->final);
|
||||
|
||||
/* if needed, initialize the inflator */
|
||||
|
||||
if (!priv->rx_init) {
|
||||
if (inflateInit2(&priv->rx,
|
||||
-priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
|
||||
lwsl_wsi_err(wsi, "iniflateInit failed");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
priv->rx_init = 1;
|
||||
if (!priv->buf_rx_inflated)
|
||||
priv->buf_rx_inflated = lws_malloc(
|
||||
(unsigned int)(LWS_PRE + 7 + 5 +
|
||||
(1 << priv->args[PMD_RX_BUF_PWR2])),
|
||||
"pmd rx inflate buf");
|
||||
if (!priv->buf_rx_inflated) {
|
||||
lwsl_wsi_err(wsi, "OOM");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* don't give us new input while we still work through
|
||||
* the last input
|
||||
*/
|
||||
|
||||
if (priv->rx.avail_in && pmdrx->eb_in.token &&
|
||||
pmdrx->eb_in.len) {
|
||||
lwsl_wsi_warn(wsi, "priv->rx.avail_in %d while getting new in",
|
||||
priv->rx.avail_in);
|
||||
// assert(0);
|
||||
}
|
||||
#endif
|
||||
if (!priv->rx.avail_in && pmdrx->eb_in.token && pmdrx->eb_in.len) {
|
||||
priv->rx.next_in = (unsigned char *)pmdrx->eb_in.token;
|
||||
priv->rx.avail_in = (uInt)pmdrx->eb_in.len;
|
||||
}
|
||||
|
||||
priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
|
||||
pmdrx->eb_out.token = priv->rx.next_out;
|
||||
priv->rx.avail_out = (uInt)(1 << priv->args[PMD_RX_BUF_PWR2]);
|
||||
|
||||
/* so... 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->ws->final &&
|
||||
!wsi->ws->rx_packet_length &&
|
||||
wsi->ws->pmd_trailer_application) {
|
||||
lwsl_wsi_ext(wsi, "trailer apply 1");
|
||||
was_fin = 1;
|
||||
wsi->ws->pmd_trailer_application = 0;
|
||||
priv->rx.next_in = trail;
|
||||
priv->rx.avail_in = sizeof(trail);
|
||||
}
|
||||
|
||||
/*
|
||||
* if after all that there's nothing pending and nothing to give
|
||||
* him right now, bail without having done anything
|
||||
*/
|
||||
|
||||
if (!priv->rx.avail_in)
|
||||
return PMDR_DID_NOTHING;
|
||||
|
||||
n = inflate(&priv->rx, was_fin ? Z_SYNC_FLUSH : Z_NO_FLUSH);
|
||||
lwsl_wsi_ext(wsi, "inflate ret %d, avi %d, avo %d, wsifinal %d", n,
|
||||
priv->rx.avail_in, priv->rx.avail_out, wsi->ws->final);
|
||||
switch (n) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_STREAM_ERROR:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
lwsl_wsi_err(wsi, "zlib error inflate %d: \"%s\"",
|
||||
n, priv->rx.msg);
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
|
||||
/*
|
||||
* track how much input was used, and advance it
|
||||
*/
|
||||
|
||||
pmdrx->eb_in.token = pmdrx->eb_in.token +
|
||||
((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->rx.avail_in);
|
||||
pmdrx->eb_in.len = (int)priv->rx.avail_in;
|
||||
|
||||
lwsl_wsi_debug(wsi, "%d %d %d %d %d",
|
||||
priv->rx.avail_in,
|
||||
wsi->ws->final,
|
||||
(int)wsi->ws->rx_packet_length,
|
||||
was_fin,
|
||||
wsi->ws->pmd_trailer_application);
|
||||
|
||||
if (!priv->rx.avail_in &&
|
||||
wsi->ws->final &&
|
||||
!wsi->ws->rx_packet_length &&
|
||||
!was_fin &&
|
||||
wsi->ws->pmd_trailer_application) {
|
||||
lwsl_wsi_ext(wsi, "RX trailer apply 2");
|
||||
|
||||
/* we overallocated just for this situation where
|
||||
* we might issue something */
|
||||
priv->rx.avail_out += 5;
|
||||
|
||||
was_fin = 1;
|
||||
wsi->ws->pmd_trailer_application = 0;
|
||||
priv->rx.next_in = trail;
|
||||
priv->rx.avail_in = sizeof(trail);
|
||||
n = inflate(&priv->rx, Z_SYNC_FLUSH);
|
||||
lwsl_wsi_ext(wsi, "RX trailer infl ret %d, avi %d, avo %d",
|
||||
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_wsi_info(wsi, "zlib error inflate %d: %s",
|
||||
n, priv->rx.msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(priv->rx.avail_out);
|
||||
}
|
||||
|
||||
pmdrx->eb_out.len = lws_ptr_diff(priv->rx.next_out,
|
||||
pmdrx->eb_out.token);
|
||||
priv->count_rx_between_fin = priv->count_rx_between_fin + (size_t)pmdrx->eb_out.len;
|
||||
|
||||
lwsl_wsi_ext(wsi, " RX leaving with new effbuff len %d, "
|
||||
"rx.avail_in=%d, TOTAL RX since FIN %lu",
|
||||
pmdrx->eb_out.len, priv->rx.avail_in,
|
||||
(unsigned long)priv->count_rx_between_fin);
|
||||
|
||||
if (was_fin) {
|
||||
lwsl_wsi_ext(wsi, "was_fin");
|
||||
priv->count_rx_between_fin = 0;
|
||||
if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
|
||||
lwsl_wsi_ext(wsi, "PMD_SERVER_NO_CONTEXT_TAKEOVER");
|
||||
(void)inflateEnd(&priv->rx);
|
||||
priv->rx_init = 0;
|
||||
}
|
||||
|
||||
return PMDR_EMPTY_FINAL;
|
||||
}
|
||||
|
||||
if (priv->rx.avail_in)
|
||||
return PMDR_HAS_PENDING;
|
||||
|
||||
return PMDR_EMPTY_NONFINAL;
|
||||
|
||||
case LWS_EXT_CB_PAYLOAD_TX:
|
||||
|
||||
/*
|
||||
* ie, we are DEFLATING
|
||||
*
|
||||
* initialize us if needed
|
||||
*/
|
||||
|
||||
if (!priv->tx_init) {
|
||||
n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
|
||||
Z_DEFLATED,
|
||||
-priv->args[PMD_SERVER_MAX_WINDOW_BITS +
|
||||
(wsi->a.vhost->listen_port <= 0)],
|
||||
priv->args[PMD_MEM_LEVEL],
|
||||
Z_DEFAULT_STRATEGY);
|
||||
if (n != Z_OK) {
|
||||
lwsl_wsi_ext(wsi, "inflateInit2 failed %d", n);
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
priv->tx_init = 1;
|
||||
}
|
||||
|
||||
if (!priv->buf_tx_deflated)
|
||||
priv->buf_tx_deflated = lws_malloc((unsigned int)(LWS_PRE + 7 + 5 +
|
||||
(1 << priv->args[PMD_TX_BUF_PWR2])),
|
||||
"pmd tx deflate buf");
|
||||
if (!priv->buf_tx_deflated) {
|
||||
lwsl_wsi_err(wsi, "OOM");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
|
||||
/* hook us up with any deflated input that the caller has */
|
||||
|
||||
if (pmdrx->eb_in.token) {
|
||||
|
||||
assert(!priv->tx.avail_in);
|
||||
|
||||
priv->count_tx_between_fin = priv->count_tx_between_fin + (size_t)pmdrx->eb_in.len;
|
||||
lwsl_wsi_ext(wsi, "TX: eb_in length %d, "
|
||||
"TOTAL TX since FIN: %d",
|
||||
pmdrx->eb_in.len,
|
||||
(int)priv->count_tx_between_fin);
|
||||
priv->tx.next_in = (unsigned char *)pmdrx->eb_in.token;
|
||||
priv->tx.avail_in = (uInt)pmdrx->eb_in.len;
|
||||
}
|
||||
|
||||
priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
|
||||
pmdrx->eb_out.token = priv->tx.next_out;
|
||||
priv->tx.avail_out = (uInt)(1 << priv->args[PMD_TX_BUF_PWR2]);
|
||||
|
||||
pen = 0;
|
||||
penbits = 0;
|
||||
deflatePending(&priv->tx, &pen, &penbits);
|
||||
pen = pen | (unsigned int)penbits;
|
||||
|
||||
if (!priv->tx.avail_in && (len & LWS_WRITE_NO_FIN)) {
|
||||
lwsl_wsi_ext(wsi, "no available in, pen: %u", pen);
|
||||
|
||||
if (!pen)
|
||||
return PMDR_DID_NOTHING;
|
||||
}
|
||||
|
||||
m = Z_NO_FLUSH;
|
||||
if (!(len & LWS_WRITE_NO_FIN)) {
|
||||
lwsl_wsi_ext(wsi, "deflate with SYNC_FLUSH, pkt len %d",
|
||||
(int)wsi->ws->rx_packet_length);
|
||||
m = Z_SYNC_FLUSH;
|
||||
}
|
||||
|
||||
n = deflate(&priv->tx, m);
|
||||
if (n == Z_STREAM_ERROR) {
|
||||
lwsl_wsi_notice(wsi, "Z_STREAM_ERROR");
|
||||
return PMDR_FAILED;
|
||||
}
|
||||
|
||||
pen = (!priv->tx.avail_out) && n != Z_STREAM_END;
|
||||
|
||||
lwsl_wsi_ext(wsi, "deflate ret %d, len 0x%x", n,
|
||||
(unsigned int)len);
|
||||
|
||||
if ((len & 0xf) == LWS_WRITE_TEXT)
|
||||
priv->tx_first_frame_type = LWSWSOPC_TEXT_FRAME;
|
||||
if ((len & 0xf) == LWS_WRITE_BINARY)
|
||||
priv->tx_first_frame_type = LWSWSOPC_BINARY_FRAME;
|
||||
|
||||
pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
|
||||
pmdrx->eb_out.token);
|
||||
|
||||
if (m == Z_SYNC_FLUSH && !(len & LWS_WRITE_NO_FIN) && !pen &&
|
||||
pmdrx->eb_out.len < 4) {
|
||||
lwsl_wsi_err(wsi, "FAIL want to trim out length %d",
|
||||
(int)pmdrx->eb_out.len);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (!(len & LWS_WRITE_NO_FIN) &&
|
||||
m == Z_SYNC_FLUSH &&
|
||||
!pen &&
|
||||
pmdrx->eb_out.len >= 4) {
|
||||
// lwsl_wsi_err(wsi, "Trimming 4 from end of write");
|
||||
priv->tx.next_out -= 4;
|
||||
priv->tx.avail_out += 4;
|
||||
priv->count_tx_between_fin = 0;
|
||||
|
||||
assert(priv->tx.next_out[0] == 0x00 &&
|
||||
priv->tx.next_out[1] == 0x00 &&
|
||||
priv->tx.next_out[2] == 0xff &&
|
||||
priv->tx.next_out[3] == 0xff);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* track how much input was used and advance it
|
||||
*/
|
||||
|
||||
pmdrx->eb_in.token = pmdrx->eb_in.token +
|
||||
((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->tx.avail_in);
|
||||
pmdrx->eb_in.len = (int)priv->tx.avail_in;
|
||||
|
||||
priv->compressed_out = 1;
|
||||
pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
|
||||
pmdrx->eb_out.token);
|
||||
|
||||
lwsl_wsi_ext(wsi, " TX rewritten with new eb_in len %d, "
|
||||
"eb_out len %d, deflatePending %d",
|
||||
pmdrx->eb_in.len, pmdrx->eb_out.len, pen);
|
||||
|
||||
if (pmdrx->eb_in.len || pen)
|
||||
return PMDR_HAS_PENDING;
|
||||
|
||||
if (!(len & LWS_WRITE_NO_FIN))
|
||||
return PMDR_EMPTY_FINAL;
|
||||
|
||||
return PMDR_EMPTY_NONFINAL;
|
||||
|
||||
case LWS_EXT_CB_PACKET_TX_PRESEND:
|
||||
if (!priv->compressed_out)
|
||||
break;
|
||||
priv->compressed_out = 0;
|
||||
|
||||
/*
|
||||
* we may have not produced any output for the actual "first"
|
||||
* write... in that case, we need to fix up the inappropriate
|
||||
* use of CONTINUATION when the first real write does come.
|
||||
*/
|
||||
if (priv->tx_first_frame_type & 0xf) {
|
||||
*pmdrx->eb_in.token = (unsigned char)((((unsigned char)*pmdrx->eb_in.token) & (unsigned char)~0xf) |
|
||||
((unsigned char)priv->tx_first_frame_type & (unsigned char)0xf));
|
||||
/*
|
||||
* We have now written the "first" fragment, only
|
||||
* do that once
|
||||
*/
|
||||
priv->tx_first_frame_type = 0;
|
||||
}
|
||||
|
||||
n = *(pmdrx->eb_in.token) & 15;
|
||||
|
||||
/* set RSV1, but not on CONTINUATION */
|
||||
if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
|
||||
*pmdrx->eb_in.token |= 0x40;
|
||||
|
||||
lwsl_wsi_ext(wsi, "PRESEND compressed: ws frame 0x%02X, len %d",
|
||||
((*pmdrx->eb_in.token) & 0xff),
|
||||
pmdrx->eb_in.len);
|
||||
|
||||
if (((*pmdrx->eb_in.token) & 0x80) && /* fin */
|
||||
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
|
||||
lwsl_wsi_debug(wsi, "PMD_CLIENT_NO_CONTEXT_TAKEOVER");
|
||||
(void)deflateEnd(&priv->tx);
|
||||
priv->tx_init = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if defined(LWS_WITH_MINIZ)
|
||||
#include <miniz.h>
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#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 */
|
||||
|
||||
unsigned char *buf_tx_holding;
|
||||
|
||||
size_t count_rx_between_fin;
|
||||
size_t count_tx_between_fin;
|
||||
|
||||
size_t len_tx_holding;
|
||||
|
||||
unsigned char args[PMD_ARG_COUNT];
|
||||
|
||||
unsigned char tx_first_frame_type;
|
||||
|
||||
unsigned char tx_init:1;
|
||||
unsigned char rx_init:1;
|
||||
unsigned char compressed_out:1;
|
||||
};
|
||||
|
||||
382
Kinc/Sources/kinc/libs/roles/ws/ext/extension.c
Normal file
382
Kinc/Sources/kinc/libs/roles/ws/ext/extension.c
Normal file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
#include "extension-permessage-deflate.h"
|
||||
|
||||
void
|
||||
lws_context_init_extensions(const struct lws_context_creation_info *info,
|
||||
struct lws_context *context)
|
||||
{
|
||||
lwsl_cx_info(context, " LWS_MAX_EXTENSIONS_ACTIVE: %u", 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
|
||||
};
|
||||
|
||||
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_wsi_ext(wsi, "'%c' %d", *in, leap);
|
||||
switch (leap) {
|
||||
case LEAPS_SEEK_NAME:
|
||||
if (*in == ' ')
|
||||
break;
|
||||
if (*in == ',') {
|
||||
len = 1;
|
||||
break;
|
||||
}
|
||||
match_map = (unsigned int)(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)) {
|
||||
m >>= 1;
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
lwsl_wsi_ext(wsi, " m=%d, n=%d, w=%d", m, n, w);
|
||||
|
||||
if (*in == opts[n].name[w]) {
|
||||
if (!opts[n].name[w + 1]) {
|
||||
oa.option_index = (int)n;
|
||||
lwsl_wsi_ext(wsi, "hit %d",
|
||||
oa.option_index);
|
||||
leap = LEAPS_SEEK_VAL;
|
||||
if (len == 1)
|
||||
goto set_arg;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
match_map &= (unsigned int)~(1 << n);
|
||||
if (!match_map) {
|
||||
lwsl_wsi_ext(wsi, "empty match map");
|
||||
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 = lws_ptr_diff(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;
|
||||
|
||||
if (!wsi->ws)
|
||||
return 0;
|
||||
|
||||
for (n = 0; n < wsi->ws->count_act_ext; n++) {
|
||||
m = wsi->ws->active_extensions[n]->callback(
|
||||
lws_get_context(wsi), wsi->ws->active_extensions[n],
|
||||
wsi, (enum lws_extension_callback_reasons)reason, wsi->ws->act_ext_user[n], arg, (size_t)len);
|
||||
if (m < 0) {
|
||||
lwsl_wsi_ext(wsi, "Ext '%s' failed to handle callback %d!",
|
||||
wsi->ws->active_extensions[n]->name, reason);
|
||||
return -1;
|
||||
}
|
||||
/* valgrind... */
|
||||
if (reason == LWS_EXT_CB_DESTROY)
|
||||
wsi->ws->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->a.vhost || !wsi->ws)
|
||||
return 0;
|
||||
|
||||
ext = wsi->a.vhost->ws.extensions;
|
||||
|
||||
while (ext && ext->callback && !handled) {
|
||||
m = ext->callback(context, ext, wsi, (enum lws_extension_callback_reasons)reason,
|
||||
(void *)(lws_intptr_t)n, arg, (size_t)len);
|
||||
if (m < 0) {
|
||||
lwsl_wsi_ext(wsi, "Ext '%s' failed to handle callback %d!",
|
||||
wsi->ws->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 ebuf;
|
||||
int ret, m, n = 0;
|
||||
|
||||
ebuf.token = buf;
|
||||
ebuf.len = (int)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,
|
||||
&ebuf, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
if (m) /* handled */
|
||||
ret = 1;
|
||||
|
||||
if (buf != ebuf.token)
|
||||
/*
|
||||
* extension recreated it:
|
||||
* need to buffer this if not all sent
|
||||
*/
|
||||
wsi->ws->clean_buffer = 0;
|
||||
|
||||
/* assuming they left us something to send, send it */
|
||||
|
||||
if (ebuf.len) {
|
||||
n = lws_issue_raw(wsi, ebuf.token, (size_t)ebuf.len);
|
||||
if (n < 0) {
|
||||
lwsl_wsi_info(wsi, "closing from ext access");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* always either sent it all or privately buffered */
|
||||
if (wsi->ws->clean_buffer)
|
||||
len = (size_t)n;
|
||||
|
||||
lwsl_wsi_ext(wsi, "written %d bytes to client", n);
|
||||
}
|
||||
|
||||
/* no extension has more to spill? Then we can go */
|
||||
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
/* we used up what we had */
|
||||
|
||||
ebuf.token = NULL;
|
||||
ebuf.len = 0;
|
||||
|
||||
/*
|
||||
* Did that leave the pipe choked?
|
||||
* Or we had to hold on to some of it?
|
||||
*/
|
||||
|
||||
if (!lws_send_pipe_choked(wsi) && !lws_has_buffered_out(wsi))
|
||||
/* no we could add more, lets's do that */
|
||||
continue;
|
||||
|
||||
lwsl_wsi_debug(wsi, "choked");
|
||||
|
||||
/*
|
||||
* 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->ws->extension_data_pending = 1;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return (int)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->a.context;
|
||||
int n, handled = 0;
|
||||
|
||||
if (!wsi->ws)
|
||||
return 0;
|
||||
|
||||
/* maybe an extension will take care of it for us */
|
||||
|
||||
for (n = 0; n < wsi->ws->count_act_ext && !handled; n++) {
|
||||
if (!wsi->ws->active_extensions[n]->callback)
|
||||
continue;
|
||||
|
||||
handled |= wsi->ws->active_extensions[n]->callback(context,
|
||||
wsi->ws->active_extensions[n], wsi,
|
||||
r, wsi->ws->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;
|
||||
|
||||
if (!wsi->ws)
|
||||
return 0;
|
||||
|
||||
/* first identify if the ext is active on this wsi */
|
||||
while (idx < wsi->ws->count_act_ext &&
|
||||
strcmp(wsi->ws->active_extensions[idx]->name, ext_name))
|
||||
idx++;
|
||||
|
||||
if (idx == wsi->ws->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->ws->active_extensions[idx]->callback(wsi->a.context,
|
||||
wsi->ws->active_extensions[idx], wsi,
|
||||
LWS_EXT_CB_NAMED_OPTION_SET, wsi->ws->act_ext_user[idx],
|
||||
&oa, 0);
|
||||
}
|
||||
2144
Kinc/Sources/kinc/libs/roles/ws/ops-ws.c
Normal file
2144
Kinc/Sources/kinc/libs/roles/ws/ops-ws.c
Normal file
File diff suppressed because it is too large
Load Diff
202
Kinc/Sources/kinc/libs/roles/ws/private-lib-roles-ws.h
Normal file
202
Kinc/Sources/kinc/libs/roles/ws/private-lib-roles-ws.h
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* This is included from private-lib-core.h if LWS_ROLE_WS
|
||||
*/
|
||||
|
||||
extern const 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
|
||||
|
||||
#define PAYLOAD_BUF_SIZE 128 - 3 + LWS_PRE
|
||||
|
||||
struct _lws_websocket_related {
|
||||
unsigned 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
|
||||
|
||||
#if defined(LWS_WITH_HTTP_PROXY)
|
||||
struct lws_dll2_owner proxy_owner;
|
||||
char actual_protocol[16];
|
||||
size_t proxy_buffered;
|
||||
#endif
|
||||
|
||||
/* Also used for close content... control opcode == < 128 */
|
||||
uint8_t ping_payload_buf[PAYLOAD_BUF_SIZE];
|
||||
uint8_t pong_payload_buf[PAYLOAD_BUF_SIZE];
|
||||
|
||||
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 pong_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;
|
||||
unsigned int pmd_trailer_application:1;
|
||||
#endif
|
||||
|
||||
uint8_t mask[4];
|
||||
|
||||
size_t rx_packet_length;
|
||||
uint32_t rx_ubuf_head;
|
||||
uint32_t rx_ubuf_alloc;
|
||||
|
||||
uint8_t pong_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;
|
||||
uint8_t allow_reserved_bits;
|
||||
uint8_t allow_unknown_opcode;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
uint8_t count_act_ext;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* we need to separately track what's happening with both compressed rx in
|
||||
* and with inflated rx out that will be passed to the user code
|
||||
*/
|
||||
|
||||
struct lws_ext_pm_deflate_rx_ebufs {
|
||||
struct lws_tokens eb_in;
|
||||
struct lws_tokens eb_out;
|
||||
};
|
||||
|
||||
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);
|
||||
LWS_EXTERN int
|
||||
lws_extension_server_handshake(struct lws *wsi, char **p, int budget);
|
||||
#endif
|
||||
|
||||
int
|
||||
handshake_0405(struct lws_context *context, struct lws *wsi);
|
||||
int
|
||||
lws_process_ws_upgrade(struct lws *wsi);
|
||||
|
||||
int
|
||||
lws_process_ws_upgrade2(struct lws *wsi);
|
||||
|
||||
extern const struct lws_protocols lws_ws_proxy;
|
||||
|
||||
int
|
||||
lws_server_init_wsi_for_ws(struct lws *wsi);
|
||||
|
||||
void
|
||||
lws_sul_wsping_cb(lws_sorted_usec_list_t *sul);
|
||||
1108
Kinc/Sources/kinc/libs/roles/ws/server-ws.c
Normal file
1108
Kinc/Sources/kinc/libs/roles/ws/server-ws.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user