Locations of visitors to this page

DSAS - DynaSet Application Server

  DSAS is a simple server and client API to execute
  Lua code at server side. Client API mimics the Lua API.
  Yami is used as MiddleWare. For client side, a Lua module
  is supplied. All supplied binaries are compiled with
  gcc/g++. Some are shrinked with Upx.
  
  Working schema is something like:
  
  .--------.    .-------.   .--------.
  | Client |<-->| DSASC |<->|  YAMI  |><---(tcp/ip)----.
  .--------.    .-------.   .--------.                 |
                                                       |
  .------------------------.   .-------.   .-------.   |
  |    LUA APPLICATIONS    |<->| DSAS  |<->|  YAMI |><-.      
  .------------------------.   .-------.   .-------.
    |   |   |   |   |   |  
  .-.---.---.---.---.---.--.
  |    wxSQLite3 etc.      |             (How is it? ;))
  .------------------------.
  
- Why?
  I was trying to add C/S to wxSQLite3; ended with this :)
  I'm sure there exists many similar tools, but it was/is
  fun to work on it.
  
- Implementation

  ./server/
    dsas.exe                  DSAS server
        options: -h            usage
                -p portnum    listening port [12340]
                -c            enable logging [off]
                      
    dsas.lua                  main application loader
                               Note: Check DSASPATH before use
             
  ./server/apps/
    authapp.lua                sample app
    luasqlite3app.lua          luasqlite3 app
   

  ./client/cpptest/
    test.exe                  test client api
        options: -h            usage
                -s host       server addr [127.0.0.1]
                -p port       server port [12340]
  
  ./client/luatest/
    tests.lua                 api tests
    tests_dsasc.lua           luasqlite3 tests (uses luasqlite3_dsasc.lua)

  
- Used libraries web addresses & readme files are in related dirs.
  No make file is given, because I'm using different tools
  to compile required libraries.
  Please check & change directories before use:
    lua:           Code::Blocks Project file
    yami:          Code::Blocks Project file
    dsas:          Code::Blocks Project file
    luadsasc:       m_luadsasc.bat
    luawxsqlite3:   m_wxsqlite3.bat
    client/cpptest: Code::Blocks Project file



- Client C-API:

// dsasc_call result codes
const int DSASC_SUCCESS        =  0;
const int DSASC_FAILURE        = -1;
const int DSASC_USAGE_ERROR    = -2;
const int DSASC_INTERNAL_ERROR = -3;
const int DSASC_INVALID_STATE  = -4;
const int DSASC_NET_ERROR      = -5;

// result value types
const int DSASC_TNONE          =-1;
const int DSASC_TNIL           = 0;
const int DSASC_TBOOLEAN       = 1;
const int DSASC_TNUMBER        = 2;
const int DSASC_TSTRING        = 3;


typedef struct dsasc_state dsasc_state;

// before using any function this should be called
int          dsasc_netinitialize();

// this should be use last
int          dsasc_netfinalize();

// to connect to server; if not successfull returns NULL
dsasc_state* dsasc_opendomainobject(const char* domainname, const char* objectname, const char* host, int port);
dsasc_state* dsasc_open(const char* host, int port);

// to disconnect
void         dsasc_close(dsasc_state* S);

// to check connection
int          dsasc_isconnected(dsasc_state* S);

// wait timeout for each dsasc_call; default=60 sn
void         dsasc_settimeout(dsasc_state* S, size_t seconds);

// to run remote app's procedure; returns one of result codes
int          dsasc_call(dsasc_state* S, const char* appname, const char* procname);

// returns last error message
const char*  dsasc_errorstring(dsasc_state* S);


// proc argument handling ---------------------------------------------------

// clear argument array; dsasc_call uses before exit
void         dsasc_clearargs(dsasc_state* S);

// #of arguments
size_t       dsasc_nargs(dsasc_state* S);

// push value to argument array
void         dsasc_pushnil(dsasc_state* S);
void         dsasc_pushnumber(dsasc_state* S, double val);
void         dsasc_pushinteger(dsasc_state* S, int val);
void         dsasc_pushboolean(dsasc_state* S, int val);
void         dsasc_pushstring(dsasc_state* S, const char* val);
void         dsasc_pushlstring(dsasc_state* S, const char* val, size_t len);


// proc result handling -----------------------------------------------------
// Note: result array is 0 indexed

// clear results array; dsasc_call uses in enterance
void         dsasc_clearresults(dsasc_state* S);

// #of results
size_t       dsasc_nresults(dsasc_state* S);

// result type; returns one of the value types
int          dsasc_type(dsasc_state* S, size_t idx);

// string representation of result type:
// "nil", "boolean", "number", "string"
const char*  dsasc_typename(dsasc_state* S, int tp);

