EventHandler for C and C++

Asynchronous Persistent Bi-directional Communication Layer

The EventHandler is a light-weight protocol stack that sits on top of the HTTP protocol. The EventHandler consists of a server plugin to the Barracuda Embedded Web-Server and a client protocol stack. Only a client can initiate a persistent connection, but as soon as a connection is established, the server can send data at any time to the client.

The benefits of using the EventHandler compared to a remote procedure protocol such as Ajax, is that the server can send asynchronous messages to a client at any time. Regular clients using the HTTP protocol are limited to polling the server for data.

The EventHandler Protocol

As we already mentioned, the EventHandler provides a Full Duplex Asynchronous Communication Link. An Asynchronous Communication Link is different from a Remote Procedure Calling protocol in that events sent to the other end cannot return a value. You can think of an event as a function in C that does not return a value.

void doSomething(...);

The ... denotes a variable length argument.

A remote procedure protocol can be emulated by sending a message back to the sender -- i.e., to the client that sent the server a message. Each message received in the server contains a Connection Identification number (CID). The server implementation can save the CID and use the CID when sending response messages. Unlike an RPC call, the EventHandler can send any number of response messages to a client.

The EventHandler protocol is designed to be simple to use. The protocol is designed such that it maps easily to C functions and to some extent C structures. When using the stub compiler, you simply call a function when sending a message. The stub code together with the EventHandler encodes the parameters and sends the data over the wire. The receiving end decodes the data, locates the callback function and calls the callback function with the parameters sent from the "sender". You can send any number of parameters, though, this is limited by memory constraints in the server.

Only C code uses callback functions. C++ uses virtual abstract functions, which you must implement. Java code uses interfaces and JavaScript uses function objects. You do not directly call a function in the far end, but a method in an object. The EventHandler protocol is designed such that it expects the name of an object and a name of a method to call in the far end.

The EventHandler protocol defines the following types: int, long, double, string, and the following array types: int[], long[], double[], string[] and byte[]. This means that an argument can be of any of these types, and you can have any number of arguments.

The EventHandler protocol handles dynamic length arrays, though, you will see later that the EventHandlerCompiler (the stub compiler) can impose restrictions and make fixed length arrays that are more compatible with C structures and fixed length arrays.

The following table shows mapping between the EventHandler types used by the EventHandlerCompiler and the types in the supported languages.

EventHandlertypeC/C++JavaJavaScript
intS32Integernumber
longU64Longnumber
doubledoubleDoublenumber
stringconst char*Stringstring
int[]S32*int[]number array
long[]U64*long[]number array
double[]double*double[]number array
string[]const char**string[]string array
byte[]U8*byte[]n/a

S32 is a signed 32 bit number defined in the Barracuda server platform. U64 is an unsigned 64 bit number, though, the type is considered signed by the EventHandler protocol. The type is also supported on platforms with no support for 64 bit numbers.

A string must be ASCII or UTF8. A UTF8 string sent from the server to a browser client is converted to JavaScript Unicode -- i.e. a UTF8 multi-byte character is converted to \uxxxx. The EventHandler is internally using function baPrintJsonString for the string formatting.

As you can see from the Java types, the Java EventHandler protocol stack handles objects and not primitive types. The EventHandlerCompiler generates Java stub code that automatically converts Integer, Long and Double to and from int, long and double.

EventHandlerCompiler

(The EventHandler Stub Compiler)

You can directly access the client side and server side of the EventHandler, but we have created a stub compiler that makes it easier for you to use the communication stack. Using the raw interface on the client side is easier than using the C or C++ interface on the server side. We strongly suggest that you use the stub compiler, as the compiler also makes sure the integrity of the interface cannot be violated.

We use the extension .ehi for EventHandler Interfaces. You may choose any extension except .js, .java, cpp, .c, hpp or .h, as the EventHandlerCompiler generates stubs with these extensions.

An EventHandler interface, "ehi", file can contain any number of interface declarations. The EventHandlerCompiler generates C or C++ stub files, JavaScript stub files, or Java stub files, for each interface in the ehi file. The code generated is controlled by various command line flags when running the EventHandlerCompiler.

