Barracuda Lua Lua

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 by 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.

The Lua scripting language is not designed for preemptive context switching, but Lua provides a light weight threading mechanism called coroutines. In 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. 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. Please contact Real Time Logic if you plan on designing user defined events or if you create Lua bindings that may block for a prolonged time.

Lua Links

LSP

LSP provides a simple, fast way of creating web pages. LSP is similar to CSP and JSP, except that LSP does not need to be compiled, however LSP can be compiled if desired.
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 used as an adjunct to CSP. CSP pages can include LSP pages and vice versa.
LSP is extensible, there are many third party tools and libraries that are available for Lua, given that the libraries support the target platform LSP can use these libraries. In turn Lua is also extensible, it is a straight forward process to create your own extension libraries for specialist interfaces. Lua libraries are available for databases (ODBC, Postgre, MySQL, SQLLite, etc..), sockets and internet protocols like SNMP, LDAP, SMTP, FTP etc, Robotics, AI and various File Systems.
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

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 Lua it is faster and easier to construct. With the ba.create family of functions, HttpDir derived objects such as WebDav, HttpResMgr, HttpResRdr etc. can be created in either preload scripts or LSP pages. The LSP Directory functions mirror the Barracuda C/C++ APIs.

Barracuda Lua APIs

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

LSP provides Helper functions and extensions to the standard Lua APIs.

request object

The request object is available to directory functions and LSP pages.

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: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 sets 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 paraneter to false prevents these defaults from being automatically added.
This function is typically used at the start of a LSP or directory function.

e.g.
  --only allow GET and POST for this page
  if not request:allow{"GET", "POST"} then return end

  -- valid request lets continue
  print"hello world"
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.
If the parameter time is older than the request "If-Modified-Since" time time the function returns false and sends a "304 Not modified" response to the client.
The time is in the format provided by os.time().

! You cannot write to the output stream if this function returns false.


e.g.
   -- 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 name, application name and authentication type of an authenticated user for this request, if one exists for the request. If there is no authenticated user then the function returns false and the application name.
Possible values of the authentication type are; 'form', 'basic' and 'digest'.
request:logout([url][[,]all])
Logout the request user and terminates the session. The optional parameter url is where to redirect the user after logging out. The user is redirected to '/' if 'url' is not set. The optional parameter 'all' defaults to false. Set 'all' to true if you like 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 as 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: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.
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().
e.g.
  --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.
e.g.
  --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.
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 would like that the web-server closes 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 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 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 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.
request:sockname()
returns a string of the server name/IP address.
request:setnodelay(on)
Enables or disables the Nagle algorithm.
request:dir()
returns the directory object that initiated this request

response object

This object is used when sending response messages back to the client. The server creates a response object and makes it available to an LSP page. This 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.

# - the length operator
Return the sum of writesize() and bytecount(). That is, the effective number of bytes in the response.
example:
  Number of bytes sent to the response <lsp= #response ?>
response:abspath(path)
Converts the input path (not a URL) to an absolute path. If the path is already absolute it is returned unchanged. The empty string results in the path of the current page being returned.
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 then 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])
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 true this method also includes all url-encoded parameters in the request line and in the body if the client sent a POST message.
response:encodeurl(url)
Encodes an absolute or relative URL or, if encoding is not needed, returns the URL unchanged. This method escapes all symbols that cannot be in a URL. The method differs from encoderedirecturl() in that it only escapes non URL compatible symbols.
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 [,true])
Forwards a request from this LSP page to another resource (LSP page, servlet, CSP file, or HTML file). 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). If the response already has been committed, this method returns a nil and an error message. Any data in the response buffer is erased when the request is forwarded.

The forward()ed page gets its own page environment/table, but the Lua environment for the forward()ed page is same as the originating page. Restated, non-local lua variables that are set in the forward()ing page will have their values available in the forward()ed page, and vice versa.

Any error thrown by the forward()ed page will be trapped by the forward()ing page.

The path is the resource to forward. 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.

