Lua/LSP API

Overview

Lua is an embeddable, extensible, extension language and is used in the Barracuda server for writing applications at a higher level than applications designed in C/C++ code. Applications designed in Lua are not limited to web-applications, thus Lua applications can be used as a method of designing your overall (embedded) application.

The Barracuda server and all plugins are designed by Real Time Logic, with the exception of the Lua scripting language. Lua uses the MIT license model and can therefore be used in commercial products.

In the Barracuda Embedded Web Server (BA), Lua code is typically activated by an event generated by C code. The following diagram illustrates the sequence of events:

In the above diagram, C code activates Lua code and the Lua code calls Lua C functions made available to the Lua script. C code that is made available to Lua code is referred to as Lua bindings.

The BA library provides a wealth of functions available to Lua script code. You simply provide the device management functionality for your platform. BA and Lua are designed to be extended and it is therefore easy to add your own device management functions. Making C functions available to Lua -- i.e. creating Lua bindings is described in the Lua book. One can also automate this task by using various tools designed to parse a C/C++ header and automatically create the Lua bindings for the C/C++ code. For example, our Online Lua Binding Generator is a tool that automatically creates Lua bindings from your C header files.

LSP

Lua Server Pages, or LSP for short, provides a simple and fast way of creating dynamically created web pages. LSP is similar to CSP, except that LSP does not need to be pre-compiled.

Lua script can be freely intermixed with HTML or XML. LSP tags are XML compliant <?lsp    ?>. Code between these tags is executed as Lua code. Expressions are provided by the <?lsp=    ?> tag. The result of an expression is emitted as HTML/XML.

The standard LSP tags are recognized by most HTML editing tools and hence these tools can be used for creating dynamic LSP pages.

LSP can be used as an adjunct to CSP. CSP pages can include LSP pages and vice versa.

Like CSP, LSP can be loaded from a compressed directory.
LSP pages are available for any target platforms supported by the Barracuda Web Server.

A standalone executable Lua interpreter and a compiler are also provided. The standalone compiler is useful for testing LSP prior to deployment without the aid of a Web server.

HTTP Directories

Objects implementing directory functionality:
ba.create.dir
ba.create.resrdr
ba.create.dav
ba.create.wfs

Lua also provides a mechanism to construct the virtual directory tree used by the Web Server. This directory tree consists of C/C++ HttpDir objects. A directory structure specified with Lua is similar in performance to a C/C++ constructed tree; however, it is faster and easier to construct with Lua. With the ba.create family of functions, HttpDir derived objects such as WebDAV, HttpResRdr, etc., can be created in either preload scripts or LSP pages. The LSP directory function mirrors the Barracuda C/C++ APIs. All directory types provide a set of common methods. See the directory object for more information.

The Command (Request/Response) Environment

Lua has the concept of environments. Environments provide scoping for collections of variables. The standalone Lua interpreter provides one global environment known as _G. The LSP plugin provides the same global environment _G, but LSP also provides an ephemeral environment called the "request/response environment". The ephemeral environment is also known as the "command environment" or CMDE for short. Any global variables created in Lua Server Pages (LSP) and/or directory functions will be saved in this ephemeral command environment. The command environment is also the environment of any included or forwarded pages. Hence variables set in an LSP page will be available to included or forwarded pages.

The diagram to the right, shows the relationship between the command environment, directory functions, LSP pages, the LSP page's private page table, and the LSP page's application table. The command environment is automatically created when the first LSP page or directory function is executed. This environment lives for the lifetime of the request and is automatically terminated when the request completes. Global variables declared in this environment are available to all included and or forwarded pages. The environment is also available to directory functions.

Note: The diagram shows a hypothetical request where an LSP page delegates the request to another LSP page, then to a directory function, and so on. The purpose with this diagram is to show you the relations between objects. One would typically not delegate a request as deep as shown in the diagram to the right.

Directory Function
A directory function is a Lua function installed in one of dir, resrdr, dav, or eh. A directory function implements directory functionality for a dir, but extends and/or changes the behavior of a resrdr, dav, or eh. Directory functions are typically used in advanced web applications.

See also
rsrdr:lspfilter()
ba.parselsp

Lua Server Pages
Lua Server Pages(LSP) is a Lua script page with the extension .lsp. The page is executed by a Resource Reader if the Resource Reader is LSP enabled -- e.g. if function rsrdr:lspfilter() has been called.

An LSP page has the following variables configured by the LSP engine:

Page Table
Each LSP page has its own private "page" table and variables created in the page table are maintained for the execution life of the Resource Reader. Resource Readers can be created dynamically and added to the virtual file system. Resource Readers can also be removed at any time from the virtual file system. Resource Readers that are not referenced are automatically terminated by the Lua garbage collector.

<?lsp
  -- Using page variables
  print("<pre>");
  page.count = (page.count or 0) + 1
  print("Access count = ", page.count)
  print("</pre>")
?>
This code updates the variable count each time the page is displayed. When there multiple users of the page, the number may increment by more than one each time you refresh the page. Even more interesting is if we change the code to the following:

<?lsp
  -- Using page variables
  print("<pre>");
  page.count = (page.count or 0) + 1
  print("Access count = ", page.count)
  ba.sleep(10);
  print("Access count = ", page.count)
  print("</pre>")
?>
then the two values of the access count may be different when we have multiple users.

Application Table
A Resource Reader makes it possible to optionally create an application table. The application table is provided when calling rsrdr:lspfilter(apptab). The application table is available to all LSP pages in a Resource Reader, but is kept private to other Resource Readers. The application table can store code and data common to all LSP pages in the application or store and execute code that is not part of the standard web application, such as timer driven Lua code, HTTP clients, Lua socket code, etc. As an example, the lspappmgr and wfs example code implements an "application concept" by allowing auxiliary loaded applications to load and run common code in a ".preload" script. The .preload's environment is then made available to all LSP applications as the "app" table.

Session Object/Table
The LSP plugin also supports a session object/table in addition to the tables described above. The session object maintains variables that are available to the the current session. Session variables are destroyed when the session terminates. As an example,a typical use of Session variables would be to maintain a users shopping basket:

<?lsp
  -- Using session variables
  print("<pre>");
  local session = request:session()
  if not session then -- no session ?
    print"you must first login using a valid username and password"
  else
    -- no basket? - create it
    session.basket = session.basket or {}
        -- add an item to the basket table
    session.basket[#session.basket +1] = "something new"
  end
  print("We have ", #session.basket, " items in the session table")
  print("</pre>")
?>

Lua globals
Lua globals (_G) are common to all pages. Variables set in one page/session may be retrieved in another page/session.

Global variables may be referenced without the _G prefix, however, to create a Global variable the _G prefix must be applied.

<?lsp
  -- Using Lua Globals
  print("<pre>");
  _G.count = (_G.count or 0) + 1
  print("Global LSP Access count = ", _G.count)

  print(_G.stdpageheader)
  print(_G.copyright)

  -- global functions
  _G.myfunction()

  print("</pre>")
?>

Thread Mapping and Coroutines

The Lua scripting language is not designed for preemptive context switching, but Lua provides a light weight threading mechanism called coroutines. In the Barracuda Server(BA), Lua event generators are mapped to coroutines, thus each event triggered by BA has its own Lua stack. In BA, multiple events may be active simultaneously even though Lua is nonpreemptive. The BA Lua event generators are typically run by native threads on the C side and each native thread is mapped to a Lua coroutine. When a Lua coroutine is running, the thread may yield to another thread when a BA lua binding is called. For example, an LSP page may be preempted when calling response:write().

The following diagram gives an overview of the event and threading mechanism in BA:

The socket event dispatcher fires events when new data is available on a socket or if a non-blocking socket is ready for writing. The socket event dispatcher delegates client HTTP requests to the server thread pool which activates one of the threads. The thread searches for the resource and if found, executes the resource in a command environment. The resource may, for example, be an LSP page or a Lua directory function.

The socket event dispatcher can also fire events directly to Lua code if the auxiliary socket library is included. The Event Handler events are also run in the context of the dispatcher.

It is important that Lua code running directly in the context of the socket event dispatcher does not block as this will prevent the web-server from serving other requests. In general, Lua code should not block unless run in the context of the Server HTTP Thread Pool or the Lua Thread Library. Events run in the context of the dispatcher or the timer thread can delegate jobs to the Lua Thread Pool.

It is also possible to create user defined events, but this requires extensive knowledge of Lua. More information on creating user defined events can be found in the C/C++ manual, under section Advanced Lua Bindings.

Introduction to Lua Authentication and Authorization

See also:
General introduction to authentication
Collection of ready to run examples
introduction to authentication in C code

Authentication in the Barracuda server is an optional component that can be installed in any of the directory types. You can have multiple authenticators or you can use one authenticator, as shown in the following example, that makes all pages in the server protected.

local dir=ba.create.dir()
dir:insert()
-- Enable authentication and
-- authorization for the directory.
dir:setauth(authenticator,authorizer)
-- Note: you must also reference (anchor)
-- the dir to prevent it from garbage collecting.
Objects implementing
directory functionality:
ba.create.dir
ba.create.resrdr
ba.create.dav
ba.create.wfs

In the above example, an unnamed directory is created and installed as a root directory. We install the directory in the root, thus covering all pages in the server. The last line inserts an authenticator and authorizer in the server. We will soon look into how one can create authenticator and authorizer objects. Authenticators can be installed, replaced, or removed in a running system. For example, the authenticator is removed by calling dir:setauth().

The Lua authentication and authorization logic are wrappers for the authentication and authorization classes implemented in C code. The C classes are based on an object oriented design and many of the classes are designed to be extended by the application designer. The Lua wrappers use the same concept and one can create multiple authenticator and authorizer types. The authenticator and authorizer can be implemented in Lua, but one can also use drop in authenticator and authorizer solutions implemented in C code. We are currently providing one authenticator and authorizer drop in solution that stores user database information as JSON. See ba.create.jsonuser() for more information.

The authenticator object is responsible for the authentication handshaking between the client and the server. The authorizer is an optional component that authorizes the action performed by authenticated users. The following example expands on the previous example and implements an authenticator, but we are not using an authorizer.

local dir=ba.create.dir()
dir:insert()
-- The username/password callback function.
local function getpassword(username)
   if username == "admin" then return "admin" end
end
-- Create the username database from our getpassword func.
local authuser=ba.create.authuser(getpassword)
-- Create authenticator by using the username database.
local authenticator=ba.create.authenticator(authuser)
-- Enable authentication for the directory.
dir:setauth(authenticator)

An authenticator is created by calling ba.create.authenticator. The authenticator requires a user database and we create a basic user database by calling ba.create.authuser. The user database object in turn requires a password lookup function. The job of the password lookup function is to look up the user and return the password if the user is found. The authenticator matches the password with the password provided by the user and accepts the user if the password matches. The above password lookup function returns the password "admin" if the username is "admin" -- i.e. the user is accepted if the username and password is "admin". The above getpassword callback function returns the password in plaintext. You can also return a hash value or use external authentication.

The above is all that is required in order to implement authentication for a directory. The authentication defaults to digest authentication since we have not configured the authenticator. The authenticator is also using an integrated HTTP response message which is displayed if the user clicks the cancel button in the browser's digest pop up window.

Authorizing Users

An authorizer object is created as follows:

local function authorizer(username, method, path)
     -- return true or false
 end
local  authorizer = ba.create.authorizer(authorizer)
dir:setauth(authenticator,authorizer)

The authorizer callback function must decide from the username, the HTTP method type, and the requested relative path if the user has access or not. The function returns true if the user has access and false or nothing if the user does not have access.

The authorizers must decide from username, method, and path if a user has access. You may want to authorize based on other criteria, and you have many options when it comes to authorizing user besides using the authorizer objects. Each LSP page can make its own decision or you can override the directory service function and implement a generic authorizer specifically tailored for your application.

The Authenticator Types

The authenticator example above is using HTTP digest authentication, which is the default if the type is not specified. The authenticator type can be specified by setting field "type" in the optional "option table".

local authenticator=ba.create.authenticator(authuser,{ type="basic" })

The following authenticator types can be specified:

Encrypted Passwords

All authenticator types are designed to work with user databases where the passwords are not stored in clear text, but as a hash value. However, the hash encoded passwords must be stored using an encoding called HA1. See storing passwords as a hash value for details.

Creating a Custom Response Message Handler

A login response message handler is responsible for sending login information and login failed information to a client. The function is not called unless the client requires login information, or if the authenticator failed to authenticate the client, or if the client is denied by the authenticator, or an installed login tracker denied the request.

From the above login sequence, the response handler is called when the user requests a protected resource (1). The response handler requests the user credentials (2) by sending login information to the client. Note: The response handler should send response data but must not set the HTTP authorization header since this header is managed by the the authenticator. The response handler is also called if the username and/or password does not match (3).

The following example shows how to create a login response message handler for form based authentication:

-- Custom Response Message Handler
local function loginresponse(_ENV, authinfo)
   if authinfo.username then response:forward".loginfailed.lsp" end
   response:forward".loginform.lsp"
end
local authenticator=ba.create.authenticator(
   authuser,{type="form", response=loginresponse})

The _ENV variable is the command environment where globals such as the request and response objects are found.

The authinfo is a table with information about the login status. The username is set if the user failed to login. The username is not set when the client requests the login page. Notice that we are not using an "else" statement when forwarding the request to the login form. The "else" statement is not needed since response:forward by default does not return to sender, thus the code below ".loginfailed.lsp" will not execute unless username is not set. The pages ".loginform.lsp" and ".loginfailed.lsp" are regular LSP pages that send HTML login information and error information to the client.

Authentication Examples

The Do It Yorself (DIY) tutorial bundled with the Barracuda Server's combined demo and tutorial includes a number of interactive authentication examples.

Creating a Form Response Message Handler

Form based authentication requires a custom response message handler. The response handler can emit the complete response page, but it is easier to create a response handler that forwards the request to a dedicated LSP page. The following example shows how to create the ".loginform.lsp" page for the form response message handler we showed previously.

<html>
  <body>
    <form method="post">
      Username: <input type="text" name="ba_username"><br>
      Password: <input type="password" name="ba_password"><br>
      <input type="submit" value="Login">
    </form>
  </body>
</html>

The above HTML form includes the two required form fields ba_username and ba_password. Pressing the submit button sends the login information in plaintext to the server. It is for this reason that one should use a secure (SSL) connection when using form based authentication. See the " force secure connection" example for more information.

Using form based authentication safely without using SSL

The form authenticator supports cryptographic hashing and implements an authentication scheme similar to digest authentication, which allows user identity to be established securely without having to send a password in plaintext over the network. The form authenticator adds two additional fields to the authinfo table that can be used by the response handler when creating the login form.

The following examples show how to create a form response message handler and an LSP login form that supports cryptographic hashing.

Form response message handler:

We must make a modification to the the form response message handler we showed previously such that the response handler supports our new form login page.

local function loginresponse(_ENV, au)
   authinfo=au -- Set authinfo in the global command environment
   if authinfo.username then response:forward".loginfailed.lsp" end
   response:forward".loginform.lsp"
end

The code above is similar to our previous example, except for that we make the variable authinfo available in the command environment. We can use authinfo in the the new ".loginform.lsp" page by making authinfo global in the command environment.

Form login page:

The new ".loginform.lsp" page includes two JavaScript files and two new form fields. We have also changed the "submit" button to a standard html button. Changing the submit button to a standard button makes it impossible to submit the form if JavaScript is disabled, thus preventing the password from being sent in plaintext.

<html>
  <head>
    <script src="/rtl/jquery.js"></script>
    <script src="/rtl/sha1.js"></script>
  </head>
  <body>
    <form method="post">
      Username: <input type="text" name="ba_username"><br>
      Password: <input type="password" name="ba_password" autocomplete="off"><br>
      <input type="hidden" name="ba_seed" value="<?lsp= authinfo.seed ?>">
      <input type="hidden" name="ba_seedkey" value="<?lsp= authinfo.seedkey ?>">
      <input type="button" id="ba_loginbut" value="Login">
    </form>
  </body>
</html> 

Notice how we dynamically create the two form fields ba_seed and ba_seedkey by using LSP and the values in the authinfo Lua table. The global authinfo table was made available to the LSP page by the response handler above. The form does not include any JavaScript code. The JavaScript code in "sha1.js" is intelligent and auto discovers the HTML form with the fields ba_password, ba_seed, and the button with id="ba_loginbut". These fields are required by the JavaScript code, which automatically calculates a SHA-1 hash from the password and the seed value. The seedkey is required by the authenticator in the server when the form is submitted. The user will not be able to login if any of these fields are missing.

The two JavaScript files are found in the Barracuda SDK /WebResources/ directory. The two pre-compiled servers that come with the SDK includes these two files in the integrated ZIP file and makes them available in the /rtl/ directory. The JavaScript file sha1.js requires jquery.js

Many browsers give the user the option of saving the username and password in the browser. The JavaScript code run when pressing the login button replaces the password entered by the user by the SHA1 hash before submitting the page, thus making it impossible for the user to save the password. This feature increases security but degrades the user experience. If you want the user to be able to save the password, make the following changes to the HTML form:

    <form method="post">
      Username: <input type="text" name="ba_username"><br>
      Password: <input type="password" id="ba_password2"><br>
      <input type="hidden" name="ba_password">
      <input type="hidden" name="ba_seed" value="<?lsp=authinfo.seed?>">
      <input type="hidden" name="ba_seedkey" value="<?lsp=authinfo.seedkey?>">
      <input type="button" id="ba_loginbut" value="Login">
    </form>

The ba_password is now changed to a hidden variable and the password field is using the id="ba_password2". Notice that we are using an id and not a name attribute for ba_bassword2. Form fields without a name are not submitting to the server, which is what we want -- i.e. we want to prevent the password from being sent in plaintext. The JavaScript code in sha1.js is designed to look for this combination. The hash is created from ba_password2 + seed and inserted into ba_password. The above construction allows the user to save the password in some browsers.

Sform Authenticator Limitations:
  1. The authentication will not work if the user has JavaScript disabled. The file sha1.js includes a JavaScript implementation of SHA1 and code to extract and manipulate the HTML form fields. The clear text password is replaced by the hash of the password + seed.
  2. The authentication will not work if using an external authenticator, where the password is kept as a secret by the authenticator.
  3. The following field must be added to the form when using a user database with encrypted passwords.

    <input type="hidden" name="ba_realm" value="Barracuda Server">
    

    Realm is the authenticator's realm value. The default realm value is Barracuda Server.

    You must also include spark-md5.min.js, which is used for calculating HA1.

    The following shows the complete example:

    <script src="/rtl/sha1.js"></script>
    <script src="/rtl/spark-md5.min.js"></script>
    <form method="post">
      Username: <input type="text" name="ba_username"><br>
      Password: <input type="password" name="ba_password" autocomplete="off"><br>
      <input type="hidden" name="ba_realm" value="Barracuda Server">
      <input type="hidden" name="ba_seed" value="<?lsp= authinfo.seed ?>">
      <input type="hidden" name="ba_seedkey" value="<?lsp= authinfo.seedkey ?>">
      <input type="button" id="ba_loginbut" value="Login">
    </form>
    

Force Secure Connection

Basic and form based authentication is unsafe unless the login information is sent over a secure connection. The following example makes sure the client is using a secure connection:

-- Create a secure URL from  URI (path)
-- cmd is the request and response object.
local function getSecureUrl(cmd)
   local host = cmd:header"host"
   -- Use host header if sent by client, otherwise use IP addr
   if host then
      host = string.gsub(host,":%d+","") -- Strip port number, if any
   else
      host = cmd:sockname()
  end
  -- Create the secure URL
  local url = "https://"..host
  return cmd:encoderedirecturl(url..cmd:uri(), true)
end
-- The username/password callback function.
-- Notice how we use the optional _ENV command environment.
local function getpassword(username, upasswd, _ENV)
   if not request:issecure() then
      -- Deny login: send redirect request
      response:sendredirect(getSecureUrl(request))
      -- sendredirect does not return to caller.
   end
   if username == "admin" then return "admin" end
end
-- Create the username database from our getpassword func.
local authuser=ba.create.authuser(getpassword)

-- The login response message handler
local function loginresponse(_ENV, authinfo)
   if not request:issecure() then
      -- Remove Authorization header set by basic or digest authenticator
      response:setheader("Authorization",nil)
      -- sendredirect does not return
      response:sendredirect(getSecureUrl(request))
   end
   -- The connection is secure if we get this far.
   if authinfo.username then response:forward".loginfailed.lsp" end
   response:forward".loginform.lsp"
end

local authenticator=ba.create.authenticator(authuser,{response=loginresponse})
dir=ba.create.dir()
dir:insert()
dir:setauth(authenticator)

The login response message handler checks if the URL is secure, and if it is not, sends a redirect request to the client. Removing the "Authorization" header set by the authenticator is necessary if using Digest or Basic authorization since we do not want the client to send the credentials before using a secure connection.

A client normally starts by requesting a secure resource without providing the user's credentials (1), but a non browser client such as a HTTP client library may directly send the credentials (3). The password is sent in plaintext and an eavesdropper could intercept the password. We cannot prevent this on the server, but we can change the getpassword() function to ignore the login request and force the connection to a secure connection. The response is not normally committed in a getpassword() function, but the authenticators are designed such that they assume the getpassword() function sent a "denied" request if the response is committed. Method response:sendredirect commits the response.

Note: The modified getpassword() function with the secure redirect is not needed if using the shtml authenticator since the secure form authenticator denies all non secure requests and delegates the request directly to the response message handler.

External Lua Links

Barracuda Lua APIs

Each LSP page is provided with some predefined variables and objects; namely, request, response, cookie, session and page.

request object

The request object is a global variable in the command environment and is available to directory functions and LSP pages. The request object provides information sent by the client to the server.

request example

<html>
<!-- request example usage -->
<?lsp
  -- has the page been modified in the last 2 minutes
  print("<h2>request methods</h2>");
  print("<pre>");
  request:checktime(response, os.time() - 120)
  print("request:checktime(response,os.time() - 120)");
  print("request:user() ", request:user())
  print("request:cookie'z9ZAqJtI'", request:cookie"z9ZAqJtI")
  print("request:header'User-Agent'", request:header"User-Agent")
  print("\nAll request headers")
  for k,v in next, request:header() do print(k,'=',v) end
  print("\nrequest:method() ", request:method())
  print("request:uri() ", request:uri())
  local t =request:data()
  print("request:data() ", request:data())
  for k,v in next, t do print("\t",k,'=',v) end

  print("\nrequest:session() ", request:session(true))
  if request:session() then
    print("session id", request:session():id(),"\n")
  end
  print("request:version() ", request:version())
  print("</pre>")
?>
</html>



The request object has the following methods:
request:abort()
This method, which aborts the execution, is the same as response:abort().
request:allow({methods, ...,} [, usedefaults])
The allow() method checks if the HTTP request method is one of the HTTP methods specified in the parameter list. The function will cause the script to terminate if the request method is not one the allowed methods and the status code is set to 405 (method not allowed).
If the request method is allowed then allow() returns true.
allow() also terminates the script when the request method is only 'OPTIONS'.
By default the methods OPTIONS and HEAD are added automatically to an allowed option list, setting the second parameter to false prevents these defaults from being automatically added.
This function is typically used at the start of a LSP or directory function.

Example,
-- only allow GET and POST for this page
request:allow{"GET", "POST"} -- Auto abort script if not GET or POST

-- valid request; let's continue
print"hello world"
request:certificate()
Returns the peer's certificate chain as a table if the connection is secure and the peer sent a certificate. A second return value is set to true if the certificate is valid i.e. if validated by the certificate store.
Note: this function requires that the auxiliary API sharkssl is installed.
request:cipher()
Returns the cipher suite and SSL protocol version being used if the connection is secure. See socket:cipher() for more information on the return values.
request:checktime(time)
Handles the "If-Modified-Since" request header.
The time parameter is effectively what you wish to be the last modified time of the LSP. If the parameter time is newer or the same as the request "If-Modified-Since" time, then the function returns true.
The function sends a "304 Not modified" response to the client and terminates the script if the parameter time is older than the request "If-Modified-Since".

Example,
   -- switch off the default cache headers
   response:setheader"Cache-control"
   response:setheader"Pragma"

  -- check the modified time (anything up to a day old)
  if not request:checktime(os.time() - (60*60*24)) then return end

  -- lets provide the updated page
  print"hello world"
request:user()
Returns the username, password (if known), and authentication type of an authenticated user for this request (if one exists for the request). If there is no authenticated user, the function returns: false,name -- where name is the application name.
Possible values of the authentication type are; 'form', 'basic' and 'digest'.
request:logout([all])
Logout the request user and terminates the session. The optional parameter 'all' defaults to false. Set 'all' to true if you want to terminate all of the user's active sessions. A user may be logged in using more than one client. One must typically set all=true when changing password since all clients must be terminated or the clients may get a 403 response. If there is no authenticated user, then false is returned.
request:cookie(cookie name)
Returns the named cookie object.
request:env()
returns the command environment.
request:header([header name])
Returns a string with the value of the named HTTP header. If no parameter is provided, then a table containing all request header name/value pairs is returned.
request:domain()
Returns the host header without any port number. Typically used with name-based virtual hosting. The returned value is always lowercase. Returns the client's IP address if the client did not provide the host header.
request:method()
Returns the name of the request method. e.g. GET, POST, etc.
request:data([name ...])
When name is not provided returns a table containing any POST or QUERY data sent to the server. The table may be empty if there is no POST or QUERY data.
When one or names are provided the values of these names are returned.
Note this function will not return multiple values. If this is required use datapairs().
Example,
  -- get some values
  custnbr = request:data("custnbr")        -- get the value of custnbr
  name, num = request:data("name", "num")  -- get two values

  -- display posted values
  for name,value in pairs(request:data()) do
    print(name,'=',value)
  end
request:datapairs()
An iterator that will traverse of all name/value pairs that have been sent to the server with POST or GET.
Example,
  -- display all posted values
  for name,value in request:datapairs() do
    print(name,'=',value)
  end
request:rawrdr([blocksize])
Returns a function that can be used as an iterator for reading PUT and POST data. You cannot use this function for posted www-url-encoded or multi-part data. The function throws an exception if the following conditions are met:
  • Request is missing content-length and not chunk encoding.
  • Request is of type URL encoded data. URL encoded data is handled directly by the server. See request:data or request:datapairs for more information.
  • Request is of type multipart/form-data. You must use request:multipart

The returned function also takes an optional blocksize as its only parameter. The default blocksize varies depending on the platform. For example, Windows is usually 512 and UNIX is usually 1024.
The iterator returned transparently handles chunk encoded data. A client can either set a content length or send data using chunk encoding.
The following LSP example echoes the uploaded data back to the client:
<?lsp
for data in request:rawrdr() do
   response:write(data)
end
?>
request:multipart(callbacks[,bufsize][,keepAlive])
Decode multi-part encoded POST data. Calling this function starts a multi byte parser, which calls your callbacks as data is parsed from the data stream.
The parser allocates "bufsize" bytes for temporary storage. The buffer must not be smaller than the content size of the largest input field element of type "text" or textarea. The default value is 8K.
You can optionally set keepAlive to false if you want the web-server to close the connection after the upload is completed and you have sent a response message to the client.
Argument callbacks is a table with callback functions:
  • formdata(name, value)
  • beginfile(name, filename, contenttype, transferencoding)
  • filedata(data)
  • endmp()
  • error(emsg)

The functions are optional. The data is simply discarded if the multi byte stream contains data not handled by a callback. The function(s) can terminate the multi byte stream parsing by returning false.
Callback function formdata is called for each input field in the form. For example, the function will be called twice if you have the following form:
<form method="post" enctype="multipart/form-data">
<input type="text" name="Text input" />
<textarea name="Text area" cols="40" rows="3"></textarea>
</form>
Callback functions beginfile and filedata are called if the form contains an input of type "file". For example, the function beginfile will be called twice if you have the following form and the filedata function is called repeatedly to give you data as it trickles in.
<form method="post" enctype="multipart/form-data">
<input type='file' size='40' name='File 1'/>
<input type='file' size='40' name='File 2'/>
</form>
Callback function beginfile(name, filename, contenttype, transferencoding) arguments:
name - the name as specified in the HTML "input" type.
fileName - the name and possible path of the file entered by the user. The path separator is platform dependent.
contentType - the content mime type such as "text/plain". This parameter is NULL if not specified in the multipart data stream.
contentTransferEncoding - the transfer encoding such as "gzip". This parameter is NULL if not specified in the multipart data stream.
Callback function filedata(data) arguments:
data -       the received chunk.
Callback function endmp() is called at the end of the multi byte stream sent from the client.
Callback function error(emsg) is called if either parsing the multi byte stream failed or the connection closed.
request:uri()
Returns the request URI. e.g. /myrequest.lsp
request:url()
Returns the request URL. e.g. http://localhost/myrequest.lsp. This value is also returned by the tostring(request) function.
request:session([create])
Returns the current session object associated with this request or, if there is no current session and create is true, returns a new session. If create is false and the request has no valid session, this method returns false. To make sure the session is properly maintained, you must call this method before the response is committed. The default value of create is `false'.
request:version()
Returns the HTTP request version as a string.
request:issecure()
Returns true if a HTTPS connection is being used.
request:peername()
Returns a string of the client name/IP address, the connected port number, and a boolean value set to true for IPv6 and false for IPv4.
request:sockname()
Returns a string of the server name/IP address, the connected port number, and a boolean value set to true for IPv6 and false for IPv4.
request:setnodelay(bool)
Disable or enable the Nagle algorithm.

response object

The response object is a global variable in the command environment and is available to directory functions and LSP pages. The response object provides methods that can be used when sending messages to the client. Most of the response methods return nil, error-message, when an error is encountered.

response:abort()
Abort the execution of the LSP page or directory function. This function may also abort across page boundaries if response:forward/redirect and response:include sets the optional "return to caller" false.
response:bytecount()
The number of bytes that have been sent to the client.
response:containsheader(header name)
Searches the internal response header database for a header with the specified name. If the header does not exist, the function returns false.
response:clearkeepalive()
Causes a close of the current connection after the response is sent.
response:createcookie(cookie name)
Creates a cookie with the provided name. The cookie object is returned.
response:downgrade()
Changes the protocol to HTTP/1.0.
response:encoderedirecturl(url [, withdata [, sessionURL]])
Encodes the specified URL into an absolute URL, or if encoding is not needed, returns the URL unchanged.

If the withdata parameter is specified and is true, this method also includes all url-encoded parameters in the request line and in the body if the client sent a POST message.

If the sessionURL parameter is specified in addition to withdata and a session is defined for the client, then this method encodes as above but also includes the Barracuda session ID as &z9ZAqJtI=ID.
response:encodeurl(path)
Translates a path to characters that are safe for use in a URL. For example, the following path "a/../path/with spaces" is translated to: "path/with%20spaces". Note, URL encoded parameters, if any, are not translated.
response:flush()
Forces any content in the response write buffer to be written to the client. A call to this method automatically commits the response, meaning the status code and headers will be written.
response:forward(path [,return2caller])
response:redirect(path[,return2caller])
Forwards a request from this LSP page to another resource (directory function, LSP page, CSP page, HTML file, etc). This method allows one LSP page to do preliminary processing of a request and another resource to generate the response (Request delegation). forward() should be called before the response has been committed to the client (before response body output has been flushed). This method throws an error if the response has already been committed or if the resource is not found. Any data in the response buffer is erased when the request is forwarded.

The forwarded page gets the same the Lua environment as the originating page. See the request environment for more information.

The path is the resource where you want to delegate the request. The path is assumed to be an absolute path value on the server if the string starts with "/". The path is otherwise assumed to be relative to the directory of the current page.

The return2caller is a boolean value that defaults to false. You must set this value to true if you want to return to the calling Lua script after forward has completed.

Redirect:
Method forward is similar to method redirect, with the exception that forward bypasses any form of required authentication and/or authentication.
response:include(path[,return2caller])
Includes the content of a resource (directory function, LSP page, CSP page, HTML file, etc) in the response. In essence, this method enables programmatic server side includes. See the introduction to Request Delegation for more information. The response object has its path elements and parameters remain unchanged from the caller's. The included servlet cannot change the response status code or set headers; any attempt to make a change is ignored.

The included page gets the same the Lua environment as the originating page. See the request environment for more information.

The path is the resource you want to include (embed). The path is assumed to be an absolute path value on the server if the string starts with "/". The path is otherwise assumed to be relative to the directory of the current page.

The return2caller is a boolean value that defaults to true. Setting this variable to false will normally still make the call return to caller unless the included page calls method such as response:sendredirect, or any other method that aborts the request.
response:committed()
Returns a boolean (true/false) indicating if the response has been committed. A committed response has already had its status code and headers sent to the client. You cannot set response headers or response status if the response is committed. The server maintains a response buffer. The size of the response buffer is configured by the C startup code.
response:env()
returns the command environment.
response:initial()
Returns false if this is an include or forward from another directory function or LSP file; that is, the first page called during the request processing. Note, directory functions can optionally decide if the page is a "forwarded" page or not.
response:isforward()
Returns true if this is a forward request from another another directory function or LSP file. The second return value is the count of the number of times that the request has been forwarded. Note, directory functions can optionally decide if the page is a "forwarded" page or not.
response:isinclude()
Returns true if this is an include from another LSP file. The second return value is the count of the number of times that the request has been included.
response:getdata()
Returns the response data or nil if data is committed. Typically used by advanced directory functions.
response:getstatus()
Returns the HTTP status code. Typically used by advanced directory functions.
response:json(table[,table]* [ret2caller])
Encode Lua table(s) as JSON and send the response. The function simplifies sending JSON response data to a client. The function accepts multiple tables. The following example illustrates how this function works internally when only one table is supplied:
function response:json(table,ret2caller)
   local data=ba.json.encode(table)
   response:reset()
   response:setheader("Content-Type","application/json")
   response:setheader("Cache-Control",
               "no-store, no-cache, must-revalidate, max-age=0")
   response:setcontentlength(#data) 
   response:send(data)
   if(ret2caller) then return true end
   response:abort()
end
response:reset([string])
Clears any data that exists in the buffer as well as the status code and headers. Clears the content of the underlying buffer in the response without clearing headers or status code. The optional string parameter may be "headers" or "buffer", if not supplied both headers and buffers are cleared. If the response has been committed, this method returns false.
response:send(data)
An advanced function that sends data to the client. You must make sure you set the content length before sending data to the client. This function differs from response:write in that data is not sent using chunk transfer encoding and therefore requires a content-length.
response:senderror(code [, message])
Sends an error response as a simple HTML page to the client using the specified status code. An optional message can be sent with the response code. If the response has been committed, this method returns false.
response:sendredirect(url [,302 [,return2caller]])
Sends a temporary redirect response to the client using the specified redirect location URL. A relative URL is automatically converted to an absolute URL during this call.

This function, by default, sends a 302 (temporary redirect). Set second argument to true if you want to send a 301 permanent redirect message.

This function, by default, does not return to the caller. Set the third argument to true if you want to return control to the caller. The default is for the server to automatically roll back the call chain to prevent writing to the response data after the redirect request is sent to the client.

Example:
response:sendredirect"../start.html";
 -- Send a permanent redirect request
response:sendredirect("http://realtimelogic.com", true);
response:setbasic(realm)
response:setdigest(realm)
Set the status code and HTTP basic or digest authenticate headers. The methods are typically used in a response message handler, when using the auth type authenticator, to force a client into authenticate using HTTP authentication.
response:setcontentlength(length)
Sets the "Content-Length" header value. If the header had already been set, the new value overwrites the previous one. LSP response data is normally sent chunk encoded and the HTTP header Transfer-Encoding: chunked is automatically set by the Web Server. Calling setcontentlength changes the internal state in the web-server to raw-response-writing and removes the Transfer-Encoding header, if previously set.
response:setcontenttype(type)
Sets the "Content-Type" header. type is a type string. e.g. "text/plain"
response:setdateheader(name, time)
Sets a response header with the given name and date value (time). The date is specified in terms of seconds. If the header had already been set, the new value overwrites the previous one. The response:containsheader() method can be used to test for the presence of a header before setting its value.
name is the name of the header to set (e.g. "Expires").
time the assigned time value in number of seconds elapsed since midnight (00:00:00), January 1, 1970. This is the time format returned by os.time().
response:setdefaultheaders()
Sets the most common header values in LSP files. Sets content type to "text/html" and sets the header "Cache-Control" to "No-Cache". This method is automatically inserted by the LSP page.
response:setheader(name [, value])
Sets a HTTP response header with the given name and value. If the header had already been set, the new value overwrites the previous one. The response:containsheader method can be used to test for the presence of a header before setting its value. If the value is not provided then the named header is removed from the response.
setheader() should be called before the response has been committed to the client, (before response body output has been flushed). If the response already has been committed, this method returns false.
response:setmaxage(seconds)
Sets header "Cache-Control: max-age=seconds". Can, for example, be used by LSP code to override the default headers inserted by the LSP page. See response:setdefaultheaders() for more information.
response:setresponse(function|true|false)
Sets a HTTP response filter that either filters the response data or traps/redirects the response data. The filter works with any resource that uses the standard buffered output functions such as response:write and the C/C++ versions such as HttpResponse::printf. The filter does not function if the response is delegated to a C/C++ resource using the non-buffered function HttpResponse:send. In other words, one cannot delegate the response to a WebDAV, or HttpResRdr. However, one can delegate the response to an LSP enabled HttpResRdr if the requested resource is an LSP page.
response:setresponse(function) Installs a generic filter function
response:setresponse([false]) Enables deflate compression using RFC1951
response:setresponse(true) Enables deflate compression using RFC1950
setresponse() returns an object with two methods:
obj:abort() Revert to the original HTTP response buffer. Returns true on success and false if response data is committed.
obj:finalize(true|false)
  • obj:finalize(true) sends compressed data to the client if setresponse is in compress mode.
  • obj:finalize([false]) returns the compressed data if setresponse is in compress mode.
  • obj:finalize() flushes any remaining data to the callback function if setresponse is in generic filter mode.

See the examples for more information on how to use method finalize().

local function serviceFunc(dir,func,path)
   local ae = request:header("Accept-Encoding")
   -- If requesting an LSP file and the client accepts deflate compression
   if path:find("%.lsp$") and ae and ae:find("deflate") then
      local xrsp = response:setresponse() -- Activate compression
      local found = func(path) -- Run the resource manager's service function
      if found then
         response:setcontenttype("text/html")
         xrsp:finalize(true) -- Send compressed data to client
         -- Above sets Content-Encoding and Content-Length before sending data
         return true -- LSP page (resource) found
      end
      xrsp:finalize() -- Not found, but we must clean up
      return false -- Resource not found
   end
   return func(path) -- Service request without using compression
end

-- Create an LSP application
myApp = ba.create.resrdr("MyDir",serviceFunc,0, myIo)
myApp:lspfilter() -- Enable LSP

In the above example, a directory function is created and inserted into the resource manager's constructor. The directory function is used as a filter. The filter function analyzes the request and enables compression if the client supports deflate compression. The resource manager's service function (func) is passed in as the second argument to the filter service function (serviceFunc). We must call this function which runs the requested LSP page. We cannot enable compression for non LSP resources so we check to see if the request is for an LSP page or not -- i.e. we check if the requested resource ends with .lsp.

Calling xrsp:finalize(true) after the resource manager's service function has rendered the response assembles all data and sends the data to the client. The default for the finalize method is to return the data and not send the data to the client. The following example shows a modified service function that fetches the compressed data and sends the data to the client using response:write.

local function serviceFunc(dir,func,path)
   local ae = request:header("Accept-Encoding")
   -- If requesting an LSP file and the client accepts deflate compression
   if path:find("%.lsp$") and ae and ae:find("deflate") then
      local xrsp = response:setresponse() -- Activate compression
      local found = func(path) -- Run the resource manager's service function
      if found then
         local data=xrsp:finalize() -- Fetch compressed data
         response:setcontenttype("text/html")
         response:setcontentlength(#data)
         response:setheader("Content-Encoding", "deflate")
         response:write(data) -- Write is now the standard socket write
         return true -- LSP page (resource) found
      end
      xrsp:finalize() -- Not found, but we must clean up
      return false -- Resource not found
   end
   return func(path) -- Service request without using compression
end

Fetching the data by calling finalize() instead of directly sending the data by calling finalize(true) is useful in an application that implements caching. A service function may be designed such that it knows how to cache dynamically generated LSP data.

As an example, a cache can be implemented by using a table.

local function serviceFunc(dir,func,path)
   if pageCacheTable[path] then
      sendCachedPage(pageCacheTable[path])
      return true -- found
   end
   -- Not in cache
   .
   .

The most common use of HTTP filters is to compress response data and the above methods are optimized for this functionality. If you have uses other than for compressing the response, a generic filter function can be installed. The following example installs a filter function by calling response:setresponse(myRespFilter).

local function serviceFunc(dir,func,path)
   -- If requesting an LSP page
   if path:find("%.lsp$") then

      -- Create a table for storing response data and a function to collect
      local dataTable={}
      local function myRespFilter(data)
         table.insert(dataTable, data)
      end

       -- Activate response filter
      local xrsp = response:setresponse(myRespFilter)

      local found = func(path) -- Run the resource manager's service function
      xrsp:finalize() -- Cleanup

      if found then
         local data
         local ae = request:header("Accept-Encoding")
         -- If client accepts deflate compression
         if ae and ae:find("deflate") then
            response:setheader("Content-Encoding", "deflate")
            data = ba.deflate(dataTable) -- Compress and assemble response data
         else
            data = table.concat(dataTable) -- Assemble response data
         end
         response:setcontenttype("text/html")
         response:setcontentlength(#data)
         response:write(data) -- Write compressed or non compressed data
         return true -- LSP page (resource) found
      end
      return false -- Resource not found
   end
   return func(path) -- Service request without using compression
end

In the above example, the myRespFilter callback function is called when the internal web-server buffer is full. The number of times the myRespFilter callback function is called is a function of (LSP data size / internal web-server buffer size). At a minimum, the function is called once when finalize() is called. Note that the internal web-server buffer can be configured by C/C++ code at startup.

Response data is inserted into a table, which is assembled into compressed data if the client supports compression or uncompressed data if the client does not support compression.

Other uses

In addition to what is shown in the above examples, the HTTP filter also works on resources either included or delegated -- i.e. response:include and response:forward. The filter works with any resource that is dynamically creating the response data such as CSP resources and CGI resources.

response:setstatus(status)
Sets the status code for this response. This method is used to set the return status code when there is no error (for example, for the status code 304 Not Modified). If there is an error, the senderror() method should be used instead. setstatus() should be called before the response has been committed to the client (before response body output has been flushed). If the response already has been committed, this method returns a non zero value.
response:write(string, ...)

Send data to the client using chunk encoding, the default LSP encoding. Note: the data is added to the response buffer, if not full. The response is automatically committed if the response buffer is full.

example:
<?lsp
  response:write"hello world"

  -- create a convenience function for writing data
  local fmt = string.format
  local function prt(...) response:write(...) end
  local function prtf(...) prt(fmt(...)) end

  prt("as ","many ", "strings as you like<br />")
  prtf("You can format numbers %d<br />", 1234)

?>


response:writesize()
Returns the number of bytes that currently exist in the response write buffer. response:flush() should reset this number to zero.

Deferred Response Object

The deferred response object is a special response object created by some methods -- e.g. upload:response(). The deferred response is typically used by a response that is detached from the original client request. The deferred response object is a Lua wrapper for the "C" class HttpAsynchResp. A deferred response object has limited buffer management and the methods must be called in the following sequence:

  1. defresp:setstatus()
  2. defresp:setheader () or defresp:setcontentlength()
  3. defresp:write() or defresp:send()
  4. defresp:close()
defresp:setstatus(status)
Sets the status code for this response. The status defaults to 200 OK.
defresp:setheader(name, value)
Sets a HTTP response header with the given name and value.
defresp:write(string, ...)
Send data to the client using chunk encoding.
defresp:setcontentlength(integer)
Sets the "Content-Length" header value. This method is used in combination with method defresp:send().
defresp:send(string)
Send data to the client. You must make sure you set the content length before sending data to the client. This function differs from defresp:write in that data is not sent using chunk transfer encoding and therefore requires a content-length.
defresp:close()
Flush and close the response.
defresp:valid()
Returns true if the response object is valid -- i.e. not closed.

cookie object

A cookie is used for exchanging a small amount of information between a page and a web browser.
A cookie's value can uniquely identify a client, so cookies are commonly used for session management.
A cookie has a name, a single value, and optional attributes such as a comment, path and domain qualifiers, a maximum age, and a version number.

Typical usage:

 cookie = request:cookie"myCookie"
 if not  cookie then  -- If no cookie set for this page
   -- Create a session cookie
   cookie = response:createcookie"myCookie"
   -- Active cookie i.e. send cookie to client
   cookie:activate()
 end
 -- This should never fail
 assert(cookie == request:cookie"myCookie")

cookie:activate()
Activates the cookie.
The cookie will not be sent to the client unless this function is called. You cannot active a cookie if the data is committed. See response:committed() for more information.
cookie:delete()
Deletes a cookie, previously made persistent with function setMaxAge.
cookie:comment([comment])
Gets or sets a comment that describes a cookie's purpose.
cookie:domain([domain])
Gets or sets the domain name set for this cookie.
cookie:maxage([time])
Gets or sets the maximum age of the cookie, specified in seconds. By default, 0 indicates the cookie will persist until browser shutdown.
cookie:name()
Returns the name of the cookie.
cookie:path([uri])
Gets or sets the path on the server to which the browser returns this cookie
cookie:secure([boolean])
Either returns true if the browser is sending cookies only over a secure protocol, or false if the browser can send cookies using any protocol.
If the boolean flag is specified, informs the browser whether the cookie should only be sent using a secure protocol, such as HTTPS or SSL.
cookie:httponly([boolean])
Returns the HttpOnly cookie attribute.
Marks or unmarks this cookie as HttpOnly if the boolean flag is specified.
HttpOnly cookies are not supposed to be exposed to client-side scripting code, and may therefore help mitigate certain kinds of cross-site scripting (XSS) attacks.
cookie:value([value])
Gets or sets the value of the cookie

session object

The session object provides a way to identify a user across more than one page request or visit to a Web site and makes it possible to store information about that user.
The HttpSession container uses this class to create a session between an HTTP client and an HTTP server. The session persists for a specified time period, across more than one connection or page request from the user. A session usually corresponds to one user who may visit a site many times. The server can maintain a session in many ways such as using cookies or rewriting URLs.

This interface allows you to:

Variables may be assigned and retrieved from the session object. This is done using normal Lua syntax.

Any attempt to create session variables with names the same as the session methods, will result in an error.
Session variables are destroyed when the session terminates or times out. LSP session variables are not accessable from CSP pages and vice versa.

A user can create a tool that could potentially overflow the session container. The Barracuda authentication system makes sure that a session object is not created before the user is authenticated. It is therefore recommended to create a session object by using the authentication system.

<html>
<!-- example session variable usage -->
<?lsp
  print("<h2>session variables</h2>");
  print("<pre>");
  session = request:session() -- returns nil/false if there is no session
  if session then
    print("we have a session ", session)
    session.counter = (session.counter or 0) + 1 -- count session usage of this page

    session:maxinactiveinterval(60);
    print ("id", session:id());
    print ("creationtime"        ,os.date("%c",session:creationtime()))
    print ("lastaccessedtime"    ,os.date("%c",session:lastaccessedtime()))
    print ("maxinactiveinterval" ,session:maxinactiveinterval())
    print ("usecounter"          ,session:usecounter())

    -- if we have any attributes print them all
    if session:attributes() then
      table.foreach(session:attributes(), print)
    end
  end
  print("</pre>")
?>
</html>
session:id([true])
Returns a unique number or string identifier assigned for this session. See ba.session for more information.
session:creationtime()
Returns the time when this session was created, measured in seconds since midnight January 1, 1970 GMT. This time can be formated with os.date()
session:lastaccessedtime()
Returns the last time the client sent a request associated with this session, as the number of seconds since midnight January 1, 1970 GMT, and marked by the time the container received the request.
This time can be formated with os.date()
session:maxinactiveinterval([interval])
Returns the maximum time interval, in seconds, that the session container will keep this session open between client accesses.
OR specifies the time, in seconds, between client requests before the session container will invalidate this session.
session:peername()
Returns a string of the client name/IP address associated with the session.
session:usecounter()
Gets the session usage counter.
session:terminate()
Unbinds any objects bound to this session object, destroys the session object, and frees the memory for this object. All session variables are destroyed.
session:user()
Returns authenticated user information.
If the session does not belong to an authenticated user, the function returns false. Otherwise, two strings are returned: the name of the user and the authentication type. The authentication type will be one of "basic", "digest", "form", or "default".
session.onterminate
The server calls onterminate when the server destroys the session object and if this attribute is set to a function.

Example:
local s=request:session(true)
function s.onterminate()
   ba.thread.run(function()
                 trace"Do the work here!"
              end)
end

json

A library that supports JSON encoding and decoding.

ba.json.encode(table[,table][,size])
The function encodes all provided tables to one JSON encoded string. The function is typically used for converting one Lua table. The function can optionally convert multiple tables in a format that is compatible with the stream based JSON parser. Converting large tables in memory constrained devices may fail. The function returns "nil,"mem" if sufficient memory cannot be allocated. The internal buffer grows as needed, when encoding the JSON data. The initial start size is 512 bytes. You can optionally set the start size to a larger value, which is suggested if you plan on converting large Lua tables. Userdata, functions, and threads are set to ba.json.null.
ba.json.decode(string)
Takes a JSON encoded string and decodes it into a Lua table. JSON null is set to the ba.json.null value.
On error, nil and an error message is returned. The message is one of 'parse', 'data', 'interface', or 'memory'.
The input string is assumed to be UTF-8 encoded.
The string parameter can contain multiple JSON tables. In this case, multiple Lua tables (or [nil,message] pairs) are returned.
ba.json.encodestr(string)
Encodes a string as JSON string.
ba.json.null
A userdata that represents the JSON null value.
ba.json.parser()
Module JSONS simplifies the use of the stream based JSON parser when used with a TCP/IP or WebSocket connection.

Create a stream based JSON parser object. The JSON parser object functions similar to ba.json.encode, but unlike function ba.json.encode which is limited to parsing complete JSON messages, the stream based parser parses data from a stream as the data trickles in.

The JSON parser is ideal for building advanced, asynchronous, message passing protocols on any type of communication channel. The communication can, for example, be between a standalone web server and other programs, or between embedded systems that are interconnected. The JSON parser can easily be used together with the socket library, but you can also use the JSON parser in combination with your own specialized communication channels such as message queues, USB connections, serial connections, etc.

parser:parse(data[,true])

Parse a chunk of data using the JSON parser object. The second argument, if true, instructs the parser to return all assembled JSON objects as one array of objects.

Return values:

  • true: The parser successfully parsed the data chunk, but the data chunk did not contain a complete JSON object.
  • true, object1, object2... : The parser successfully parsed the data chunk and assembled at least one object. The JSON parser can return multiple objects if the data chunk contained more than one JSON object. The parser returns multiple values if the second argument to parser:parse() is false or not present.
  • true, array : The parser successfully parsed the data chunk and assembled at least one object. All assembled objects are stored in the array table. The parser returns one array with the assembled objects if the second argument to parser:parse() is true.
  • nil, errorcode: The parser failed parsing the data chunk. You cannot reuse the JSON parser if the parser aborts the stream parsing. You must start over by creating a new JSON parser object.

Example 1, JSON Server:

The following example shows a potential use case for the JSON parser. A specialized LSP page is designed to extract the active socket connection from the current request and morph the HTTP request into an asynchronous receive channel for JSON data. See the socket API for more information on how to use sockets. Note: see also the JSONS module.

<?lsp

-- Activated when s:event(asyncReceiveCoroutine) is called below
local function asyncReceiveCoroutine(s)
   local parser=ba.json.parser()
   -- variable x: multiple types, or nil on error
   local x,err=true,nil
   while x do -- While no error
      -- Block and wait for data
      x,err=s:read()
      if x then -- x is JSON socket data
         local array 
         x, array = parser:parse(x,true)
         if x then -- If ok
            if array then -- If at least one object
               for _,v in ipairs(array) do
                  -- Dispatch the parsed JSON object 'v'
               end
            end
         else
            -- Parse error
            err=array -- array is now error code
         end
      end
   end
   if err == "closed" then
      print"Socket closed"
   else
      print("Socket or JSON parser error:", err)
   end
   send=nil
   -- Return: exit and close connection
end


response:flush() -- Send HTTP response headers
-- Morph HTTP request into a socket connection
local s = ba.socket.req2sock(request)
-- Enable asynchronous socket receive
s:event(asyncReceiveCoroutine)

?>

The above code is designed to receive data. You can easily extend the code to also send asynchronous JSON data to the client side.

Example 2, JSON Client:

The following example shows a basic client designed using the Barracuda Embedded Web Server HTTP client library.

local c=http.create.basic()
c:request{method="GET",url="URL-TO-SERVER_RESOURCE"}
-- Morph HTTP request into a socket connection
local s=ba.socket.http2sock(c)
local data={txt="hello"} -- The data to send
s:write(ba.json.encode(data)) -- Encode and send

Module JSONS

JSONS (JSON stream parser) is a small Lua library that simplifies the use of the JSON parser object when using the JSON parser for parsing data that trickles in on a TCP/IP connection or on a WebSocket connection. A complete IoT example using JSON for communication includes the use of the JSONS library on the server side. See the M2M LED JSON demo for more information.

To enable this library a require("JSONS") must be issued.

      local js = require("JSONS")
    
JSONS functions:
js.create(table, socket [, maxsize])

Returns a JSON Stream parser object. The following example shows how to create a JSON stream parser object:

        local js = require("JSONS").create({}, socket, 2048)
      
Parameters:
  • table - A Lua table which is converted into a JSONS object. This is also the return value from the "create" function. You may store any value in this table.
  • socket - A valid socket object such as a standard TCP/IP client/server socket or a WebSocket.
  • maxsize - An optional security limit on the maximum number of bytes a JSON stream can contain before a complete JSON object is parsed. Setting a limit prevents a rough sender from sending a huge JSON object that may exhaust the memory in the server.
JSON stream parser object methods:
js:get([timeout])
Reads from the socket and returns one JSON object as a Lua table when fully assembled. The optional timeout is passed to the socket:read method. Returns nil,error on error, where error is an error code from socket:read, parser:parse, or "maxsize". The error code "maxsize" is returned when the optional security parameter "maxsize" triggers.

The method blocks on the socket:read method until "timeout" or until a complete JSON object is parsed and assembled. Should a chunk of data read from the socket include multiple objects, the first object will be returned and the other objects will be saved internally. Subsequent calls return the stored JSON objects, one at a time, until the internal list is empty. Method socket:read will then be called when the internal list is empty. The method can be used with standard blocking sockets and asynchronous sockets, including cosockets when used by a socket coroutine.
js:put(table)
Encodes Lua table to JSON and calls socket:write. Returns the return value(s) from socket:write. The method can be used with standard blocking sockets and cosockets when used by a socket coroutine.
js:close()
Closes the socket by calling socket:close. Returns the return value(s) from socket:close.

ba

A library that provides a number of Barracuda utility and IO functions.

ba.aesdecode(key, string)
Decodes and decrypts a string encrypted and encoded with function ba.aesencode. The key must be the same as the key you used when encoding the string.
ba.aesencode(key,string)
Encrypts a string using AES encryption and returns the encrypted string as a B64 encoded string.

key: The key can be any string, but is preferably created with function ba.aeskey()
string: The string to be encrypted and converted to B64 encoding.

This function is typically used as a substitute for creating a session object. The LSP page can instead store the session state as a hidden variable in the dynamically created page returned to the browser. The following example illustrates how to encode and decode state information in a hidden variable.

Encoding:
-- Data to be encoded and stored in hidden variable
local stateInfo={x=10,y="Top Secret"}
local data=ba.aesdecode(app.key, ba.json.encode(stateInfo))
Decoding:
-- Data received via hidden variable from client
local stateInfo = ba.json.decode(
   ba.aesdecode(app.key, request:data"MyHiddenVariable"))
ba.aeskey([len]])
Create a 16 or 32 byte long key that can be used by ba.aesencode() and ba.aesdecode(). The key is typically created at startup in the .preload script and stored in the app table.

len: The key length must be 16 (AES 128 bit) or 32 (AES 256 bit). The default length is set to 16.

ba.b64decode(string)
decodes a BASE64 string.
ba.b64encode(string)
encodes a string as BASE64.
ba.urldecode(string)
Decodes a URL encoded string.
ba.urlencode(string)
URL encodes a string.
ba.clock()
returns the number of milliseconds since system start.
ba.cmpaddr(addr1, addr2)
Compares two IP addresses. Returns true if the two IP addresses are equal.
print(ba.cmpaddr("127.0.0.1", "127.0.0.1")) -- Prints true
print(ba.cmpaddr("::1", "0:0:0:0:0:0:0:1"))  -- Prints true
print(ba.cmpaddr("68.4.198.129","::ffff:68.4.198.129"))  -- Prints true
print(ba.cmpaddr("68.4.198.129","::ffff:68.4.198.128"))  -- Prints false
ba.deflate(data [,rfc1950])
Deflates (compresses) a string or a table of strings and returns the compressed data.

data: The data to be compressed, which must be a string or a table of strings. The function works similarly to the Lua function table.concat when this argument is a table. The table is concatenated and compressed.

rfc1950: Optional Boolean argument, which defaults to false. The default action is to deflate data using RFC1951 -- i.e. compress without adding the ZLIB deflate header. The correct deflate method is to use RFC1950, but Internet Explorer does not seem to follow the RFC1950 specification, thus IE fails if a ZLIB header is added. All other browsers also accept RFC1951.

ba.create

A table of functions for creating Barracuda objects:

ba.create.authenticator(authuser[,options])
Creates an authenticator object for a directory. The returned object can be used as the target for dir:setauth()
authuser=object -- An object created with ba.create.authuser or ba.create.jsonuser.
options=table -- A table with the following optional fields:
type=string -- "auth" | "digest" | "basic" | "form" | "sform" | "dav". Default is "digest". See the authenticator example for details.
realm=string -- The name of the authentication realm (used by basic and digest authenticators).
tracker=boolean -- If set to true, the client login tracker will be used if it is available. Defaults to false.
response = function(_ENV, authinfo) -- The optional Response Message Handler.
_ENV
The command environment.
authinfo=table
A table with the following fields:
type=string
type=form|basic|digest
username=string
Name of the user attempting to authenticate.
password=string
The password entered by the user.
maxusers=number
The maxusers variable returned by the authenticate callback function. This variable is negated when the maximum number of logins is reached.
recycle=boolean
true|false.
inactive=boolean
The maximum session inactivity time in seconds.
loginattempts=number
The number of login attempts.
denied=boolean
denied=true|false. Set if the user is denied access by the login tracker -- i.e. if the user is banned.
seed=string
Set if using the form based authenticator. The seed can be used for cryptographic hashing. See Using form based authentication safely without using SSL for more information.
seedkey=string
Set if using the form based authenticator. Field seedkey is an encrypted version of the seed.
Fields username,password,maxusers,recycle,and inactive are only set if the user failed to login or is denied access by the tracker. Fields loginattempts and denied are only set if a user tracker is active.
ba.create.authorizer(function)
Creates an authorize object for a directory. The returned object can be used as the target for dir:setauth(). The only parameter is a function that returns true if the user is authorized for the resource. The user is the users login name. Method is one of the same names as returns by request:method(). Path is the path name relative to the owning directory object.
function(username, method, relpath)
user
The username that is trying to authorize.
method
One of "GET", "POST" etc. See request:method()
relpath
The relative path that access is required.
returns: true|false
ba.create.authuser(function)
Creates an authenticator user database object. The returned object can be used as the target for ba.create.authenticator() See the Introduction to Lua Authentication for example code.
function(username [, upasswd [, _ENV ]])
username -- The username sent from the client.
upasswd -- The password sent from the client is provided when using Basic and Form Authentication. This value is nil when a Digest Authenticator calls the callback function.
_ENV -- The command environment is normally not needed, but can be used in special cases.
The function returns the following parameters in order: password [, maxusers [, recycle, inactive]].
password=string|table|boolean
The default is to return a string (the plaintext password), or nil if user is not found.
  • String: return the password if the user is found in the database [and if the user is accepted], otherwise return nil or nothing.
  • Table: return an HA1 hash.
  • Boolean: return true (authenticated) or false (not authenticated). Boolean return value is compatible with the Basic and Form Authenticator. See using an external authenticator for more information.
maxusers=number
The maximum number of concurrent login sessions the user is allowed to create. Setting maxusers to 0 will prevent the user from logging in. Note, the Response Message Handler must be designed to respond with a special message if maxusers is zero.
recycle=boolean
Enable recycling of older sessions if the maxusers ceiling is hit. Note, the Response Message Handler must be designed to respond with a special message if this variable (is not returned or is false) and maxusers is hit.
inactive=boolean
The maximum inactive time interval in seconds. If this time elapses without any user activity, the user will be logged out. The default session time is used if no value is returned.
ba.create.jsonuser()
Creates an authenticator user database object that is using a JSON as the user database format. The returned object can be used as the target for ba.create.authenticator().
The created object has the following additional authenticator methods:
jauthenticator:set(userdb)
userdb = table or string -- A Lua table or a JSON encoded string. The Lua table must have the following fields:
 {username1=user1, username2=user2} -- etc. 
Where user1 and user2 are tables with the following fields:
{
 pwd='password',
 roles={'role1','rol2','etc'},
 maxUsers=number,
 recycle=boolean,
 inactive=number
}

The pwd field is either a string storing a password in plaintext or a Lua table encapsulating an HA1 hash.

The fields maxUsers, recycle, and inactive are optional. The roles table is required, but you can set it to an empty table if you do not plan on using authorization. The following basic example sets up a database with two users and no authorizer.

local ju=ba.create.jsonuser()
ju:set{username1={pwd='pwd1',roles={}},username2={pwd='pwd2',roles={}}}
dir:setauth(ba.create.authenticator(ju))
jauthenticator:authorizer()
Creates and returns a jauthorizer (JSON authorizer) object. Each JSON user database can be associated with an unlimited number of JSON authorizers. You can, for example, use the same authenticator on multiple directories, but create a unique JSON authorizers for each directory.
The combined JSON authenticator user database and the JSON authorizer provides a ready to use user database and constraint management using JSON as the database format. The JSON user database and the JSON constraints can be saved to a file system. The FuguHub server is using the JSON authenticator user database and the JSON authorizer for authenticating and authorizing the users. An easy to use web application is designed as a front end to manage the JSON files. Download and install FuguHub if you want more information on how the JSON authenticator user database and the JSON authorizer is used in FuguHub. The help file includes additional information on how to setup users and constraints using the integrated web management application.

The following example sets up an authenticator and authorizer that simulates the same authentication and authorization logic as in the security C code example.

-- Setup the users, Defaults: maxusers=3,recycle=false,inactive=false
local userGuest={pwd='guest',roles={'guest'},maxusers=100}
local userKids={pwd='kids',roles={'guest','family'}}
local userDad={pwd='dad',roles={'guest','family','dad'},recycle=true,inactive=60*60}
local userMom={pwd='mom',roles={'guest','family','mom'},recycle=true,inactive=60*60}

-- Create a JSON user database object and install the user database
local authuser=ba.create.jsonuser()
authuser:set{guest=userGuest,kids=userKids,dad=userDad,mom=userMom}

-- Create a digest authenticator (using default values).
local authenticator=ba.create.authenticator(authuser)

-- Setup the constraints
local constr1={urls={'/*'},methods={'GET'},roles={'guest'}}
local constr2={urls={'/*','/family/*'},methods={'POST'},roles={'mom','dad'}}
local constr3={urls={'/family/*'},methods={'GET'},roles={'family'}}
local constr4={urls={'/family/mom/*','/family/dad/*'},methods={'GET'},roles={'mom','dad'}}
local constr5={urls={'/family/dad/*'},methods={'POST'},roles={'dad'}}
local constr6={urls={'/family/mom/*'},methods={'POST'},roles={'mom'}}
local constr7={urls={'/family/kids/*'},methods={'GET','POST'},roles={'family'}}
-- Note, the constraint names are not used by the authorizer.
local constraints={Guest=constr1,FamilyPost=constr2,FamilyGet=constr3,
   Parents=constr4,Dad=constr5,Mom=constr6,Kids=constr7}

-- Create the authorizer and install the constraints.
local authorizer=authuser:authorizer()
authorizer:set(constraints)

-- Create a directory and set the authenticator and authorizer
local dir=ba.create.dir()
dir:setauth(authenticator,authorizer)
-- Note: you must also reference (anchor) the dir so it is not garbage collected.
ba.create.dav([,name] [,priority], io [,lockdir] [,maxuploads, maxlocks])
Creates a WebDAV directory. The first two parameters are the same as ba.create.dir().
io - an IO interface created by ba.mkio() or ba.openio()
lockDir - is the name of the lock directory. The lock directory, which should use a ".name" on Unix, is where the WebDAV server keeps information about client locks. a WebDAV client can use the HTTP method LOCK to lock a resource.
Some tools such as Microsoft Word use locking to prevent a second user from editing the same document. If the lockdir parameter is nil or if the directory does not exist and the WebDAV server is unable to create this directory, the WebDAV server will operate in read only mode. It is recommended to create this directory before creating a WebDAV object.
maxuploads - is the limit value for concurrent uploads. The WebDAV server uploads data to the server using asynchronous sockets and does not need a dedicated thread for the upload. The WebDAV server needs to dynamically allocate resources when starting a new upload. You can set a maximum number of concurrent uploads to limit memory use. Maxuploads is more useful for an embedded device with limited memory. Downloads are also using asynchronous sockets, but it is not possible to set a limit on the number of downloads.
maxlocks - is the maximum number of WebDAV locks. Note that not all tools use locking, and not all WebDAV clients support locking.
The created object has the following additional directory method:
dav:io()
returns the objects IO interface.
ba.create.dir([name] [,priority])
Creates a virtual directory node.
Parameters:
  • name - the name of the directory; nil or nothing indicates an unnamed directory. An unnamed directory can be installed as a root directory or as a chained child directory. A root directory and chained child directory act as if they are part of the parent -- i.e. a root directory is part of the top server node and a chained child behaves as an extension to the parent. Named directories are only activated if the name matches the "top" name in the path.
  • priority - optional priority of the directory. Default is zero. Negative gives lower priority and positive gives higher priority.

The HTTP directory object returned by this function supports a variable set of functions.


local function testfunc(_ENV,path)
  response:setdefaultheaders()
  response:write[[
    <!-- example usage -->
    <html>
    <head>
    <title>test</title>
    <meta http-equiv="Content-Type"
       content="text/html; charset=utf-8">
    </head>
    <body>
    <pre>HELLO WORLD!
    ]]
  print("relative path=",path)
  print("absolute path=",request:uri())
  response:write[[
    </body>
    </html>]]
  return true; -- say we are done
end
testdir = ba.create.dir("test")
testdir:setfunc(testfunc)
testdir:insert() 

  Output:

HELLO WORLD!
relative path= xyz
absolute path= /test/xyz

ba.create.domainresrdr(domainname [,priority] ,io [,404-page])
Creates a Barracuda Domain Resource Reader. A domainresrdr is similar to a standard Resource Reader, except for that the service function only searches for the resource if the domain name matches the client's host header. A domainresrdr acts as a domain filter for a resrdr. The additional directory functions available to a Resource Reader are also available to a domainresrdr.
domainname - represents a domain name.
priority - optional priority of the directory.
io - an IO interface created by ba.mkio() or ba.openio() a reference to the IO interface exists for as long as the created object exists.
404page - is the name of CSP, LSP or HTML page to be displayed when the URL is not found. The domainresrdr requests the virtual file system to continue searching if the requested page is not found and a 404 page is not provided.
ba.create.eh(name [,priority] [,con-listen-table])
The EventHandler plugin has been deprecated. New design should use SMQ or raw WebSockets.
name - is the required EventHandler directory name.
con-listen-table - is an optional connection listen table with two functions: connect,disconnect. The two functions get the following arguments: cid -- the Connection ID, session -- the session object, if any.

The created object has the following additional directory method:
eh:setintf(servername, clientname, serverfunctions)
Install an EventHandler interface, a named asynchronous event channel interface node. EventHandler interfaces use a name for the server side and a name for the client side. The names must match the names declared in the client.
servername - the server interface name.
clientname - the client interface name.
serverfunctions - a table defining the server functions the client can call.
  local serverfunctions = {
  msg1 = function(cid,...),
  msg2 = function(cid,...)
  }
The parameters received are the cid (Connection ID) plus any arguments sent by the client.
Return values: eh:setintf() returns two objects, a multi cast client interface and a unicast client interface. The multicast interface sends messages to all connected clients, and the unicast client interface sends messages to one client.
Example:
local unis, multis = eh:setintf("chat", "chat", serverfunctions)
unis:clientmsg1(cid,param1,param2)
multis: clientmsg1(param1,param2)
The unicast and multicast interfaces are the bindings for the client interface functions. You can call any method in the client interface. The unicast interface takes an initial cid which specifies the client you want to send the message to.
See the EventHandler example for more information.
eh:setnodelay(bool)
Disable or enable the Nagle algorithm.
eh:keepalive(cid,bool[, keepidle, keepintv])
Setting this option to true enables the periodic transmission of messages on a connected socket.

The function returns true on success and nil,error if the operation failed.

Error codes for 'keepalive':
  • 'notfound' is returned if the cid is not valid or if the connection is an iframe push connection and not a WebSocket connection.
  • 'noimplementation' is returned when supplying the two additional parameters, keepidle and keepintv, and when these two parameters are not supported on your platform. You should then call method setoption again, but without providing these two parameters. Consult your platform's TCP/IP guide for how to manually tune these parameters on a global level.
  • 'noimplementation' is returned when the keepalive option is not supported at all on your platform (when keepidle and keepintv are not provided).
  • 'failed' is returned if the keepalive operation failed.
See the more detailed explanation for method socket:setoption and parameter 'keepalive' for more information on keepalive. Note: most WebSocket enabled browsers enables keepalive so you do not need to enable this on the server side if the purpose is to keep a NAT connection open.
ba.create.wfs([,name] [,priority], io [,lockdir] [,maxuploads, maxlocks])

Creates a Web File Server (WFS) directory. A Web File Server is a Lua implemented directory function that combines a WebDAV object and a Web File Manager. The Web File Server makes it possible for the same directory to serve requests from browser clients and WebDAV clients. The Web File Server directory function makes the assumption that browser clients send a "User-Agent" string containing the word "Mozilla" and WebDAV clients do not. Browser requests are delegated to the Web File Manager directory function, and WebDAV requests are delegated to the WebDAV directory function.

The WFS can be used as is or be customized for various purposes. See the Web File Manager Readme File for an introduction to the Web File Server's capabilities.

See also
ba.create.dav

The BAROOT/bin directory includes a pre-compiled version of the wfs example server, which incorporates a WFS instance.

Note: NetIo instances are designed to interoperate with a Web File Server and will not function unless the server has a WFS instance installed in the virtual file system.

The Web File Server's integrated Web File Manager is a Lua implemented version of the Web File Manager in directory BAROOT/xrc/misc/HttpResMgr.c

The Lua implemented Web File Manager requires a number of resource files. The resource files required are the same files required by the HttpResMgr. See the HttpResMgr example for more information.

The created object has the following additional directory method:
wfs:configure(options)
options=table -- A table with the following optional fields:
tmo=number
The Web file Manager adds an additional session URL button if the 'tmo' field is set to a non zero value and an authenticator is set on the Web File Server. The 'tmo' field specifies the maximum idle time in seconds before the session URL expires.
helpuri=string
A help button is added to the Web File Manager if the helpuri is provided. The uri must point to your customized Web File Manager Readme File.
logouturi=string
A logout button is added to the Web File Manager if the logouturi is provided.
Example:
require"wfs"
fs=ba.create.wfs("fs", ba.openio("root"), ".LOCK")
fs:configure{tmo=2*60*60,helpuri="/my/help/",logouturi="/logout/"}
fs:insert()
ba.create.resrdr([name] [,priority], io)
Creates a Barracuda Resource Reader. The first two parameters are the same as ba.create.dir().
The name represents a directory name. io - an IO interface created by ba.mkio() or ba.openio() a reference to the IO interface exists for as long as the created object exists.
The created object has the following additional directory methods:
dir:getapp()
returns the application table if installed, otherwise nil is returned.
dir:io()
returns the objects IO interface.
dir:lspfilter([apptab])
enables Lua Server Pages (LSP) for the directory. The optional apptab is an optional table that can be made available to all LSP pages in the resrdr. See the command environment: application table for more information.
dir:header(table-with-key-value-pairs)
Include additional/custom HTTP headers as part of every response for all resources delivered from the Resource Reader, including LSP pages.
-- Example: harden security policy
dir:header{
   ["content-security-policy"]=
      "default-src 'self'; connect-src https://mydomain.com",
   ["x-xss-protection"]="1; mode=block",
   ["x-frame-options"]="SAMEORIGIN",
   ["x-content-type"]="nosniff",
   ["Strict-Transport-Security"]="max-age=31536000; includeSubDomains",
}
dir:maxage(seconds)
Sets header "Cache-Control: max-age=seconds" an all resources delivered by the Resource Reader, except for LSP pages.
dir:insertprolog(dir [,reference])
Inserts a directory as a child in the Resource Reader. The method is similar to dir:insert, except for that the directory is inserted in the Resource Reader's prologue directory list. The prologue directory list is search prior to searching for resources in the Resource Reader. Directories inserted with dir:insert() are only searched if the resource was not found in the Resource Reader. Method dir:insertprolog() is typically used when authentication is only needed on sub-directory in the Resource Reader. A prologue directory with an authenticator and directory name matching the directory in the Resource Reader that must be protected can be inserted as a prologue directory. The prologue directory will be activated if the relative URL matches the prologue directory, the authenticator will kick in, and make sure the user is authenticated.

ba.create.upload(io, maxuploads)
Creates an upload object that can upload files using either HTTP PUT or HTTP POST multipart/form-data. Argument io, the I/O-interface is where the upload is saved. Argument maxuploads is the maximum allowed number of concurrent uploads.

The upload functionality has been specifically designed for uploading (large) files asynchronously to persistent storage, such as a hard drive, on the server. The upload functionality can be activated from an LSP page or from a directory function. The benefit of using a directory function is that one can use the relative path received in the directory callback function as the path to where to store the uploaded file. In other words, one can use the upload functionality as a base for creating a Web File Manager. For example, the WFS is internally using an upload object.

Unlike request:multipart() and request:rawrdr(), which requires a dedicated thread while the upload is in progress, ba.create.upload() uses asynchronous sockets for the upload. The active socket is moved out of the web-server and into an object handled by the upload logic. The upload logic is particularly efficient at handling many concurrent uploads. There are no limitations to the file upload size, except for hard drive limitations. The upload logic maintains HTTP 1.1 persistent connections. A persistent connection is automatically moved back into the web-server at the end of the upload and after a response is sent to the client.

Function ba.create.upload() is returning an object that is internally using the C/C++ HttpUpload class. The HttpUpload class is also used by the WebDAV server when uploading files.

Calling the object returned by ba.create.upload() as a function activates the upload. The function does not return control to the caller: upload(request, path, startfunc, completefunc, errorfunc [,environment])

  • Argument "request" is the LSP or "directory function's" request object.
  • Argument "path" must be a (directory+)filename if the upload is HTTP PUT.
  • Argument "path" must be a directory name if the upload is HTTP POST multipart/form-data. The uploaded file is saved as path+name, where name is the file name encoded in the multipart/form-data stream. The name originates from the HTML form input type name attribute: <input type='file' name='file'>
  • The arguments startfunc, completefunc, and errorfunc are Lua callback functions that are activated on start of the upload, when the upload completes, or on upload error. The 3 functions must be declared as follows:
    function uploadcallback(_ENV, upload)

    _ENV - is the environment table passed into the upload object. The upload object creates a new environment table if no table was passed into the upload object.

    upload - provides information about the upload by the following methods:

    • upload:name()
      Returns the path name relative to the IO Interface used in ba.upload().
    • upload:url()
      Returns the request URL including the relative path..
    • upload:multipart()
      Returns true if the request is a HTTP POST multipart/form-data stream. Returns false if the request is HTTP PUT.
    • upload:session()
      Returns the session object, if any.
    • upload:response()
      Returns the deferred response object.

The callback functions:

  • Function start(_ENV, upload):
    The optional "start" function is called if the uploaded data is encoded as a HTTP POST multipart/form-data stream. The callback is called as soon as the "name" element is extracted from the multipart/form-data encoded stream. The Lua callback can, for example, be used as a method for authorizing the upload -- e.g. checks if the uploaded file name is correct. Calling upload:response() in the "start" callback function terminates the upload.
  • Function complete(_ENV, upload):
    The "complete" function is called when the upload completed successfully. This function typically fetches the deferred response object by calling upload:response() and sends a response message to the client.
  • Function error(_ENV, upload, error, extra):
    The "error" function is called if the upload fails. The upload may fail if the socket connection breaks or if the hard drive is full. Argument "error" is the error code, and argument "extra" is any additional error information upload may provide. You cannot send a response to the client if the socket connection is broken.
ba.exec(prog)

prog - string containing the executable and optional arguments.

Starts program prog in a separated process. The function waits for the process to terminate and returns the result printed to standard out by the child process. This function is similar in functionality to io.popen, but is specifically designed for the threading design in the Barracuda Embedded Web Server. Calling ba.exec allows other threads to execute while the thread calling ba.exec waits for the child to terminate. Calling io.open blocks all other threads until the child process terminates.

This function returns the result produced by the child process on success, otherwise nil followed by an error code is returned.

This function is available on systems that support the process model such as Linux, QNX, and Windows.

See also ba.forkpty().

ba.loadfile(filename[,io][,_ENV])
This function is similar to the native Lua loadfile(), but differs in that it uses a Barracuda IO object to read the file that is loaded. The IO, if not provided, defaults to the VM IO. The file is loaded using the VM's default IO if io is not provided. If env is provided, sets env as the value of the first upvalue of the created function. The first upvalue will be the _ENV variable. Example: io:dofile("myscript.lua",setmetatable({},{__index=_G})).
ba.mime(extension)
Returns the mime type for the file extension. The second argument is a boolean which indicates if the extension is known. The server returns "application/octet-stream", false, if the extension is unknown.

I/O interface

The I/O interface is supported on all environments, from High Level Operating Systems (HLOS) to deep embedded RTOS environments. The standard Lua I/O is also supported on all HLOS, but not RTOS environments. The standard Lua I/O can be accessed by prefixing 'io' with _G. Example: local io = _G.io.

The I/O interface provides a common set of functions for working with files stored in various media types such as data stored on a standard file system, ZIP files, and network files. The Lua I/O interface provides an interface to the C side implementation for the IoIntf. The C startup code can initialize and provide any number of I/O interfaces to the Lua code. The I/O interfaces provided by the C startup code is available to the Lua code via ba.openio(ioname). New I/O interfaces can be created by calling ba.mkio(baseio, path).

The DiskIo and the NetIo are delivered as C code and must be compiled, linked, and installed by the C startup code. Exceptions are the Windows and Linux release, which are delivered with the DiskIo precompiled. The wfs and the lspappmgr C source code examples show how to install the I/O interfaces and make the I/O interfaces available to Lua code. See the C side IoIntf introduction for detailed information.

ba.io()
Returns a table with all I/O resources registered by the C/C++ startup code by calling function balua_iointf(). The key is the I/O name and the value is the I/O resource.
for name,io in pairs(ba.io()) do
   print(name, " : ", io:resourcetype())
end
ba.openio(ioname)
Returns a Barracuda I/O object. The I/O object must have been installed by the C startup code by calling function balua_iointf().

C/C++ startup code example:

/* Register the DiskIo as "disk" */
balua_iointf(L, "disk",  (IoIntf*)&diskIo);

Lua code example:

--Open the "disk" I/O interface created by the C startup code:
local diskio = ba.openio("disk")

--Open the "net" I/O interface created by the C startup code:
local netio = ba.openio("net")

ba.mkio(baseio, path)
Dynamically create an IO interface object by opening a directory, a URL, or a ZIP file. The path must be relative to the base IO. The IO object returned from this function have the same methods as ba.openio().
-- Create a new DiskIo instance by using the sub directory "apps" as
-- the base directory for the new I/O:
local appsio = ba.mkio(ba.openio("disk"), "apps/")

-- Create a ZipIo by opening a ZIP file in the "disk" (DiskIo) root
-- directory:
local zio = ba.mkio(ba.openio("disk"), "myzip.zip")

-- Create a new NetIo by using the URL http://192.168.1.100/fs/c/apps/
-- as the base path.  Note: Requires that the wfs is running on 192.168.1.100.
local netio = ba.mkio(ba.openio("net"), "http://192.168.1.100/fs/c/apps/")

-- Open the ZIP file c:\myzip.zip on a computer, where the wfs is
-- running, with IP address 192.168.1.100:
local netzipio = ba.mkio(
   ba.openio("net"), "http://192.168.1.100/fs/c/myzip.zip")
I/O object Methods:
Barracuda I/O objects have the following methods associated with them:
io:open(path[, mode)]
Opens a resource and returns an object that can be used for IO. The optional mode is the same as the file modes used in standard Lua.
The objects returned by open() have the following methods:

fh:close(object)
Same as standard Lua.
fh:flush()
Same as standard Lua.
fh:read([option])
Same as standard Lua, except that the only options supported are a number of bytes or the "*a" (all of the file). The default option reads LUAL_BUFFERSIZE bytes.
fh:seek(offset)
Same as standard Lua.
fh:write(string, ...)
Same as standard Lua.
io:resourcetype()
Returns two strings indicating the type of resource where files exist (e.g. disk) and the type of operating system.
io:type(object)
Returns true if the object is a Barracuda file handle.
io:files(dirname [,true])
Returns an iterator that will iterate over all the files in the named directory. The iterator returns each file name in the directory. If the optional second argument is set to true, the iterator returns: name, isdir, mtime, size.
io:stat(path)
Returns a table of attributes for the named path (file or directory). If the path does not exist then 'nil' is returned. The fields of the table are named as follows:
  • name - the path name
  • mtime - last modified time
  • size - the size of the file (in bytes)
  • isdir - true if the path is a directory
  • type - either "directory" or "regular"
io:realpath(path)
Returns the real path (absolute) path name of the provided relative path.
io:mkdir(path)
Creates a new directory.
io:rmdir(path)
Removes a directory.
io:remove(path)
Removes (deletes) a file.
io:rename(oldpath, newpath)
Renames a file or directory.
io:loadfile(path [,_ENV])
Loads a Lua source file. Refer to the Lua base library loadfile() function. If env is provided, sets env as the value of the first upvalue of the created function. The first upvalue will be the _ENV variable. Example: io:dofile("myscript.lua",setmetatable({},{__index=_G})).
io:dofile(path [,env])
Loads and executes a Lua source file. Refer to the Lua base library dofile() function. If env is provided, sets env as the value of the first upvalue of the created function.
Extended methods:
The extended methods provide additional features for the I/O interfaces. Additional features are provided by the I/O interfaces by calling the C/C++ side I/O interface property function. The following Lua bindings are wrappers for calling the underlying C side property function. Calling one of the extended methods on an I/O interface of incorrect type has no effect.

DiskIo:
io:hide(true|false)
Some files systems such as DOS based file systems support attributes for hiding files. The hide method sets the file attribute to hidden or clears the attribute. The hide method has no effect on file systems that do not support the "hidden" attribute. UNIX type file systems typically have hidden files starting with a dot. Examples: .config, .myfile.lsp.
ZipIo:
io:setpasswd(password)
You can set the password if the ZIP file opened with ba.openio or ba.mkio is password protected. Barracuda only supports AES encrypted ZIP files.
io:reqpasswd(enable)
Enable password protection. If enabled, the file being opened must match the password set with io:setpasswd. It is possible to replace an encrypted file inside a ZIP file with a non encrypted file. Enabling password protection makes sure your ZIP file is not compromised.
io:encrypted(path)
Returns true if file is AES encrypted. Returns nil if not found.
io:close()
Close a dynamically created ZipIo, i.e. a ZipIo created with function ba.mkio().The ZipIo can no longer be used after you call io:close().

A dynamically created ZipIo keeps the ZIP file open until the garbage collector collects the IO or until you explicitly call method io:close(). This function is designed for operating systems such as Windows and CE that locks open files. The file cannot be deleted from the file system before the file is closed. Explicitly calling method io:close() makes it possible to delete the ZIP file from the file system without having to wait for the garbage collector to collect the dynamically created ZipIo.
NetIo:

The NetIo is similar to a network file system and makes it possible for the server to access resources on another Barracuda server.

io:netconf({options})
Configure a NetIo.

A NetIo is typically created and installed uninitialized by the C/C++ startup code. Method io:netconf() makes it possible to either configure the NetIo installed by the C/C++ startup code or to configure a NetIo clone created with with ba.mkio(). See the NetIo C documentation for more information.

{options}
user=string
Username sent to destination server.
pass=string
Password sent to destination server.
proxy=string
Use a HTTPS or SOCKS5 proxy.
proxyport=number
Proxy port number defaults to 8080 for HTTPS proxy and 1080 for SOCKS5 proxy.
socks=bool
Set to true if you are using a SOCKS5 proxy. The default is to use HTTPS proxy by using HTTP CONNECT.
proxyuser=string
Proxy username
proxypass=string
Proxy password
intf=string
Bind to interface-name/IP-address. The default is to bind to any interface.
ipv6=bool
Force use of IPv6 address translation. Note, the server must have been compiled with IPv6 support.
shark = SharkSSL object
The SharkSSL object is required when using secure connections -- i.e. URLs starting with https://. This attribute is ignored if the AUX library is not enabled.

All attributes are optional and you can call the method multiple times to configure the NetIo. For example, setting user and password multiple times does not affect any of the other attributes set previously.

Note: "User, pass" and "proxyuser, proxypass" should be in pairs. The password is set to the empty string "" if missing.

The NetIo must be configured before calling ba.mkio(ba.openio("net"), URL) if a proxy and/or password are required, since ba.mkio verifies the URL before creating a new NetIo. You can either configure the NetIo installed by the C/C++ code or you can make a copy by calling ba.mkio(ba.openio("net")) -- i.e. without providing a URL. Calling ba.mkio() without providing a URL creates a copy of the original I/O.

Error codes:
The methods in the IO object returns nil and three error codes if calling the method(s) fail.

Error codes returned:
  1. Error Type (short string).
  2. Descriptive error message.
  3. Optional error message that may be returned by the native file system.

The error type can be any of the following:
Error Type Description
invalidname Name not accepted by file system
notfound Not found
exist Resource exists
enoent Directory not found
noaccess Resource locked by file system
notempty Directory not empty
ioerror Some kind of I/O error
nospace No space left on device
mem No memory for operation
buftoosmall Buf too small
noimplementation Not implemented
noaeslib AES not enabled
notcompressed ZIP not compressed
ziperror Unknown ZIP file format
noziplib ZLIB not installed
aesnosupport Unknown AES encryption
nopassword Resource is encrypted, but no password provided
wrongpassword Wrong password provided
aeswrongauth Wrong password or file compromised
aescompromised AES protected file compromised
invalidsocketcon Invalid socket con
gethostbyname Gethostbyname failed
bind Bind failed
socketwritefailed Socket write failed
socketreadfailed Socket read failed
timeout Timeout period expired before receiving data or event
malloc Malloc failed
alreadyinserted Already inserted
toomuchdata Too much data
pagenotfound Page not found
iscommitted Is committed
invalidparam Invalid param
mixingwritesend Mixing write send
toomanyincludes Too many includes
toomanyforwards Too many forwards
includeopnotvalid Include op not valid
cannotresolve Cannot resolve
cannotconnect Cannot connect
invalidurl Invalid URL
invalidresponse Invalid response

ba.parsedate(string)
Parses the date string and returns the time with epoch of 1970-01-01 00:00:10 GMT.

ba.parselsp(string[,embed])

Parses and returns the Lua Server Page as a Lua chunk. The returned string can for example be loaded by function load().

The default is to parse the LSP page as a standalone page, unless embed is set to true. The LSP page expects the following arguments when compiled as a standalone page: page(_ENV,pathname,io,page,app), where _ENV is the command environment, pathname is typically derived from a directory functions relative path. The arguments io,page, and app are the LSP page's IO object, page table, and application table.

Function parselsp can for example be used as an alternative to response:include(). The following example shows how to use parselsp in one LSP page to include another LSP page.

<?lsp
local fp,err=io:open"mypage.lsp"
if fp then
   local data
   data,err = fp:read"*a"
   fp:close()
   if data then
      -- Convert LSP to Lua
      data,err = ba.parselsp(data)
      if data then
         local func
         -- Compile Lua code
         func,err = load(data,"mypage.lsp","t",_ENV)
         if func then
            local ok
            -- Let child use our: _ENV,io,page,app
            ok,err = pcall(func,_ENV,"mypage.lsp",io,page,app)
         end
      end
   end
end
?>
ba.rndseed(seed)
Seeds SharkSSL, ba.aeskey(), and ba.rnd(). Argument seed is a number between 0 and 0xFFFFFFFF. SharkSSL uses a circular buffer for seed values and you improve the randomness if you call this function several times.
-- Seed SharkSSL by connecting to remote seed generator
ba.thread.run(function() -- Run the blocking HTTP request in a separate thread
    -- Create a HTTP object using the low level HTTP library.
   local http=require"httpc".create()
   -- Send request and required parameters
   -- See http://www.random.org for more information
   http:request{
      method="GET",
      url="http://www.random.org/integers/",
      query={
         num=1,
         col=1,
         min=1,
         max=1000000000,
         base=10,
         format="plain",
         rnd="new"
      }
   }
   if http:status() == 200 then
      local rnd=http:read"*a"
      if rnd then
         rnd=tonumber(rnd)
         if rnd then
            -- We received a number
            -- Seed SharkSSL
            ba.rndseed(rnd)
         end
      end
   end
   -- Seed using millisec clock.
   -- The HTTP response time creates a random delay.
   ba.rndseed(ba.clock())
end)
ba.rnd([ [low ,] high])
Generates a random number. If no parameters are given a positive integer between 0 and 0x7fffffff is returned. If one parameter is provided then it represents the upper value of a random integer between 0 and the upper value and if two parameters are provided it represents the lower and upper limits of a random integer.
ba.rnds(size)
Generates a random number of byte size 'size'.
size - in bytes. Valid range is 1 to 4.
ba.session(args)

Function ba.session() is an alternative to method request:session(). Method request:session() can only be used within the command environment. Function ba.session() does not have this limitation and can therefore be used from any environment.

Function ba.session() can be used in three modes:

ba.session(session-number)
session-number - number created with method session:id() or from the ID's returned by ba.sessions().
Returns the session object if found.
local s = request:session(true)
local id = s:id() -- Returns a number suitable for local server use
assert(id == ba.session(id):id())
ba.session(session-ref, request [,create])
session-number - a number created with method session:id(true). Notice the difference between calling session:id() and session:id(true), the former returns a number suitable for local server use and the latter returns a string suitable for public use -- i.e. for use by a HTTP client such as a browser.
request - the request object.
create - a boolean value that defaults to false. Setting this variable to true also stores the session permanently in the server for the current requests thus making method request:session() return the same object, if called.
Returns the session object if found.
local s = request:session(true)
local id = s:id(true) -- Returns a string suitable for public use
assert(id == ba.session(id,request):id(true)) 

ba.session(request, relpath [,create])
Search for the session object by using a session URL. A session URL is a URL that includes the session-reference embedded as the first component in the relpath (relative path). As an example, the Web File Server is managing session URL's by using this function. Barracuda server sessions are normally automatically managed by using session cookies. Using a session URL is a convenient way to manage sessions if the client does not support cookies or does not support any standard authentication mechanism.
request - the request object.
relpath - the relative path is typically provided by a directory function.
create - a boolean value that defaults to false. Setting this variable to true also stores the session permanently in the server for the current requests thus making method request:session() return the same object, if called.
Returns: session,relpath where session is the session object and relpath is the original relpath minus the session-reference that was embedded in the original relpath.

The following code fragment is from the Web File Server implementation. The Web File Server code can be found in the Barracuda SDK /WebResources/rtl/.lua/ directory. The code fragment shows the authService directory service function, which is installed if an authenticator is set on the Web File Server and if the session URL is enabled.

local function authService(_ENV,relpath)
   local s
   if not request:user() then
       -- User is not authenticated or no session cookie.
      local p
      -- Does the relative path include a session reference?
      s,p=ba.session(request,relpath,true)
      if s then
         -- Yes, relpath included the session
         s:maxinactiveinterval(sesTmo)
         -- Set the new relative path
         relpath=p
      end
   end
   -- Delegate the request to the standard directory service function
   service(_ENV,relpath,s)
end
ba.sessions()
Returns an array with the session id for all sessions active in the server.
ba.sessions(username)
Returns an array with the session id for all active sessions used by the authenticated user 'username'.
ba.sleep(milliseconds)
Pauses execution of this request or thread for the specified number of milliseconds. This function may yield to other Barracuda threads, consequently the function may pause for longer than the specified period. ba.sleep(0) would simply allow other pending Barracuda threads to execute (i.e. a yield).
ba.seterrh(function)

Install a Lua error handler. The error handler is called if a Lua script should cause an exception. The callback function gets one argument, the error message. Note: All errors are sent to the Barracuda HttpTrace library, thus a Lua error handler is typically not needed during debugging.

An error handler could for example be installed in a deployed system and send the error message by using the SMTP library. Another possibility is to upload the error message to an online database by using the HTTPS client library.

ba.timer()
The timer object makes it possible to create events that are activated at regular intervals. One can also create an event that is activated only one time. The Lua timer is internally using the C/C++ Barracuda timer class.

Creating a timer:
local timer = ba.timer(
  function()
    -- Do something
  end
)

ba.timer() returns an inactive timer object -- that is, the timer is created, but not started. The returned object must be referenced. The Lua garbage collector collects all objects not being referenced. A collected timer object is automatically cancelled.

The timer object returned by ba.timer() has the following methods:

timer:set(millisecs [,bool [,bool]])
Activates an inactive timer object. If the timer is active, the timer is cancelled and then activated. The timer callback function is activated in "millisecs" time unless method "reset" or "cancel" is called before the timer triggers.

The timer will be automatically referenced if the second argument is set to true. The reference will be maintained until the timer is cancelled by calling function set or cancel, or when the timer function stops the timer. The purpose with the reference is to prevent the garbage collector from collecting the timer object when the timer is run as an interval timer. Without the auto reference, a global reference maintained by the Lua code creating the timer would have been required.

The timer function is run immediately if the third argument is set to true. Running the timer function immediately is sometimes useful when running the timer function in interval mode and as a coroutine. The timer function can be used in a similar manner to how one creates a thread -- i.e. the thread function is called which then enters a forever loop.

timer:reset(millisecs)
Resets the timer if the timer is active. Returns true if the timer was active and the timer was successfully reset. Returns nil if the timer was inactive.

timer:cancel()
Cancels the timer if the timer is active. Returns true if the timer was active or nil if the timer was inactive.

The timer callback function:

The timer callback function is run as a "one-shot timer" if the timer callback function does not return a value or if the function returns false. The timer is automatically re-activated with the previous timeout value if the function returns true.
-- One-shot timer example:
function timeout()
  -- do something
end
-- Create a timer. The timer object must be global.
t = ba.timer(timeout)
-- Set the timeout to one second.
t:set(1000)

-- Interval timer that never stops.
function timeout()
  -- do something
  return true
end

-- Create a self referencing interval timer
ba.timer(timeout):set(1000,true)

As an optional feature, the timer callback function can be run as a Lua coroutine. In coroutine mode, the timer callback is run as an interval timer.

The following example illustrates how the timer callback function can be run in coroutine mode. The timer saves the counter variable "i" on the stack. The variable is re-used when the timer function is reactivated. The timer is activated a total of five times before the timer exits.
local function timeout()
   for i=1,5 do
      trace("Interval", i)
      coroutine.yield(true)
   end
   trace("Stop interval")
   coroutine.yield(false)
end

-- Create a self referencing interval timer and start the coroutine immediately.
ba.timer(timeout):set(1000,true,true)


A timer callback function in coroutine mode cannot be re-used when the function calls coroutine.yield(false). However, the function can call timer:cancel(), or timer:reset() to either temporarily cancel an active timer or to change the interval time.
ba.tracker
A table of functions for accessing the data provided by the default client login tracker. A login tracker can be associated with an authenticator as an optional security enhancement. See introduction to authentication for more information. The table is not installed unless the C startup code has activated the default tracker by calling function balua_usertracker_create().
ba.tracker.successful()
Returns an array of tables containing:
name
The user's login name.
time
The time the user last logged in.
addr
The user's IP address
ba.tracker.attempted()
Returns an array of tables containing:
name
The attempted login name.
time
The attempted login time.
addr
The user's IP address.
counter
The total number of login attempts.
aux
Auxiliary counter. Number of last login attempts is counter - aux.
ba.tracker.clearcache()
Removes all failed login attempts from the login tracker cache.
ba.tracker.setlogh(function)
Set a Lua login handler callback. The function will be called when a user logins or fails to login.
Callback function(successful, name, addr [, _ENV]) arguments:
successful - Boolean set to true if successful login or false on failed login.
name - The (attempted) login name.
addr - The user's IP address.
_ENV - The command environment is normally not needed, but can be useful in special cases.
ba.users()
Returns an array with the name(s) of the current active user(s).

The following example terminates all sessions:

-- Iterate all authenticated users
for _,name in ipairs(ba.users()) do
   -- Iterate all sessions for the user
   for _,sesid in ipairs(ba.sessions(name)) do
      local s = ba.session(sesid) -- Fetch session using session ID.
      print(string.format("Logging out %s:%X IP address: %s",name,sesid,s:peername()))
      s:terminate()
   end
end
ba.version()
returns 3 values: the BAS library version, the LUA version, and the time and date the library was compiled.

Authenticator User Database Object

The objects created by ba.create.authuser() and by ba.create.jsonuser() are typically used by the underlying C authenticator code for granting or denying access.
The authenticator user database object has the following method that can be used by Lua:
authuser:getpwd(username)
Returns nil if username is not found in the database or if the authenticator returns a boolean value.
Returns the following if username is found: username [, maxusers [, recycle, inactive]]
The returned string 'username' can be a plaintext password or an HA1 hash.

Authenticator Object

The authenticator object created by ba.create.authenticator() is typically used by the underlying C code for a directory type when authenticating the user. The authenticator can also be used by Lua directory functions.
All authenticator object types have the following method that can be used by Lua:
authenticator:authenticate(request, path)
Authenticate the client
request=object
The request object from the command environment.
path=string
The path is typically from the directory function's relative path argument.
Example:
local function myDirectoryService(_ENV,relpath)
   if authenticator:authenticate(request, relpath) then
      response:write"You are authenticated"
   else
      -- The response was sent by the
      -- authenticator's Response Message Handler
   end
   return true -- True means resource found and response is committed
end

Authorizer Object

The authorizer object created by a number of functions such as ba.create.authorizer() is typically used by the underlying C code for a directory type when authorizing the user. The authorizer can also be used by Lua directory functions.
All authorizer object types have the following method that can be used by Lua:
authorizer:authorize(request,method,path)
request=object
The request object from the command environment.
method=string
The HTTP method type the user must have access to execute -- i.e. 'GET'.
path=string
The path is typically from the directory function's relative path argument.

JSON Authorizer (jauthorizer) Object

The JSON authorizer object, which inherits from the authorizer object, created by method jauthenticator:authorizer() is an optional authorizer that can be used as the target for ba.create.authenticator(), when used together with the jauthenticator that created the jauthorizer.
The jauthorizer object has the following additional authorizer methods:
jauthorizer:set(constraintdb)
jauthorizer:casesensitive([boolean])
The JSON Authorizer is by default case sensitive. Turn off the case sensitive authorizer for case insensitive file systems such as FAT based file systems.
constraintdb = table or string -- A Lua table or a JSON encoded string. The Lua table must have the following fields:
 {constraintname1=constraint1, constraintname2=constraint2} -- etc. 
where constraint1 and constraint2 are tables with the following fields:
 {urls={'url1','url2','etc'},methods={'GET','etc'},roles={'role1','etc'}} 
The constraint name is not used by the authorizer, but the authorizer requires a table and not an array; thus dummy names must be created for the constraints -- e.g. constraintname1

directory object

Objects implementing directory functionality:
ba.create.dir
ba.create.resrdr
ba.create.dav
ba.create.wfs

The HTTP directory object is returned by a number of functions such as the ba.create.xxx functions.

The Lua directory object is a wrapper for the HttpDir "C" class or any class that inherits from HttpDir such as HttpResRdr.

dir:baseuri()
Returns the URI to the location where the directory is installed in the virtual file system. Returns dir:name()+"/" if the directory is not installed in the virtual file system.
dir:insert() -- Insert as root directory
dir:insert(dir [,reference]) -- Insert a child directory
Inserts a directory into the server's virtual file system. See the Virtual File System Introduction and Constructing a Virtual File System Tree for more information. See also dir:insertprolog().
reference -- Reference the child directory in the parent directory. Referencing the child prevents the child from garbage collecting as long as the parent exists.
dir:name()
Returns the name of the directory
dir:p403(path)
Set a 403 denied request handler. The directory forwards the request to the page if an authorizer is installed and the user is denied access by the authorizer. The default for a directory is to send a basic 403 message if a 403 denied request handler is not installed. Parameter 'path' is the path to a page that can be accessed by response:forward.
dir:setauth(authenticator, [authorizer])
Enables authentication and optionally authorization for this directory. The authenticator is created by ba.create.authenticate() and the authorizer is created by ba.create.authorize() or jauthenticator:authorizer(). Returns true on success.
dir:service(request, relpath [,sim-forward])
Calls the service function of this directory. This function delegates the response to the active service function if you call another directory than "self" and the original service function if called from "self".
The request object is required.
The relative path (relpath) parameter is required.
Setting the optional parameter sim-forward, a boolean value, to true, makes the service function simulate a response:forward call without resetting the response buffers. Simulating a response:forward is useful if you need to bypass authentication and/or authorization in. It is also possible to access hidden files when delegating to a Resource Reader. A hidden file is a file that starts with a dot -- e.g. ".preload".
dir:setfunc(function)
Sets the directory service function.
Apply dir:setfunc() to objects created with:

A directory function is the directory's service callback function which is activated by the server's virtual file system when searching for resources. All directory types have a default service function implemented in C code. The Lua code can install a new directory function that either replaces the original service function or extends the functionality of the original service function. The original "C" service function can be called from within the Lua service function by calling dir:service().

The directory function must be declared as follows:

  local function myservice(_ENV,relpath)

The _ENV variable is the command environment. The relpath variable is constructed from the URL and is the relative URL path component for the directory branch.

The directory function must return true if the resource was found and a response was sent to the client. The function must return false if the resource was not found and no response was sent to the client. The virtual file system will continue its search for a suitable resource if the directory function returns false. A no return value is interpreted as returning true.

See ba.create.dir for example code.

Removes the directory from its parent (or the server).

Standard Lua Libraries

All of the standard Lua libraries are implemented with the two exceptions that the io library is only supported when the target platform supports ANSI IO (MAC, Windows, POSIX) and the os library functions are implemented when it is appropriate on the target platform. os.exit() is not implemented on any Barracuda platform.
Platform independence is achieved by using the ba library.

Barracuda global functions

print(string, ...)

When calling function print() from the command environment, print() is a function that sends response data to the client. Function print() is otherwise the standard Lua print() function. To call the standard print() from a command environment, call print() using the global scope variable -- e.g. _G.print().

Function print() differs from response:write() in that print() calls the Lua tostring() function on each parameter that is passed to print(). Function print() is useful for generating diagnostics and small amounts of text on a web page, use response:write() in preference to print().

Note: the global _G.print(), that prints to the console, may not be available in embedded systems. Use trace() as a replacement.

Example:

<?lsp
  print("hello", "world")
?>
The above example would emit "hello\tworld\n".
trace(string, ...)
tracep([info,] priority, string, ...)
Writes the string to the web server trace log. Function trace() is useful for generating diagnostics and print debug information. The optional info is a boolean that defaults to true. Info makes tracep emit line/function info. This cannot be disabled for function trace.
Function trace is hard coded to use priority 0, the highest priority level. Function tracep lets you set the priority.
The trace library must be initialized at system start by the C startup code.
_emit(string)
Function _emit() is available in the command environment and is used by the LSP pages when sending parsed HTML chunks to the client. The LSP parser converts an LSP page and instruments the parsed code with _emit[[data-chunk]] around each parsed chunk.

Standalone Executable Lua Interpreter

A standalone executable Lua interpreter is delivered with the Barracuda Server's SDK. The executable, called blua, is found in the SDK's bin directory.

The executable blua is an enhanced version of the standard standalone executable Lua interpreter and includes many API's from the Barracuda server in addition to the original API's.

The standalone interpreter makes it easy to create standalone applications for sending (secure) E-mail, using the HTTP(S) client libraries, and creating (secure) client and server socket applications in Lua.

The extended executable includes: