Getting Started

This Getting Started guide provides Lua and C/C++ programmers an introduction to the Barracuda App/Web Server library.

Lua and C programmers will be introduced to the Barracuda Virtual File System which is what makes Barracuda truly unique and powerful. It is crucial to have a good understanding of the Virtual File System because it is the foundation of your web application(s).

The Barracuda App/Web Server provides a C/C++ API and a Lua API for application developers. With the exception of the startup code which must be in C code, many of the Barracuda Web Server API's can be accessed from the Lua scripting language.

Constructing a Server

Creating a Barracuda Web Server involves creating a number of objects. These objects are assembled into a web server specifically designed for your application.

C code

The Barracuda App/Web Server Library is designed in ANSI C. This C library is designed in an object-oriented way compatible with C++. The header files contain special wrappers for C++ code. The following example uses C++. See the introduction to the C API for how to assemble a server using C code.

int main(int argc, char* argv[])
{
   ThreadMutex m; //Create the mutex protecting the dispatcher and server.
   SoDisp dispatcher(&m); //Create the socket dispatcher object.
   //Create the Web-Server object and bind it to the dispatcher
   HttpServer server(&dispatcher);
   // Create a listen object that listen on port 80
   HttpServCon servCon(&server, &dispatcher);
   // Create a secure listen object that listen on port 443
   HttpSharkSslServCon sslServCon(&server, &dispatcher, cert);
   dispatcher.run(); //Never returns.
}

The objects created above are explained in the C/C++ reference manual. The above code creates a platform independent mutex which protects the server in a multi thread enabled Barracuda configuration. The SoDisp is a platform independent socket dispatcher. This object dispatches socket events to the registered objects. For example, the servCon object and the sslServCon object are triggered when a client connects to port 80 and port 443. The HttpServCon object is the standard server listen object. This object typically listens for new client connections on port 80. The sslServCon object listens for connections on port 443 -- that is, it listens for secure HTTP connections. Argument cert is a pointer to a SSL certificate generated by using the command line tool SharkParseCert, which is explained in the C/C++ section.

We are using default arguments for the constructors above. You have many optional arguments that can be passed to the constructors above. For example, you can configure the HttpServCon object to listen to a non standard HTTP port, or you can configure the server to create N threads -- i.e., you can thread enable the server.

In the above example, two listen objects are created. The server permits you to create any number of listen objects. You can, for example, create another HttpServCon object listening for connections on port 80 using IPv6.

The above code shows how to assemble a standalone web server. An application embedding Barracuda should replace the main function with a thread dedicated to the server.

The above assembled web server will return the message "404 page not found" since no resources are so far installed in the Virtual File System. The Virtual File System must at a minimum contain one resource before a client can access the server.

Resources can be installed into the Virtual File System as Lua objects and/or as C/C++ objects. Objects created by C code can be manipulated by Lua code.

Enabling Lua

The following C code fragment is the minimum of what it takes to enable Lua for a Barracuda server. It assumes that we have already created the HttpServer and IoIntfs (IO interface).

lua_State* L;                           /* pointer to a Lua virtual machine */
struct BaLua_param blp = {0};           /* configuration parameters */

blp.vmio   = pvmio;                     /* The required VM io. Pointer to an IoIntf */
blp.server = pserver;                   /* pointer to a HttpServer */
L = balua_create(&blp);                 /* create the Lua state */

The above configuration parameters are the only parameters required to enable Lua. There are additional parameters that you can optionally configure. For example, a timer object is installed as follows:

BaTimer timer(&m,8000);
blp.timer = &timer;

The timer is very useful as it enables the server to trigger timer events. Many web application frameworks such as PHP must rely on external cron scripts to run maintenance services. Barracuda includes a timer object that permits Lua code to run at regular intervals.

For detailed explanation refer to the Barracuda Lua C interface.

Lua Server Pages and C/C++ Server Pages