This method is similar to method redirect, with the exception that forward bypasses any form of required authentication and/or authentication.

This function does not return to the caller unless the optional second argument is set to true.
response:redirect(path[,true])
Internally redirects the request to another resource.

This method is similar to method forward, with the exception that forward bypasses any form of required authentication and/or authentication.

This function does not return to the caller unless the optional second argument is set to true.
response:include(path)
Includes the content of a resource (LSP, servlet, 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 include()ed page gets its own page environment/table, but the Lua environment for the include()ed page is same as the oriinating page. Restated, non-local lua variables that are set in the include()ing page will have their values available in the include()ed page, and vice-versa.
Any error thrown by the include()ed page will be trapped by the include()ing page.
The included page cannot be gzip'ed if the zlib compression library is not installed in the server. The path is the resource to include. 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.
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 written
response:isforward()
Returns true if this is a forward request from another servlet or LSP file. The second return value is the count of the number of times that the request has been forwarded.
response:initial()
Returns false if this is an include or forward from another servlet or LSP file, that is, the first page called during the request processing. The method returns true if this is a redirected call initiated by method redirect.
response:isinclude()
Returns true if this is an include from another servlet or 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: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: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)
Sends a temporary redirect response to the client using the specified redirect location URL. This function cannot accept relative URLs; the URL is converted to an absolute URL during this call.
example:
response:sendredirect"start.html";
response:sendredirect"https://127.0.0.1:9357/intro/start.html";
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, HttpResMgr, or HttpResRdr. However, one can delegate the response to a LSP enabled HttpResRdr if the requested resource is a 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 one method:
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 a 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 a 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 a 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 a 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 a 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, ...)
Sends strings to the client. The internal process buffers write()s to the client. Also refer to the print() function.
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()
The number of bytes that currently exist in the response write buffer. flush() should reset this number to zero.

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.
The cookie implementation is almost identical to the cookie implementation in the JavaTM Enterprise Edition

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:iscommitted() 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:value([value])
Gets or sets the value of the cookie

session object

Provides a way to identify a user across more than one page request or visit to a Web site and 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:

