mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-30 00:00:16 +01:00

This adds a per-streamtype JSON mapping table in the policy. In addition to the previous flow, it lets you generate custom SS state notifications for specific http response codes, eg: "http_resp_map": [ { "530": 1530 }, { "531": 1531 } ], It's not recommended to overload the transport-layer response code with application layer responses. It's better to return a 200 and then in the application protocol inside http, explain what happened from the application perspective, usually with JSON. But this is designed to let you handle existing systems that do overload the transport layer response code. SS states for user use start at LWSSSCS_USER_BASE, which is 1000. You can do a basic test with minimal-secure-streams and --respmap flag, this will go to httpbin.org and get a 404, and the warmcat.com policy has the mapping for 404 -> LWSSSCS_USER_BASE (1000). Since the mapping emits states, these are serialized and handled like any other state in the proxy case. The policy2c example / tool is also updated to handle the additional mapping tables.
301 lines
8.8 KiB
C
301 lines
8.8 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/** \defgroup lejp JSON parser
|
|
* ##JSON parsing related functions
|
|
* \ingroup lwsapi
|
|
*
|
|
* LEJP is an extremely lightweight JSON stream parser included in lws.
|
|
*/
|
|
//@{
|
|
struct lejp_ctx;
|
|
|
|
#if !defined(LWS_ARRAY_SIZE)
|
|
#define LWS_ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
|
|
#endif
|
|
#define LEJP_FLAG_WS_KEEP 64
|
|
#define LEJP_FLAG_WS_COMMENTLINE 32
|
|
|
|
enum lejp_states {
|
|
LEJP_IDLE = 0,
|
|
LEJP_MEMBERS = 1,
|
|
LEJP_M_P = 2,
|
|
LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3,
|
|
LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4,
|
|
LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5,
|
|
LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6,
|
|
LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7,
|
|
LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8,
|
|
LEJP_MP_DELIM = 9,
|
|
LEJP_MP_VALUE = 10,
|
|
LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11,
|
|
LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12,
|
|
LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13,
|
|
LEJP_MP_COMMA_OR_END = 14,
|
|
LEJP_MP_ARRAY_END = 15,
|
|
};
|
|
|
|
enum lejp_reasons {
|
|
LEJP_CONTINUE = -1,
|
|
LEJP_REJECT_IDLE_NO_BRACE = -2,
|
|
LEJP_REJECT_MEMBERS_NO_CLOSE = -3,
|
|
LEJP_REJECT_MP_NO_OPEN_QUOTE = -4,
|
|
LEJP_REJECT_MP_STRING_UNDERRUN = -5,
|
|
LEJP_REJECT_MP_ILLEGAL_CTRL = -6,
|
|
LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7,
|
|
LEJP_REJECT_ILLEGAL_HEX = -8,
|
|
LEJP_REJECT_MP_DELIM_MISSING_COLON = -9,
|
|
LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10,
|
|
LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11,
|
|
LEJP_REJECT_MP_VAL_NUM_FORMAT = -12,
|
|
LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13,
|
|
LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14,
|
|
LEJP_REJECT_MP_C_OR_E_UNDERF = -15,
|
|
LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16,
|
|
LEJP_REJECT_MP_ARRAY_END_MISSING = -17,
|
|
LEJP_REJECT_STACK_OVERFLOW = -18,
|
|
LEJP_REJECT_MP_DELIM_ISTACK = -19,
|
|
LEJP_REJECT_NUM_TOO_LONG = -20,
|
|
LEJP_REJECT_MP_C_OR_E_NEITHER = -21,
|
|
LEJP_REJECT_UNKNOWN = -22,
|
|
LEJP_REJECT_CALLBACK = -23
|
|
};
|
|
|
|
#define LEJP_FLAG_CB_IS_VALUE 64
|
|
|
|
enum lejp_callbacks {
|
|
LEJPCB_CONSTRUCTED = 0,
|
|
LEJPCB_DESTRUCTED = 1,
|
|
|
|
LEJPCB_START = 2,
|
|
LEJPCB_COMPLETE = 3,
|
|
LEJPCB_FAILED = 4,
|
|
|
|
LEJPCB_PAIR_NAME = 5,
|
|
|
|
LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6,
|
|
LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7,
|
|
LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8,
|
|
LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9,
|
|
LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10,
|
|
LEJPCB_VAL_STR_START = 11, /* notice handle separately */
|
|
LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12,
|
|
LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13,
|
|
|
|
LEJPCB_ARRAY_START = 14,
|
|
LEJPCB_ARRAY_END = 15,
|
|
|
|
LEJPCB_OBJECT_START = 16,
|
|
LEJPCB_OBJECT_END = 17,
|
|
};
|
|
|
|
/**
|
|
* _lejp_callback() - User parser actions
|
|
* \param ctx: LEJP context
|
|
* \param reason: Callback reason
|
|
*
|
|
* Your user callback is associated with the context at construction time,
|
|
* and receives calls as the parsing progresses.
|
|
*
|
|
* All of the callbacks may be ignored and just return 0.
|
|
*
|
|
* The reasons it might get called, found in @reason, are:
|
|
*
|
|
* LEJPCB_CONSTRUCTED: The context was just constructed... you might want to
|
|
* perform one-time allocation for the life of the context.
|
|
*
|
|
* LEJPCB_DESTRUCTED: The context is being destructed... if you made any
|
|
* allocations at construction-time, you can free them now
|
|
*
|
|
* LEJPCB_START: Parsing is beginning at the first byte of input
|
|
*
|
|
* LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or
|
|
* positive return code from lejp_parse indicating the
|
|
* amount of unused bytes left in the input buffer
|
|
*
|
|
* LEJPCB_FAILED: Parsing failed. You'll get a negative error code
|
|
* returned from lejp_parse
|
|
*
|
|
* LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed,
|
|
* this callback occurs. You can find the new name at
|
|
* the end of ctx->path[]
|
|
*
|
|
* LEJPCB_VAL_TRUE: The "true" value appeared
|
|
*
|
|
* LEJPCB_VAL_FALSE: The "false" value appeared
|
|
*
|
|
* LEJPCB_VAL_NULL: The "null" value appeared
|
|
*
|
|
* LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf
|
|
*
|
|
* LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf
|
|
*
|
|
* LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet
|
|
*
|
|
* LEJPCB_VAL_STR_CHUNK: We filled the string buffer in the ctx, but it's not
|
|
* the end of the string. We produce this to spill the
|
|
* intermediate buffer to the user code, so we can handle
|
|
* huge JSON strings using only the small buffer in the
|
|
* ctx. If the whole JSON string fits in the ctx buffer,
|
|
* you won't get these callbacks.
|
|
*
|
|
* LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the
|
|
* string is in ctx->buf.
|
|
*
|
|
* LEJPCB_ARRAY_START: An array started
|
|
*
|
|
* LEJPCB_ARRAY_END: An array ended
|
|
*
|
|
* LEJPCB_OBJECT_START: An object started
|
|
*
|
|
* LEJPCB_OBJECT_END: An object ended
|
|
*/
|
|
LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason);
|
|
|
|
typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason);
|
|
|
|
#ifndef LEJP_MAX_PARSING_STACK_DEPTH
|
|
#define LEJP_MAX_PARSING_STACK_DEPTH 5
|
|
#endif
|
|
#ifndef LEJP_MAX_DEPTH
|
|
#define LEJP_MAX_DEPTH 12
|
|
#endif
|
|
#ifndef LEJP_MAX_INDEX_DEPTH
|
|
#define LEJP_MAX_INDEX_DEPTH 6
|
|
#endif
|
|
#ifndef LEJP_MAX_PATH
|
|
#define LEJP_MAX_PATH 128
|
|
#endif
|
|
#ifndef LEJP_STRING_CHUNK
|
|
/* must be >= 30 to assemble floats */
|
|
#define LEJP_STRING_CHUNK 254
|
|
#endif
|
|
|
|
enum num_flags {
|
|
LEJP_SEEN_MINUS = (1 << 0),
|
|
LEJP_SEEN_POINT = (1 << 1),
|
|
LEJP_SEEN_POST_POINT = (1 << 2),
|
|
LEJP_SEEN_EXP = (1 << 3)
|
|
};
|
|
|
|
struct _lejp_stack {
|
|
char s; /* lejp_state stack*/
|
|
char p; /* path length */
|
|
char i; /* index array length */
|
|
char b; /* user bitfield */
|
|
};
|
|
|
|
struct _lejp_parsing_stack {
|
|
void *user; /* private to the stack level */
|
|
signed char (*callback)(struct lejp_ctx *ctx, char reason);
|
|
const char * const *paths;
|
|
uint8_t count_paths;
|
|
uint8_t ppos;
|
|
uint8_t path_match;
|
|
};
|
|
|
|
struct lejp_ctx {
|
|
|
|
/* sorted by type for most compact alignment
|
|
*
|
|
* pointers
|
|
*/
|
|
void *user;
|
|
|
|
/* arrays */
|
|
|
|
struct _lejp_parsing_stack pst[LEJP_MAX_PARSING_STACK_DEPTH];
|
|
struct _lejp_stack st[LEJP_MAX_DEPTH];
|
|
uint16_t i[LEJP_MAX_INDEX_DEPTH]; /* index array */
|
|
uint16_t wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
|
|
char path[LEJP_MAX_PATH];
|
|
char buf[LEJP_STRING_CHUNK + 1];
|
|
|
|
/* size_t */
|
|
|
|
size_t path_stride; /* 0 means default ptr size, else stride */
|
|
|
|
/* int */
|
|
|
|
uint32_t line;
|
|
|
|
/* short */
|
|
|
|
uint16_t uni;
|
|
|
|
/* char */
|
|
|
|
uint8_t npos;
|
|
uint8_t dcount;
|
|
uint8_t f;
|
|
uint8_t sp; /* stack head */
|
|
uint8_t ipos; /* index stack depth */
|
|
uint8_t count_paths;
|
|
uint8_t path_match;
|
|
uint8_t path_match_len;
|
|
uint8_t wildcount;
|
|
uint8_t pst_sp; /* parsing stack head */
|
|
uint8_t outer_array;
|
|
};
|
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
lejp_construct(struct lejp_ctx *ctx,
|
|
signed char (*callback)(struct lejp_ctx *ctx, char reason),
|
|
void *user, const char * const *paths, unsigned char paths_count);
|
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
lejp_destruct(struct lejp_ctx *ctx);
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len);
|
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
lejp_change_callback(struct lejp_ctx *ctx,
|
|
signed char (*callback)(struct lejp_ctx *ctx, char reason));
|
|
|
|
/*
|
|
* push the current paths / paths_count and lejp_cb to a stack in the ctx, and
|
|
* start using the new ones
|
|
*/
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
lejp_parser_push(struct lejp_ctx *ctx, void *user, const char * const *paths,
|
|
unsigned char paths_count, lejp_callback lejp_cb);
|
|
|
|
/*
|
|
* pop the previously used paths / paths_count and lejp_cb, and continue
|
|
* parsing using those as before
|
|
*/
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
lejp_parser_pop(struct lejp_ctx *ctx);
|
|
|
|
/* exported for use when reevaluating a path for use with a subcontext */
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
lejp_check_path_match(struct lejp_ctx *ctx);
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);
|
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
lejp_error_to_string(int e);
|
|
//@}
|