Lua Server Pages, or LSP for short, and C/C++ Server Pages , or CSP for short, are technologies that enable you to make dynamic and interactive web pages.

What are LSP and CSP?

A LSP file normally contains HTML tags, just like a HTML file. However, an LSP file can also contain Lua scripts, normally surrounded by the delimiters <?lsp and ?>. Lua server scripts are executed on the server, and can contain any valid Lua expressions, statements, functions, etc.

A CSP file is similar to an LSP page; however, a CSP file contains C or C++ code normally surrounded by the delimiters <% and %>, which are also known as CSP tags. The CSP compiler supports a number of tags specifically designed for C and C++ code.

LSP pages and CSP pages are provided with some predefined objects. The request object is used to get information from the client, and the response object is used to send output to the client from the server.

The following two examples show an LSP and a CSP web page hit counter. The examples execute code in the server and dynamically produce a HTML page:

LSP exampleCSP example
<?lsp
   page.count = (page.count or 0) + 1
?>
<html>
  <body>
    <p>
      You are visitor <?lsp=page.count?>.
    </p>
  </body>
</html>
<%! int count; %>
<%!! count = 0; %>
<html>
  <body>
    <p>
      You are visitor <%="%d" ++count%>.
    </p>
  </body>
</html>

The above examples use expression tags, <?lsp= ?> for LSP and <%="format flag" %> for CSP. The result of an expression is emitted as HTML/XML.

Device Management with CSP and LSP

Let's say we have the two following C functions that directly interact with the hardware. The first function enables us to set an LED on or off. The second function returns the LED state, which is 1 for on and 0 for off.

void setLed(int on);
int getLed(void);

To control the LED from a CSP and LSP page, we start by making a simple web page template with one HTML form.

<html>
  <body>
    <form method="post">
      Set LED: <input type="checkbox" name="LED" >
      <br>
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

Controlling the LED using CSP

When using CSP, we inject C or C++ code into an HTML page. Since we use C code, we can directly call the two LED functions setLed and getLed. The following example shows the above template converted to a fully functional CSP page (using C code, not C++).

<%g #include "LED.h" %>
<%
if(HttpMethod_Post == HttpRequest_getMethodType(request))
{
   /* Fetch 'POST' data: param 'LED' (from HTML form) */
   const char* led = HttpRequest_getParameter(request, "LED");
   setLed(led ? 1 : 0);
}
%>
<html>
  <body>
    <form method="post">
      Set LED:
       <input type="checkbox" name="LED" <%= getLed() ? "checked" : "" %>>
      <br>
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

In the above CSP page, we check if the HTTP method type is POST and if it is, we set the LED to either on or off. The on/off state is fetched from the HTTP parameter "LED", i.e., from the browser's POST data sent from the "LED" checkbox in the HTML code.

On line 14, in the LED checkbox, we must dynamically set the attribute "checked" if the LED is set to "on". The "checked" attribute makes the browser automatically check the checkbox element.

Controlling the LED using LSP

When using LSP, we cannot directly call C functions from within an LSP page since a Lua script cannot directly call a C function. What we need is Lua bindings for the two LED functions. The bindings, when registered in the Lua VM, enable Lua scripts to call C functions via the bindings.

Creating Lua bindings can be done automatically or manually. We will first look into how to do it automatically and then how to do it manually.

Automatic Lua binding generation for the two LED functions

Create a new text file, name it "functions.txt", and copy the two LED function prototypes to this file such that the file contains:

void setLed(int on);
int getLed(void);

Navigate to https://realtimelogic.info/swig/, enter the name LED, and click "continue". Drag and drop the text file "functions.txt" onto the online binding generator's web page. The online web application generates the Lua bindings, and the file should automatically download. Include the generated C file in your build, and follow the instructions provided by the online Lua bindings generator. In particular, you must initialize the bindings by calling luaopen_LED(L) from your C startup code.

After compiling the new Lua bindings (C code) and after modifying the C startup code, compile and run the server. Create the following LSP file in the LSP directory (where you have configured the server to read LSP pages):

