Use minimal server echo example instead.feature/data-structs
@@ -70,7 +70,7 @@ add_executable( | |||||
src/packages/game/input/IZ_midi.h | src/packages/game/input/IZ_midi.h | ||||
src/packages/game/data/IZ_list.c | src/packages/game/data/IZ_list.c | ||||
src/packages/game/data/IZ_list.h | src/packages/game/data/IZ_list.h | ||||
src/packages/game/network/IZ_wsclient.c src/packages/game/network/IZ_wsclient.h) | |||||
src/packages/game/network/IZ_wsclient.c src/packages/game/network/IZ_wsclient.h src/packages/game/log/IZ_log.c src/packages/game/log/IZ_log.h) | |||||
target_link_libraries( | target_link_libraries( | ||||
game | game | ||||
@@ -170,11 +170,15 @@ add_executable( | |||||
dependencies/minIni/dev/minIni.h | dependencies/minIni/dev/minIni.h | ||||
dependencies/minIni/dev/minIni.c | dependencies/minIni/dev/minIni.c | ||||
src/packages/server/IZ_common.h | src/packages/server/IZ_common.h | ||||
src/packages/server/log/IZ_log.h | |||||
src/packages/server/log/IZ_log.c | |||||
src/packages/server/main.c | src/packages/server/main.c | ||||
src/packages/server/protocol_lws_minimal.c) | |||||
src/packages/server/protocol_lws_minimal_server_echo.c src/packages/server/IZ_app.c src/packages/server/IZ_app.h) | |||||
target_link_libraries( | target_link_libraries( | ||||
server | server | ||||
SDL2main | |||||
SDL2 | |||||
libcrypto | libcrypto | ||||
libssl | libssl | ||||
websockets | websockets | ||||
@@ -0,0 +1,24 @@ | |||||
#include "IZ_log.h" | |||||
void IZ_LogHandleFromWS(i32 level, const char* line) { | |||||
switch (level) { | |||||
case LLL_ERR: | |||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "%s", line); | |||||
return; | |||||
case LLL_WARN: | |||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "%s", line); | |||||
return; | |||||
case LLL_NOTICE: | |||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s", line); | |||||
return; | |||||
case LLL_USER: | |||||
default: | |||||
break; | |||||
} | |||||
SDL_Log("%s", line); | |||||
} | |||||
void IZ_LogInterceptWSMessages(i32 level) { | |||||
lws_set_log_level(level, IZ_LogHandleFromWS); | |||||
} |
@@ -0,0 +1,10 @@ | |||||
#ifndef IZ_LOG_H | |||||
#define IZ_LOG_H | |||||
#include <SDL_log.h> | |||||
#include <libwebsockets.h> | |||||
#include "../IZ_common.h" | |||||
void IZ_LogInterceptWSMessages(i32); | |||||
#endif |
@@ -0,0 +1,57 @@ | |||||
#include "IZ_app.h" | |||||
void IZ_AppHandleSignal(i32 _signal) { | |||||
interrupted = true; | |||||
} | |||||
void IZ_AppCreateContext(IZ_App* app) { | |||||
struct lws_context_creation_info info; | |||||
memset(&info, 0, sizeof info); | |||||
info.port = app->config.port; | |||||
// TODO initialize protocols | |||||
info.options = ( | |||||
LWS_SERVER_OPTION_VALIDATE_UTF8 | |||||
| LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | |||||
); | |||||
} | |||||
void IZ_AppLoadConfig(IZ_AppConfig* config, u8 argc, const char** argv) { | |||||
memcpy_s(config, sizeof(IZ_AppConfig), &IZ_APP_DEFAULT_CONFIG, sizeof(IZ_AppConfig)); | |||||
const char* cmdline_buffer; | |||||
if ((cmdline_buffer = lws_cmdline_option(argc, argv, "-d"))) { | |||||
config->log_level = atoi(cmdline_buffer); | |||||
} | |||||
if ((cmdline_buffer = lws_cmdline_option(argc, argv, "-p"))) { | |||||
config->port = atoi(cmdline_buffer); | |||||
} | |||||
if (lws_cmdline_option(argc, argv, "-o")) { | |||||
// connect once | |||||
config->vhost_options |= 1; | |||||
} | |||||
if (!lws_cmdline_option(argc, argv, "-n")) { | |||||
config->extensions_enabled = true; | |||||
} | |||||
} | |||||
IZ_ProcedureResult IZ_AppInitialize(IZ_App* app, u8 argc, const char** argv) { | |||||
interrupted = false; | |||||
signal(SIGINT, IZ_AppHandleSignal); | |||||
IZ_AppLoadConfig(&app->config, argc, argv); | |||||
IZ_LogInterceptWSMessages(app->config.log_level); | |||||
IZ_AppCreateContext(app); | |||||
return 0; | |||||
} | |||||
IZ_ProcedureResult IZ_AppRun(IZ_App* app, u8 argc, const char** argv) { | |||||
if (IZ_AppInitialize(app, argc, argv)) { | |||||
return 1; | |||||
} | |||||
return 0; | |||||
} |
@@ -0,0 +1,33 @@ | |||||
#ifndef IZ_APP_H | |||||
#define IZ_APP_H | |||||
#include <signal.h> | |||||
#include <stdbool.h> | |||||
#include <libwebsockets.h> | |||||
#include "IZ_common.h" | |||||
#include "log/IZ_log.h" | |||||
static bool interrupted; | |||||
typedef struct { | |||||
i32 log_level; | |||||
u16 port; | |||||
i32 vhost_options; | |||||
bool extensions_enabled; | |||||
} IZ_AppConfig; | |||||
typedef struct { | |||||
IZ_AppConfig config; | |||||
struct lws_context* context; | |||||
} IZ_App; | |||||
static const IZ_AppConfig IZ_APP_DEFAULT_CONFIG = { | |||||
.log_level = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE, | |||||
.port = 42069, | |||||
.vhost_options = 0, | |||||
.extensions_enabled = false, | |||||
}; | |||||
IZ_ProcedureResult IZ_AppRun(IZ_App*, u8, const char**); | |||||
#endif |
@@ -0,0 +1 @@ | |||||
E:/Projects/Games/izanagi/src/packages/game/log |
@@ -1,104 +1,117 @@ | |||||
#include <libwebsockets.h> | #include <libwebsockets.h> | ||||
#include <string.h> | |||||
#include <signal.h> | #include <signal.h> | ||||
#include "IZ_common.h" | #include "IZ_common.h" | ||||
#include "log/IZ_log.h" | |||||
#define LWS_PLUGIN_STATIC | #define LWS_PLUGIN_STATIC | ||||
#include "protocol_lws_minimal.c" | |||||
#include "protocol_lws_minimal_server_echo.c" | |||||
static struct lws_protocols protocols[] = { | static struct lws_protocols protocols[] = { | ||||
{ "http", lws_callback_http_dummy, 0, 0, 0, NULL, 0}, | |||||
LWS_PLUGIN_PROTOCOL_MINIMAL, | |||||
LWS_PLUGIN_PROTOCOL_MINIMAL_SERVER_ECHO, | |||||
LWS_PROTOCOL_LIST_TERM | LWS_PROTOCOL_LIST_TERM | ||||
}; | }; | ||||
static const lws_retry_bo_t retry = { | |||||
.secs_since_valid_ping = 3, | |||||
.secs_since_valid_hangup = 10, | |||||
static int interrupted, port = 7681, options; | |||||
/* pass pointers to shared vars to the protocol */ | |||||
static const struct lws_protocol_vhost_options pvo_options = { | |||||
NULL, | |||||
NULL, | |||||
"options", /* pvo name */ | |||||
(void *)&options /* pvo value */ | |||||
}; | }; | ||||
static i32 interrupted; | |||||
static const struct lws_http_mount mount = { | |||||
.mount_next = NULL, /* linked-list "next" */ | |||||
.mountpoint = "/", /* mountpoint URL */ | |||||
.origin = "./mount-origin", /* serve from dir */ | |||||
.def = "index.html", /* default filename */ | |||||
.protocol = NULL, | |||||
.cgienv = NULL, | |||||
.extra_mimetypes = NULL, | |||||
.interpret = NULL, | |||||
.cgi_timeout = 0, | |||||
.cache_max_age = 0, | |||||
.auth_mask = 0, | |||||
.cache_reusable = 0, | |||||
.cache_revalidate = 0, | |||||
.cache_intermediaries = 0, | |||||
.origin_protocol = LWSMPRO_FILE, /* files in a dir */ | |||||
.mountpoint_len = 1, /* char count */ | |||||
.basic_auth_login_file = NULL, | |||||
static const struct lws_protocol_vhost_options pvo_interrupted = { | |||||
&pvo_options, | |||||
NULL, | |||||
"interrupted", /* pvo name */ | |||||
(void *)&interrupted /* pvo value */ | |||||
}; | }; | ||||
#if defined(LWS_WITH_PLUGINS) | |||||
/* if plugins enabled, only protocols explicitly named in pvo bind to vhost */ | |||||
static struct lws_protocol_vhost_options pvo = { NULL, NULL, "lws-minimal", "" }; | |||||
#endif | |||||
static const struct lws_protocol_vhost_options pvo = { | |||||
NULL, /* "next" pvo linked-list */ | |||||
&pvo_interrupted, /* "child" pvo linked-list */ | |||||
"lws-minimal-server-echo", /* protocol name we belong to on this vhost */ | |||||
"" /* ignored */ | |||||
}; | |||||
static const struct lws_extension extensions[] = { | |||||
{ | |||||
"permessage-deflate", | |||||
lws_extension_callback_pm_deflate, | |||||
"permessage-deflate" | |||||
"; client_no_context_takeover" | |||||
"; client_max_window_bits" | |||||
}, | |||||
{ NULL, NULL, NULL /* terminator */ } | |||||
}; | |||||
void sigint_handler(i32 sig) { | |||||
void sigint_handler(int sig) | |||||
{ | |||||
interrupted = 1; | interrupted = 1; | ||||
} | } | ||||
IZ_ProcedureResult main(i32 arg_count, char* arg_values[]) { | |||||
struct lws_context_creation_info info; | |||||
struct lws_context* context; | |||||
const char* p; | |||||
i32 n = 0; | |||||
i32 logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; | |||||
IZ_ProcedureResult main(i32 argc, const char **argv) | |||||
{ | |||||
const char *cmdline_buffer; | |||||
i32 logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE | |||||
/* for LLL_ verbosity above NOTICE to be built into lws, | |||||
* lws must have been configured and built with | |||||
* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ | |||||
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ | |||||
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ | |||||
/* | LLL_DEBUG */; | |||||
signal(SIGINT, sigint_handler); | signal(SIGINT, sigint_handler); | ||||
if ((cmdline_buffer = lws_cmdline_option(argc, argv, "-d"))) { | |||||
logs = atoi(cmdline_buffer); | |||||
} | |||||
if ((p = lws_cmdline_option(arg_count, arg_values, "-d"))) | |||||
logs = atoi(p); | |||||
IZ_LogInterceptWSMessages(logs); | |||||
lwsl_user("LWS minimal ws client echo + permessage-deflate + multifragment bulk message\n"); | |||||
lwsl_user(" lws-minimal-ws-client-echo [-n (no exts)] [-p port] [-o (once)]\n"); | |||||
lws_set_log_level(logs, NULL); | |||||
lwsl_user("LWS minimal ws server | visit http://localhost:7681 (-s = use TLS / https)\n"); | |||||
if ((cmdline_buffer = lws_cmdline_option(argc, argv, "-p"))) { | |||||
port = atoi(cmdline_buffer); | |||||
} | |||||
if (lws_cmdline_option(argc, argv, "-o")) { | |||||
// connect once | |||||
options |= 1; | |||||
} | |||||
struct lws_context_creation_info info; | |||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ | memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ | ||||
info.port = 6969; | |||||
info.mounts = &mount; | |||||
info.port = port; | |||||
info.protocols = protocols; | info.protocols = protocols; | ||||
info.vhost_name = "localhost"; | |||||
#if defined(LWS_WITH_PLUGINS) | |||||
info.pvo = &pvo; | info.pvo = &pvo; | ||||
#endif | |||||
info.options = | |||||
LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; | |||||
#if defined(LWS_WITH_TLS) | |||||
if (lws_cmdline_option(arg_count, arg_values, "-s")) { | |||||
lwsl_user("Server using TLS\n"); | |||||
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; | |||||
info.ssl_cert_filepath = "localhost-100y.cert"; | |||||
info.ssl_private_key_filepath = "localhost-100y.key"; | |||||
} | |||||
#endif | |||||
if (lws_cmdline_option(arg_count, arg_values, "-h")) | |||||
info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK; | |||||
if (lws_cmdline_option(arg_count, arg_values, "-v")) | |||||
info.retry_and_idle_policy = &retry; | |||||
if (!lws_cmdline_option(argc, argv, "-n")) | |||||
info.extensions = extensions; | |||||
info.pt_serv_buf_size = 32 * 1024; | |||||
info.options = LWS_SERVER_OPTION_VALIDATE_UTF8 | | |||||
LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; | |||||
struct lws_context *context; | |||||
context = lws_create_context(&info); | context = lws_create_context(&info); | ||||
if (!context) { | if (!context) { | ||||
lwsl_err("lws init failed\n"); | lwsl_err("lws init failed\n"); | ||||
return 1; | return 1; | ||||
} | } | ||||
while (n >= 0 && !interrupted) | |||||
i32 n; | |||||
while (!interrupted) { | |||||
n = lws_service(context, 0); | n = lws_service(context, 0); | ||||
printf("%d\n", n); | |||||
if (n < 0) { | |||||
break; | |||||
} | |||||
} | |||||
lws_context_destroy(context); | lws_context_destroy(context); | ||||
lwsl_user("Completed %s\n", interrupted == 2 ? "OK" : "failed"); | |||||
return 0; | |||||
return interrupted != 2; | |||||
} | } |
@@ -1,143 +0,0 @@ | |||||
#if !defined (LWS_PLUGIN_STATIC) | |||||
#define LWS_DLL | |||||
#define LWS_INTERNAL | |||||
#include <libwebsockets.h> | |||||
#endif | |||||
#include <string.h> | |||||
/* one of these created for each message */ | |||||
struct msg { | |||||
void *payload; /* is malloc'd */ | |||||
size_t len; | |||||
}; | |||||
/* one of these is created for each client connecting to us */ | |||||
struct per_session_data__minimal { | |||||
struct per_session_data__minimal *pss_list; | |||||
struct lws *wsi; | |||||
int last; /* the last message number we sent */ | |||||
}; | |||||
/* one of these is created for each vhost our protocol is used with */ | |||||
struct per_vhost_data__minimal { | |||||
struct lws_context *context; | |||||
struct lws_vhost *vhost; | |||||
const struct lws_protocols *protocol; | |||||
struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ | |||||
struct msg amsg; /* the one pending message... */ | |||||
int current; /* the current message number we are caching */ | |||||
}; | |||||
/* destroys the message when everyone has had a copy of it */ | |||||
static void | |||||
__minimal_destroy_message(void *_msg) | |||||
{ | |||||
struct msg *msg = _msg; | |||||
free(msg->payload); | |||||
msg->payload = NULL; | |||||
msg->len = 0; | |||||
} | |||||
static int | |||||
callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, | |||||
void *user, void *in, size_t len) | |||||
{ | |||||
struct per_session_data__minimal *pss = | |||||
(struct per_session_data__minimal *)user; | |||||
struct per_vhost_data__minimal *vhd = | |||||
(struct per_vhost_data__minimal *) | |||||
lws_protocol_vh_priv_get(lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi)); | |||||
int m; | |||||
switch (reason) { | |||||
case LWS_CALLBACK_PROTOCOL_INIT: | |||||
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi), | |||||
sizeof(struct per_vhost_data__minimal)); | |||||
vhd->context = lws_get_context(wsi); | |||||
vhd->protocol = lws_get_protocol(wsi); | |||||
vhd->vhost = lws_get_vhost(wsi); | |||||
break; | |||||
case LWS_CALLBACK_ESTABLISHED: | |||||
/* add ourselves to the list of live pss held in the vhd */ | |||||
lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); | |||||
pss->wsi = wsi; | |||||
pss->last = vhd->current; | |||||
break; | |||||
case LWS_CALLBACK_CLOSED: | |||||
/* remove our closing pss from the list of live pss */ | |||||
lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, | |||||
pss, vhd->pss_list); | |||||
break; | |||||
case LWS_CALLBACK_SERVER_WRITEABLE: | |||||
if (!vhd->amsg.payload) | |||||
break; | |||||
if (pss->last == vhd->current) | |||||
break; | |||||
/* notice we allowed for LWS_PRE in the payload already */ | |||||
m = lws_write(wsi, ((unsigned char *)vhd->amsg.payload) + | |||||
LWS_PRE, vhd->amsg.len, LWS_WRITE_TEXT); | |||||
if (m < (int)vhd->amsg.len) { | |||||
lwsl_err("ERROR %d writing to ws\n", m); | |||||
return -1; | |||||
} | |||||
pss->last = vhd->current; | |||||
break; | |||||
case LWS_CALLBACK_RECEIVE: | |||||
if (vhd->amsg.payload) | |||||
__minimal_destroy_message(&vhd->amsg); | |||||
vhd->amsg.len = len; | |||||
/* notice we over-allocate by LWS_PRE */ | |||||
vhd->amsg.payload = malloc(LWS_PRE + len); | |||||
if (!vhd->amsg.payload) { | |||||
lwsl_user("OOM: dropping\n"); | |||||
break; | |||||
} | |||||
memcpy((char *)vhd->amsg.payload + LWS_PRE, in, len); | |||||
vhd->current++; | |||||
/* | |||||
* let everybody know we want to write something on them | |||||
* as soon as they are ready | |||||
*/ | |||||
lws_start_foreach_llp(struct per_session_data__minimal **, | |||||
ppss, vhd->pss_list) { | |||||
lws_callback_on_writable((*ppss)->wsi); | |||||
} lws_end_foreach_llp(ppss, pss_list); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return 0; | |||||
} | |||||
#define LWS_PLUGIN_PROTOCOL_MINIMAL \ | |||||
{ \ | |||||
"lws-minimal", \ | |||||
callback_minimal, \ | |||||
sizeof(struct per_session_data__minimal), \ | |||||
128, \ | |||||
0, NULL, 0 \ | |||||
} | |||||
@@ -0,0 +1,219 @@ | |||||
#ifndef LWS_PLUGIN_STATIC | |||||
#define LWS_DLL | |||||
#define LWS_INTERNAL | |||||
#include <libwebsockets.h> | |||||
#endif | |||||
#include <string.h> | |||||
#define RING_DEPTH 4096 | |||||
/* one of these created for each message */ | |||||
struct msg { | |||||
void *payload; /* is malloc'd */ | |||||
size_t len; | |||||
char binary; | |||||
char first; | |||||
char final; | |||||
}; | |||||
struct per_session_data__minimal_server_echo { | |||||
struct lws_ring *ring; | |||||
uint32_t msglen; | |||||
uint32_t tail; | |||||
uint8_t completed: 1; | |||||
uint8_t flow_controlled: 1; | |||||
uint8_t write_consume_pending: 1; | |||||
}; | |||||
struct vhd_minimal_server_echo { | |||||
struct lws_context *context; | |||||
struct lws_vhost *vhost; | |||||
int *interrupted; | |||||
int *options; | |||||
}; | |||||
static void | |||||
__minimal_destroy_message(void *_msg) { | |||||
struct msg *msg = _msg; | |||||
free(msg->payload); | |||||
msg->payload = NULL; | |||||
msg->len = 0; | |||||
} | |||||
static int | |||||
callback_minimal_server_echo(struct lws *wsi, enum lws_callback_reasons reason, | |||||
void *user, void *in, size_t len) { | |||||
struct per_session_data__minimal_server_echo *pss = | |||||
(struct per_session_data__minimal_server_echo *) user; | |||||
struct vhd_minimal_server_echo *vhd = (struct vhd_minimal_server_echo *) | |||||
lws_protocol_vh_priv_get(lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi)); | |||||
const struct msg *pmsg; | |||||
struct msg amsg; | |||||
int m, n, flags; | |||||
switch (reason) { | |||||
case LWS_CALLBACK_PROTOCOL_INIT: | |||||
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi), | |||||
sizeof(struct vhd_minimal_server_echo)); | |||||
if (!vhd) | |||||
return -1; | |||||
vhd->context = lws_get_context(wsi); | |||||
vhd->vhost = lws_get_vhost(wsi); | |||||
/* get the pointers we were passed in pvo */ | |||||
vhd->interrupted = (int *) lws_pvo_search( | |||||
(const struct lws_protocol_vhost_options *) in, | |||||
"interrupted")->value; | |||||
vhd->options = (int *) lws_pvo_search( | |||||
(const struct lws_protocol_vhost_options *) in, | |||||
"options")->value; | |||||
break; | |||||
case LWS_CALLBACK_ESTABLISHED: | |||||
/* generate a block of output before travis times us out */ | |||||
lwsl_warn("LWS_CALLBACK_ESTABLISHED\n"); | |||||
pss->ring = lws_ring_create(sizeof(struct msg), RING_DEPTH, | |||||
__minimal_destroy_message); | |||||
if (!pss->ring) | |||||
return 1; | |||||
pss->tail = 0; | |||||
break; | |||||
case LWS_CALLBACK_SERVER_WRITEABLE: | |||||
lwsl_user("LWS_CALLBACK_SERVER_WRITEABLE\n"); | |||||
if (pss->write_consume_pending) { | |||||
/* perform the deferred fifo consume */ | |||||
lws_ring_consume_single_tail(pss->ring, &pss->tail, 1); | |||||
pss->write_consume_pending = 0; | |||||
} | |||||
pmsg = lws_ring_get_element(pss->ring, &pss->tail); | |||||
if (!pmsg) { | |||||
lwsl_user(" (nothing in ring)\n"); | |||||
break; | |||||
} | |||||
flags = lws_write_ws_flags( | |||||
pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT, | |||||
pmsg->first, pmsg->final); | |||||
/* notice we allowed for LWS_PRE in the payload already */ | |||||
m = lws_write(wsi, ((unsigned char *) pmsg->payload) + | |||||
LWS_PRE, pmsg->len, (enum lws_write_protocol) flags); | |||||
if (m < (int) pmsg->len) { | |||||
lwsl_err("ERROR %d writing to ws socket\n", m); | |||||
return -1; | |||||
} | |||||
lwsl_user(" wrote %d: flags: 0x%x first: %d final %d\n", | |||||
m, flags, pmsg->first, pmsg->final); | |||||
/* | |||||
* Workaround deferred deflate in pmd extension by only | |||||
* consuming the fifo entry when we are certain it has been | |||||
* fully deflated at the next WRITABLE callback. You only need | |||||
* this if you're using pmd. | |||||
*/ | |||||
pss->write_consume_pending = 1; | |||||
lws_callback_on_writable(wsi); | |||||
if (pss->flow_controlled && | |||||
(int) lws_ring_get_count_free_elements(pss->ring) > RING_DEPTH - 5) { | |||||
lws_rx_flow_control(wsi, 1); | |||||
pss->flow_controlled = 0; | |||||
} | |||||
if ((*vhd->options & 1) && pmsg && pmsg->final) | |||||
pss->completed = 1; | |||||
break; | |||||
case LWS_CALLBACK_RECEIVE: | |||||
lwsl_user("LWS_CALLBACK_RECEIVE: %4d (rpp %5d, first %d, " | |||||
"last %d, bin %d, msglen %d (+ %d = %d))\n", | |||||
(int) len, (int) lws_remaining_packet_payload(wsi), | |||||
lws_is_first_fragment(wsi), | |||||
lws_is_final_fragment(wsi), | |||||
lws_frame_is_binary(wsi), pss->msglen, (int) len, | |||||
(int) pss->msglen + (int) len); | |||||
if (len) { ; | |||||
//puts((const char *)in); | |||||
//lwsl_hexdump_notice(in, len); | |||||
} | |||||
amsg.first = (char) lws_is_first_fragment(wsi); | |||||
amsg.final = (char) lws_is_final_fragment(wsi); | |||||
amsg.binary = (char) lws_frame_is_binary(wsi); | |||||
n = (int) lws_ring_get_count_free_elements(pss->ring); | |||||
if (!n) { | |||||
lwsl_user("dropping!\n"); | |||||
break; | |||||
} | |||||
if (amsg.final) | |||||
pss->msglen = 0; | |||||
else | |||||
pss->msglen += (uint32_t) len; | |||||
amsg.len = len; | |||||
/* notice we over-allocate by LWS_PRE */ | |||||
amsg.payload = malloc(LWS_PRE + len); | |||||
if (!amsg.payload) { | |||||
lwsl_user("OOM: dropping\n"); | |||||
break; | |||||
} | |||||
memcpy((char *) amsg.payload + LWS_PRE, in, len); | |||||
if (!lws_ring_insert(pss->ring, &amsg, 1)) { | |||||
__minimal_destroy_message(&amsg); | |||||
lwsl_user("dropping!\n"); | |||||
break; | |||||
} | |||||
lws_callback_on_writable(wsi); | |||||
if (n < 3 && !pss->flow_controlled) { | |||||
pss->flow_controlled = 1; | |||||
lws_rx_flow_control(wsi, 0); | |||||
} | |||||
break; | |||||
case LWS_CALLBACK_CLOSED: | |||||
lwsl_user("LWS_CALLBACK_CLOSED\n"); | |||||
lws_ring_destroy(pss->ring); | |||||
if (*vhd->options & 1) { | |||||
if (!*vhd->interrupted) | |||||
*vhd->interrupted = 1 + pss->completed; | |||||
lws_cancel_service(lws_get_context(wsi)); | |||||
} | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return 0; | |||||
} | |||||
#define LWS_PLUGIN_PROTOCOL_MINIMAL_SERVER_ECHO \ | |||||
{ \ | |||||
"lws-minimal-server-echo", \ | |||||
callback_minimal_server_echo, \ | |||||
sizeof(struct per_session_data__minimal_server_echo), \ | |||||
1024, \ | |||||
0, NULL, 0 \ | |||||
} | |||||