The Barracuda App Server (BAS) and Barracuda Web Server (BWS) use a Virtual File System (VFS) to route URL paths. A VFS node can serve static files, execute Lua Server Pages (LSP), implement a REST endpoint, mount a ZIP archive, provide WebDAV or Web File Server access, or run a custom directory callback. A server with no matching VFS node returns HTTP 404 Not Found.
After the server parses an HTTP request, the VFS decodes the URL path and searches the installed directory nodes in priority order. When a node matches part of the path, that prefix is removed and the node processes the remainder. A node that does not handle the resource lets the VFS continue to the next eligible node.
Figure 1: A virtual file system with three nodes
The VFS is not limited to physical files. Its node types include resource readers, directory callbacks, WebDAV, Web File Server, and other handlers. Mako Server and Xedge use this runtime structure to insert a running application and remove it when the application stops.
A basic server needs at least one VFS resource. The following Lua code mounts the configured disk I/O interface at the URL root:
local diskio=ba.openio("disk")
myapp = ba.create.resrdr(diskio)
myapp:insert() -- No argument = insert as a root directory.
In this 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 disk I/O interface.
I/O interfaces abstract 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 disk 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("disk"),"myzip.zip")
zipapp = ba.create.resrdr(0,io) -- Priority 0
zipapp:insert()
local davm={PROPFIND=true,OPTIONS=true}
local function manage404(_ENV,path)
if davm[request:method()] then return false end
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 "disk" and "root" are initialized by the C/C++ code. This 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.
WebDAV and 404 Page:
Lines 7 and 9 are required when a WebDAV instance is installed in the virtual file system and Microsoft's WebDAV client is used.
The following introduces you to the Virtual File System (VFS) nodes, which serve as the foundation for managing and organizing resources within BAS. These nodes allow dynamic handling of web requests, enabling developers to map URLs to various functionality, including serving static files, executing scripts, and integrating custom logic.
Acts as the base type of the Virtual File System and can be extended with custom callbacks to handle dynamic routing.
Enables the server to return static content like HTML, JavaScript, and images. It can be upgraded to execute Lua Server Pages (LSP), allowing dynamic content generation.
Provides an easy way to upload and download files via a web browser. Allows exposing server directories as web-accessible storage.
Extends HTTP functionality to allow remote file system access and enables mounting the server as a network drive, which is helpful for editing LSP files remotely.
The resource reader, Web File Server, and WebDAV inherit from the base Directory Node; thus, all methods for the Directory Node are also available for the other nodes, including installing a directory callback function.
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"
-- Enable the directory service function
dir:setfunc(myservice)
dir:insert() -- Insert "mydir" as a root directory.
In this 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 is then inserted as a root directory in the server. We name the directory "mydir", making it available at http://localhost/mydir/, where "localhost" is the address of the server.
The directory service callback function "myservice", installed by calling dir:setfunc, 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 URLs
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 this 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
local cartdir = ba.create.dir("cart")
cartdir:setfunc(cart)
dir:insert(cartdir, true)
Download the shopping cart example from GitHub
In this 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". Error checking is omitted for simplicity.
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.
A directory object provides a callback function that receives the relative path for each incoming request. As explained earlier, this mechanism provides the foundation for a rudimentary REST service, as you can determine how to respond based on the path and HTTP method.
The following tutorials show how to take this concept further and transform a simple directory object into a fully functional RESTful service object:
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 Lua Server Pages (LSP), thus delivering dynamic content.
-- Create a new ZIP I/O interface by loading "myzip.zip" from another I/O.
-- I/O root directory.
local myzipio=ba.mkio(ba.openio("disk"), "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, also called an I/O interface. In this example, we create a new I/O interface by attaching the I/O to a standard ZIP file located in the disk 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 so it is available at base URI /myzip/.
Let's assume the ZIP file contains the following files:
By using a ZIP file, BAS allows these files to be accessed just like a regular directory structure on a traditional file system.
|
The Web File Server enables easy file upload and download using a web browser. Example: local iodisk = ba.openio"disk" wfs=ba.create.wfs("fs", iodisk) wfs:insert() |
In this 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.
The Web File Server includes the WebDAV server; see the tutorial What Is an Embedded Web File Manager for details.
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 WebDAV Server's product page and the tutorial Why WebDAV Beats FTP.
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 iodisk = ba.openio"disk"
davd=ba.create.dav("mydav",ba.openio"disk",".LOCK")
davd:insert()
In this 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 of the server.
On Windows, this directory can be mapped as a drive from a DOS command window as follows:
net use * http://address/mydav/
The built-in WebClient service must be running on your Windows system. If the command fails, make sure this service is enabled and started. The service is not enabled by default. When working, 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:
Hidden files are files whose names begin with a dot (e.g., .page.lsp). These files can only be accessed by server-side code. If a client requests a hidden file directly, the server responds with a 404 Not Found error.
The VFS Design Skill collects routing patterns for resource readers, directory callbacks, mounted directories, REST endpoints, fallback handlers, WebDAV, Web File Server, LSP resources, and authentication boundaries. It can be used as a developer checklist or supplied to an AI assistant.
This tutorial provides a ready-to-use foundation for building industrial web applications. It demonstrates how to create a web-based management interface by leveraging a directory function to dynamically map URLs to files. For a complete walkthrough, see the Interactive Dashboard App Tutorial.
This tutorial explains how to implement URL-to-database mapping through a directory function combined with the SQLite API. It provides a step-by-step guide to building a basic wiki engine, demonstrating efficient techniques for linking URLs to database queries. For more details, see the Lua Wiki Engine Tutorial.
The article Single Sign-On demonstrates how to dynamically embed a session in the URL for an HTTP client that cannot use the normal authentication flow. Users sign in through a browser and copy the resulting authenticated URL to the WebDAV client. Treat the URL as a temporary credential and protect it from logs, history, and disclosure.
The tutorial Designing Embedded RESTful Services in C and C++ explains how to build a REST-style embedded web service in pure C and C++. It explains how to create a web server from scratch, mapping HTTP requests to a C and C++ function, handling JSON payloads, and structuring efficient server-side logic for resource-constrained environments.