<?lsp
if "POST" == request:method() then
   local led = request:data("LED") -- Fetch 'POST' data: param 'LED'
   LED.setLed(led and true or false) -- Call auto generated C binding
end
?>
<html>
  <body>
    <form method="post">
      Set LED:
       <input type="checkbox" name="LED"
          <?lsp= LED.getLed() and "checked" or "" ?>>
      <br>
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

In the above example, LED.setLed expects an integer that is either one or zero and function LED.getLed returns either one or zero. Although this works, it is better to change the C prototypes such that one can use a Lua boolean value. We will not go into how this can be done in this tutorial, but you should consult the online swig documentation and make the bindings use boolean values.

Creating Lua bindings manually

Start by creating two function prototypes as follows:

static int LED_setLed(lua_State* L);
static int LED_getLed(lua_State* L);

As you can see from the above, the two functions take a Lua state as argument and return an integer. Lua uses a virtual stack to pass values to and from C. We will not go into this in detail here, but you should consult the Lua book, which describes this in detail. An older version of this book is available online.

static int LED_setLed(lua_State* L)
{
   /* Fetch value at stack position 1 */
   int on = lua_toboolean(L, 1);
   /* Call the LED function that is interfacing the hardware */
   setLed(on);
   return 0; /* No Lua return value */
}

static int LED_getLed(lua_State* L)
{
   /* Call the LED function that get's the LED state from the hardware */
   int on = getLed();
   /* Push the on/off state onto the Lua stack */
   lua_pushboolean(L, on);
   return 1; /* Inform Lua that we have one return value */
}

The above two functions must be registered in the Lua state that was created by the C startup code. The following code creates a Lua table by the name "LED" and attaches the two C functions to this table.

static int LED_register(lua_State* L)
{
   static const luaL_Reg reg[] = {
      {"setLed", LED_setLed},
      {"getLed", LED_getLed},
      {NULL, NULL}
   };
   luaL_newlib(L, reg);
   return 1;
} /* End */


int luaopen_LED(lua_State* L)
{
   luaL_requiref(L, "LED", LED_register, TRUE);
   return 1;
}

A detailed explanation on how the two above functions work is explained in the advanced Lua bindings tutorial. Notice that we use the same name for registering the Lua bindings as we used in the bindings that were automatically generated. The C startup code must call luaopen_LED(L) in order to register the Lua bindings.

The LSP page we used for the automatically generated bindings must now be changed slightly since the setLed function now expects a boolean value and the getLed function returns a boolean value. The following shows what must be changed:

Original: LED.setLed(led and 1 or 0)
New code: LED.setLed(led and true or false)

Original: <?lsp= LED.getLed() == 1 and "checked" or "" ?>
New code: <?lsp= LED.getLed() and "checked" or "" ?>

Introduction to the Barracuda Virtual File System

The web server delegates the request to the Virtual File System after it has parsed and analyzed a client HTTP request command. The job of the Virtual File System is to locate the resource requested by the client and to delegate the HTTP request command to the requested resource. If no resource is found or if the Virtual File System is empty, a "404 not found" error message is sent to the client.

The following Lua example shows how to assemble a basic web server that can serve web pages to a client.

-- Create a "resource reader" (resrdr) using the Lua VM's I/O interface.
myapp = ba.create.resrdr(ba.openio("vm"))
myapp:insert() -- Insert as root application

The above example initializes a resource reader and adds the resource to the Virtual File System. A resource reader makes it, for example, possible for a browser to fetch HTML pages from your file system. We will explain the resource reader in more detail later.

The Barracuda App/Web Server is designed to run in embedded devices with non standard file systems. The server encapsulates file system operations into an I/O interface which shields the server from the underlying architecture. An I/O interface implementation can be a file system, a ZIP file, or even a database. The construction ba.openio("vm") above initializes the resource reader with the Lua VM's I/O interface which was configured by the C/C++ startup code.

