This guide introduces the concept of the Virtual File System (VFS) in the Barracuda App/Web Server (BAS/BWS). It explains that the server requires the user to build a VFS to provide routing and to serve HTTP resources. Any attempt to access resources without a properly configured VFS will result in a 404 error. The VFS allows for the organization and management of various resources such as static files, Lua Server Pages (LSP), and directories, providing a flexible and powerful structure for serving content.
The Barracuda App Server's Virtual File System provides a more flexible way to manage routing than what is found in traditional web servers, where routing often involves mapping URL paths directly to filesystem directories or predefined controller actions. BAS, however, employs a VFS that offers a more dynamic and programmable approach to routing. This means you can define how URLs are handled at runtime, providing flexibility that surpasses conventional static routing methods.
Key Advantages of BAS's Routing via VFS:
BAS and BWS differ from standard web servers because URLs are not automatically mapped to a file system or to predefined controller actions. Instead, the URL path component is initially processed by the VFS, which decodes it and attempts to find a matching node. If a node is found, the matched path portion is stripped, and the node processes the remaining path component. This allows for a dynamic and programmable VFS structure, constructed at runtime, as illustrated in the following diagram.
Figure 1: A virtual file system with three nodes
After parsing client HTTP requests, the web server delegates the request to the VFS. The VFS then locates and processes the requested resource. If it cannot find a matching node, it returns a "404 not found" error to the HTTP client.
The VFS resources extend beyond managing physical files to include items like Lua Server Pages (LSP), which contain Lua code executed upon request. The VFS supports various directory types, including Resource Reader (depicted as Dir 2 in Figure 1), WebDAV, and others, enhancing its flexibility.
Here's a Lua example to demonstrate setting up a basic web server:
-- Create a "resource reader" (resrdr) using the Lua disk I/O interface. myapp = ba.create.resrdr(ba.openio("disk")) myapp:insert() -- Insert as root application
This code initializes a Resource Reader and integrates it into the VFS, enabling browsers to fetch HTML pages from your file system. Detailed explanations of the resource reader will follow later in the guide.
One of the VFS's unique features is that nodes can be inserted and removed at runtime. Both the Mako Server and Xedge include easy-to-use APIs for dynamically starting and stopping web applications, which basically means inserting and removing VFS directory nodes at runtime. The Xedge introductory video shows how this works.
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 diskio=ba.openio("disk") myapp = ba.create.resrdr(diskio) 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 disk 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 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. 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.
WebDAV and 404 Page:
Line 7 and 9 are required when a WebDAV instance is installed in the virtual file system and when using Microsoft's WebDAV client.
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 functionalities, 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 functionality via callback 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 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", 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 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 local cartdir = ba.create.dir("cart") cartdir:setfunc(cart) dir:insert(cartdir, true)
Download the shopping cart examle from GitHub
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.
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, 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 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 such that 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 one to easily upload and download files using a Web Browser. Example: local iodisk = ba.openio"disk" wfs=ba.create.wfs("fs", iodisk) 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.
Note: When you click the above run button, you will get an accessed denied message due to the cross-origin safety setting. You resolve this by clicking in the browser’s address bar and pressing the enter key.
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 iodisk = ba.openio"disk" davd=ba.create.dav("mydav",ba.openio"disk",".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:
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.