Connect client to server, save connection details.feature/data-structs
@@ -186,6 +186,8 @@ add_executable( | |||||
src/packages/server/main.c | src/packages/server/main.c | ||||
src/packages/server/IZ_app.c | src/packages/server/IZ_app.c | ||||
src/packages/server/IZ_app.h | src/packages/server/IZ_app.h | ||||
src/packages/server/IZ_config.c | |||||
src/packages/server/IZ_config.h | |||||
src/packages/server/network/IZ_wsserver.c | src/packages/server/network/IZ_wsserver.c | ||||
src/packages/server/network/IZ_wsserver.h | src/packages/server/network/IZ_wsserver.h | ||||
src/packages/server/network/IZ_websocket.h | src/packages/server/network/IZ_websocket.h | ||||
@@ -1,7 +1,7 @@ | |||||
#include "IZ_app.h" | #include "IZ_app.h" | ||||
IZ_ProcedureResult IZ_AppWSClientInitialize(IZ_App* app, IZ_WSClientInitializeParams params) { | |||||
if (IZ_WSClientInitialize(&app->client, params)) { | |||||
IZ_ProcedureResult IZ_AppWSClientInitialize(IZ_App* app) { | |||||
if (IZ_WSClientInitialize(&app->client)) { | |||||
printf("error\n"); | printf("error\n"); | ||||
return -1; | return -1; | ||||
} | } | ||||
@@ -10,14 +10,7 @@ IZ_ProcedureResult IZ_AppWSClientInitialize(IZ_App* app, IZ_WSClientInitializePa | |||||
IZ_ProcedureResult IZ_AppConnect(void* app_raw) { | IZ_ProcedureResult IZ_AppConnect(void* app_raw) { | ||||
IZ_App* app = app_raw; | IZ_App* app = app_raw; | ||||
IZ_WSClientInitializeParams params = { | |||||
.address = "127.0.0.1", | |||||
.path = "/", | |||||
.port = 42069, | |||||
.userdata = app, | |||||
}; | |||||
if (IZ_AppWSClientInitialize(app, params)) { | |||||
if (IZ_AppWSClientInitialize(app)) { | |||||
return -1; | return -1; | ||||
} | } | ||||
@@ -37,11 +30,29 @@ IZ_ProcedureResult IZ_AppConnect(void* app_raw) { | |||||
return result; | return result; | ||||
} | } | ||||
void IZ_AppEstablishConnection(IZ_App* app) { | |||||
void IZ_AppHandleNetworkingOutboundEvents(IZ_App* app) { | |||||
struct lws* wsi = app->client.ws.connection; | |||||
printf("%p\n", wsi); | |||||
if (!wsi) { | |||||
return; | |||||
} | |||||
IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
IZ_WebsocketMessage* amsg = IZ_WSClientCreateMessage(wsi, false, "hello", 5); | |||||
lws_ring_insert(vhd->ring, amsg, 1); | |||||
lws_callback_on_writable(wsi); | |||||
} | |||||
void IZ_AppEstablishConnection(IZ_App* app, IZ_WSClientInitializeParams params) { | |||||
if (app->client.ws.context) { | if (app->client.ws.context) { | ||||
return; | return; | ||||
} | } | ||||
app->client.params = params; | |||||
app->client.userdata = app; | |||||
app->client_thread = SDL_CreateThread(IZ_AppConnect, "networking", app); | app->client_thread = SDL_CreateThread(IZ_AppConnect, "networking", app); | ||||
SDL_DetachThread(app->client_thread); | SDL_DetachThread(app->client_thread); | ||||
} | } | ||||
@@ -108,9 +119,15 @@ IZ_ProcedureResult IZ_AppHandleSDLEvents(IZ_App* app) { | |||||
if (e.type == SDL_KEYDOWN) { | if (e.type == SDL_KEYDOWN) { | ||||
if (e.key.keysym.sym == SDLK_PAGEUP) { | if (e.key.keysym.sym == SDLK_PAGEUP) { | ||||
IZ_AppEstablishConnection(app); | |||||
IZ_AppEstablishConnection(app, (IZ_WSClientInitializeParams){ | |||||
.address = "127.0.0.1", | |||||
.path = "/", | |||||
.port = 42069, | |||||
}); | |||||
} else if (e.key.keysym.sym == SDLK_PAGEDOWN) { | } else if (e.key.keysym.sym == SDLK_PAGEDOWN) { | ||||
IZ_AppCloseConnection(app); | IZ_AppCloseConnection(app); | ||||
} else if (e.key.keysym.sym == SDLK_INSERT) { | |||||
IZ_AppHandleNetworkingOutboundEvents(app); | |||||
} | } | ||||
} | } | ||||
@@ -184,15 +201,12 @@ IZ_ProcedureResult IZ_AppRun(IZ_App* app, u8 argc, const char* argv[]) { | |||||
// TODO do audio processing | // TODO do audio processing | ||||
// TODO do networking | // TODO do networking | ||||
//if (app->client.connection) { | |||||
// FIXME stuck in infinite loop | |||||
//IZ_WSClientHandle(&app->client); | |||||
//} | |||||
if (IZ_AppHandleInputEvents(app)) { | if (IZ_AppHandleInputEvents(app)) { | ||||
break; | break; | ||||
} | } | ||||
IZ_VideoUpdate(&app->video_state, app->ticks, &app->input_state); | IZ_VideoUpdate(&app->video_state, app->ticks, &app->input_state); | ||||
} | } | ||||
@@ -275,7 +289,6 @@ void IZ_WSClientProtocolTeardown(struct lws* wsi) { | |||||
lws_get_protocol(wsi) | lws_get_protocol(wsi) | ||||
); | ); | ||||
vhd->finished = true; | |||||
if (vhd->ring) { | if (vhd->ring) { | ||||
lws_ring_destroy(vhd->ring); | lws_ring_destroy(vhd->ring); | ||||
} | } | ||||
@@ -290,6 +303,8 @@ void IZ_WSClientConnectionError(struct lws* wsi, void* in) { | |||||
); | ); | ||||
lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char *)in : "(null)"); | lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char *)in : "(null)"); | ||||
IZ_App* app = (IZ_App*) vhd->app; | |||||
app->client.ws.connection = NULL; | |||||
vhd->client_wsi = NULL; | vhd->client_wsi = NULL; | ||||
lws_sul_schedule( | lws_sul_schedule( | ||||
vhd->context, | vhd->context, | ||||
@@ -306,7 +321,8 @@ IZ_ProcedureResult IZ_WSClientOnOpen(struct lws* wsi, IZ_WSClientSessionData* ps | |||||
lws_get_protocol(wsi) | lws_get_protocol(wsi) | ||||
); | ); | ||||
vhd->established = true; | |||||
IZ_App* app = (IZ_App*) vhd->app; | |||||
app->client.ws.connection = wsi; | |||||
pss->ring = lws_ring_create(sizeof(IZ_WebsocketMessage), RING_COUNT,IZ_WebsocketDestroyMessage); | pss->ring = lws_ring_create(sizeof(IZ_WebsocketMessage), RING_COUNT,IZ_WebsocketDestroyMessage); | ||||
if (!pss->ring) { | if (!pss->ring) { | ||||
return -1; | return -1; | ||||
@@ -321,8 +337,9 @@ void IZ_WSClientOnClose(struct lws* wsi) { | |||||
lws_get_protocol(wsi) | lws_get_protocol(wsi) | ||||
); | ); | ||||
IZ_App* app = (IZ_App*) vhd->app; | |||||
app->client.ws.connection = NULL; | |||||
vhd->client_wsi = NULL; | vhd->client_wsi = NULL; | ||||
vhd->established = false; | |||||
lws_sul_schedule( | lws_sul_schedule( | ||||
vhd->context, | vhd->context, | ||||
0, | 0, | ||||
@@ -367,87 +384,65 @@ IZ_ProcedureResult IZ_WSClientWritable(struct lws* wsi) { | |||||
return 0; | return 0; | ||||
} | } | ||||
void IZ_WSClientOnReceive(struct lws* wsi, IZ_WSClientSessionData* pss, void* in, size_t len) { | |||||
lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %4d (rpp %5d, first %d, last %d, bin %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)); | |||||
// lwsl_hexdump_notice(in, len); | |||||
IZ_WebsocketMessage* IZ_WSClientCreateMessage(struct lws* wsi, bool binary, void* in, size_t len) { | |||||
IZ_WebsocketMessage amsg; | IZ_WebsocketMessage amsg; | ||||
amsg.first = (char)lws_is_first_fragment(wsi); | amsg.first = (char)lws_is_first_fragment(wsi); | ||||
amsg.final = (char)lws_is_final_fragment(wsi); | amsg.final = (char)lws_is_final_fragment(wsi); | ||||
amsg.binary = (char)lws_frame_is_binary(wsi); | |||||
amsg.binary = binary; | |||||
amsg.len = len; | |||||
/* notice we over-allocate by LWS_PRE */ | |||||
amsg.payload = malloc(LWS_PRE + len); | |||||
if (!amsg.payload) { | |||||
return NULL; | |||||
} | |||||
memcpy((char*) amsg.payload + LWS_PRE, in, len); | |||||
return &amsg; | |||||
} | |||||
void IZ_WSClientOnReceive(struct lws* wsi, IZ_WSClientSessionData* pss, void* in, size_t len) { | |||||
i32 n = (i32) lws_ring_get_count_free_elements(pss->ring); | i32 n = (i32) lws_ring_get_count_free_elements(pss->ring); | ||||
if (!n) { | if (!n) { | ||||
lwsl_user("dropping!\n"); | lwsl_user("dropping!\n"); | ||||
return; | return; | ||||
} | } | ||||
amsg.len = len; | |||||
/* notice we over-allocate by LWS_PRE */ | |||||
amsg.payload = malloc(LWS_PRE + len); | |||||
if (!amsg.payload) { | |||||
lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %4d (rpp %5d, first %d, last %d, bin %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)); | |||||
// lwsl_hexdump_notice(in, len); | |||||
IZ_WebsocketMessage* amsg = IZ_WSClientCreateMessage(wsi, (bool) lws_frame_is_binary(wsi), in, len); | |||||
if (!amsg) { | |||||
lwsl_user("OOM: dropping\n"); | lwsl_user("OOM: dropping\n"); | ||||
return; | return; | ||||
} | } | ||||
memcpy((char *)amsg.payload + LWS_PRE, in, len); | |||||
IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( | IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( | ||||
lws_get_vhost(wsi), | lws_get_vhost(wsi), | ||||
lws_get_protocol(wsi) | lws_get_protocol(wsi) | ||||
); | ); | ||||
IZ_App* app = (IZ_App*) vhd->app; | IZ_App* app = (IZ_App*) vhd->app; | ||||
if (amsg.binary) { | |||||
if (amsg->binary) { | |||||
IZ_AppHandleNetworkingInboundBinaryEvents(app, in, len); | IZ_AppHandleNetworkingInboundBinaryEvents(app, in, len); | ||||
} else { | } else { | ||||
IZ_AppHandleNetworkingInboundTextEvents(app, in, len); | IZ_AppHandleNetworkingInboundTextEvents(app, in, len); | ||||
} | } | ||||
if (!lws_ring_insert(pss->ring, &amsg, 1)) { | |||||
IZ_WebsocketDestroyMessage(&amsg); | |||||
if (!lws_ring_insert(pss->ring, amsg, 1)) { | |||||
IZ_WebsocketDestroyMessage(amsg); | |||||
lwsl_user("dropping!\n"); | lwsl_user("dropping!\n"); | ||||
return; | return; | ||||
} | } | ||||
lws_callback_on_writable(wsi); | lws_callback_on_writable(wsi); | ||||
if (!pss->flow_controlled && n < 3) { | if (!pss->flow_controlled && n < 3) { | ||||
pss->flow_controlled = 1; | |||||
pss->flow_controlled = true; | |||||
lws_rx_flow_control(wsi, 0); | lws_rx_flow_control(wsi, 0); | ||||
} | } | ||||
} | } | ||||
IZ_ProcedureResult IZ_WSClientCallback( | |||||
struct lws* wsi, | |||||
enum lws_callback_reasons reason, | |||||
void* user, | |||||
void* in, | |||||
size_t len | |||||
) { | |||||
switch (reason) { | |||||
case LWS_CALLBACK_PROTOCOL_INIT: | |||||
return IZ_WSClientProtocolInitialize(wsi, in); | |||||
case LWS_CALLBACK_PROTOCOL_DESTROY: | |||||
IZ_WSClientProtocolTeardown(wsi); | |||||
break; | |||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: | |||||
IZ_WSClientConnectionError(wsi, in); | |||||
break; | |||||
case LWS_CALLBACK_CLIENT_ESTABLISHED: | |||||
return IZ_WSClientOnOpen(wsi, user); | |||||
case LWS_CALLBACK_CLIENT_CLOSED: | |||||
IZ_WSClientOnClose(wsi); | |||||
break; | |||||
case LWS_CALLBACK_CLIENT_RECEIVE: | |||||
IZ_WSClientOnReceive(wsi, user, in, len); | |||||
break; | |||||
case LWS_CALLBACK_CLIENT_WRITEABLE: | |||||
return IZ_WSClientWritable(wsi); | |||||
default: | |||||
break; | |||||
} | |||||
return lws_callback_http_dummy(wsi, reason, user, in, len); | |||||
} |
@@ -4,5 +4,5 @@ void IZ_ConfigGetDefaultPath(const char* config_path, size_t string_size) { | |||||
//const char* config_path_dir = SDL_GetPrefPath("Modal Studios", APP_NAME); | //const char* config_path_dir = SDL_GetPrefPath("Modal Studios", APP_NAME); | ||||
const char* config_path_dir = SDL_GetBasePath(); | const char* config_path_dir = SDL_GetBasePath(); | ||||
memcpy_s(config_path, string_size, config_path_dir, 128); | memcpy_s(config_path, string_size, config_path_dir, 128); | ||||
strcat_s(config_path, string_size, "config.ini"); | |||||
strcat_s(config_path, string_size, "config-game.ini"); | |||||
} | } |
@@ -18,6 +18,7 @@ typedef struct { | |||||
typedef struct { | typedef struct { | ||||
struct lws_context* context; | struct lws_context* context; | ||||
struct lws* connection; | |||||
u8 interrupted: 1; | u8 interrupted: 1; | ||||
} IZ_Websocket; | } IZ_Websocket; | ||||
@@ -1,6 +1,39 @@ | |||||
#include "IZ_wsclient.h" | #include "IZ_wsclient.h" | ||||
IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state, IZ_WSClientInitializeParams params) { | |||||
IZ_ProcedureResult IZ_WSClientCallback( | |||||
struct lws* wsi, | |||||
enum lws_callback_reasons reason, | |||||
void* user, | |||||
void* in, | |||||
size_t len | |||||
) { | |||||
switch (reason) { | |||||
case LWS_CALLBACK_PROTOCOL_INIT: | |||||
return IZ_WSClientProtocolInitialize(wsi, in); | |||||
case LWS_CALLBACK_PROTOCOL_DESTROY: | |||||
IZ_WSClientProtocolTeardown(wsi); | |||||
break; | |||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: | |||||
IZ_WSClientConnectionError(wsi, in); | |||||
break; | |||||
case LWS_CALLBACK_CLIENT_ESTABLISHED: | |||||
return IZ_WSClientOnOpen(wsi, user); | |||||
case LWS_CALLBACK_CLIENT_CLOSED: | |||||
IZ_WSClientOnClose(wsi); | |||||
break; | |||||
case LWS_CALLBACK_CLIENT_RECEIVE: | |||||
IZ_WSClientOnReceive(wsi, user, in, len); | |||||
break; | |||||
case LWS_CALLBACK_CLIENT_WRITEABLE: | |||||
return IZ_WSClientWritable(wsi); | |||||
default: | |||||
break; | |||||
} | |||||
return lws_callback_http_dummy(wsi, reason, user, in, len); | |||||
} | |||||
IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state) { | |||||
struct lws_context_creation_info info; | struct lws_context_creation_info info; | ||||
memset(&info, 0, sizeof info); | memset(&info, 0, sizeof info); | ||||
info.port = CONTEXT_PORT_NO_LISTEN; | info.port = CONTEXT_PORT_NO_LISTEN; | ||||
@@ -25,7 +58,7 @@ IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state, IZ_WSClientIni | |||||
"address", /* pvo name */ | "address", /* pvo name */ | ||||
"localhost" /* pvo value */ | "localhost" /* pvo value */ | ||||
}; | }; | ||||
pvo_address.value = params.address; | |||||
pvo_address.value = state->params.address; | |||||
static struct lws_protocol_vhost_options pvo_path = { | static struct lws_protocol_vhost_options pvo_path = { | ||||
&pvo_address, | &pvo_address, | ||||
@@ -33,7 +66,7 @@ IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state, IZ_WSClientIni | |||||
"path", /* pvo name */ | "path", /* pvo name */ | ||||
"/" /* pvo value */ | "/" /* pvo value */ | ||||
}; | }; | ||||
pvo_path.value = params.path; | |||||
pvo_path.value = state->params.path; | |||||
static struct lws_protocol_vhost_options pvo_port = { | static struct lws_protocol_vhost_options pvo_port = { | ||||
&pvo_path, | &pvo_path, | ||||
@@ -41,7 +74,7 @@ IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state, IZ_WSClientIni | |||||
"port", /* pvo name */ | "port", /* pvo name */ | ||||
NULL /* pvo value */ | NULL /* pvo value */ | ||||
}; | }; | ||||
pvo_port.value = (void*) ¶ms.port; | |||||
pvo_port.value = (void*) &state->params.port; | |||||
static struct lws_protocol_vhost_options pvo_app = { | static struct lws_protocol_vhost_options pvo_app = { | ||||
&pvo_port, | &pvo_port, | ||||
@@ -49,7 +82,7 @@ IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state, IZ_WSClientIni | |||||
"app", | "app", | ||||
NULL, | NULL, | ||||
}; | }; | ||||
pvo_app.value = params.userdata; | |||||
pvo_app.value = state->userdata; | |||||
static const struct lws_protocol_vhost_options pvo = { | static const struct lws_protocol_vhost_options pvo = { | ||||
NULL, /* "next" pvo linked-list */ | NULL, /* "next" pvo linked-list */ | ||||
@@ -18,8 +18,6 @@ typedef struct { | |||||
struct lws_client_connect_info i; | struct lws_client_connect_info i; | ||||
struct lws *client_wsi; | struct lws *client_wsi; | ||||
u8 finished: 1; | |||||
u8 established: 1; | |||||
const char* address; | const char* address; | ||||
const char* path; | const char* path; | ||||
u16* port; | u16* port; | ||||
@@ -29,23 +27,22 @@ typedef struct { | |||||
typedef struct { | typedef struct { | ||||
struct lws_ring *ring; | struct lws_ring *ring; | ||||
u32 tail; | u32 tail; | ||||
char flow_controlled; | |||||
u8 completed: 1; | |||||
u8 write_consume_pending: 1; | |||||
unsigned char flow_controlled: 1; | |||||
} IZ_WSClientSessionData; | } IZ_WSClientSessionData; | ||||
typedef struct { | typedef struct { | ||||
const char* address; | const char* address; | ||||
const char* path; | const char* path; | ||||
u16 port; | u16 port; | ||||
void* userdata; | |||||
} IZ_WSClientInitializeParams; | } IZ_WSClientInitializeParams; | ||||
typedef struct { | typedef struct { | ||||
IZ_Websocket ws; | IZ_Websocket ws; | ||||
void* userdata; | |||||
IZ_WSClientInitializeParams params; | |||||
} IZ_WSClientState; | } IZ_WSClientState; | ||||
IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState*, IZ_WSClientInitializeParams); | |||||
IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState*); | |||||
IZ_ProcedureResult IZ_WSClientHandle(IZ_WSClientState*); | IZ_ProcedureResult IZ_WSClientHandle(IZ_WSClientState*); | ||||
@@ -53,6 +50,20 @@ void IZ_WSClientTeardown(IZ_WSClientState*); | |||||
void IZ_WSClientCancelService(IZ_WSClientState*); | void IZ_WSClientCancelService(IZ_WSClientState*); | ||||
IZ_ProcedureResult IZ_WSClientCallback(struct lws*, enum lws_callback_reasons, void*, void*, size_t); | |||||
IZ_ProcedureResult IZ_WSClientProtocolInitialize(struct lws*, void*); | |||||
void IZ_WSClientProtocolTeardown(struct lws*); | |||||
void IZ_WSClientConnectionError(struct lws*, void*); | |||||
IZ_ProcedureResult IZ_WSClientOnOpen(struct lws*, IZ_WSClientSessionData*); | |||||
void IZ_WSClientOnClose(struct lws*); | |||||
IZ_ProcedureResult IZ_WSClientWritable(struct lws*); | |||||
void IZ_WSClientOnReceive(struct lws*, IZ_WSClientSessionData*, void*, size_t); | |||||
IZ_WebsocketMessage* IZ_WSClientCreateMessage(struct lws*, bool, void*, size_t); | |||||
#endif | #endif |
@@ -1,42 +1,33 @@ | |||||
#include "IZ_app.h" | #include "IZ_app.h" | ||||
static IZ_App* global_app; | |||||
void IZ_AppHandleSignal(i32 _signal) { | void IZ_AppHandleSignal(i32 _signal) { | ||||
global_app->server.ws.interrupted = true; | global_app->server.ws.interrupted = true; | ||||
IZ_WSServerCancelService(&global_app->server); | IZ_WSServerCancelService(&global_app->server); | ||||
} | } | ||||
// TODO move to each subsystem | |||||
// TODO unify loading of config from cmdline and config file | |||||
void IZ_AppLoadConfig(IZ_App *app, u8 argc, const char **argv) { | |||||
memcpy_s(app, sizeof(IZ_App), &IZ_APP_DEFAULT_STATE, sizeof(IZ_App)); | |||||
IZ_ProcedureResult IZ_AppInitialize(IZ_App *app, u8 argc, const char **argv) { | |||||
global_app = app; | |||||
memset(app, 0, sizeof(IZ_App)); | |||||
signal(SIGINT, IZ_AppHandleSignal); | |||||
const char *cmdline_buffer; | |||||
if ((cmdline_buffer = lws_cmdline_option(argc, argv, "-d"))) { | |||||
app->config.log_level = atoi(cmdline_buffer); | |||||
} | |||||
// IZ_LogInterceptWSMessages(app->config.log_level); | |||||
IZ_LogInterceptWSMessages(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE); | |||||
if ((cmdline_buffer = lws_cmdline_option(argc, argv, "-p"))) { | |||||
app->config.port = atoi(cmdline_buffer); | |||||
const char* cmdline_buffer; | |||||
char config_path[128]; | |||||
// TODO abstract command line args parsing | |||||
if ((cmdline_buffer = lws_cmdline_option(argc, argv, "-c"))) { | |||||
memcpy_s(config_path, 128, cmdline_buffer, 128); | |||||
} else { | |||||
IZ_ConfigGetDefaultPath(config_path, 128); | |||||
} | } | ||||
} | |||||
IZ_ProcedureResult IZ_AppInitialize(IZ_App *app, u8 argc, const char **argv) { | |||||
signal(SIGINT, IZ_AppHandleSignal); | |||||
signal(9, IZ_AppHandleSignal); | |||||
signal(SIGTERM, IZ_AppHandleSignal); | |||||
IZ_AppLoadConfig(app, argc, argv); | |||||
IZ_LogInterceptWSMessages(app->config.log_level); | |||||
if (IZ_WSServerInitialize(&app->server, (IZ_WSServerInitializeParams) { | |||||
.protocol = NULL, | |||||
.port = app->config.port, | |||||
.default_filename = NULL, | |||||
.origin = NULL, | |||||
})) { | |||||
if (IZ_WSServerInitialize(&app->server, app, config_path, argc, argv)) { | |||||
return -1; | return -1; | ||||
} | } | ||||
global_app = app; | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -61,3 +52,255 @@ IZ_ProcedureResult IZ_AppRun(IZ_App *app, u8 argc, const char **argv) { | |||||
lwsl_user("Server closed. Bye!\n"); | lwsl_user("Server closed. Bye!\n"); | ||||
return result; | return result; | ||||
} | } | ||||
void IZ_WSServerCullLaggingClients(IZ_WSServerVHostData *vhd) { | |||||
u32 oldest_tail = lws_ring_get_oldest_tail(vhd->ring); | |||||
IZ_WSServerSessionData *old_pss = NULL; | |||||
i32 most = 0; | |||||
i32 before = (i32) lws_ring_get_count_waiting_elements(vhd->ring, &oldest_tail); | |||||
i32 m; | |||||
/* | |||||
* At least one guy with the oldest tail has lagged too far, filling | |||||
* the ringbuffer with stuff waiting for them, while new stuff is | |||||
* coming in, and they must close, freeing up ringbuffer entries. | |||||
*/ | |||||
lws_start_foreach_llp_safe( | |||||
IZ_WSServerSessionData**, | |||||
ppss, | |||||
vhd->pss_list, | |||||
pss_list | |||||
) { | |||||
if ((*ppss)->tail == oldest_tail) { | |||||
old_pss = *ppss; | |||||
lwsl_user("Killing lagging client %p\n", (*ppss)->wsi); | |||||
lws_set_timeout((*ppss)->wsi, PENDING_TIMEOUT_LAGGING, | |||||
/* | |||||
* we may kill the wsi we came in on, | |||||
* so the actual close is deferred | |||||
*/ | |||||
LWS_TO_KILL_ASYNC); | |||||
/* | |||||
* We might try to write something before we get a | |||||
* chance to close. But this pss is now detached | |||||
* from the ring buffer. Mark this pss as culled so we | |||||
* don't try to do anything more with it. | |||||
*/ | |||||
(*ppss)->culled = true; | |||||
/* | |||||
* Because we can't kill it synchronously, but we | |||||
* know it's closing momentarily and don't want its | |||||
* participation any more, remove its pss from the | |||||
* vhd pss list early. (This is safe to repeat | |||||
* uselessly later in the close flow). | |||||
* | |||||
* Notice this changes *ppss! | |||||
*/ | |||||
lws_ll_fwd_remove(IZ_WSServerSessionData, pss_list, (*ppss), vhd->pss_list); | |||||
/* use the changed *ppss so we won't skip anything */ | |||||
continue; | |||||
} | |||||
/* | |||||
* so this guy is a survivor of the cull. Let's track | |||||
* what is the largest number of pending ring elements | |||||
* for any survivor. | |||||
*/ | |||||
m = (i32) lws_ring_get_count_waiting_elements(vhd->ring, &((*ppss)->tail)); | |||||
if (m > most) { | |||||
most = m; | |||||
} | |||||
} lws_end_foreach_llp_safe(ppss); | |||||
/* it would mean we lost track of oldest... but Coverity insists */ | |||||
if (!old_pss) { | |||||
return; | |||||
} | |||||
/* | |||||
* Let's recover (ie, free up) all the ring slots between the | |||||
* original oldest's last one and the "worst" survivor. | |||||
*/ | |||||
lws_ring_consume_and_update_oldest_tail( | |||||
vhd->ring, | |||||
IZ_WSServerSessionData, | |||||
&old_pss->tail, | |||||
(size_t) (before - most), | |||||
vhd->pss_list, | |||||
tail, | |||||
pss_list | |||||
); | |||||
lwsl_user("%s: shrunk ring from %d to %d\n", __func__, before, most); | |||||
} | |||||
/* destroys the message when everyone has had a copy of it */ | |||||
IZ_ProcedureResult IZ_WSServerProtocolInitialize(struct lws* wsi, void* in) { | |||||
IZ_WSServerVHostData* vhd_instance = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
IZ_WSServerVHostData** vhd = &vhd_instance; | |||||
*vhd = lws_protocol_vh_priv_zalloc( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi), | |||||
sizeof(IZ_WSServerVHostData) | |||||
); | |||||
(*vhd)->context = lws_get_context(wsi); | |||||
(*vhd)->protocol = lws_get_protocol(wsi); | |||||
(*vhd)->vhost = lws_get_vhost(wsi); | |||||
(*vhd)->port = (u16*) lws_pvo_search( | |||||
(const struct lws_protocol_vhost_options *)in, | |||||
"port" | |||||
)->value; | |||||
(*vhd)->app = lws_pvo_search( | |||||
(const struct lws_protocol_vhost_options *)in, | |||||
"app" | |||||
)->value; | |||||
(*vhd)->ring = lws_ring_create( | |||||
sizeof(IZ_WebsocketMessage), | |||||
RING_COUNT, | |||||
IZ_WebsocketDestroyMessage | |||||
); | |||||
if (!(*vhd)->ring) { | |||||
return -1; | |||||
} | |||||
return 0; | |||||
} | |||||
void IZ_WSServerProtocolTeardown(struct lws* wsi) { | |||||
IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
lws_ring_destroy(vhd->ring); | |||||
} | |||||
void IZ_WSServerOnOpen(struct lws* wsi, IZ_WSServerSessionData* pss) { | |||||
IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
/* add ourselves to the list of live pss held in the vhd */ | |||||
lwsl_user("LWS_CALLBACK_ESTABLISHED: wsi %p\n", wsi); | |||||
lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); | |||||
pss->tail = lws_ring_get_oldest_tail(vhd->ring); | |||||
pss->wsi = wsi; | |||||
} | |||||
void IZ_WSServerOnClose(struct lws* wsi, IZ_WSServerSessionData* pss) { | |||||
IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
lwsl_user("LWS_CALLBACK_CLOSED: wsi %p\n", wsi); | |||||
/* remove our closing pss from the list of live pss */ | |||||
lws_ll_fwd_remove(IZ_WSServerSessionData, pss_list, pss, vhd->pss_list); | |||||
} | |||||
IZ_ProcedureResult IZ_WSServerWritable(struct lws* wsi, IZ_WSServerSessionData* pss) { | |||||
IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
if (pss->culled) { | |||||
return 0; | |||||
} | |||||
const IZ_WebsocketMessage* pmsg = lws_ring_get_element(vhd->ring, &pss->tail); | |||||
if (!pmsg) { | |||||
return 0; | |||||
} | |||||
/* notice we allowed for LWS_PRE in the payload already */ | |||||
i32 m = lws_write( | |||||
wsi, | |||||
((unsigned char*) pmsg->payload) + LWS_PRE, | |||||
pmsg->len, | |||||
pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT | |||||
); | |||||
if (m < (i32) pmsg->len) { | |||||
lwsl_err("ERROR %d writing to ws socket\n", m); | |||||
return -1; | |||||
} | |||||
lws_ring_consume_and_update_oldest_tail( | |||||
vhd->ring, /* lws_ring object */ | |||||
IZ_WSServerSessionData, /* type of objects with tails */ | |||||
&pss->tail, /* tail of guy doing the consuming */ | |||||
1, /* number of payload objects being consumed */ | |||||
vhd->pss_list, /* head of list of objects with tails */ | |||||
tail, /* member name of tail in objects with tails */ | |||||
pss_list /* member name of next object in objects with tails */ | |||||
); | |||||
/* more to do for us? */ | |||||
if (lws_ring_get_element(vhd->ring, &pss->tail)) { | |||||
/* come back as soon as we can write more */ | |||||
lws_callback_on_writable(pss->wsi); | |||||
} | |||||
return 0; | |||||
} | |||||
IZ_ProcedureResult IZ_WSServerOnReceive(struct lws* wsi, void* in, size_t len) { | |||||
IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
i32 n = (i32) lws_ring_get_count_free_elements(vhd->ring); | |||||
if (!n) { | |||||
/* forcibly make space */ | |||||
IZ_WSServerCullLaggingClients(vhd); | |||||
n = (i32) lws_ring_get_count_free_elements(vhd->ring); | |||||
} | |||||
if (!n) { | |||||
return 0; | |||||
} | |||||
lwsl_user("LWS_CALLBACK_RECEIVE: free space %d\n", n); | |||||
IZ_WebsocketMessage amsg; | |||||
amsg.len = len; | |||||
/* notice we over-allocate by LWS_PRE... */ | |||||
amsg.payload = malloc(LWS_PRE + len); | |||||
amsg.binary = (u8) lws_frame_is_binary(wsi); | |||||
if (!amsg.payload) { | |||||
lwsl_user("OOM: dropping\n"); | |||||
return 1; | |||||
} | |||||
/* ...and we copy the payload in at +LWS_PRE */ | |||||
memcpy((char *) amsg.payload + LWS_PRE, in, len); | |||||
if (!lws_ring_insert(vhd->ring, &amsg, 1)) { | |||||
IZ_WebsocketDestroyMessage(&amsg); | |||||
lwsl_user("dropping!\n"); | |||||
return 1; | |||||
} | |||||
/* | |||||
* let everybody know we want to write something on them | |||||
* as soon as they are ready | |||||
*/ | |||||
lws_start_foreach_llp(IZ_WSServerSessionData**, ppss, vhd->pss_list) { | |||||
lws_callback_on_writable((*ppss)->wsi); | |||||
} | |||||
lws_end_foreach_llp(ppss, pss_list); | |||||
return 0; | |||||
} |
@@ -6,26 +6,12 @@ | |||||
#include "network/IZ_wsserver.h" | #include "network/IZ_wsserver.h" | ||||
#include "log/IZ_log.h" | #include "log/IZ_log.h" | ||||
#include "IZ_common.h" | #include "IZ_common.h" | ||||
#include "IZ_config.h" | |||||
typedef struct { | typedef struct { | ||||
i32 log_level; | |||||
u16 port; | |||||
} IZ_AppConfig; | |||||
typedef struct { | |||||
IZ_AppConfig config; | |||||
IZ_WSServerState server; | IZ_WSServerState server; | ||||
} IZ_App; | } IZ_App; | ||||
static const IZ_App IZ_APP_DEFAULT_STATE = { | |||||
.config = { | |||||
.log_level = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE, | |||||
.port = 42069, | |||||
}, | |||||
}; | |||||
static IZ_App* global_app; | |||||
IZ_ProcedureResult IZ_AppRun(IZ_App*, u8, const char**); | IZ_ProcedureResult IZ_AppRun(IZ_App*, u8, const char**); | ||||
#endif | #endif |
@@ -0,0 +1,8 @@ | |||||
#include "IZ_config.h" | |||||
void IZ_ConfigGetDefaultPath(const char* config_path, size_t string_size) { | |||||
//const char* config_path_dir = SDL_GetPrefPath("Modal Studios", APP_NAME); | |||||
const char* config_path_dir = SDL_GetBasePath(); | |||||
memcpy_s(config_path, string_size, config_path_dir, 128); | |||||
strcat_s(config_path, string_size, "config-server.ini"); | |||||
} |
@@ -0,0 +1,10 @@ | |||||
#ifndef IZ_CONFIG_H | |||||
#define IZ_CONFIG_H | |||||
#include <SDL_filesystem.h> | |||||
#include <string.h> | |||||
// TODO unify loading of config from cmdline and config file | |||||
void IZ_ConfigGetDefaultPath(const char*, size_t); | |||||
#endif |
@@ -1,248 +1,5 @@ | |||||
#include "IZ_wsserver.h" | #include "IZ_wsserver.h" | ||||
void IZ_ProtocolCullLaggingClients(IZ_WSServerVHostData *vhd) { | |||||
u32 oldest_tail = lws_ring_get_oldest_tail(vhd->ring); | |||||
IZ_WSServerSessionData *old_pss = NULL; | |||||
i32 most = 0; | |||||
i32 before = (i32) lws_ring_get_count_waiting_elements(vhd->ring, &oldest_tail); | |||||
i32 m; | |||||
/* | |||||
* At least one guy with the oldest tail has lagged too far, filling | |||||
* the ringbuffer with stuff waiting for them, while new stuff is | |||||
* coming in, and they must close, freeing up ringbuffer entries. | |||||
*/ | |||||
lws_start_foreach_llp_safe( | |||||
IZ_WSServerSessionData**, | |||||
ppss, | |||||
vhd->pss_list, | |||||
pss_list | |||||
) { | |||||
if ((*ppss)->tail == oldest_tail) { | |||||
old_pss = *ppss; | |||||
lwsl_user("Killing lagging client %p\n", (*ppss)->wsi); | |||||
lws_set_timeout((*ppss)->wsi, PENDING_TIMEOUT_LAGGING, | |||||
/* | |||||
* we may kill the wsi we came in on, | |||||
* so the actual close is deferred | |||||
*/ | |||||
LWS_TO_KILL_ASYNC); | |||||
/* | |||||
* We might try to write something before we get a | |||||
* chance to close. But this pss is now detached | |||||
* from the ring buffer. Mark this pss as culled so we | |||||
* don't try to do anything more with it. | |||||
*/ | |||||
(*ppss)->culled = true; | |||||
/* | |||||
* Because we can't kill it synchronously, but we | |||||
* know it's closing momentarily and don't want its | |||||
* participation any more, remove its pss from the | |||||
* vhd pss list early. (This is safe to repeat | |||||
* uselessly later in the close flow). | |||||
* | |||||
* Notice this changes *ppss! | |||||
*/ | |||||
lws_ll_fwd_remove(IZ_WSServerSessionData, pss_list, (*ppss), vhd->pss_list); | |||||
/* use the changed *ppss so we won't skip anything */ | |||||
continue; | |||||
} | |||||
/* | |||||
* so this guy is a survivor of the cull. Let's track | |||||
* what is the largest number of pending ring elements | |||||
* for any survivor. | |||||
*/ | |||||
m = (i32) lws_ring_get_count_waiting_elements(vhd->ring, &((*ppss)->tail)); | |||||
if (m > most) { | |||||
most = m; | |||||
} | |||||
} lws_end_foreach_llp_safe(ppss); | |||||
/* it would mean we lost track of oldest... but Coverity insists */ | |||||
if (!old_pss) { | |||||
return; | |||||
} | |||||
/* | |||||
* Let's recover (ie, free up) all the ring slots between the | |||||
* original oldest's last one and the "worst" survivor. | |||||
*/ | |||||
lws_ring_consume_and_update_oldest_tail( | |||||
vhd->ring, | |||||
IZ_WSServerSessionData, | |||||
&old_pss->tail, | |||||
(size_t) (before - most), | |||||
vhd->pss_list, | |||||
tail, | |||||
pss_list | |||||
); | |||||
lwsl_user("%s: shrunk ring from %d to %d\n", __func__, before, most); | |||||
} | |||||
/* destroys the message when everyone has had a copy of it */ | |||||
IZ_ProcedureResult IZ_WSServerProtocolInitialize(struct lws* wsi) { | |||||
IZ_WSServerVHostData* vhd_instance = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
IZ_WSServerVHostData** vhd = &vhd_instance; | |||||
*vhd = lws_protocol_vh_priv_zalloc( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi), | |||||
sizeof(IZ_WSServerVHostData) | |||||
); | |||||
(*vhd)->context = lws_get_context(wsi); | |||||
(*vhd)->protocol = lws_get_protocol(wsi); | |||||
(*vhd)->vhost = lws_get_vhost(wsi); | |||||
(*vhd)->ring = lws_ring_create( | |||||
sizeof(IZ_WebsocketMessage), | |||||
RING_COUNT, | |||||
IZ_WebsocketDestroyMessage | |||||
); | |||||
if (!(*vhd)->ring) { | |||||
return 1; | |||||
} | |||||
return 0; | |||||
} | |||||
void IZ_WSServerProtocolTeardown(struct lws* wsi) { | |||||
IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
lws_ring_destroy(vhd->ring); | |||||
} | |||||
void IZ_WSServerOnOpen(struct lws* wsi, IZ_WSServerSessionData* pss) { | |||||
IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
/* add ourselves to the list of live pss held in the vhd */ | |||||
lwsl_user("LWS_CALLBACK_ESTABLISHED: wsi %p\n", wsi); | |||||
lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); | |||||
pss->tail = lws_ring_get_oldest_tail(vhd->ring); | |||||
pss->wsi = wsi; | |||||
} | |||||
void IZ_WSServerOnClose(struct lws* wsi, IZ_WSServerSessionData* pss) { | |||||
IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
lwsl_user("LWS_CALLBACK_CLOSED: wsi %p\n", wsi); | |||||
/* remove our closing pss from the list of live pss */ | |||||
lws_ll_fwd_remove(IZ_WSServerSessionData, pss_list, pss, vhd->pss_list); | |||||
} | |||||
IZ_ProcedureResult IZ_WSServerWritable(struct lws* wsi, IZ_WSServerSessionData* pss) { | |||||
IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
if (pss->culled) { | |||||
return 0; | |||||
} | |||||
const IZ_WebsocketMessage* pmsg = lws_ring_get_element(vhd->ring, &pss->tail); | |||||
if (!pmsg) { | |||||
return 0; | |||||
} | |||||
/* notice we allowed for LWS_PRE in the payload already */ | |||||
i32 m = lws_write( | |||||
wsi, | |||||
((unsigned char*) pmsg->payload) + LWS_PRE, | |||||
pmsg->len, | |||||
pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT | |||||
); | |||||
if (m < (i32) pmsg->len) { | |||||
lwsl_err("ERROR %d writing to ws socket\n", m); | |||||
return -1; | |||||
} | |||||
lws_ring_consume_and_update_oldest_tail( | |||||
vhd->ring, /* lws_ring object */ | |||||
IZ_WSServerSessionData, /* type of objects with tails */ | |||||
&pss->tail, /* tail of guy doing the consuming */ | |||||
1, /* number of payload objects being consumed */ | |||||
vhd->pss_list, /* head of list of objects with tails */ | |||||
tail, /* member name of tail in objects with tails */ | |||||
pss_list /* member name of next object in objects with tails */ | |||||
); | |||||
/* more to do for us? */ | |||||
if (lws_ring_get_element(vhd->ring, &pss->tail)) { | |||||
/* come back as soon as we can write more */ | |||||
lws_callback_on_writable(pss->wsi); | |||||
} | |||||
return 0; | |||||
} | |||||
void IZ_WSServerOnReceive(struct lws* wsi, void* in, size_t len) { | |||||
IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get( | |||||
lws_get_vhost(wsi), | |||||
lws_get_protocol(wsi) | |||||
); | |||||
IZ_WebsocketMessage amsg; | |||||
i32 n = (i32) lws_ring_get_count_free_elements(vhd->ring); | |||||
if (!n) { | |||||
/* forcibly make space */ | |||||
IZ_ProtocolCullLaggingClients(vhd); | |||||
n = (i32) lws_ring_get_count_free_elements(vhd->ring); | |||||
} | |||||
if (!n) { | |||||
return; | |||||
} | |||||
lwsl_user("LWS_CALLBACK_RECEIVE: free space %d\n", n); | |||||
amsg.len = len; | |||||
/* notice we over-allocate by LWS_PRE... */ | |||||
amsg.payload = malloc(LWS_PRE + len); | |||||
amsg.binary = (u8) lws_frame_is_binary(wsi); | |||||
if (!amsg.payload) { | |||||
lwsl_user("OOM: dropping\n"); | |||||
return; | |||||
} | |||||
/* ...and we copy the payload in at +LWS_PRE */ | |||||
memcpy((char *) amsg.payload + LWS_PRE, in, len); | |||||
if (!lws_ring_insert(vhd->ring, &amsg, 1)) { | |||||
IZ_WebsocketDestroyMessage(&amsg); | |||||
lwsl_user("dropping!\n"); | |||||
return; | |||||
} | |||||
/* | |||||
* let everybody know we want to write something on them | |||||
* as soon as they are ready | |||||
*/ | |||||
lws_start_foreach_llp(IZ_WSServerSessionData**, ppss, vhd->pss_list) { | |||||
lws_callback_on_writable((*ppss)->wsi); | |||||
} | |||||
lws_end_foreach_llp(ppss, pss_list); | |||||
} | |||||
IZ_ProcedureResult IZ_WSServerCallback( | IZ_ProcedureResult IZ_WSServerCallback( | ||||
struct lws* wsi, | struct lws* wsi, | ||||
enum lws_callback_reasons reason, | enum lws_callback_reasons reason, | ||||
@@ -252,7 +9,7 @@ IZ_ProcedureResult IZ_WSServerCallback( | |||||
) { | ) { | ||||
switch (reason) { | switch (reason) { | ||||
case LWS_CALLBACK_PROTOCOL_INIT: | case LWS_CALLBACK_PROTOCOL_INIT: | ||||
return IZ_WSServerProtocolInitialize(wsi); | |||||
return IZ_WSServerProtocolInitialize(wsi, in); | |||||
case LWS_CALLBACK_PROTOCOL_DESTROY: | case LWS_CALLBACK_PROTOCOL_DESTROY: | ||||
IZ_WSServerProtocolTeardown(wsi); | IZ_WSServerProtocolTeardown(wsi); | ||||
break; | break; | ||||
@@ -265,8 +22,7 @@ IZ_ProcedureResult IZ_WSServerCallback( | |||||
case LWS_CALLBACK_SERVER_WRITEABLE: | case LWS_CALLBACK_SERVER_WRITEABLE: | ||||
return IZ_WSServerWritable(wsi, user); | return IZ_WSServerWritable(wsi, user); | ||||
case LWS_CALLBACK_RECEIVE: | case LWS_CALLBACK_RECEIVE: | ||||
IZ_WSServerOnReceive(wsi, in, len); | |||||
break; | |||||
return IZ_WSServerOnReceive(wsi, in, len); | |||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
@@ -274,10 +30,27 @@ IZ_ProcedureResult IZ_WSServerCallback( | |||||
return 0; | return 0; | ||||
} | } | ||||
IZ_ProcedureResult IZ_WSServerInitialize(IZ_WSServerState* state, IZ_WSServerInitializeParams params) { | |||||
void IZ_WSServerLoadConfig(IZ_WSServerState* state, const char* config_path, u8 argc, const char* argv[]) { | |||||
// TODO unify loading of config from cmdline and config file | |||||
memcpy_s(state, sizeof(IZ_WSServerState), &IZ_DEFAULT_STATE, sizeof(IZ_WSServerState)); | |||||
const char *cmdline_buffer; | |||||
// if ((cmdline_buffer = lws_cmdline_option(argc, argv, "-d"))) { | |||||
// state->config.log_level = atoi(cmdline_buffer); | |||||
// } | |||||
if ((cmdline_buffer = lws_cmdline_option(argc, argv, "-p"))) { | |||||
state->config.port = atoi(cmdline_buffer); | |||||
} | |||||
} | |||||
IZ_ProcedureResult IZ_WSServerInitialize(IZ_WSServerState* state, void* userdata, const char* config_path, u8 argc, const char* argv[]) { | |||||
IZ_WSServerLoadConfig(state, config_path, argc, argv); | |||||
state->userdata = userdata; | |||||
struct lws_context_creation_info info; | struct lws_context_creation_info info; | ||||
memset(&info, 0, sizeof info); | memset(&info, 0, sizeof info); | ||||
info.port = params.port; | |||||
info.port = state->config.port; | |||||
static struct lws_http_mount mount = { | static struct lws_http_mount mount = { | ||||
.mount_next = NULL, /* linked-list "next" */ | .mount_next = NULL, /* linked-list "next" */ | ||||
@@ -298,17 +71,11 @@ IZ_ProcedureResult IZ_WSServerInitialize(IZ_WSServerState* state, IZ_WSServerIni | |||||
.mountpoint_len = 1, /* char count */ | .mountpoint_len = 1, /* char count */ | ||||
.basic_auth_login_file = NULL, | .basic_auth_login_file = NULL, | ||||
}; | }; | ||||
if (params.origin) { | |||||
mount.origin = params.origin; | |||||
if (state->config.origin) { | |||||
mount.origin = state->config.origin; | |||||
} | } | ||||
if (params.default_filename) { | |||||
mount.def = params.default_filename; | |||||
} | |||||
if (params.protocol) { | |||||
mount.protocol = params.protocol; | |||||
if (state->config.default_filename) { | |||||
mount.def = state->config.default_filename; | |||||
} | } | ||||
info.mounts = &mount; | info.mounts = &mount; | ||||
@@ -334,6 +101,30 @@ IZ_ProcedureResult IZ_WSServerInitialize(IZ_WSServerState* state, IZ_WSServerIni | |||||
LWS_PROTOCOL_LIST_TERM, | LWS_PROTOCOL_LIST_TERM, | ||||
}; | }; | ||||
info.protocols = protocols; | info.protocols = protocols; | ||||
static struct lws_protocol_vhost_options pvo_port = { | |||||
NULL, | |||||
NULL, | |||||
"port", /* pvo name */ | |||||
NULL /* pvo value */ | |||||
}; | |||||
pvo_port.value = (void*) &state->config.port; | |||||
static struct lws_protocol_vhost_options pvo_app = { | |||||
&pvo_port, | |||||
NULL, | |||||
"app", | |||||
NULL, | |||||
}; | |||||
pvo_app.value = state->userdata; | |||||
static const struct lws_protocol_vhost_options pvo = { | |||||
NULL, /* "next" pvo linked-list */ | |||||
&pvo_app, /* "child" pvo linked-list */ | |||||
NETWORK_PROTOCOL, /* protocol name we belong to on this vhost */ | |||||
"" /* ignored */ | |||||
}; | |||||
info.pvo = &pvo; | |||||
info.options = ( | info.options = ( | ||||
LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_VALIDATE_UTF8 | ||||
| LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | | LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | ||||
@@ -21,34 +21,36 @@ typedef struct { | |||||
const struct lws_protocols *protocol; | const struct lws_protocols *protocol; | ||||
IZ_WSServerSessionData *pss_list; /* linked-list of live pss*/ | IZ_WSServerSessionData *pss_list; /* linked-list of live pss*/ | ||||
struct lws_ring *ring; /* ringbuffer holding unsent messages */ | struct lws_ring *ring; /* ringbuffer holding unsent messages */ | ||||
u16* port; | |||||
const void* app; | |||||
} IZ_WSServerVHostData; | } IZ_WSServerVHostData; | ||||
IZ_ProcedureResult IZ_WSServerCallback( | |||||
struct lws*, | |||||
enum lws_callback_reasons, | |||||
void*, | |||||
void*, | |||||
size_t | |||||
); | |||||
typedef struct { | typedef struct { | ||||
u16 port; | u16 port; | ||||
const char* origin; | const char* origin; | ||||
const char* default_filename; | const char* default_filename; | ||||
const char* protocol; | |||||
} IZ_WSServerInitializeParams; | } IZ_WSServerInitializeParams; | ||||
typedef struct { | typedef struct { | ||||
i32 log_level; | |||||
u16 port; | |||||
} IZ_WSServerConfig; | |||||
typedef struct { | |||||
IZ_WSServerConfig config; | |||||
IZ_WSServerInitializeParams config; | |||||
void* userdata; | |||||
IZ_Websocket ws; | IZ_Websocket ws; | ||||
} IZ_WSServerState; | } IZ_WSServerState; | ||||
IZ_ProcedureResult IZ_WSServerInitialize(IZ_WSServerState*, IZ_WSServerInitializeParams); | |||||
static IZ_WSServerState IZ_DEFAULT_STATE = { | |||||
.config = { | |||||
.port = 42069, | |||||
.origin = "./public", | |||||
.default_filename = "index.html", | |||||
}, | |||||
.userdata = NULL, | |||||
.ws = { | |||||
.interrupted = false, | |||||
.context = NULL, | |||||
}, | |||||
}; | |||||
IZ_ProcedureResult IZ_WSServerInitialize(IZ_WSServerState*, void*, const char*, u8, const char**); | |||||
IZ_ProcedureResult IZ_WSServerHandle(IZ_WSServerState*); | IZ_ProcedureResult IZ_WSServerHandle(IZ_WSServerState*); | ||||
@@ -56,4 +58,16 @@ void IZ_WSServerTeardown(IZ_WSServerState*); | |||||
void IZ_WSServerCancelService(IZ_WSServerState*); | void IZ_WSServerCancelService(IZ_WSServerState*); | ||||
IZ_ProcedureResult IZ_WSServerProtocolInitialize(struct lws*, void*); | |||||
void IZ_WSServerProtocolTeardown(struct lws*); | |||||
IZ_ProcedureResult IZ_WSServerWritable(struct lws*, IZ_WSServerSessionData*); | |||||
IZ_ProcedureResult IZ_WSServerOnReceive(struct lws*, void*, size_t); | |||||
void IZ_WSServerOnOpen(struct lws*, IZ_WSServerSessionData*); | |||||
void IZ_WSServerOnClose(struct lws*, IZ_WSServerSessionData*); | |||||
#endif | #endif |