In many web servers, a resource is synonymous with a file in the file system. The web-server simply locates the file in the file system and sends the file to the client. In Barracuda, a resource can basically be anything and, most importantly, a resource does not have to be a physical file.

An example of a resource in the Virtual File System is an LSP resource. A LSP resource contains Lua code that is executed when the client requests the resource. Another example of a resource is CSP. A CSP resource contains either C or C++ code that is executed when the client requests the resource.

Resources such as LSP and CSP are contained in a resource collection, aka a directory. The Barracuda Web Server includes many directory types such as the Resource Reader, the Resource Manager, WebDAV, and EventHandler. You can also create your own specialized directory type.

The following figure shows the C++ inheritance diagram for directories. The C++ names are not identical to the Lua names.

HttpDir

Directory

The directory object is the Virtual File System's "base type", also known as HttpDir when writing C code. The directory function is not doing much unless you extend its functionality. The directory type's default implementation is to chain or link in other directories. We will focus more on linking directories when we explain how to construct a virtual file system tree.

Extending the directory function is best illustrated with an example:

local function myservice(_ENV,path)
   -- _ENV is the command environment where the
   -- globals print, request, and response are found.
   print("<html><body><p>The relative path is: ",
         path,
         "<br> The absolute path is: ",
         request:uri(), -- Print the URI component of the URL
         "</p></body></html>")
end

dir = ba.create.dir("mydir") -- Create directory "mydir"
dir:setfunc(myservice) -- Enable the directory service function
dir:insert() -- Insert "mydir" as a root directory.

In the above code, we create the Lua function "myservice". We register this function as a directory callback function just after creating a directory object by calling ba.create.dir. The directory object created is then inserted as a root directory in the server. We name the directory created "mydir", thus making the directory available at http://localhost/mydir/, where "localhost" is the address to the server.

The directory service callback function "myservice" is executed when a client accesses the directory. Just like an LSP page, a directory service function has a number of predefined objects in the command environment such as the request object, the response object, and function print. Function print sends data back to the browser.

The third argument to function "myservice" is the relative path to the virtual resource requested. For example, entering http://localhost/mydir/a/b/c in a browser displays the following:

The relative path is: a/b/c
The absolute path is: /mydir/a/b/c

A shopping cart with clean URL's

You have probably noticed that many online shopping carts use dirty URLs such as:

http://example.com/cgi-bin/gen.pl?category=flowers&color=blue

The last part of the above URL contains URL encoded data: ?category=flowers&color=blue. In Barracuda, URL encoded data can be accessed as follows:

local data = request:data() -- Return URL encoded data as a Lua table
print(data.category) -- prints flowers
print(data.color) -- prints blue

Dirty URLs do not promote usability and are difficult to remember. Also, browsers can have a hard time caching pages containing URL encoded data.

Using clean URLs with Barracuda is easy. The above URL converted to a clean URL would look like:

http://example.com/cart/flowers/blue

Our cart implementation:

local function cart(_ENV,path)
   category,color=string.match(path, "(%w+)/(%w+)")
   response:forward(".cart.lsp")
end
dir = ba.create.dir("cart", cart)
dir:insert()

In the above example, we create a cart directory and make the directory available at URI /cart/. The cart directory service function extracts the category and the color from the path and then transfers the request to the LSP page ".cart.lsp". We have, for the sake of simplicity, omitted error checking.

The URL http://example.com/cart/flowers/blue uses a combination of physical and logical elements. The base path http://example.com/cart/ is a physical URL to the directory instance installed in the Virtual File System. The URL http://example.com/cart/flowers/blue is a logical URL; in other words, the path flowers/blue does not relate to a physical page.

The two variables, category and color which are extracted from the path, are declared in the temporary command environment. The temporary "command environment" is valid for the length of the request. This means that the Lua Server Page ".cart.lsp" can access these two variables.

Designing applications in Barracuda promotes the mediator-view architecture, where one page is doing preliminary processing and another page renders the response.

