Ukázka použití API knihovny libxr

Níže naleznete kompletní implementaci (v C) ukázkového serveru, který umožňuje heslem zabezpečený přístup k stavovým informacím Zbloku, přes HTTP/SSL pomocí XML-RPC. Zblok takovýto kód nepoužívá, je to jen ukázka malé části toho co libxr umí.

Samotná knihovna není až zas tak zajímavá. Implementuje běžný více-vláknový HTTP server. Server má frontou příchozích spojení, krmenou hlavním threadem a thread pool workerů, kteří frontu vyjídávají a obsluhují samotná připojení a jednotlivá vzdálená volání procedur.

Libxr se odlišuje od ostatních knihoven v tom, že nabízí kompilátor jazyka pro popis rozhraní (XDL). XDL kompilátor provádí generování nudného kódu, který převádí nativní datové typy jazyka C na objekty xr_value a zpět.

V tom je hlavní hodnota libxr pro programátora, protože:

XDL soubor obsahující definici a implementaci rozhraní

namespace ZM; // Zblok Management Interface

error AUTH_FAILED = 1;
error NOT_AUTHORIZED = 2;

struct User
{
  string  username;
  string  realname;
  int     mail_usage;
}

struct Folder
{
  string  name;
  string  type;
  int     size;
}

struct SystemStatus
{
  int            uptime;
  array<User>    users;
  array<Folder>  folders;
}

servlet Server
{
  // Some code you want to get included at the top of servlet subs file.
  <%
#include <string.h>
  %>

  // Servlet variables (This code is inserted into struct definition. You can
  // access these variables using _priv pointer, see below for example)
  __attrs__
  <%
    int auth_ok;     // True if client is authenticated
    char* username;  // Contains username of the user (when authenticated)
  %>

  // This is called before first RPC call.
  __init__
  <%
    _priv->auth_ok = FALSE;
  %>

  // Called when client disconnects.
  __fini__
  <%
    g_free(_priv->username);
  %>

  // Called before RPC is processed using methods defined below. You may do some
  // common call processing here (authentication check, locking, etc.) Return
  // FALSE if you want to inhibit the actual RPC call.
  __pre_call__
  <%
    const char* method = xr_call_get_method(_call);
    if (strcmp(method, "auth"))
    {
      if (!_priv->auth_ok)
      {
        char* msg = g_strdup_printf("Method %s requires authentication. (call auth please).",
          method);
        xr_call_set_error(_call, ZM_XMLRPC_ERROR_NOT_AUTHORIZED, msg);
        g_free(msg);
        return FALSE;
      }
    }
    return TRUE;
  %>

  // Called after each call is processed and before server sends response to the
  // client.
  __post_call__
  <%
    xr_call_dump(_call, 0);
  %>

  boolean auth(string username, string password)
  <%
    g_free(_priv->username);
    _priv->username = NULL;
    _priv->auth_ok = FALSE;

    if (!strcmp(username, "bob") && !strcmp(password, "qwe"))
    {
      _priv->username = g_strdup(username);
      _priv->auth_ok = TRUE;
      return TRUE;
    }

    g_set_error(_error, 0, ZM_XMLRPC_ERROR_AUTH_FAILED, "Auth failed!");
  %>

  SystemStatus getSystemStatus()
  <%
    ZMUser *user;
    ZMFolder *folder;
    // create SystemStatus object
    retval = ZMSystemStatus_new();
    // create User object for bob
    user = ZMUser_new();
    user->username = g_strdup("bob");
    user->realname = g_strdup("Bobby");
    user->mail_usage = 20;
    retval->users = g_slist_append(retval->users, user);
    // create Folder object
    folder = ZMFolder_new();
    folder->name = g_strdup("data");
    folder->type = g_strdup("shared");
    folder->size = 1024*45;
    retval->folders = g_slist_append(retval->folders, folder);
  %>

  boolean changeUserPassword(string newpassword)
  <%
    if (strlen(newpassword) <= 5)
    {
      g_set_error(_error, 0, ZM_XMLRPC_ERROR_NOT_AUTHORIZED, "Password is too short!");
      return FALSE;
    }

    //XXX: change password

    return TRUE;
  %>
}

Kód XML-RPC serveru

#include "ZMServer.xrs.h"

int main(int ac, char* av[])
{
  GError* err = NULL;
  xr_servlet_def* defs[2] = { __ZMServerServlet_def(), NULL };

  g_thread_init(NULL);

  // start HTTPS server, with 5 threads in the pool, listening on port 1234,
  // and implementing ZMServer servlet.
  xr_server_simple("server.pem", 5, "*:1234", defs, &err);
  if (err)
    g_printerr("ERROR: %s\n", err->message);

  return !!err;
}

Zjednodušený XDL soubor obsahující definici a implementaci rozhraní

Výše uvedený kód lze ještě více zjednodušit, když vynecháme autentizaci. V C už to jednodušeji nejde. :-D

namespace ZM; // Zblok Management Interface

struct User
{
  string  username;
  string  realname;
  int     mail_usage;
}

struct Folder
{
  string  name;
  string  type;
  int     size;
}

struct SystemStatus
{
  int            uptime;
  array<User>    users;
  array<Folder>  folders;
}

servlet Server
{
  SystemStatus getSystemStatus()
  <%
    ZMUser *user;
    ZMFolder *folder;
    // create SystemStatus object
    retval = ZMSystemStatus_new();
    // create User object for bob
    user = ZMUser_new();
    user->username = g_strdup("bob");
    user->realname = g_strdup("Bobby");
    user->mail_usage = 20;
    retval->users = g_slist_append(retval->users, user);
    // create Folder object
    folder = ZMFolder_new();
    folder->name = g_strdup("data");
    folder->type = g_strdup("shared");
    folder->size = 1024*45;
    retval->folders = g_slist_append(retval->folders, folder);
  %>

  boolean changeUserPassword(string newpassword)
  <%
    if (strlen(newpassword) <= 5)
    {
      g_set_error(_error, 0, -1, "Password is too short!");
      return FALSE;
    }

    //XXX: change password

    return TRUE;
  %>
}

VALA klient

Bonus závěrem. :-D XDL kompiláor jsme naučil generovat bindings pro nový perspektivní jazyk VALA. Zde je krátká ukázka použití s výše uvedeným rozhraním.

using GLib;
using ZM;
using XR;

namespace ZM
{
  // Print the list of zblok users
  static void sysinfo() throws GLib.Error
  {
    var s = new ZM.Server();
    s.open("https://localhost:1234");

    var data = s.getSystemStatus();
    foreach (weak User u in data.users)
      stdout.printf("User %s '%s' (mail usage %d kB)\n",
        u.username, u.realname, u.mail_usage);
  }

  static int main()
  {
    XR.init();

    try {
      sysinfo();
    } catch(GLib.Error e) {
      stderr.printf("ERROR: %d: %s\n", e.code, e.message);
    }

    XR.fini();

    return 0;
  }
}

Alternativní XML-RPC knihovny napsané v C