An interface starts with either the "client" or "server" keyword followed by a name. The keyword "client" indicates the interface the client exposes to the server -- that is, the function(s) the server can call in the client. The keyword "server" indicates the interface the server exposes to the client -- that is, the function(s) the client can call in the server.

An interface must have at least one declared function, but there is no limit to the number of functions an interface can contain.

An interface function can take no arguments or any number of the types: int, double, string, int[], double[], string[] and byte[]. There is no limit to the number of arguments a function can take.

You cannot construct complex types -- that is, types consisting of other types. You may consider using our JSON-RPC stack if your application requires that you send types not supported by the EventHandler. The JSON-RPC stack can be used together with the EventHandler stack. You can, for example, use the EventHandler such that it sends an asynchronous event to a client, telling the client to load a new object using JSON-RPC.

Ehi EBNF notation:

Ehi file: (interface)+

interface : 
  (  <client>  | 
     <server>
  ) <name> { (function)+ }

function : <name> {  (argument)* }

argument: 
  (  <int>    |
     <double> |
     <string> |
     <byte>
  )  <name> ( '[' ( <number> )? ']' )?

An example of a very simple interface a client exposes to the server:

client ClientStocksExchange
{
   databaseChanged
   {
   }
}

The client intererface ClientStocksExchange contains one function -- the databaseChanged function. This function takes no arguments.

The above interface could be used in a system where the client would normally have to continuously poll a server for new Stock Exchange information. By using the EventHandler, the server can instead notify the client of any new information on the server. The server calls a JavaScript function on the client side and this function would fetch a new HTML page from the server. Mixing events and server side scripting makes it possible to instantly update the HTML page when there is new data on the server side.

The above interface could be changed to include information about which page to load:

client ClientStocksExchange
{
   databaseChanged
   {
      string url;
   }
}

The server now informs the client of which page to load when the database changes.


A method can contain any number of arguments:

server MyIntf1
{
   updateUser
   {
      string firstName;
      string lastName;
      int    day;
      int    month;
      int    year;
      double mortgageRate;
   }
}


You can see from the EBNF notation above that it is also possible to send and receive arrays:

server MyIntf2
{
   updateTable
   {
      int table[10];
      string names[];
   }
}

Here, we declared an array of type S32 with length 5 and an array of type string with a dynamic length.

Command Line

The EventHandlerCompiler needs a java virtual machine in order to run. You can download a virtual machine from SUN if you do not have a virtual machine installed on your computer. We have converted the EventHandlerCompiler.jar file to a Windows executable on the Windows platform.

The EventHandlerCompiler is started with the following command:

$EventHandlerCompiler
Barracuda EventHandler stub-compiler.
Compiles interface files for the Bi-Directional EventHandler stack.
Copyright (C) 2003 - 2005 Real Time Logic. All rights reserved.

command [flags] 
flags:
  -CS    Generate C server code.
  -CppS  Generate C++ server code. (Overrides -CS).
  -JS    Generate client JavaScript code.
  -J <P> Generate client Java code. P=package name.

  -v     Generate argument validation code.
  -o <N> output directory. N=Directory name.
  -nd    Do not generate code for 'double'.
  -na    Do not generate C argument conversion code.

Always use the -v flag unless you produce production code.
-nd must be used if the EventHandler code is compiled with macro NO_DOUBLE
-na cannot be used if the server communicates with clients using the text based EventHandler protocol such as the JavaScript EventHandler stack.

EventHandler Interface

The following example will be referred to throughout the documentation. The person.ehi file consists of two interfaces: one interface from the client to the server (the Request interface) and one interface from the server to the client (the Response interface).

server PersonRequest
{
   name
   {
      string ssn; //Social Security Number
   }
}

client PersonResponse
{
   name
   {
      string firstName;
      string lastName;
      int    year;
   }
}

EventHandler Stub Code

Please see the reference manual if you are using the Java EventHandler stack.

JavaScript Stub Code

The command:
EventHandlerCompiler -JS person.ehi

Generates PersonRequest.js and PersonResponse.txt.

PersonRequest.js:

Contains the JavaScript object PersonRequest.

You instantiate one instance of this object as:

var  personRequest = PersonRequest(eventHandler);