This class is similar (but much easier to use) to the java Interface HttpSession.

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:attributes()
returns a table containing any attributes that have been set for this session.
If no attributes have been set this function returns false.
Attribute values can be any Lua data type. e.g. strings, numbers, tables etc.
Session attributes can be removed by setting their value to `nil'. e.g. session.myattribute = nil
NOTE: LSP session attributes are private to LSP and are not accessible from CSP pages, nor can LSP pages access CSP session attributes.
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()
Get 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".

page object

A table containing the following fields, page variables may be added to this table and are persistent (they are maintained across different requests).

The page table is available to LSP scripts, it is not available to directory functions.

page.path
the path name of the resource that was requested. (Not the URL).
page.modified
the last modified time of this page. This field is a number which can be passed to the Lua function os.date() for formatting.
page.size
the size of this page in bytes.
page.url
The URL which requested this page
page.iopath
The path of the page relative to the IO interface in which it is installed.
page.iodir
The directory name part of iopath.

Page example

<html>
<!-- example usage -->
<?lsp
  print("<h2>page fields</h2>");
  print("<pre>");
  print("Path = ", page.path)
  print("Last Modified Date = ", os.date("%c", page.modified))
  print("Page size = ", page.size)
  print("URL = ", page.url)
  page.count = (page.count or 0) + 1
  print("Access count = ", page.count)
  print("</pre>")
?>
</html>



json

A library that supports JSON dencoding and decoding.
To enable this library a require "json" must be issued.

json.encode(table)
returns a JSON encoded string. The function iterates over the provided table and converts all table entries to JSON encoding. Userdata, functions and threads are set to json.null.
json.decode(string)
Takes a JSON encoded string and decodes it into a Lua table. JSON null is set to the 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.
json.encodestr(string)
Encodes a string as JSON string.
json.null
A userdata that represents the JSON null value.
Json.parser()

Create a stream based JSON parser object. The JSON parser object functions similar to json.encode, but unlike function 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.
  • false, 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.

<?lsp

-- Activated when s:event(asyncReceiveCoroutine) is called below
local function asyncReceiveCoroutine(s)
   local parser=json.parser()
   -- variable x: multiple types, or nil on error
   local x,err=true,nil
   while x do
      -- 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 ok and we have data
         if x and array then
            for _,v in ipairs(array) do
               -- Dispatch the parsed JSON object 'v'
            end
         end
      end
   end
   trace("Socket or JSON parser error:", err)
   -- 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(json.encode(data)) -- Encode and send

Example 3, JSON WebSockets Server:

WebSockets is a new technology that provides a full-duplex, bi-directional, communications channel between JavaScript code in a browser and a server. JSON is one of many protocols that can be layered on top of WebSockets. The following example is a modification of our first example. We have omitted the asyncReceiveCoroutine function since the function can be used without modification.

local origin=request:header("Origin")
if origin then -- A WebSocket request
   -- Calculate ws URL, which is ws:// or wss:// for secure
   local url=request:url()
   local ws=request:url():match"http(s?://.+)"
   if not ws then error"Invalid URL" end
   -- Assemble the WebSocket HTTP response header
   local h=string.format("%s\r\n%s\r\n%s\r\n%s%s\r\n%sws%s\r\n\r\n",
                         "HTTP/1.1 101 Web Socket Protocol Handshake",
                         "Upgrade: WebSocket",
                         "Connection: Upgrade",
                         "WebSocket-Origin: ",origin,
                         "WebSocket-Location: ",ws)
   -- Morph HTTP request into a socket connection
   local s = ba.socket.req2sock(request)
   s:write(h) -- Send HTTP header
   -- Enable asynchronous socket receive
   s:event(asyncReceiveCoroutine) -- Use function from example one.
end

We used method response:flush() to send a HTTP response message in the first server example. The web-server cannot be used for sending the HTTP response message when using WebSockets since the HTTP header must be constructed exactly as above.

WebSockets is a frame based protocol and not stream based. A WebSockets frame starts with the hex code 0x00 and ends with 0xFF. The JSON parser discards the two WebSockets frame markers and view the data received as a stream. We can therefore use the asyncReceiveCoroutine function from the first example unmodified.

The above example does not show how to send JSON encoded WebSockets data. Data can be encoded and sent from the server to the client as follows:

socket:write(string.format("%c%s%c", 0x00,json.encode(data),0xFF))

Example 4, JSON WebSockets Client:

The following JavaScript example is designed to interoperate with example 3.

<html>
<head>
<script type='text/javascript' src='/rtl/json/json.js'></script>
<script type='text/javascript'>
onload=function() {
   if( ! ("WebSocket" in window) ) {
      alert("WebSockets not supported by your browser")
      return
   }
   //Example ws://localhost/websockets.lsp
   var s = new WebSocket("ws://"+"PATH-TO-SERVER_RESOURCE");
   //onclose and onmessage are not implemented
   s.onclose = function(evt) { alert("Connection closed."); };
   s.onmessage = function(evt) { alert( "Received Message:"); };
   //When the connection opens, send one message to the server.
   s.onopen = function(evt) {
      //Encode the same data as in example 2.
      var data={txt:"hello"};
      var json=JSON.stringify(data); //In '/rtl/json/json.js'
      s.send(json); // Send to WebSockets server.
   };
};
</script>
</head>
<body>
</body>
</html>

The JavaScript JSON utility included in the Barracuda Embedded Web Server is loaded in the above example. The JavaScript file is integrated and available at URI /rtl/json/json.js in many of the Barracuda server examples.

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, json.encode(stateInfo))
Decoding:
-- Data received via hidden variable from client
local stateInfo = json.decode(ba.aesdecode(app.key, request:data"MyHiddenVariable"))
ba.aeskey([len[,seed]])
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.
seed: The Key is calculated by successively calling random. You can optionally specify a seed value. The default seed value is a function of the time and the system clock.

ba.authdefaults([maxusers, recycle, timeout])
gets or sets the the defaults for Authentication functions. When called with or without parameters the current defaults are returned.
ba.b64decode(string)
decodes a BASE64 string.
ba.b64encode(string)
encodes a string as BASE64.
ba.basedir()
returns the base directory name(executable directory)
ba.clock()
returns the number of milliseconds since system start
ba.cachelsp(true|false)
sets LSP caching on or off. When the cache is off LSP code is loaded each time the page is executed.
ba.create
a table of functions for creating Barracuda objects
ba.create.dav(name, function, [priority], io, maxuploads, lockdir, maxlocks)
Creates a WebDAV directory. The first three parameters are the same as ba.create.dir().
io - an IO interface created by ba.mkio() or ba.openio()
maxuploads - is the limit value for concurrent uploads.
lockDir - is the name of the lock directory.
maxNumberOfLocks is the maximum number of WebDAV locks.
ba.create.dir(name, function, [priority])
creates a virtual directory node.
Parameters:
  • name - the name of the directory, nil or "" indicates a root directory
  • function - specifies a Lua callback function to be called for this directory. This function will be called with the following parameters:
    (dir, base function, relative path, absolute path, root path)
    Respectively, the directory object, the parent object service function, the relative path, the absolute path and the root path.
  • priority - optional priority of the directory. Default is zero.

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


local function testfunc(hdir, basefunc,rel,abs,root)

  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("<H3>dir=",tostring(hdir))
  print("basefunc=",tostring(basefunc))
  print("relative path=",tostring(rel))
  print("absolute path=",tostring(abs))
  print("root path=",tostring(root),"</pre>)
  response:write[[
    </body>
    </html>]]
  response:flush()
  return true; -- say we are done
end

  Output:

HELLO WORLD!
dir=  httpdir:00000 '/test'
basefunc=  function:00000
relative path= xyz
absolute path= /test/xyz
root path=     /test/


ba.create.domainresrdr(name,function, [priority], io, [404page])
Creates a Barracuda Domain Resource Reader. The first three parameters are the same as ba.create.dir().
name - represents a domain 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.
404page - is the name of CSP, LSP or HTML page to be displayed when the URL is not found
ba.create.eh(name, {table})
name - is the directory name.
table - .
ba.create.wfs(name, prio, io, maxuploads, lockdir, maxlocks)
Creates a Web File Server (wfs) directory function. A Web File Server is a Lua implemented directory function that combines a WebDAV directory function and a Web File Manager directory function. The purpose of the Web File Server is to make 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 Web File Server code is located in the same file as the Web File Manager and the module is loaded by using "require".
Example:
require"wfm"
fs=ba.create.wfs("fs", 0, ba.openio("root"), 100, ".LOCK", 100)
ba.dirtop():insert(fs)
ba.create.resmgr(name,function, [priority],io, maxuploads)
Creates a Barracuda Resource Manager. The first three parameters are the same as ba.create.dir().
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.
maxuploads - is the limit value for concurrent uploads.

The Lua Resource Manager is the Lua version of the C class HttpResMgr. It is not recommended to use this directory function directly from Lua. Lua users should rather use the enhanced version returned by function ba.create.wfm.
ba.create.resrdr(name,function, [priority], io)
Creates a Barracuda Resource Reader. The first three 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 additional methods, namely;
dir:io()
returns the objects IO interface.
dir:lspfilter()
enables LSP for the directory.
dir:maxage(seconds)
sets the max-age parameter for resources returned by this directory.

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 a 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.

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.

The Lua ba.create.upload is internally using the C/C++ HttpUpload class, which is the class 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, {start=func,complete=func,error=func})

  • 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 third argument to the upload object is a table with callback functions:
    {
       start=function(upload)
       end,
       complete=function(upload)
       end,
       error=function(upload, error, extra)
       end
    }
    
The argument "upload" passed to the three callback functions has 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(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(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(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.create.wfm(name, prio, io, maxuploads)
Creates a Barracuda Web File Manager.

Parameters:
  • name - the name of the directory, nil or "" indicates a root directory.
  • io - an IO interface
  • priority - Priority of the directory.
  • Maxuploads - Maximum number of concurrent uploads.

The Web File Manager is a Lua enhanced version of the C code HttpResMgr and by the Lua equivalent ba.create.resmgr. The enhanced version is not available unless the code has been loaded using "require".

Example:
require "wfm"
wfm=ba.create.wfm("drive", 0, io, 100)
The Web File Manager implementation can be found in the following Lua script: WebResources\rtl\.lua\wfm.lua

The Web File Manager client user interface requires a number of resources such as images and JavaScript files. These files must be made available by the startup code. More information regarding these files can be found in the HttpResMgr C example.
ba.create.authenticate(function, {options})
See also
Authentication
Creates an authenticate object for a directory. The returned object can be used as the target for dir:setauth().

function(user)
user
The username that is trying to authenticate
returns: password or false [, maxusers [, recycle, timeoutinterval]]
If the user is valid this function either returns the password of this user or false/nil to prevent the user completing the login. Setting maxusers to 0 will prevent the user logging in. Setting recycle to true will allow other currently logged in users to be logged out to allow this user to log in.
Return values
password
password of the user
maxusers
The current maximum number of instances of this user that are permitted to login. This can be changed with a return value. This is an optional return value. The default set by ba.authdefaults() is used if no value is returned.
recycle
Current value of a Boolean to a allow recycling of users This is an optional return value. The default set by ba.authdefaults() is used if no value is returned.
timeout interval
The maximum inactive time interval in seconds. If this time elapses without any user activity then the user will be logged out. This is an optional return value. The default set by ba.authdefaults() is used if no value is returned.
{options}
realm=string
This field is required. It is the name of the authentication realm.
type=string
Optional string, defaults to "digest". May be one of "basic", "digest", "form" or "dav".
login=function({authinfo}, relpath, abspath, rootpath)
This is a login function that is required unless using "dav" authentication. The return value from this function is ignored. The role of this function is to provide the "login" dialog and the response for login failures.
authinfo
a table with the following fields
username
name of the user to log in. This field will be blank or nil, if the user has not logged in.
password
the user password that was entered.
recycle
true|false.
maxusers
The maxusers variable returned by the authenticate callback function. This variable is negated when the maximum number of logins is reached.
type
type=form|basic|digest
realm
The authenticators realm name
logincounter
The number of login attempts. Requires that the login tracker is active on the authenticator.
denied
denied=true|false. Set if the user is denied access by the login tracker -- i.e. if the user is banned.
relpath
relative path name
abspath
absolute path name
rootpath
root path name
tracker=true|false
If set to true then the server login will be used if it is available.
secure=true|false
This will force the authentication process to be performed over a https secure connection.

The following login function is used in the BarracudaDrive application server:
   local function login(authinfo)
      -- Forward to the banned page if denied access by the
      --login tracker.
      if authinfo.denied then
         response:forward"/rtl/login/.banned.lsp"

      elseif authinfo.username then
         -- User failed authenticating using one of form, basic, or digest. 

         -- Negative if active logins for this user is exhausted.
         if authinfo.maxusers < 1 then
            response:forward"/rtl/login/.MaxLogin.lsp"
         else
            response:forward"/rtl/login/.failed.lsp"
         end

      elseif not request:issecure() then
         -- Give the user two options: Switch to secure connection or
         -- use Digest authentication.
         response:forward"/rtl/login/.NotSecure.lsp"

      -- The username can only be nil if the form authenticator is
      -- used and the user has not logged in.
      else
         -- Show the form login page.
         response:forward"/rtl/login/.login.lsp"
      end
   end
ba.create.authorize(function(username,method,path))
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, path)
user
The username that is trying to authorize
method
One of "GET", "POST" etc. See request:method()
path
The path that access is required
returns: true|false
 --the following authorize function will always allow access
  realm = ba.create.authorize(
    function(user, method, path)
      trace("user",user)
      trace("method",method)
      trace("path",path)
      return true
    end
  )

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 the Internet Explorer team did not properly read the specification so IE fails if a ZLIB header is added. All other browsers also accept RFC1951. Set this variable to true if you want all browsers to work except IE.

ba.dirattach(cdir)
creates a directory object from a "C" language structure pointer.
ba.dirtop()
returns the first root directory of the virtual directory tree
ba.getenvc()
returns the Barracuda environment table
ba.io()
returns a table with all I/O resources created by the C/C++ startup code. 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.loadfile(filename)
This function is similar to the native Lua loadfile(), but differs in that it uses the Barracuda VM IO object to read the file that is loaded.
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 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.openio(ioname)
returns a Barracuda I/O object. The I/O object must have been installed by the C startup code.

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:
baio: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). There is no default option.
fh:seek(offset)
same as standard Lua.
fh:write(string, ...)
same as standard Lua.
baio:resourcetype()
returns two strings indicating the type of resource where files exist. (e.g disk) and the type of operating system.
baio:type(object)
returns true if the object is a Barracuda file handle.
baio: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.
baio: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"
baio:realpath(path)
returns the real path (absolute) path name of the provided relative path.
baio:mkdir(path)
creates a new directory.
baio:rmdir(path)
removes a directory.
baio:remove(path)
removes (deletes) a file.
baio:rename(oldpath, newpath)
renames a file or directory.
baio:loadfile(path)
loads a Lua source file. Refer to the Lua base library loadfile() function.
baio:dofile(path)
loads and executes a Lua source file. Refer to the Lua base library dofile() 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:
baio: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:
baio: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.
baio:reqpasswd(enable)
Enable password protection. If enabled, the file being opened must match the password set with baio: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.
baio:encrypted(path)
Returns true if file is AES encrypted. Returns nil if not found.
NetIo:
baio:netconf({options})
Configure a NetIo.

A NetIo is typically created and installed uninitialized by the C/C++ startup code. This method makes it possible to either configure the NetIo installed by the C/C++ startup code or to configure a NetIO created with ba.mkio().

{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.

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

Note: user, pass and proxyuser, proxypass should be provided 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 TypeDescription
invalidnameName not accepted by file system
notfoundNot found
existResource exists
enoentDirectory not found
noaccessResource locked by file system
notemptyDirectory not empty
ioerrorSome kind of I/O error
nospaceNo space left on device
memNo memory for operation
buftoosmallBuf too small
noimplementationNot implemented
noaeslibAES not enabled
notcompressedZIP not compressed
ziperrorUnknown ZIP file format
noziplibZLIB not installed
aesnosupportUnknown AES encryption
nopasswordResource is encrypted, but no password provided
wrongpasswordWrong password provided
aeswrongauthWrong password or file compromised
aescompromisedAES protected file compromised
invalidsocketconInvalid socket con
gethostbynameGethostbyname failed
bindBind failed
socketwritefailedSocket write failed
socketreadfailedSocket read failed
mallocMalloc failed
alreadyinsertedAlready inserted
toomuchdataToo much data
pagenotfoundPage not found
iscommittedIs committed
invalidparamInvalid param
mixingwritesendMixing write send
toomanyincludesToo many includes
toomanyforwardsToo many forwards
includeopnotvalidInclude op not valid
cannotresolveCannot resolve
cannotconnectCannot connect
invalidurlInvalid URL
invalidresponseInvalid response

ba.parselsp(file)
returns a string of the parsed LSP file. As an example the following code will parse an input file as LSP and write the parsed (Lua) code to the provided output file.

#!/usr/local/bin/blua
--
--  script to parse lsp
--
--  to produce stripped binaries use
--  blua parselsp.lua in.lua|luac -s -o out.lua -
--

require "balua"

assert(arg and arg[1], "parselsp infile [outfile]")
assert(ba.parselsp, "paselsp not loaded")

ih=assert(io.open(arg[1],"r"))
s=ih:read"*a"
ih:close()
s=assert(ba.parselsp(s))

if arg[2] then io.output(arg[2]) end

io.write(s)
io.close()
ba.session(id[,request])
Returns the session object for session with identity "id" or false if not found.

local s = request:session(true)

-- First example
local id = s:id() -- Returns a number suitable for local server use
assert(id == ba.session(id):id())

-- Second example
local id = s:id(true) -- Returns a string suitable for public use
-- The string version requires session for IP address comparison
assert(id == ba.session(id,request):id(true)) 

The second example above fetches a 16 character long string that is safe for public use. The string is typically used by a Lua directory function as an alternative to logging in by for example a WebDAV server. The user logs in by using standard form login and a WebDAV directory function uses the first part of the URI for session authentication.
ba.showlsp(true{false)
When set to true, LSP source code is displayed when errors are encountered. The default is false.
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 fourth 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.exec(prog)

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.tracker
A table of functions for accessing the data provided by the default user tracker. The table is not installed unless the C startup code has activated the default tracker by calling function LuaUserTracker_create().
ba.tracker.successfull()
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.urldecode(string)
Decodes a URL encoded string.
ba.urlencode(string)
URL encodes a string.
ba.sessions(name)
Returns an array with the session id for the user's active session(s).
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.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

directory object

The HTTP directory object is returned by a number of functions.

See also
ba.dirtop
ba.create.dir
ba.create.resrdr
ba.create.domainresrdr
ba.create.resmgr
ba.create.eh
request:dir

dir(relative path)
Calls the service function of this directory. This would usually be called from within another directory function.
The relative path parameter is required. This is an object call.
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:child()
returns the first child directory or nil
dir:insert([name], [priority])
inserts a directory into the server.
dir:insert(dir, [priority])
inserts a child directory
dir:name()
returns the name of the directory
dir:next("self"|"child"|"parent" )
returns an iterator that iterates over relative directories.
self This is the default, it iterates over subsequent sibling directories.
child iterates over all child directories.
parent iterates over all directories that are a child of the parent. In other words, all siblings plus self.
dir:parent()
returns the parent directory or nil
dir:priority(priority)
sets the priority to the specified number. This function will fail if the directory has already been insert()ed.
dir:setauth(authenticator, [authorizer])
Enables authentication and optionally authorization for this directory. The authenticator is create by ba.create.authenticate() and the authorizer is created by ba.create.authorize(). Returns true on success.
dir:settable({table})
the contents of the provided table are inserted into the environment of the directory function.
dir:type()
returns the type name of the directory. This can be one of "lua", "dav", "rdr" or "unknown".
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.
For POSIX and Windows platforms the os library has small change to getenv() functionality and the library is augmented with the following functions:

os.getenv([string])
gets the value of the named environment string. If no environment string is provided then a table of all environment variables is returned.
os.putenv(name=value [,value])
Sets a value in the environment. The string format may be either name=value or one can use two paramters, the first is the name and the second is the value.
os.rmvenv(variable)
Removes a string from the environment.
os.fullpath(path)
returns the full path name of the provided path name.
os.mkdir(dir)
creates the named directory.
os.rmdir(dir)
removes the named directory.
unlinks or removes the named file.
os.access(path[, mode])
check the named path. mode may be any combination of the characters "rwxf". For example, os.access("myfile","r") checks the file for readability. "f" checks the file for existence, this is the default mode.
os.getcwd()
returns the current directory.
os.files(dir)
returns an iterator that will iterate over all the files in the named directory. The iterator returns each file name in the directory and the full path name of the directory parameter that was originally passed to os.files(). The following example script will recursively print all files and directories in the named directory.

local diriter
function diriter(dir)
  for file, path in os.files(dir) do
    local fn = path .. file
    print(fn)
    local st = os.stat(fn)
    if string.sub(file,1,1) ~= '.' and st and st.isdir then
      diriter(path .. file)
    end
  end
end
diriter(arg[1])

os.stat(path)
returns a table of attributes for the named path. The fields in the table are the similar to the POSIX stat() function:
  • mtime - last modified time
  • ctime - last changed time
  • atime - last accessed time
  • size - size of the file in bytes
  • isdir - true if the file is a directory
  • isreg - true if the file is a regular file
  • mode - the file mode as a string. e.g. "drwx" or "-rwx". Only public modes are shown.
  • _mode - the mode as a decimal number.
  • type - the type of the file. Possible values are "regular", "link", "directory", "character device", "block device", "fifo" "socket" or "?" when the type of file is unknown.
  • name - the full path name of the input path.
  • dev - as per POSIX
  • nlink - as per POSIX
  • gid - as per POSIX
  • uid - as per POSIX
  • ino - as per POSIX

Barracuda global functions

The following functions are available in the request environment. Unless otherwise stated all functions will return false, message upon error.

_G.print(string, ...)
prints strings in the HTML stream as preformatted text. Each string is separated by a TAB character. Also refer to the response:write() method. print() differs from response:write() in that print calls the Lua tostring() function on each parameter that is passed to print(). print() is useful for generating diagnostics and small amounts of text on a web page, use response:write() in preference to print().
example
<?lsp
  print("hello", "world")
?>
The above example would emit "hello\tworld\n".
_G.trace(string, ...)
writes the string to the web server trace log.
_G.rndseed(seed)
Seeds the rnd() function.
_G.rnd([ [low ,] high])
Generates a random number. If no parameters are given a positive integer between 0 and 0x3ffffff is returned. If one parameter is provided then it represents the upper value of a random integer between 1 and the upper value and if two parameters are provided it represents the lower and upper limits of a random integer.


Compiling Lua script

Barracuda converts LSP pages to Lua source code, this source code may in turn be compiled with the Lua compiler bluac. Compiled code saves time when loading large pages (compiled code is smaller) and can save memory by optionally removing debugging information. Compiled code also means that the LSP source code is not available.
The standalone Lua script <barracuda>/lua/parselsp.lua can be used to parse the LSP pages into Lua script that can then be compiled by bluac, The compiled code can then replace the source LSP. Be careful that you do not overwrite the source LSP with the compiled code.
Compiled code enables the Lua parser to be omitted from builds of Barracuda libraries, making the implemented code size smaller. Compiled code is portable across platforms with the same integer size double precision format and endianess.

Compile and strip debug information.

blua parselsp.lua mypage.lsp|bluac -s -o ../compiledlsp/mypage.lsp -

Compile and keep debug information.

blua parselsp.lua mypage.lsp|bluac -o ../compiledlsp/mypage.lsp -

parselsp.lua can also be used to diagnose syntax errors in LSP.




Lua environments

Lua has the concept of environments. Environments provide scoping for collections of variables.
A global environment is provided for each LSP page or HTTP directory function and this environment exists only for the current request, this is referred to as the "request environment". Any global variables created in a LSP will be saved in this (temporary) request environment. The request environment is also the environment of any included or forwarded pages. Hence variables set in a LSP page will be available to included or forwarded pages.

request environment
The request environment maintains the following variables and objects:

Variables can persist across invocations (different requests) of the page by using one of the following persistent Lua objects:

page object
The page object maintains variables that are available to the specific page.
Variables created in the page table are maintained for the execution life of the Barracuda server. The "cachelsp" configuration setting has no impact on page variables.
LSP script can only address the page table of the currently executing LSP page.
LSP responses are executed in one or more operating system threads and these threads may yield during the execution of a Lua Server Page. This means that other users of a page may update the page table. For example:

<?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)
  request: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.
session object
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>")
?>
Using Lua Globals
Lua globals (_G) are common to all pages. Variables set in one page/session may be retrieved in another page/session.
One possible use of Lua Globals might be to maintain common HTML fragments of HTML or even common chunks of Lua code.
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>")
?>