// result type checking
int          dsasc_isnil(dsasc_state* S, size_t idx);
int          dsasc_isnumber(dsasc_state* S, size_t idx);
int          dsasc_isboolean(dsasc_state* S, size_t idx);
int          dsasc_isstring(dsasc_state* S, size_t idx);
int          dsasc_islstring(dsasc_state* S, size_t idx);  // having embedded 0'es?

// result value
double       dsasc_tonumber(dsasc_state* S, size_t idx);
int          dsasc_tointeger(dsasc_state* S, size_t idx);
int          dsasc_toboolean(dsasc_state* S, size_t idx);
const char*  dsasc_tostring(dsasc_state* S, size_t idx);
const char*  dsasc_tolstring(dsasc_state* S, size_t idx, size_t* len);


- Client Lua-API:

{ S }  = dsasc("host", port)
         S:settimeout(seconds)
bool   = S:isconnected()
int    = S:call("appname", "procname")
string = S:errorstring()
int    = S:nargs()
int    = S:nresults()
         S:push(value)
value  = S:result(idx)
string = S:resulttype(idx)
bool   = S:isnil(idx)
bool   = S:isnumber(idx)
bool   = S:isboolean(idx)
bool   = S:isstring(idx)
bool   = S:isbinary(idx)



- Usage:

- DSAS Application example: './server/apps/authapp.lua'

-- start of authapp.lua ----------------------------------------------------

local appname = "authapp"
module(appname, package.seeall)



_G[appname].Register = function()
    local R = {}
    R.objs  = {}
    R.procs = {}

    -------------------------------------------------------------------------
    -- args table structure:
    --   args[-2] : procedure name
    --   args[-1] : args count
    --   args[0]..: real arguments
    -------------------------------------------------------------------------

    -------------------------------------------------------------------------
    R.procs.Login = function(R, args)
            local usr = args[0]
            local pwd = args[1]
            assert(usr, "invalid user name")
            assert(pwd, "invalid password")
            -- here we can check user ...
            -- returning userid, etc.
            return 123, "Hello, "..usr.."!"
        end
    -------------------------------------------------------------------------
    R.procs.Logout = function(R, args)
            local usrid = args[0]
            assert(usrid, "invalid user id")
            return 1, "Bye!"
        end
    -------------------------------------------------------------------------

    _G[appname].registry = R
end

_G[appname].UnRegister = function()
    _G[appname].registry = nil
    collectgarbage()
end

_G[appname].GetRegistry = function()
    if not _G[appname].registry then
        _G[appname].Register()
    end
    return _G[appname].registry
end


-- dispatcher: called by DSAS
function runproc(args)
    local procname = args[-2]
    local nargs    = args[-1]
    local R        = _G[appname].GetRegistry()
    local proc     = R.procs[procname]

    assert(proc, "unknown proc")
    return proc(R, args)
end

-- end of authapp.lua --------------------------------------------------------

- To register authapp, add a 'require' line to './server/dsas.lua' as:

    require("authapp")         -- Sample application


- DSAS Lua Client example: from './client/luatest/tests.lua'

-- start of tests.lua ---------------------------------------------------------

require("luadsasc") --dsas client library

local S = dsasc("127.0.0.1", 12340) -- connect to localhost, with port:12340
assert(S, "couldn't connect")

--test Login proc--
S:push("myusername") --1st arg
S:push("mypassword") --2nd arg
local rc = S:call("authapp", "Login") --call remote proc
-- If successful rc==0, else rc<0. Use S:errorstring() to get error message
S:result(0) --remote proc results
S:result(1)
--
--test Logout proc--
S:push(2423)
local rc = S:call("authapp", "Logout")
S:result(0)
S:result(1)
--

-- end of tests.lua -----------------------------------------------------------



- DSAS cpp Client example: from './client/cpptest/test.cpp'

-- start of test.cpp ---------------------------------------------------------

#include "dsasc.h"

int main()
{
  if (DSASC_SUCCESS != dsasc_netinitialize()) return EXIT_FAILURE;
  dsasc_state* S = dsasc_open("127.0.0.1", 12340);
  if (!S) return EXIT_FAILURE;
  dsasc_pushstring(S, "myusername");
  dsasc_pushstring(S, "mypassword");
  int rc = dsasc_call(S, "authapp", "Login");
  if (rc != DSASC_SUCCESS)
  {
     cout << dsasc_errorstring(S);
  }
  else
  {
    cout << dsasc_tointeger(S, 0)
         << dsasc_tostring(S, 1)
  }
  dsasc_close(S);
  dsasc_netfinalize();
  return EXIT_SUCCESS;
}

-- end of test.cpp -----------------------------------------------------------


- License
  I don't understand license terms, so choosed one: wxWidgets
  
- Thanks to:
Lua
Yami
SQLite
luasqlite3
wxWidgets
wxSQLite3
Upx
  
- Comments and suggestions are welcome.