We will show you later how to create the EventHandler object you pass into the constructor of PersonRequest.

You can now call the only member function in this interface:

personRequest.name("999-99-9999");

The request is sent to the server and handled by the server side C++ code.

PersonResponse.txt

This text file contains all the stub functions the server can call on the client side. You should copy the content of this file into your JavaScript code and add your code for handling the incoming data.

function PersonResponse(eh)
{
   this.eh = eh;
   eh.addInterface(this,'PersonResponse');
};

PersonResponse.prototype.name=function(firstName, lastName, year)
{
// ADD CODE HERE
};

You will get a JavaScript error popping up if you do not include all the functions in your code.

C++ stub code

The compiler can generate either C or C++ code for your server side code. Use the C++ interface if you have a C++ compiler. We will show you how to use the C++ interface. Please see the Simple Debugger example for how to use the C interface.

The command:
EventHandlerCompiler -CppS person.ehi

Generates PersonRequest.hpp, PersonResponse.hpp, and PersonRequest.cpp.

PersonRequest.hpp and PersonRequest.cpp

The PersonRequest header file contains the abstract base class PersonRequest. You must implement the abstract functions in this interface. See "Using the server side EventHandler" for more information on how to implement the interface in an abstract class.

The EventHandler calls the server side function when an incoming request is parsed.

virtual void name(U32 cid, const char* ssn)=0;

The first parameter identifies the client and can be used when sending data back to the same client. The second parameter is the ssn number.

PersonResponse.hpp

The response header file contains the PersonResponse class. All client interface functions for this interface are defined in this class. Each client function has two corresponding functions in the client response class.

void name(const char* firstName, const char* lastName, S32 year);
void name(U32 cid, const char* firstName, const char* lastName, S32 year);

The first function sends a response to all connected clients -- i.e., a multicast message to all connected clients on this interface. The second function sends a response to one client. You would normally send a response back to the same client where the request came from.

Loading the JavaScript EventHandler

Please see the reference manual if you are using the Java EventHandler stack.

A typical GUI interface is designed in JavaScript. The JavaScript uses the browser's DOM API in order to change elements in the browser window dynamically. The GUI can be updated by either user interaction or when the server sends an asynchronous event to the client.

A rich-client interface would typically look like the following HTML page:

<html>
<head>
<script src="/rtl/eh/eh.js"></script>
<script src="PersonRequest.js"></script> 
<script src="myApplication.js"></script> 
<script>

var eh; //EventHandler
var personRequest;

function ConnectionStatus()
{
};
ConnectionStatus.prototype.onConnect=function(cid, pushConType)
{
   //Do something
};
ConnectionStatus.prototype.onDisconnect=function(e)
{
   alert("onDisconnect: "+e.toString());
};
ConnectionStatus.prototype.onError=function(e)
{
   alert("onError: "+e.toString());
};

onload = function()
{
   /* Create one instance of the Persistent bi-directional
    * asynchronous communication stack.
    */
   try {
      eh = new EventHandler(new ConnectionStatus(),
                            "/PersonRequest.interface");
      new PersonResponse(eh); //Bind EH to our response interface.
      eh.connect(); //Connect to server.
   }
   catch(e) {
         alert("Creating EventHandler failed: "+e.toString());
         return;
   }

   /* Create an instance of our client to server interface and
    * bind the interface to the EventHandler.
    */
   personRequest = new PersonRequest(eh);
};

</script>
</head>
<body>
</body>
</html> 

The script "/rtl/eh/eh.js" and "/rtl/eh/ODB.js" is the client EventHandler protocol stack and a library needed by the EventHandler.

The script "PersonRequest.js" is the PersonRequest interface generated by the EventHandler stub compiler.

The script "myApplication.js" is where you store the rich client application code. This code would typically create and modify html elements in the browser by using the browser's DOM API. As you can see from the above example, the page contains no html; i.e., the <body></body> section is empty.

The onload function is automatically run by the browser when the page and all imported scripts are loaded. Do not create the EventHandler stack before this function is run.

The EventHandler constructor takes a minimum of two arguments. The first argument is an object with a minimum of 3 methods: onConnect, onDisconnect and onError. The second argument is the path to where the server side EventHandler protocol stack is installed in the Virtual File System.