Resource Reader

A default resource reader enables the server to return static content to a client. Static content includes HTML files, images, JavaScript Files, etc. A resource reader can also be upgraded to run LSP pages, thus delivering dynamic content.

-- Create a new ZIP I/O interface by loading "myzip.zip" from the VM's
-- I/O root directory.
local myzipio=ba.mkio(ba.openio("vm"), "myzip.zip")
zipapp = ba.create.resrdr("myzip",myzipio)
zipapp:lspfilter() -- Upgrade resource reader to execute LSP resources.
zipapp:insert() -- Install as a root directory.

A resource reader instance must be associated with a Barracuda file system, a.k.a. I/O interface. In the above example, we create a new I/O interface by attaching the I/O to a standard ZIP file located in the VM's root directory. The created I/O is then associated with a resource reader instance. Finally, the resource reader is inserted into the Virtual File System such that it is available at base URI /myzip/.

Let's assume the ZIP file contains two files:

index.lsp
directory/index.html

The first file can be accessed from a browser as: http://localhost/myzip/index.lsp
The second file can be accessed as: http://localhost/myzip/directory/index.html

When a browser accesses index.lsp, the LSP page is uncompressed, compiled, and executed by the LSP engine. The LSP page then sends uncompressed dynamically created data to the browser.

When a browser accesses directory/index.html, the resource reader checks if the browser accepts GZIP encoding. If it does, the internal index.html file in the ZIP file is converted, without uncompressing the data to a GZIP file and finally sent to the client. The resource reader uncompresses the file before sending it if the browser does not support GZIP encoding.

Web File Server

The Web File Server enables one to easily upload and download files using a Web Browser. Since the Web File Server is included in the FuguHub consumer product, we suggest that you refer to the FuguHub Web File Server product page for an introduction to the Web File Server.

Example:

local iovm = ba.openio"vm"
wfs=ba.create.wfs("fs", iovm)
wfs:insert()

In the above example, a Web File Server directory is created and made available at base URI /fs/ -- i.e. URL: http://address/fs/, where "address" is the domain or IP address of the server.

WebDAV

WebDAV is a set of extensions to the HTTP protocol that enables a web server to appear as a standard network drive. If you are new to WebDAV, take a look at the FuguHub's WebDAV plugin product sheet for a brief overview of WebDAV.

WebDAV is particularly useful when developing LSP pages and when the server is not running on "localhost". By mapping a drive in Windows to the remote server or mounting the remote server on Linux, a standard text editor can be used to directly edit the LSP pages on the remote computer. Refreshing the LSP page in a web browser automatically recompiles the file after it has been changed.

local iovm = ba.openio"vm"
davd=ba.create.dav("mydav",ba.openio"vm",".LOCK")
davd:insert()

In the above example, a WebDAV directory is created and made available at base URI /mydav/ -- i.e.
URL: http://address/mydav/, where "address" is the domain or IP address to the server.

On Windows, the above can be mapped as a drive from a DOS command window as follows:

net use * http://address/mydav/

Windows should subsequently show information similar to the following:

Drive Y: is now connected to http://address/mydav/.
The command completed successfully.

On Linux, davfs2 is the recommended file system driver.
On Mac, go to the Finder and press "Apple-K". Enter the WebDAV URL and click the connect button.

Tips:

EventHandler

The EventHandler, or EH for short, may take some time to become familiar with if you are a WEB 2.0 and AJAX developer. However, an embedded software engineer will more than likely find the philosophy of the EH easier to understand due to its foundation being similar to the interprocess communications (IPC) in real time operating systems. The EH is based on the asynchronous IPC mechanism typically found in industrial telecom software.

Unlike a normal HTTP request which terminates as soon as the server returns the response, the EH maintains a persistent, full duplex, bi-directional communication channel between the server and the connected client.

The EventHandler client initiates the connection by using standard HTTP. This connection is then morphed into a persistent connection, where both the client and server can send asynchronous messages to the peer side.

