Update Files
This commit is contained in:
96
Kinc/Sources/kinc/libs/core/http/CMakeLists.txt
Normal file
96
Kinc/Sources/kinc/libs/core/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/core/http/client/client-http.c
Normal file
2097
Kinc/Sources/kinc/libs/core/http/client/client-http.c
Normal file
File diff suppressed because it is too large
Load Diff
17
Kinc/Sources/kinc/libs/core/http/compression/README.md
Normal file
17
Kinc/Sources/kinc/libs/core/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/core/http/compression/brotli/brotli.c
Normal file
124
Kinc/Sources/kinc/libs/core/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/core/http/compression/deflate/deflate.c
Normal file
113
Kinc/Sources/kinc/libs/core/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/core/http/compression/stream.c
Normal file
227
Kinc/Sources/kinc/libs/core/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/core/http/cookie.c
Normal file
735
Kinc/Sources/kinc/libs/core/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/core/http/date.c
Normal file
225
Kinc/Sources/kinc/libs/core/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/core/http/header.c
Normal file
666
Kinc/Sources/kinc/libs/core/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/core/http/lextable-strings.h
Normal file
136
Kinc/Sources/kinc/libs/core/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/core/http/lextable.h
Normal file
6896
Kinc/Sources/kinc/libs/core/http/lextable.h
Normal file
File diff suppressed because it is too large
Load Diff
500
Kinc/Sources/kinc/libs/core/http/minilex.c
Normal file
500
Kinc/Sources/kinc/libs/core/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/core/http/parsers.c
Normal file
1726
Kinc/Sources/kinc/libs/core/http/parsers.c
Normal file
File diff suppressed because it is too large
Load Diff
355
Kinc/Sources/kinc/libs/core/http/private-lib-roles-http.h
Normal file
355
Kinc/Sources/kinc/libs/core/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/core/http/server/access-log.c
Normal file
209
Kinc/Sources/kinc/libs/core/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/core/http/server/fops-zip.c
Normal file
657
Kinc/Sources/kinc/libs/core/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/core/http/server/lejp-conf.c
Normal file
1112
Kinc/Sources/kinc/libs/core/http/server/lejp-conf.c
Normal file
File diff suppressed because it is too large
Load Diff
725
Kinc/Sources/kinc/libs/core/http/server/lws-spa.c
Normal file
725
Kinc/Sources/kinc/libs/core/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/core/http/server/ranges.c
Normal file
214
Kinc/Sources/kinc/libs/core/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/core/http/server/rewrite.c
Normal file
55
Kinc/Sources/kinc/libs/core/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/core/http/server/server.c
Normal file
3511
Kinc/Sources/kinc/libs/core/http/server/server.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user