The client and server side must agree on the number of interfaces, the number of functions and the function's prototype. One interface can have a number of server and client side functions. The path to the server side EventHandler protocol stack is given to the client side EventHandler constructor, and the constructor binds the client side to the server side, thus creating a permanent connection.

The client and server can send asynchronous messages to each other as soon as a permanent connection is established. The client can also send messages prior to establishing the persistent connection. The messages will be added to an internal queue and flushed as soon as a persistent link is established.

Initializing the Server Side

The web-server works in co-operation with the EventHandler protocol stack on the server side. All requests sent up-stream, from the client to the server, look like any ordinary HTTP request to the Web-Server. The web-server parses the incoming data, extracts the path to the requested page, and gives the path to the virtual file system. The virtual file system tries to locate the resource and calls the page service function.

If you have read about server side scripting and the virtual file system, you know that it is possible to overload the functionality in a given directory. This is what the server side of the EventHandler is doing.

The path specified when you create the EventHandler on the client side must match the path on the server side. For example, in the PersonRequest interface, the client specifies the path as "/PersonRequest.interface".

The server side must match this path and have an EventHandler object installed in the "/PersonRequest.interface" directory. You can use any name for the directory, and you can use any number of sub directories, as long as the client and server side path are the same.

The following steps must be followed in order to initializing the server side EventHandler. The following example is in C++.

01: ThreadMutex mutex;
02: SoDisp dispatcher(&mutex); 
03: HttpServer server(&dispatcher); //Create the Web-Server
04: DiskIo io;
05  HttpResRdr rootDir(&io, NULL);
06: EhDir ehDir(&server, "PersonRequest.interface"); //Create the EventHandler
07: rootDir.insertDir(&ehDir); //Insert the EventHandler in the root directory.
08: server.insertRootDir(&rootDir); //Insert root dir in Web-Server
09: server.insertRootDir(getEhDir(0)); // From  ClientEh.zip -> bin2c -> ClientEh.c

Line 1 to 3
You must first create one instance of the socket dispatcher loop and the web-server. The parameters to the web-server constructor are not shown in the above example.

Line 4 and 5
Create a root directory node.

Line 6
The EhDir is a container object for the EventHandler. You get to the EventHandler object through this object. There is no limit to the number of EhDir instances you can create and add to the virtual file system, as long as the objects do not have the same name and are installed in the same directory.

Line 7
The EhDir inherits from the HttpDir object, and an instance of this object must be installed in the path you specify on the client side, when you create the client side of the EventHandler.

Line 8
The root directory must be inserted into the Web-Server.

Line 9
This code installs the client side of the EventHandler in the virtual file system, thus making it possible for the client to load the client side EventHandler resources such as the JavaScript code. The getEhDir is in the file autogen/ClientEh.c, which we generate below.

The file autogen/ClientEh.c is generated from autogen/ClientEh.zip by running:

bin2c -Z getEhDir ClientEh.zip ClientEh.c

Please see the reference manual for more information on the content of the ClientEh.zip ZIP file.

Installing Server Side Callback Functions

A user defined callback function in the server is called every time a client calls a function in the ehi interface. The code generated by the EventHandlerCompiler simplifies the construction of the the callback functions. The code generated is either C or C++ code. We will show you how to use the C++ interface here. The C interface is similar.

The EventHandlerCompiler generates an abstract base class for each interface. As an example, we can construct the following class for the PersonRequest interface you saw earlier.

