Browse Source

Implement a more involved example

Use minimal server echo example instead.
feature/data-structs
TheoryOfNekomata 2 years ago
parent
commit
9d2fe5b007
9 changed files with 428 additions and 210 deletions
  1. +6
    -2
      CMakeLists.txt
  2. +24
    -0
      src/packages/game/log/IZ_log.c
  3. +10
    -0
      src/packages/game/log/IZ_log.h
  4. +57
    -0
      src/packages/server/IZ_app.c
  5. +33
    -0
      src/packages/server/IZ_app.h
  6. +1
    -0
      src/packages/server/log
  7. +78
    -65
      src/packages/server/main.c
  8. +0
    -143
      src/packages/server/protocol_lws_minimal.c
  9. +219
    -0
      src/packages/server/protocol_lws_minimal_server_echo.c

+ 6
- 2
CMakeLists.txt View File

@@ -70,7 +70,7 @@ add_executable(
src/packages/game/input/IZ_midi.h
src/packages/game/data/IZ_list.c
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(
game
@@ -170,11 +170,15 @@ add_executable(
dependencies/minIni/dev/minIni.h
dependencies/minIni/dev/minIni.c
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/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(
server
SDL2main
SDL2
libcrypto
libssl
websockets


+ 24
- 0
src/packages/game/log/IZ_log.c View File

@@ -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);
}

+ 10
- 0
src/packages/game/log/IZ_log.h View File

@@ -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

+ 57
- 0
src/packages/server/IZ_app.c View File

@@ -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;
}

+ 33
- 0
src/packages/server/IZ_app.h View File

@@ -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

+ 1
- 0
src/packages/server/log View File

@@ -0,0 +1 @@
E:/Projects/Games/izanagi/src/packages/game/log

+ 78
- 65
src/packages/server/main.c View File

@@ -1,104 +1,117 @@
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>

#include "IZ_common.h"
#include "log/IZ_log.h"

#define LWS_PLUGIN_STATIC
#include "protocol_lws_minimal.c"
#include "protocol_lws_minimal_server_echo.c"

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
};

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;
}

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);
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 */
info.port = 6969;
info.mounts = &mount;
info.port = port;
info.protocols = protocols;
info.vhost_name = "localhost";
#if defined(LWS_WITH_PLUGINS)
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);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
}

while (n >= 0 && !interrupted)
i32 n;
while (!interrupted) {
n = lws_service(context, 0);
printf("%d\n", n);
if (n < 0) {
break;
}
}

lws_context_destroy(context);

lwsl_user("Completed %s\n", interrupted == 2 ? "OK" : "failed");

return 0;
return interrupted != 2;
}

+ 0
- 143
src/packages/server/protocol_lws_minimal.c View File

@@ -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 \
}


+ 219
- 0
src/packages/server/protocol_lws_minimal_server_echo.c View File

@@ -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 \
}


Loading…
Cancel
Save