text: ﻿#include <stdlib.h>
  #include <stdio.h>
  #include "server.h"
  
  #pragma comment(lib, "websockets_static.lib")
  #pragma comment(lib, "winmm.lib")
  #pragma comment(lib, "Ws2_32.lib")
  
  client_t *client_insert(client_t **head, libwebsocket *socket) {
  	client_t *client = (server_client_t *)malloc(sizeof(server_client_t));
  	client->socket = socket;
  	client->next = *head;
  	*head = client;
  	return client;
  }
  
  void client_remove(client_t **head, libwebsocket *socket) {
  	for( client_t **current = head; *current; current = &(*current)->next ) {
  		if( (*current)->socket == socket ) {
  			client_t* next = (*current)->next;
  			free(*current);
  			*current = next;
  			break;
  		}
  	}
  }
  
  
  static int callback_http(struct libwebsocket_context *, struct libwebsocket *, enum libwebsocket_callback_reasons, void *, void *, size_t);
  static int callback_websockets(struct libwebsocket_context *, struct libwebsocket *, enum libwebsocket_callback_reasons,void *, void *, size_t);
  
  static struct libwebsocket_protocols server_protocols[] = {
  	{ "http-only", callback_http, 0 },
  	{ NULL, callback_websockets, sizeof(int), 1024*1024 },
  	{ NULL, NULL, 0 /* End of list */ }
  };
  
  server_t *server_create(int port, size_t buffer_size) {
  	server_t *self = (server_t *)malloc(sizeof(server_t));
  	memset(self, 0, sizeof(server_t));
  	
  	self->buffer_size = buffer_size;
  	size_t full_buffer_size = LWS_SEND_BUFFER_PRE_PADDING + buffer_size + LWS_SEND_BUFFER_POST_PADDING;
  	self->send_buffer_with_padding = (unsigned char *)malloc(full_buffer_size);
  	self->send_buffer = &self->send_buffer_with_padding[LWS_SEND_BUFFER_PRE_PADDING];
  
  	self->port = port;
  	self->clients = NULL;
  
  	lws_set_log_level(LLL_ERR | LLL_WARN, NULL);
  
  	struct lws_context_creation_info info = {0};
  	info.port = port;
  	info.gid = -1;
  	info.uid = -1;
  	info.user = (void *)self;
  	info.protocols = server_protocols;
  	self->context = libwebsocket_create_context(&info);
  
  	if( !self->context ) {
  		server_destroy(self);
  		return NULL;
  	}
  
  	return self;
  }
  
  void server_destroy(server_t *self) {
  	if( self == NULL ) { return; }
  
  	if( self->context ) {
  		libwebsocket_context_destroy(self->context);
  	}
  	
  	free(self->send_buffer_with_padding);
  	free(self);
  }
  
  char *server_get_host_address(server_t *self) {
  	char host_name[80];
  	hostent *host;
  	if( 
  		gethostname(host_name, sizeof(host_name)) == SOCKET_ERROR ||
  		!(host = gethostbyname(host_name))
  	) {
  		return "127.0.0.1";
  	}
  
  	return inet_ntoa( *(IN_ADDR *)(host->h_addr_list[0]) );
  }
  
  char *server_get_client_address(server_t *self, libwebsocket *wsi) {
  	static char ip_buffer[32];
  	static char name_buffer[32];
  
  	libwebsockets_get_peer_addresses(
  		self->context, wsi, libwebsocket_get_socket_fd(wsi), 
  		name_buffer, sizeof(name_buffer), 
  		ip_buffer, sizeof(ip_buffer)
  	);
  	return ip_buffer;
  }
  
  void server_update(server_t *self) {
  	libwebsocket_callback_on_writable_all_protocol(&(server_protocols[1]));
  	libwebsocket_service(self->context, 0);
  }
  
  void server_send(server_t *self, libwebsocket *socket, void *data, size_t size, server_data_type_t type) {
  	// Caution, this may explode! The libwebsocket docs advise against ever calling libwebsocket_write()
  	// outside of LWS_CALLBACK_SERVER_WRITEABLE. Honoring this advise would complicate things quite
  	// a bit - and it seems to work just fine on my system as it is anyway.
  	// This won't work reliably on mobile systems where network buffers are typically much smaller.
  	// ¯\_(ツ)_/¯
  
  	if( size > self->buffer_size ) {
  		printf("Cant send %d bytes; exceeds buffer size (%d bytes)\n", size, self->buffer_size);
  		return;
  	}
  	memcpy(self->send_buffer, data, size);
  	libwebsocket_write(socket, self->send_buffer, size, (libwebsocket_write_protocol)type);
  }
  
  void server_broadcast(server_t *self, void *data, size_t size, server_data_type_t type) {
  	if( size > self->buffer_size ) {
  		printf("Cant send %d bytes; exceeds buffer size (%d bytes)\n", size, self->buffer_size);
  		return;
  	}
  	memcpy(self->send_buffer, data, size);
  
  	client_foreach(self->clients, client) {
  		libwebsocket_write(client->socket, self->send_buffer, size, (libwebsocket_write_protocol)type);
  	}
  	libwebsocket_callback_on_writable_all_protocol(&(server_protocols[1]));
  }
  
  static int callback_websockets(
  	struct libwebsocket_context *context,
  	struct libwebsocket *wsi,
  	enum libwebsocket_callback_reasons reason,
  	void *user, void *in, size_t len
  ) {
  	server_t *self = (server_t *)libwebsocket_context_user(context);
  
  	switch( reason ) {
  		case LWS_CALLBACK_ESTABLISHED:
  			client_insert(&self->clients, wsi);
  
  			if( self->on_connect ) {
  				self->on_connect(self, wsi);
  			}
  			break;
  
  		case LWS_CALLBACK_RECEIVE:
  			if( self->on_message ) {
  				self->on_message(self, wsi, in, len);
  			}
  			break;
  
  		case LWS_CALLBACK_CLOSED:
  			client_remove(&self->clients, wsi);		
  		
  			if( self->on_close ) {
  				self->on_close(self, wsi);
  			}
  			break;
  	}
  	
  	return 0;
  }
  
  static int callback_http(
  	struct libwebsocket_context *context,
  	struct libwebsocket *wsi,
  	enum libwebsocket_callback_reasons reason, void *user,
  	void *in, size_t len
  ) {
  	server_t *self = (server_t *)libwebsocket_context_user(context);
  	
  	if( reason == LWS_CALLBACK_HTTP ) {
  		if( self->on_http_req && self->on_http_req(self, wsi, (char *)in) ) {
  			return 0;
  		}
  		libwebsockets_return_http_status(context, wsi, HTTP_STATUS_NOT_FOUND, NULL);
  		return 0;
  	}
  	
  	return 0;
  }
  
  