WebSocket Connection

The EventHandler is a directory object and an instance of the EventHandler can be installed into any location in the Virtual File System. The initial HTTP GET request sent from the client is sent to one EventHandler object installed in the virtual file system. You can install any number of EventHandler instances in the virtual file system.

See the EventHandler documentation for more information.

Constructing a Virtual File System Tree

A Barracuda App/Web Server with an empty Virtual File System (VFS) cannot do anything. Attempting to access the server using a browser will return a "404 resource not found" error message.

The Virtual File System must contain at least one resource. A basic server can be assembled as follows:

local vmio=ba.openio("vm")
myapp = ba.create.resrdr(vmio)
myapp:insert() -- No argument = insert as a root directory.

In the above example, a resource reader is created and inserted as a root directory. The resource reader is initialized with the Virtual Machine's I/O interface.

A browser entering http://localhost/index.html loads index.html from the base of the VM's I/O interface.

I/O interfaces abstracts the underlying file-system architecture for the server. The Barracuda App/Web Server supports a number of I/O interface types such as DiskIo and ZipIo. A DiskIo maps to a location on a standard file system and a ZipIo maps to the internal structure of a ZIP file. The VM's I/O is initialized by the C/C++ startup code.

Directory properties:

The Lua .config script

The Lua .config script which must be located in the root of the VM's I/O interface is automatically loaded and executed when the C/C++ code creates the Lua VM.

The .config script is typically where you assemble your default Virtual File System. One thing to keep in mind is that Lua garbage collects all objects not being referenced. You must make sure that the directories you create in the .config script are referenced (global or owned by a global variable). An unreferenced directory will be removed from the VFS by the Lua garbage collector.

Example:

The following example shows a practical use of the directory priority mechanism in the VFS. Two resource readers are created: diskapp and zipapp. The diskapp is given priority 1 and the zipapp is given priority 0. The idea behind this construction is that you have designed an application and put the application into a ZIP file. The users can overload any of the files you have in the zip file by putting the files into c:/www. When the browser accesses a resource, the VFS first searches the user's c:/www directory. If the file is not found in the www directory, the ZIP file is searched. Assume that you have the style sheet style.css. The users can copy your style sheet from the ZIP file, change it, and put the file into c:\www. The browser loads the user's changed style sheet since it has a higher priority than the style sheet in the ZIP file.

We also create a Directory function and give it a very low priority. If the VFS cannot find the resource in diskapp or zipapp, the notFoundApp's directory callback function is activated. This function returns a 404 message to the user. The notFoundApp shows how to create customized "global" error handlers in the VFS.

local io=ba.mkio(ba.openio("root"), "c:/www")
diskapp = ba.create.resrdr(1,io) -- Priority 1
diskapp:insert()

io=ba.mkio(ba.openio("vm"),"myzip.zip")
zipapp = ba.create.resrdr(0,io) -- Priority 0
zipapp:insert()

local function manage404(_ENV,path)
   print("<html><body><p>",
         path,
         " not found",
         "</p></body></html>")
end
notFoundApp = ba.create.dir(-126) -- Priority -126
notFoundApp:setfunc(manage404) -- Enable 404 handler
notFoundApp:insert()

The Two I/O interfaces "vm" and "root" are initialized by the C/C++ code. The above example assumes that the "root" I/O is initialized to the root in a Windows computer. The base of an I/O interface can, for example, be set to a subdirectory by the C/C++ code.

Hidden Files

Hidden files are files that start with a dot; for example, .page.lsp. Hidden files can only be accessed by server code. A direct request from a browser yields "404 not found" in the response. Hidden files can be accessed indirectly via method response:forward and response:include, but not by method response:redirect. A directory that starts with a dot hides the directory's content from direct client access. HTML files that end with .shtml are also hidden. The extension .shtml in Barracuda means a server side include file. Files that end with .shtml are typically used when creating hidden C Server Pages (CSP).