/*
* NAME: binary.c
* DESCRIPTION: connection object specialized for binary connections
*/
inherit "/std/connection";
# include <moo/errors.h>
private string inbuf; /* incoming data buffer */
private string outbuf; /* outgoing data buffer */
private int co_flush; /* flush() call_out handle */
private int flags; /* special binary flags */
# define F_CURRENT 0x01 /* execution started with this object */
# define F_CLOSING 0x02 /* connection is trying to close */
# define BUFTHRESH 2048 /* buffer limit before flushing */
/*
* NAME: create()
* DESCRIPTION: initialize data
*/
static
void create(void)
{
inbuf = outbuf = "";
::create();
}
/*
* NAME: flush()
* DESCRIPTION: try to send the output buffer to the connection
*/
void flush(void)
{
int len;
if (co_flush)
{
remove_call_out(co_flush);
co_flush = 0;
}
if (! strlen(outbuf))
return;
len = send_message(outbuf);
if (len >= 0)
{
if (strlen(outbuf = outbuf[len ..]))
co_flush = call_out("flush", 1);
}
else
outbuf = ""; /* not connected? */
}
/*
* NAME: finish()
* DESCRIPTION: called to finish processing; flush the output buffer
*/
void finish(void)
{
::finish();
flush();
flags &= ~F_CURRENT;
}
/*
* NAME: send()
* DESCRIPTION: called to send a partial line to the user (raw data)
*/
void send(string msg)
{
outbuf += msg;
if (! (flags & F_CURRENT) || strlen(outbuf) > BUFTHRESH)
flush();
}
/*
* NAME: notify()
* DESCRIPTION: send a line of text
*/
void notify(string msg)
{
send(msg + "\r\n");
}
/*
* NAME: get_line()
* DESCRIPTION: return a line of pending input, if available
*/
string get_line(void)
{
string line;
return (sscanf(inbuf, "%s\r\n%s", line, inbuf) == 2 ||
sscanf(inbuf, "%s\n%s", line, inbuf) == 2) ? line : 0;
}
/*
* NAME: get_data()
* DESCRIPTION: return any pending input as raw data
*/
string get_data(void)
{
string data;
if (strlen(inbuf))
{
data = inbuf;
inbuf = "";
}
else
data = 0;
return data;
}
/*
* NAME: receive_message()
* DESCRIPTION: called by DGD when binary data arrives
*/
static
void receive_message(string data)
{
string line;
if (flags & F_CLOSING) /* ignore further input */
return;
inbuf += data;
if (! strlen(inbuf))
return;
flags |= F_CURRENT;
if (want_binary())
{
data = inbuf;
inbuf = "";
process_data(data);
}
else if (line = get_line())
{
if (strlen(inbuf))
call_out("receive_message", 0, "");
process_line(line);
}
finish();
}
/*
* NAME: destruct()
* DESCRIPTION: attempt to flush the output buffer before destructing
*/
static
void destruct(void)
{
flags |= F_CLOSING;
if (co_flush)
{
call_out("destruct", 1);
return;
}
::destruct();
}