00: class Person : public EhConListener,
01:                       public EhDir,
02:                       public PersonRequest
03: {
04:    public:
05:       Person(HttpServer* server, HttpDir* rootDir);
06:    private:
07:       //Used with class EhConListener
08:       static int newClientCon(EhConListener* l, EventHandler* eh, U32 cid);
09:       static void clientConTerminated(EhConListener* l,EventHandler* eh,U32 cid);
10:       //From abstract base class PersonRequest
11:       virtual void name(U32 cid, const char* ssn);
12: };
13: Person::Person(HttpServer* server, HttpDir* rootDir) :
14:    EhConListener(newClientCon, clientConTerminated),
15:    EhDir("/PersonRequest.interface",server),
16:    PersonRequest(getEventHandler())
17: {
18:    EventHandler* eh = getEventHandler(); //getEventHandler is in EhDir
19:    eh->addConListener(this);
20:    rootDir->insertDir(this);
21: }
22: int Person::newClientCon(EhConListener* l, EventHandler* eh, U32 cid)
23: {
24:    Person* o = (Person*)l;
       return 0; //Return "accept connection". -1 = not accepted.
25: }
26: void Person::clientConTerminated(EhConListener* l,EventHandler* eh, U32 cid)
27: {
28:    Person* o = (Person*)l;
29: }
30: void Person::name(U32 cid, const char* ssn)
31: {
32:   PersonResponse response(getEventHandler());
33:   response.name(cid, "firstName", "lastName", 1985);
34: }

Line 0-3
We use multiple inheritance in the example. The EhConListener class makes it possible to be notified when a new client connects or disconnects. The EhDir class is the EventHandler container directory. PersonRequest is the class generated by the EventHandlerCompiler.

Line 8-9
The EhConListener class uses static callback functions instead of using virtual functions, since the EhConListener class must be compatible with C code.

Line 11
The overloaded virtual function from class PersonRequest.

Line 14
The EhConListener class takes two function pointers: one is to the function the EventHandler object calls when a new client connects, and one is to the function the EventHandler object calls when a client disconnects.

Line 16
The PersonRequest constructor takes the EventHandler object as argument. The EventHandler object is returned by the base class EhDir.

Line 19
Add the base class EhConListener to the listener list in the EventHandler such that we get connect and disconnect events.

Line 22-29
THe EventHandler calls these two functions every time a client connects and disconnects. These two functions do not have a "this" pointer since they are "static". We can get the "this" pointer by typecasting the base class EhConListener to the Person class.

Line 30-34
The EventHandler calls this function every time a client calls the "name" function in the PersonRequest interface. We have also shown you how you can use the response object. You would normally not create the response object in the callback function.

Threads and the EventHandler

The EventHandler callback functions run in the context of the dispatcher. The dispatcher locks the dispatcher mutex before delegating the request to the web-server. This means that any callback function running in the context of the dispatcher thread is protected by the dispatcher mutex.

The EventHandler also makes it possible to send asynchronous events to a client at any time. This event must be sent from a thread other than the thread running the dispatcher.

Threads calling any function in the web-server must lock the dispatcher mutex prior to making the function call. The thread may be locked by this mutex if the dispatcher is currently dispatching a socket event.

The purpose with the Dispatcher Mutex is to protect the Barracuda code when being used from more than one thread. The Dispatcher Mutex is automatically released just before the web-server sends data to a client, thus the Dispatcher Mutex does not protect a socket connection from being run from several threads.

The Simple Debugger example shows how to lock the dispatcher mutex.

Authentication

The following limitation applies to the JavaScript EventHandler client:

The JavaScript EventHandler can be used with any of the authenticator classes, but it is not recommended to directly use class FormAuthenticator for the EventHandler resource -- i.e. for the EhDir resource in the server.

Starting and running an EventHandler application is a three step operation:

  1. The DHTML and any JavaScript files used by the client application are loaded by the browser.
  2. The DHTML interface connects the client EventHandler to the EhDir resource in the server.
  3. During operation, the client EventHandler sends asynchronous data as HTTP POST requests to the EhDir resource.

Step one can use a FormAuthenticator as the browser will simply display the login form if the user is not authenticated. Step two can also use the FormAuthenticator as the client is, at this point, authenticated. The form authenticator simply forwards the request to the EhDir resource.

Step three works as long as the user is authenticated. If the user is for some reason logged out, the form authenticator sends a HTML page to the client EventHandler code. The client EventHandler code will not understand the HTML code and throws an exception. This exception is then presented as an error message to the user. This error message will be difficult to understand for a normal user.

Persistent Session

The EventHandler locks the HttpSession object when connected to a client. Thus, the user will normally not be logged out as long as a persistent EH connection is established. A user can be logged out if method AuthenticatedUser::logout is explicitly called.

See the Authenticator class for more information.