/* /lib/server.c
* from the Nightmare IV LPC Library
* a TCP server object
* created by Descartes of Borg 950429
*/
#include <lib.h>
#include <network.h>
#include "server.h"
inherit LIB_DAEMON;
private static int DestructOnClose;
private static function Read;
private static class server Listen;
private static mapping Sockets;
static void create() {
daemon::create();
Sockets = ([]);
}
int eventCreateSocket(int port) {
int x;
Listen = new(class server);
Listen->Blocking = 0; /* servers are not blocking to start */
x = socket_create(STREAM, "eventServerReadCallback",
"eventServerAbortCallback");
if( x < 0 ) {
eventSocketError("Error in socket_create().", x);
return x;
}
Listen->Descriptor = x;
x = socket_bind(Listen->Descriptor, port);
if( x != EESUCCESS ) {
eventClose(Listen);
eventSocketError("Error in socket_bind().", x);
return x;
}
x = socket_listen(Listen->Descriptor, "eventServerListenCallback");
if( x != EESUCCESS ) {
eventClose(Listen);
eventSocketError("Error in socket_listen().", x);
return x;
}
}
static void eventServerListenCallback(int fd) {
int x;
x = socket_accept(fd, "eventServerReadCallback",
"eventServerWriteCallback");
if( x < 0 ) {
eventSocketError("Error in socket_accept().", x);
return;
}
eventNewConnection(x);
}
static void eventServerAbortCallback(int fd) {
if( Listen && Listen->Descriptor == fd) eventClose(Listen);
else if( Sockets[fd] ) eventClose(Sockets[fd]);
}
static void eventServerReadCallback(int fd, string str) {
if( functionp(Read) ) evaluate(Read, fd, str);
else eventRead(fd, str);
}
static void eventRead(int fd, string str) { }
static void eventServerWriteCallback(int fd) {
class server sock;
int x;
if( Listen && Listen->Descriptor == fd ) sock = Listen;
else if( Sockets[fd] ) sock = (class server)Sockets[fd];
else return;
sock->Blocking = 0;
if( !sock->Buffer && sock->Closing ) {
eventClose(sock);
return;
}
x = EESUCCESS;
while( sock->Buffer && x == EESUCCESS ) {
switch( x = socket_write(sock->Descriptor, sock->Buffer[0]) ) {
case EESUCCESS:
break;
case EECALLBACK:
sock->Blocking = 1;
break;
case EEWOULDBLOCK:
call_out( (: eventServerWriteCallback :), 0, fd);
return;
case EEALREADY:
sock->Blocking = 1;
return;
default:
eventClose(sock);
eventSocketError("Error in socket_write().", x);
return;
}
if( sizeof(sock->Buffer) == 1 ) {
sock->Buffer = 0;
if( sock->Closing && !sock->Blocking ) eventClose(sock);
}
else sock->Buffer = sock->Buffer[1..];
}
}
varargs void eventWrite(int fd, string str, int close) {
class server sock;
int x;
if( Listen && Listen->Descriptor == fd ) sock = Listen;
else if( Sockets[fd] ) sock = (class server)Sockets[fd];
else return;
if( sock->Buffer ) sock->Buffer += ({ str });
else sock->Buffer = ({ str });
sock->Closing = close;
if( sock->Blocking ) return;
else eventServerWriteCallback(sock->Descriptor);
}
static void eventClose(class server sock) {
if( !sock ) return;
if( Sockets[sock->Descriptor] ) map_delete(Sockets, sock->Descriptor);
socket_close(sock->Descriptor);
eventSocketClosed(sock->Descriptor);
sock = 0;
if( DestructOnClose && sock == Listen ) Destruct();
}
static void eventSocketClosed(int fd) { }
int eventDestruct() {
if( daemon::eventDestruct() ) {
eventClose(Listen);
return 1;
}
else return 0;
}
static void eventNewConnection(int fd) {
Sockets[fd] = new(class server);
((class server)Sockets[fd])->Descriptor = fd;
((class server)Sockets[fd])->Blocking = 0;
}
static void eventSocketError(string str, int x) { }
function SetRead(function f) { return (Read = f); }
int SetDestructOnClose(int x) { return (DestructOnClose = x); }