The Auxiliary Lua API

The auxiliary Lua APIs add BAS features that are useful in many products but are not required in every build. They include client-side protocol libraries such as HTTP(S), SMTP, and sockets, as well as supporting modules for crypto, certificate handling, process control, and related infrastructure.

These APIs are optional from a build perspective: a custom BAS integration can omit them, while standard products and examples such as Mako Server and Xedge usually include them. The source code for these libraries is found in the xrc directory. Use this page when you need BAS client functionality or higher-level optional services that sit on top of the core API documented in lua.html.

Universal Binary JSON

The UBJSON API mirrors the core JSON API, but uses Universal Binary JSON instead of textual JSON. Use it when you want the same table-oriented programming model as ba.json, but with a compact binary representation for storage or transport.

ba.ubjson.parser()
ba.ubjson.encode(table [,table] [,size])
ba.ubjson.decode(data [,stacksize [,namelen [,offset]]])
-- Prints ["Hello World"]
print(ba.json.encode(ba.ubjson.decode(ba.rndbs(5)..ba.ubjson.encode{"Hello World"},1,0,5)))

ByteArray

ByteArray is a mutable companion to Lua's immutable string type and is included with the socket library. It is useful when you need to build or modify binary data in place, reuse buffers, or avoid unnecessary copying before calling APIs such as socket:write(). The object automatically converts to a string when needed and supports the meta-methods __index, __newindex, __tostring, and __len.

ba.bytearray.create(size)
ba.bytearray.create(string)

Allocates a mutable byte buffer. The string form copies the input bytes into new storage.

Parameters

Return values

Throws

Throws for an unsupported argument type, an empty string, or an invalid size. Allocation failure raises a Lua memory error. Does not return nil/error.

ba.bytearray.copy(to-array, n, string|array [, i [, j]])

Copies bytes into the destination's current view. Copies only what fits and returns the amount left over. Overlapping copies within the same ByteArray are supported.

Parameters

Return values

Throws

Throws for invalid argument types or positions outside the allowed source/destination ranges. A full destination is reported by overflow, not an exception. String conversion or Lua allocation errors can propagate.

ba.bytearray.h2n(array, n, size, number)

Writes network-order integer bytes into the current array view.

Parameters

Return values

None.

Throws

Throws for an invalid array, an unsupported size, invalid numeric arguments, or a range outside the current view. No operational nil/error pair is returned.

ba.bytearray.n2h(array, n, size)

Reads network-order integer bytes from the current array view.

Parameters

Return values

Throws

Throws for an invalid array, an unsupported size, invalid numeric arguments, or a range outside the current view. No operational nil/error pair is returned.

ba.bytearray.setsize(array [, i [, j]])

Changes the visible start/end markers without reallocating the buffer. Positions refer to the original allocation, so a previously hidden region can be restored.

Parameters

Return values

None.

Throws

Throws for invalid argument types or markers outside the allocation, or if the resolved end is less than the start minus one. Invalid markers leave the current view unchanged.

ba.bytearray.size(array)

Reports the allocation size and current view markers.

Parameters

Return values

Throws

Throws for an invalid ByteArray object. No operational nil/error pair is returned.

ba.bytearray.tostring(array [, i [, j]])

Returns an immutable string copied from the current view.

Parameters

Return values

Throws

Throws for invalid argument types, a start outside the allowed view positions, or an end beyond the current view. Lua allocation errors can propagate. No operational nil/error pair is returned.

array[n] = values

Copies a table of bytes into the current view. Each table key selects a position relative to n: key 1 writes at n, key 2 at n+1, and so on. Holes are supported; missing keys leave the corresponding destination bytes unchanged. Table traversal order does not affect the result.

Parameters

Return values

None.

Throws

Throws for an invalid destination position, a noninteger or nonpositive table key, or a key whose destination lies outside the current view. If an invalid key is encountered after other entries have been copied, those writes remain; assignment does not roll back earlier writes.

local array = ba.bytearray.create("abcdefgh")
array[4] = {[1]=10, [3]=30}
-- Positions 4 and 6 contain 10 and 30. Position 5 still contains 'e'.
Examples

In the following examples, the __tostring metamethod is triggered when the array is printed.

local array = ba.bytearray.create(23)
for i=1,20 do array[i]=64+i end

print(#array) -- 23
ba.bytearray.setsize(array,1,20)
print(#array) -- 20
print(ba.bytearray.size(array)) -- 23	1	20
print(array) -- ABCDEFGHIJKLMNOPQRST

local t={}
 -- #array length is 20
for i=1,#array do table.insert(t,array[i]) end
ba.bytearray.setsize(array) -- same as: (array,1,-1); length is now 23

array[1]="abcdefghijklmnopqrstuvz"
print(array) -- abcdefghijklmnopqrstuvz

array[4]=t -- Copy table into ByteArray starting at position 4
print(array) -- abcABCDEFGHIJKLMNOPQRST

ba.bytearray.setsize(array,-3) -- Same as (array,-3,-1)
print(#array,array) -- 3	RST

ba.bytearray.setsize(array) -- Restore normal size
for i=1,#array do array[i]='*' end -- Fill with '*'

ba.bytearray.setsize(array,4)
array[1]=t -- Copy table
print(array) -- ABCDEFGHIJKLMNOPQRST
ba.bytearray.setsize(array,1,-4)
print(array) -- ***ABCDEFGHIJKLMNOPQ

Crypto Library

The auxiliary crypto library provides higher-level primitives for hashing, symmetric encryption, asymmetric cryptography, JWT handling, and key/certificate processing. These APIs are typically used when BAS Lua code must authenticate peers, protect stored data, verify signatures, or participate in secure client/server protocols.

ba.crypto.hash([algorithm [, algorithm, key]])

Creates and returns an incremental hash object represented as a Lua function. You can feed data into the function in one or more calls and then finalize it to obtain the digest. The default algorithm is sha1. Valid values for the first argument are md5, sha1, sha256, sha384, sha512, and hmac. When using hmac, the second argument selects the underlying hash algorithm and the third argument is the secret key.

Parameters

Return values

Throws

Throws for an unknown or disabled algorithm, missing required HMAC arguments, or invalid argument types. Lua allocation errors can propagate. There is no operational nil, error return.

The following example shows how to calculate B64(MD5(username password)):

local hfunc = ba.crypto.hash"md5"
hfunc(username)
hfunc(password)
local data = hfunc(true,"b64")

Appending data returns a function sharing the same hash state, making it possible to chain calls. It does not copy the accumulated hash state. The following example produces the same result as the previous example:

local data = ba.crypto.hash"md5"(username)(password)(true,"b64")

The following example shows how to create an MD5 HMAC for the username and password by using the secret key "qwerty".

local data = ba.crypto.hash("hmac","md5","qwerty")(username)(password)(true,"b64")
f(data)

Parameters

Return values

Throws

Lua allocation errors can propagate. There is no operational nil, error return.

f([true [,encoding]])

Parameters

Return values

Finalization resets the shared state for another message. HMAC retains its algorithm and key. With no appended data, the digest is for an empty message.

Throws

Throws for an unsupported encoding or invalid encoding type. Lua allocation errors can propagate. There is no operational nil, error return.

DK = ba.crypto.PBKDF2(PRF, Password, Salt, c, dkLen)

PBKDF2 derives a cryptographic key from a password and salt by repeatedly applying HMAC. Use it when a password must be turned into a fixed-length key for encryption or authentication. The repeated iteration count makes brute-force and dictionary attacks more expensive than directly hashing the password once.

Parameters

Return values

Throws

Throws for missing required arguments, invalid argument types or an unknown/disabled digest. Lua allocation errors can propagate.

Implementation note: The current native PBKDF2 implementation has a block-copy-length defect affecting requests longer than one digest. A native correction is pending; longer requests must not be assumed reliable. The Lua binding currently preserves its existing output-length behavior.

Asymmetric Encryption

This API provides the BAS Lua interface for asymmetric cryptography using RSA and ECC keys and X.509 certificates. Use it when Lua code must encrypt small payloads, sign data, verify signatures, or inspect key material that already exists in certificate or PEM form.

Functions:

ba.crypto.keysize(key [,op])

Returns an operation-buffer length in bytes and the key type. The length is not cryptographic strength in bits.

Parameters

Return values

Throws

Throws for missing or invalid key arguments and invalid option field types. The shared asymmetric-options parser can also throw for an unknown padding or digest option. Lua allocation errors and exceptions from option-table getters can propagate. A reported private-key parse failure returns nil, "Invalid key".

ba.crypto.encrypt(plaintext, cert [,op])

Encrypts a single RSA block using a public key.

Parameters

Return values

Throws

Throws for missing required arguments, invalid argument or option-field types, an unknown padding option, or an unknown/disabled OAEP digest. Exceptions from option-table getters and Lua allocation errors can propagate. Reported operation failures return nil, error.

ba.crypto.decrypt(ciphertext, key [,op])

Decrypts a single RSA block using a private key.

Parameters

Return values

Throws

Throws for missing required arguments, invalid argument or option-field types, an unknown padding option, or an unknown/disabled OAEP digest. Exceptions from option-table getters and Lua allocation errors can propagate. Reported operation failures return nil, error.

ba.crypto.sign(hash, key [,op])

Signs a precomputed digest using RSA PKCS#1 v1.5 or ECDSA.

Parameters

Return values

Throws

Throws for missing required arguments, invalid argument or option-field types, an unknown padding option, or an unknown/disabled OAEP digest. Exceptions from option-table getters and Lua allocation errors can propagate. Reported operation failures return nil, error.

The digest algorithm is inferred from hash length; op.hashid does not select it. See also ba.tpm.sign().

ba.crypto.verify(signature [,cert], hash [,op])

Verifies a precomputed digest using RSA PKCS#1 v1.5 or ECDSA.

Parameters

Return values

Throws

Throws for a missing or invalid signature or required certificate argument, invalid option-field types, an unknown padding option, or an unknown/disabled OAEP digest. Exceptions from option-table getters and Lua allocation errors can propagate. Reported operation failures return nil, error.

The digest algorithm is inferred from hash length, rather than op.hashid.

Symmetric Encryption

ba.crypto.symmetric(algorithm, key, IV [, mode])

Creates a symmetric cipher object.

Parameters

Return values

Throws

Throws for missing or invalid arguments, an unsupported algorithm, an invalid key/IV length or missing CBC mode. Lua allocation errors can propagate.

GCM and CCM calls process individual authenticated messages. Their IV is fixed for the lifetime of the object; create a new object with an appropriate new IV for each new message encrypted with the same key. CBC updates its chaining IV between calls and supports chunked processing of one message.

s:setauth(auth)

Sets additional authenticated data (AAD) for GCM or CCM.

Parameters

Return values

None.

Throws

Throws for an invalid object or argument type, or AAD exceeding the applicable limit. Lua allocation errors can propagate.

AAD is authenticated but not encrypted. Supply identical AAD when encrypting and decrypting. An empty string clears the AAD contents.

s:encrypt(data [,"PKCS7"])

Encrypts data using the object's key and IV.

Parameters

Return values

Throws

Throws for invalid object/data types, an unknown padding marker, invalid block alignment, excessive length, or a CBC object created for decryption. Lua allocation errors can propagate. Buffer allocation failure returns nil, "malloc".

For CBC chunks, pad only the final chunk. GCM/CCM calls are separate messages, not a streaming continuation. A reported buffer-allocation failure occurs before encryption and permits retry.

s:decrypt(encdata [,tag [,"PKCS7"]])

Decrypts data, checking the tag for GCM and CCM.

Parameters

Return values

Throws

Throws for invalid object/data/tag types, an incorrect tag length, an unknown padding marker, invalid unpadded block alignment, excessive length, or a CBC object created for encryption. Malformed CBC ciphertext with padding removal requested returns nil, "decrypt failed" for zero or misaligned length. Lua allocation errors can propagate.

Use the same key, IV and AAD as encryption. Failed authentication does not return plaintext. For CBC chunks, remove padding only on the final chunk. Buffer-allocation failure occurs before decryption and permits retry.

Symmetric encryption examples

Examples:

AES- GCM, CCM, and CBC examples:

local key = "0123456789ABCDEF" -- Preferably use ba.rndbs(16) or ba.rndbs(32)
local iv = "0123456789AB" -- Preferably use ba.rndbs(12)
local message="Hello World!"

local gcmEnc = ba.crypto.symmetric("GCM", key, iv)
local gcmDec = ba.crypto.symmetric("GCM", key, iv)
local cipher,tag = gcmEnc:encrypt(message)
local data=gcmDec:decrypt(cipher,tag)
trace("GCM",#data,data)

local ccmEnc = ba.crypto.symmetric("CCM", key, iv)
local ccmDec = ba.crypto.symmetric("CCM", key, iv)
cipher,tag = ccmEnc:encrypt(message,"PKCS7")
data=ccmDec:decrypt(cipher,tag,"PKCS7")
trace("CCM",#data,data)

iv = "0123456789ABcdef" -- 16 bytes
local cbcEnc = ba.crypto.symmetric("CBC", key, iv, "encrypt")
local cbcDec = ba.crypto.symmetric("CBC", key, iv, "decrypt")
cipher = cbcEnc:encrypt(message,"PKCS7")
data=cbcDec:decrypt(cipher,"PKCS7")
trace("CBC",#data,data)

Fernet (AES-CBC) Decrypt Example:

Fernet is common in the Python world and provides a simple method for encrypting and decrypting a message. The following article shows how to use it from Python as well as providing a good introduction to the format: pythoninformer.com.

local function decodeFernet(key,token)
   key,token=ba.b64decode(key),ba.b64decode(token)
   if key and token and token:byte(1) == 0x80 then
      local signingKey,encryptionKey=key:sub(1,16),key:sub(17)
      local iv,cipher,hmac = token:sub(10,25),token:sub(26,-33),token:sub(-32)
      if hmac ~= ba.crypto.hash("hmac","sha256",signingKey)(token:sub(1,-33))() then
         return nil, "Invalid HMAC"
      end
      local datetime = ba.datetime(ba.socket.n2h(8,token,2))
      local plain=ba.crypto.symmetric(
         "CBC",encryptionKey,iv,"decrypt"):decrypt(cipher,"PKCS7")
      return plain,datetime
   end
   return nil, "Invalid key or token"
end

local key="M516vNeYFaEauWp_C7Dovyms7ZF1xNyizKPFZ4ucBS0="
local token=[[
gAAAAABftfpRGg8HwvomaO8Uj71gJgplLDJH05-
OcprsFgw2aAYt1b7ngdUv7vsfPGNdxr-WjpaFY8
gx9Gf-j8qiJrpFaVg3BQ==
]]

local text,datetime = decodeFernet(key,token)
if text then
   print("Text:",text)
   print("DateTime:",datetime)
else
   print("Error:",datetime) -- datetime is now error
end
  

Cryptographic Parameters

The following functions enable extraction of cryptographic parameters used with JSON Web Tokens and JSON Web Signatures.

ba.crypto.keyparams(key [, password])

Extracts the public components of an RSA or ECC key, including a private key returned by ba.create.key(). Values are binary, big-endian strings.

Parameters

Return values

Throws

Throws for missing or invalid argument types. Lua allocation errors can propagate. A reported key-loading or component-extraction failure returns nil, "failed".

ba.crypto.sigparams(signature)

Extracts the r and s integers from a binary DER-encoded ECDSA signature, such as one returned by ba.crypto.sign(). This converts the representation; it does not verify the signature.

Parameters

Return values

Throws

Throws for a missing or invalid argument type. Lua allocation errors can propagate. A reported signature-format failure returns nil, "invalid".

ba.crypto.sigparams(r, s)

Constructs a binary DER-encoded ECDSA signature from its two components. See the jwt.lua module for an example of conversion in both directions.

Parameters

Return values

Throws

Throws for missing or invalid argument types. Lua allocation errors can propagate. Invalid component lengths or a reported encoding failure return nil, "invalid".

JSON Web Token (JWT) Library

This API provides functions for signing and verifying JSON Web Tokens (JWTs) using HMAC (HSxxx), RSA (RSxxx), and ECDSA (ESxxx) algorithms.

-- Load the JWT module
local jwt = require"jwt"
jwt.sign(payload, secret [, options])

Generates a signed JWT. See also ba.tpm.jwtsign().

Parameters

Return values

Throws

Incorrect argument types, missing options.alg, or unsupported digest names can throw. Exceptions from JSON encoding, cryptographic functions, a supplied signing function, or Lua allocation propagate. Reported signing failures return nil, error; an unsupported algorithm is not guaranteed to reach that return path.

jwt.verify(jwt, secret [,kid])

Checks the token's signature and returns its decoded header and payload. The application must check its permitted algorithm, issuer, audience, expiration and other required claims; this function does not enforce them.

Parameters

Return values

Earlier versions returned the HMAC payload as the second value. HMAC callers must now read the third value, matching RSA/ECDSA callers.

Throws

Throws for incorrect argument types or a public-component table without n/e or x/y. Malformed token fields and unsupported digest names can also cause exceptions in decoding or cryptographic functions; not every token error returns nil, error. Exceptions from key-table lookups and Lua allocation can propagate.

EventEmitter Library

This library provides event-driven functionalities for Lua.

Example:

local EventEmitter = require"EventEmitter"

-- Create an instance
local emitter = EventEmitter.create()

-- Register a listener
emitter:on("myEvent", function(msg) print("Received:", msg) end)

-- Emit an event
emitter:emit("myEvent", "Hello World")

-- Emit and retain an event
emitter:emit({name = "myEvent", retain = true}, "Retained Message")

-- Register a new listener; delivery of the retained message is queued
emitter:on("myEvent", function(msg) print("New listener received:", msg) end)

EventEmitter API

Ordinary emission calls listeners synchronously. Registering a listener for a retained event queues delivery through ba.thread.run(). Listener order is unspecified.

EventEmitter.create([self])

Creates an emitter. To use the constructor, load the module with local EventEmitter = require"EventEmitter".

Parameters

Return values

Throws

Throws if self cannot be used as an emitter table, including a table with a protected metatable. Table-metamethod exceptions and Lua allocation errors can propagate.

E:on(event, cb)

Registers a listener. Registering the same function again does not duplicate it. If a retained message exists, each registration queues a callback with that message.

Parameters

Return values

Throws

Incorrect arguments or an invalid emitter can throw. Lua allocation and job-submission errors can propagate. A queued listener exception is handled by the error reporter; it is not returned from on().

E:emit(event, ...)

Calls registered listeners before returning. Listener errors are caught and reported. Retaining a message also works when there are no listeners.

Parameters

Return values

Throws

Throws for an invalid event name or emitter. Listener exceptions are caught, but an exception from reporterr() or the fallback trace() call propagates and stops the current emission. Lua allocation and table-metamethod exceptions can propagate.

E:removeListener(event [,cb2rem])

Removes listeners without removing the retained message. Already queued retained-delivery callbacks are not canceled.

Parameters

Return values

Throws

An invalid emitter or incorrect arguments can throw. Table-metamethod exceptions can propagate. Removing an absent listener does not throw.

E.reporterr(cb, error)

Optional callback assigned by the application. Called without an implicit self argument when a listener throws.

Parameters

Return values

No return values are used.

Throws

Exceptions from this callback are not caught by EventEmitter. During ordinary emission they propagate to emit(); during retained delivery they propagate to the queued job.

forkpty

The forkpty library provides a combined fork, exec, and child process pseudo-terminal. The code is available for Linux, Mac, and QNX. Windows users may use the simpler io.popen library. For simpler tasks, use ba.exec.

The code can be used for executing and managing Linux executables such as "ls", "kill", etc. The forkpty library provides advanced child process management.

Blocking Read Example

local pty,err = ba.forkpty("/bin/sh")
pty:write"ls -l\n"
local data,err = pty:read(500)
while(data) do
   print("data:",data)
   data,err = pty:read(500)
end
pty:terminate()

Download: the Web Shell, a fully working web-based terminal (alternative to using SSH), which is using the forkpty library for the shell process management and communication. The CGI plugin is also using the forkpty library for process management.

ba.forkpty([X,] prog [, args...])

Starts a child with stdin/stdout connected to a pseudo-terminal and stderr connected to a separate pipe. Use polling mode by default, or supply an asynchronous callback. prog is passed directly to execve; no shell or PATH lookup is added by the binding.

Parameters

Return values

A returned handle does not confirm that the child's executable started successfully. Later child-side setup errors cause child exit; an execve failure is written to stderr. Read the child's output and collect its exit status.

Throws

Throws for missing or invalid program/argument values and invalid string option or environment-entry types. Option-table exceptions and Lua allocation errors can propagate. Reported parent-side native setup failures return nil, error. Exceptions in the asynchronous callback are reported by the coroutine error handler and trigger cleanup.

PTY Methods

pty:read([timeout])

Reads a chunk from the child's stdout or stderr. In the constructor's asynchronous callback coroutine, this method yields until the dispatcher resumes it; timeout is ignored there.

Parameters

Return values

Throws

Throws for an invalid object or a call from another Lua state while the object is busy. Out-of-range numeric timeouts throw before polling. Lua allocation errors can propagate. Native read/select failures are returned as nil, error. Use close() or terminate() to release resources after an error.

pty:write(data)

Writes bytes to the child's stdin. The global server mutex is released during the native write.

Parameters

Return values

Throws

Throws for an invalid object, missing or invalid data argument, or a call from another Lua state while the object is busy. Lua allocation errors can propagate. Native write errors or zero progress return nil, error. A failure can occur after some bytes have already been written; those bytes are not rolled back.

pty:pause(stop)

Requests SIGSTOP or SIGCONT for the child. In asynchronous mode, also disables or enables stdout/stderr dispatch notifications.

Parameters

Return values

Throws

Throws for an invalid object or a call from another Lua state while the object is busy. Lua allocation errors can propagate. Native signal errors return nil, error without changing dispatcher notifications. Calling this method on a closed handle returns nil, "terminated".

pty:winsize(lines, cols)

Sets the child's terminal dimensions in rows and columns.

Parameters

Return values

Throws

Throws for missing, invalid or out-of-range dimensions, an invalid object, or a call from another Lua state while the object is busy. Lua allocation errors can propagate. Native terminal-operation failures return nil, error.

pty:close([wait])

Sends SIGINT once to request termination, collects the child's exit status when available, and releases resources after termination. Call again if the child is still running. Repeated calls after an exit status has been collected return the same values.

Parameters

Return values

The three additional status fields are returned when the native wait status is nonzero. A signal can therefore produce status 0 together with WIFSIGNALED=true; do not interpret that 0 as a successful normal exit. A native wait error triggers resource cleanup before nil, error is returned.

Throws

Throws for an invalid object or a call from another Lua state while the object is busy. Lua allocation errors can propagate. Native wait errors return nil, error.

pty:terminate()

Releases the PTY resources and collects this child's exit status. If the child is still running, sends SIGKILL and waits for it to exit with the server mutex released. Other children's exit statuses are not collected.

Parameters

No parameters.

Return values

Throws

Throws for an invalid object or a call from another Lua state while the object is busy. Lua allocation errors can propagate. Native cleanup errors are not reported by this method.

Asynchronous PTY Mode

In polling mode, keep a reference to the PTY object to prevent garbage collection. Asynchronous mode retains the object internally. Returning normally from the callback automatically stops notifications, closes the descriptors and releases the internal reference. Cleanup sends SIGKILL if the child is still running and waits for that child to exit with the server mutex released. A callback exception performs the same cleanup and is reported through the BAS coroutine error handler. Explicit close() or terminate() before returning is also supported.

The following example shows how to use asynchronous mode:

-- The asynchronous receive function keeps reading until the process terminates
function recData(pty)
   while true do
      local d,e = pty:read() -- Read data. Arguments to pty:read are ignored.
      if not d then -- Child process terminated
         break
      end
      trace(d,e)
   end
   -- Release descriptors and the internal reference before returning.
   pty:terminate()
end

local pty,err -- Local variables i.e. we do not need a reference to prevent GC
pty,err = ba.forkpty(recData, "/bin/ls", "-l") -- Execute ls -l
trace(pty,err)

In asynchronous mode, a function must be provided as the first argument to ba.forkpty. This function is executed as a Lua coroutine and is automatically yielded by read when no data is available. The function is resumed when there is data available or if the child process terminates.

All pty:xxx functions operate as normal, except for read when called from within the coroutine.

HTTP(S) Client Libraries

The Barracuda HTTP(S) client library, which is implemented in C code, can be accessed from Lua by using the Lua bindings for the HTTP implementation. The Lua bindings are found in xrc/lua/lhttp.c.

In addition to the low level C implementation, two additional HTTP libraries implemented in Lua are provided. The additional libraries wrap around the low level library and simplify the use of the low level implementation.

The libraries are loaded as follows:

The HTTP(S) Client Libraries implementation conforms to the HTTP/1.1 standard, RFC 2616.

The HTTP client libraries use blocking socket calls and should therefore run in the context of the Server's Thread Pool such as an LSP page or the Lua Thread Library. See Thread Mapping for more information.

local http = require"httpc".create()
ba.thread.run(function()
    http:request{url="https://x.com"}
    trace(http:read"*a")
end)

The following example shows how to send JSON data as part of the HTTP body to a JSON echo service. The service responds with the original data and additional JSON data. Note that many JSON services are designed to accept POST data as URL encoded data and not directly as JSON as used in the following example. The method http:json() has been specifically designed to post JSON using URL encoded key/value pairs.

local rdata -- rec data
local sdata={ -- data to send
   hello = "world",
   vector = { i=10, j=15 }
}

local http = require"httpc".create()

local ok,err=http:request{
   url="https://postman-echo.com/post",
   method="POST",
   header={["Content-Type"]="application/json; charset=utf-8"}
}
if ok then
   ok,err=http:write(ba.json.encode(sdata))
   if ok then
      rdata,err=http:read"*a"
   end
end
if rdata then
   print("Rec Data:", rdata)
   print("Valid JSON: ", ba.json.decode(rdata) and "yes" or "no")
else
   print("Err:",err)
end

The "httpc" library

The httpc library is the low-level HTTP client interface. It gives you explicit control over connection setup, request submission, response inspection, and streaming reads/writes. Use it when you need full control over the request/response exchange. For higher-level convenience methods such as managed redirects, uploads, downloads, and JSON helpers, see the "http" library further below.

httpc.create([op])

Creates a low-level HTTP client. Load the module with require"httpc". The http and httpm wrapper constructors accept these connection settings and also save request defaults.

Parameters

Fields in op:

Return values

Returns client on success, or nil, "malloc" if allocation of the proxy, interface name, or proxy credentials fails. The http and httpm constructors preserve this error. No partially configured client is returned; its allocated state is reclaimed by garbage collection.

Throws

Throws for an invalid options argument or a configuration value rejected by the native type checks, including an incompatible SharkSSL object. Native option allocation failures are returned as error values.

Methods:

The methods associated with the object returned by require"httpc".create()

http:timeout(milliseconds)

Sets the HTTP client's I/O timeout. The default is 20000 milliseconds (20 seconds). Applies to subsequent native operations that use the client's timeout.

Parameters

Return values

Throws

Throws for an invalid client, a missing or invalid argument, a fractional numeric value, or a value outside the supported range, including zero. Invalid values leave the previous timeout unchanged.

http:request(op)

Starts a request. For a request with a body, send the data with write(), then inspect the response with status() or read(). A successful request() result does not establish that the server returned a successful HTTP status.

Parameters

Fields in op:

Return values

Returns true on success, or nil, error on failure. A received TLS alert can also provide alertLevel and alertDescription.

Throws

Throws for an invalid client or incorrect request arguments, including an invalid URL prefix, unsupported method, invalid size, or invalid header/query table entries. The wrappers also throw for a malformed query in an explicitly supplied URL. Connection and request failures are returned as error values.

http:status()

Get the HTTP response status. The http and httpm wrappers process supported redirects before returning and cache the resulting status until the next request.

Parameters

None.

Return values

Returns status on success, or nil, error with any additional error details.

Throws

Throws for an invalid HTTP client object or API misuse during redirect processing. Reported operational failures, including failure while discarding a redirect response, are returned as error values.

http:header([name])

Get all response headers or look up one header by name.

Parameters

Return values

Returns headers, httpStatus without name, or value, httpStatus with name. Request failures return nil, error, with two additional values for a received TLS alert.

Throws

Throws for an invalid HTTP client object or a name that cannot be used as a string. Request failures are returned as error values.

http:headerpairs()

Iterate over all response header entries, including repeated names. Finish iteration before closing the client or starting another request; these operations invalidate the iterator's header storage.

Parameters

None.

Return values

If response retrieval fails, the iterator produces no entries. The empty iterator does not distinguish this failure from a response with no matching entries.

Throws

Throws for an invalid HTTP client object. Response retrieval errors do not throw.

http:cookie()

Returns an iterator that will extract and traverse all cookies sent by the server.

The server may send multiple "Set-Cookie" headers:

   Set-Cookie: name=value [; expires=date] [; path=path] [; domain=domain] [; secure]

Finish iteration before closing the client or starting another request; these operations invalidate the iterator's header storage.

Parameters

None.

Return values

If response retrieval fails, the iterator produces no entries. The empty iterator does not distinguish this failure from a response with no matching entries. This method extracts fields; it does not enforce cookie attributes.

Throws

Throws for an invalid HTTP client object. Response retrieval errors do not throw.

local http = require"httpc".create()
local ok,err=http:request{url="https://www.google.com/"}
if ok then
   for name,value,t in http:cookie() do
      print("cookie name:", name, ",value", value)
      if t then
         for k,v in pairs(t) do
            print(k,v)
         end
      end
   end
end
http:read([size])

Read response body data. Omit size to read the entire remaining response.

Parameters

Return values

Throws

Throws for an invalid HTTP client object, an invalid size option, or a negative, fractional, or out-of-range numeric size. Invalid arguments are rejected before response data is consumed. Read failures are returned as error values.

http:write(data)

Send request body data. Call this method repeatedly to upload data in parts.

Parameters

Return values

Returns true on success, or nil, status on failure.

Throws

Throws for an invalid HTTP client object, data that cannot be used as a string, or a string longer than INT_MAX bytes. Oversized strings are rejected before sending. Send failures are returned as error values.

The caller can set a "Expect: 100-continue" header prior to uploading data, but this is not necessary since the write function asynchronously detects if the server denied the upload. Any 100 continue messages sent by the server are silently consumed by the write function.

Note: The data is uploaded using chunked transfer encoding unless the size attribute was set on the http:request() option table. Not all servers support chunked transfer encoding.

http:certificate()

Return the peer certificate information retained by TLS. The chain uses linked parent tables, not an array. See socket:certificate() for the named and typed certificate fields.

Parameters

None.

Return values

Throws

Throws for an invalid HTTP client object. A plain connection is reported through return values. Lua allocation errors can propagate.

http:cipher()

Return the negotiated cipher suite and TLS version. Query after a successful handshake. See socket:cipher() for supported cipher names.

Parameters

None.

Return values

Throws

Throws for an invalid HTTP client object. A plain connection is reported through return values. Lua allocation errors can propagate.

http:trusted()

Check the peer certificate chain and the request host name. Certificate date checks depend on the SharkSSL build configuration. Returns true on success or nil, reason otherwise.

Parameters

None.

Return values

Throws

In a TLS-enabled build, throws for an invalid HTTP client object. A failed trust check is returned as nil, reason. Without SharkSSL support, returns nil, "NO_SHARKSSL" without validating the object.

http:peername()

Get the remote server endpoint of the connection.

Parameters

None.

Return values

Returns address, port, ipv6 on success, or nil, error on failure.

Throws

Throws for an invalid HTTP client object. Socket lookup failures are returned as error values.

http:sockname()

Get the local client endpoint of the connection.

Parameters

None.

Return values

Returns address, port, ipv6 on success, or nil, error on failure.

Throws

Throws for an invalid HTTP client object. Socket lookup failures are returned as error values.

http:close()

Close the connection and invalidate any outstanding header or cookie iterator. The client object can be used for another request.

Parameters

None.

Return values

Throws

Throws for an invalid HTTP client object.

The "http" library

The "http" library extends the httpc library and simplifies the use of the low level implementation when HTTP redirect management is required.

The library is loaded and an HTTP instance is created as follows:

local http = require"http".create([op])

Redirect management:

The wrapper processes redirects when status(), read(), or url() is called. It follows 301, 302, 303, 307 and 308 responses, with these exceptions: PUT and POST return nil, status, redirectURL for 301 or 302; PUT, POST, PATCH and DELETE return the same result for 307 or 308. HEAD retains its method for every supported redirect. GET retains its method for 307 and 308. The returned redirectURL includes its query string. The http and httpm wrappers accept this complete URL in request(). The wrapper does not retain request bodies for replay. A 303 is followed using GET unless the current method is HEAD; subsequent redirects use that current method. At most nine redirected requests are sent; another redirect returns nil, "redirect". A missing Location header returns nil, "invalidresponse". A redirect query name or value rejected by the URL decoder also returns nil, "invalidresponse", without sending the redirected request. Failure to allocate the query parser buffer returns nil, "malloc" and also stops the redirect. Errors while discarding the redirect response stop processing and retain their error details. The url() method returns the resulting URL and query table.

Relative Location values are resolved against the current request URL. For example, from http://example.com/dir/start, /next selects http://example.com/next and next selects http://example.com/dir/next. Dot segments (. and ..) are resolved. A new path replaces the old query; a query-only Location keeps the current path. Fragment identifiers are omitted from the request. The url() method continues to return the URL and query table separately.

On a redirect to a different origin (scheme, host or port), the wrapper removes the user and password options and any explicitly supplied Authorization and Cookie headers before making the next request. Header-name comparisons ignore case. For origin comparison, host names ignore case and omitted ports mean 80 for HTTP or 443 for HTTPS. Values removed during a redirect chain stay removed, even if a later redirect returns to the original origin. The caller's option and header tables and the client's saved defaults are not modified.

Default http:request() options:

One of the benefits of the Lua implemented wrapper is that it simplifies the use of the option table. Options that are required on method http:request() can be set when creating the HTTP client instance. The following example illustrates this:

local http=require"http"
 
-- Set default options
local op={
   url="https://realtimelogic.com/products/",
   method="GET" -- Not needed since it is the default
}
local h=http.create(op)
 
 -- Use default options: method "GET" and url https://realtimelogic.com/products/"
h:request()
-- Prints nil since response is generated by LSP and is chunk encoded
print("GET: Content-Length",h:header()["Content-Length"])
print("Calculated length", #h:read"a")
 
h:request{method="HEAD"} -- Override default "GET" method
-- This works.
print("HEAD: Content-Length",h:header()["Content-Length"])
 
h:request() -- We are using "GET".
-- prints nil
print("GET: Content-Length",h:header()["Content-Length"])
h:close() -- We are done

An instance of the "httpc" library would have thrown an exception when calling h:request(), but the Lua version remembers the extra options added when we created the object.

You can optionally provide an option table and override any of the default settings. The HEAD request above illustrates this.

A query table supplied to http.create() or httpm.create() persists as a default for subsequent requests and httpm helpers. A request-specific query table replaces the whole default table for that call; query={} selects an empty table. These overrides and redirects do not change the saved default. When the URL itself contains query values, the selected query table overrides matching URL keys.

Methods:

The "http" library provides, in addition to the httpc methods, the following method:

http:url()

Get the current request URL and query table after processing supported redirects. This method is provided by http and inherited by httpm.

Parameters

None.

Return values

Returns url, query on success. If redirect processing fails, returns nil, error with the same additional details as status().

Throws

Throws for an invalid client or API misuse during redirect processing. Reported operational failures are returned as error values.

The "httpm" library

The "httpm" (HTTP Managed) library extends the http library and simplifies common tasks such as uploading and downloading data, sending HTTP POST requests, and communicating with a server using JSON.

The post() and json() helpers add their request headers to a copy of the selected header table. They do not modify headers supplied by the caller or saved as client defaults.

The library is loaded and an HTTP instance is created as follows:

local http = require"httpm".create([op])

Methods:

The "httpm" library provides, in addition to the httpc and http methods, the following methods:

http:stat(url [,op])

Request resource metadata using HEAD by default. Set op.method to use another HTTP method.

Parameters

Return values

Returns attributes for HTTP 200, nil, status for another response status, or nil, error with any additional underlying error details.

Throws

Throws for invalid arguments or API misuse reported by the underlying HTTP methods. Reported request, status and header failures are returned as error values.

Examples:

local http = require"httpm"
local h=http.create()
-- Dynamic resource
local st,err=h:stat("https://realtimelogic.com/")
-- prints val,nil
if st then print("A:",st.size,st.mtime) end
-- Static resource
st,err=h:stat("https://realtimelogic.com/downloads/docs/IoT-Security-Solutions.pdf")
-- prints val,val
if st then print("B:",st.size,st.mtime) end
-- Dynamic resource using HTTP GET
st,err=h:stat("https://realtimelogic.com/",
              { method="GET", query={foo="bar"}})
-- prints nil,nil
if st then print("C:",st.size,st.mtime) end
h:close()
-- Static resource using HTTP GET
st,err=h:stat("https://realtimelogic.com/downloads/docs/IoT-Security-Solutions.pdf",
              { method="GET"})
-- prints val,val
if st then print("D:",st.size,st.mtime) end
http:post(url,tab [,op])

Submit form data using HTTP POST by default. The option table can select another method; the implementation uses GET for methods other than POST.

Parameters

Return values

Returns httpStatus, body when a response is read, or nil, error on failure, with additional redirect or TLS details when provided.

Throws

Throws for invalid arguments, invalid form fields, or API misuse reported by the underlying HTTP methods. Operational failures are returned as error values.

The following example is sending url encoded data in the query component of the URL and in the body of the message.

local http = require"httpm".create()
local ok,data=http:post("https://postman-echo.com/post",
                    {formKey1="formVal1",formKey2="formVal2"},
                    {query={queryK1="queryVal1",queryK2="queryVal2"}})
print(ok) -- HTTP 200
print("JSON resp:",data)
 -- Postman Echo returns everything packaged as json
data=ba.json.decode(data)
print"\nArgs (query):"
for k,v in pairs(data.args) do print("",k,v) end
print"\nForm (POST body):"
for k,v in pairs(data.form) do print("",k,v) end
http:upload(conf [,op])

Upload a file using PUT by default. After sending the data, checks the final response through status(). Returns true only for a final HTTP status from 200 through 299. You do not need a separate status() call to check success.

Parameters

Return values

Returns true after sending completes and the final status is 200 through 299. Other final HTTP statuses return nil, status. File-close failures return nil, error. An earlier setup, file read or HTTP write error takes precedence over a reported close error. Underlying failures retain their error details.

Throws

Throws for missing required configuration or API misuse reported by the file and HTTP methods. Exceptions from the progress callback propagate. Reported file read, file close, HTTP write and final status failures are returned as error values.

The function can be used in two modes:

The HTTP library is monitoring response data from the server while the upload is in progress. The upload is automatically terminated if the server sends an error response. It is for this reason not necessary to do the typical one byte test upload prior to uploading the actual data to test if the server accepts the upload. HTTP 100-continue messages sent from the server are silently consumed.

http:download(conf [,op])

Download response data to a file using GET by default. Requires an HTTP 200 response before copying data.

Parameters

Return values

Returns true when copying and closing the file succeed. A response other than 200 returns nil, status. Other reported failures return nil, error with any additional error details. An earlier setup, response status, response header, HTTP read or file write error takes precedence over a reported close error. Failure does not undo data already written.

Throws

Throws for missing required configuration or API misuse reported by the file and HTTP methods. Exceptions from the progress callback propagate. Reported operational failures are returned as error values.

Download a file from a server and save the file. The function can be used in two modes:

http:json(url, data [,op])

Request JSON data using GET by default. Only an HTTP 200 response is decoded. Set op.method to "POST" to send URL-encoded form data as described for post().

Parameters

Return values

Returns the decoded table on success. Failures return one of the following:

Throws

Throws for invalid arguments or API misuse reported by the underlying HTTP or JSON methods. Operational failures and reported JSON decoding errors are returned as error values.

Example 1:

local http=require"httpm".create()
local data={} -- Empty, no key/value pairs
local t,err=http:json("http://ip.jsontest.com/",data)
if t then -- If we got a Lua table (decoded JSON string)
   print("IP address:",t.ip)
end

Example 2:

local http=require"httpm".create()
local data={lat=33.466972,lng=-117.698105} -- Dana Point, CA
local t,err=http:json("http://api.sunrise-sunset.org/json",data)
if t then
   print(require"serpent".block(t,{comment=false}))
end

Example 3:

local http=require"httpm".create()
-- Connect to a Web-File-Manager in a Barracuda server
-- and request a JSON directory listing.
local data={cmd="lj"}
local t,err = http:json("https://tutorial.realtimelogic.com/fs/home/mako", data)
if t then
   for _,r in ipairs(t) do -- Iterate all resources
      response:write("name=",r.n,", size=",r.s < 0 and "DIR" or r.s,
                     ", date=",os.date("%c",r.t),"<br>")
   end
else
   response:write(err)
end

See also how to send JSON as part of the HTTP body.

The Lua Debug Module

The Lua debug module dbgmon implements the Debug Adapter Protocol and can be used by any debugger implementing this protocol. Module dbgmon is implemented in C code and interfaces to the Lua debug API.

See the how to use the debugger example on GitHub for more information on how to use this module.

Load the module as follows:

local dbgmon = require"dbgmon"

The returned value dbgmon is a table with the following functions:

dbgmon.connect([op])

Establishes a persistent TCP debugger connection and waits for the debugger to finish configuration. This synchronous setup intentionally freezes normal operation while waiting for the debugger.

Parameters

Return values

Throws

Throws for option fields that cannot be converted to the expected integer, string, or boolean type, a port outside 0 through 65535, or retry greater than C INT_MAX. Boolean options require actual booleans. All supplied fields are validated even when their option is unused in the selected mode. Option-table metamethod errors and Lua allocation errors can propagate. Connection failures are reported by return values.

dbgmon.close()

Closes any active TCP debugger connection, disables debugger hooks, and releases session data.

Parameters

None.

Return values

Throws

Does not intentionally throw for an already closed connection or return an operational error pair. Lua allocation errors can propagate.

dbgmon.pause()

Disables debugger hooks so Lua code runs normally, keeping the TCP debugger connection open. This pauses debugging, not application execution.

Parameters

None.

Return values

Throws

Does not intentionally throw when debugging is already paused or disconnected. No operational error pair is returned.

dbgmon.resume([stop])

Enables debugger hooks for a connected debugger. Calling it while debugging is already enabled leaves the hooks enabled.

Parameters

Return values

Throws

Throws if stop is neither a boolean nor nil. A disconnected debugger returns false.

LuaIo

The LuaIo module maps the Barracuda I/O interface into Lua, making it possible to implement an I/O provider entirely in Lua code.

Use LuaIo when you want BAS components that expect an I/O object to work against something other than a normal filesystem. Common patterns include wrapping an existing I/O object with filtering logic, exposing remote or virtual storage as an I/O tree, or presenting data from a database or service as files and directories. For example, a SQL-backed LuaIo can be used to expose WebDAV access to database content.

Ready-to-run Examples:

Error handling:

LuaIo functions follow the same style as the native Barracuda I/O APIs. Functions that normally return an object or table return nil on error, while functions that return status information return false on error. In addition to nil/false, the functions may return one of the following error codes:

-- Open resource in read or write mode. mode ="r" or "w"
local function open(name, mode)
   -- open resource, return nil on error.
   -- Resource management functions
   local function read(maxsize)
      -- Return data
   end
   local function write(data)
      -- Return true or false
   end
   local function seek(offset)
      -- Return true or false
   end
   local function flush()
      -- Return true or false
   end
   local function close()
      -- Return true or false
   end
   local res={
      read=read,
      write=write,
      seek=seek,
      flush=flush,
      close=close
   }
   return res -- Table with functions: read,write,seek,flush,close
end

-- Directory iterator
local function files(name)
   -- Find resource, return nil if not found.
   local function read()
      -- Iterate to first/next resource, return true if more files or false
      -- if no more files.
   end
   local function name()
      -- return resource name
   end
   local function stat()
      -- return table with mtime,size, and isdir (true/false)
   end
   return {read=read,name=name,stat=stat} -- Return table with functions
end

-- Return resource information
local function stat(name)
   -- return table with mtime,size, and isdir (true/false) if found or
   -- nil if not found
end

-- Create directory
local function mkdir(name)
   -- Return true on success and false on error
end

-- Remove directory
local function rmdir(name)
   -- Return true on success and false on error
end

-- Remove file
local function remove(name)
   -- Return true on success and false on error
end

-- Table with the Lua I/O callback functions
local iofuncs = {
   open=open,
   files=files,
   stat=stat,
   mkdir=mkdir,
   rmdir=rmdir,
   remove=remove
}

-- Create a Lua I/O instance
local io=ba.create.luaio(iofuncs)

-- Install the Lua I/O in any Barracuda resource that takes an I/O
-- interface as argument.

PathIo

The pathio module creates a nested Barracuda I/O object rooted at a path in another Barracuda I/O object. It is useful when an application should expose or consume one subtree without changing the backing I/O object. The backing object may be any Barracuda I/O implementation, including DiskIo, ZipIo, and LuaIo.

A directory-backed I/O such as DiskIo can normally create a nested I/O directly with ba.mkio(). ZipIo cannot create a nested I/O that way; pathio is the only way to create a nested I/O view inside a ZIP file. Since pathio works with both directory-backed and ZIP-backed objects, it provides a portable solution for applications that run from an ordinary directory during development and from a deployed ZIP file in production.

PathIo does not copy files or create independent storage. Operations on the returned object are translated to base-path/name and forwarded to the backing I/O object. Changes are therefore visible through both objects, and a read-only backing object produces a read-only PathIo view. Path validation, canonicalization, and access restrictions are handled by the backing Barracuda I/O implementation.

pathio(base-io, base-path)

Creates a LuaIo view that prefixes file and directory names with base-path. Load the function with require"pathio".

Parameters

Return values

Throws

Construction can throw if LuaIo allocation fails. The constructor does not validate the backing object or path; invalid arguments may only be detected when an operation uses them.

The view forwards open, files, stat, mkdir, rmdir and remove through LuaIo callbacks. File handles forward read, write, seek, flush and close. Directory iteration is adapted to LuaIo's directory interface. Errors are interpreted by LuaIo using its callback contract; arbitrary extra return values from the backing object are not guaranteed to survive. Rename and backing-specific properties are not supplied by this wrapper.

local pathio = require "pathio"
local homeIo = ba.openio "home"
local zipIo, err = ba.mkio(homeIo, "myapp.zip")

if not zipIo then
   trace("Cannot open myapp.zip:", err)
   return
end

-- Make the public/ directory inside myapp.zip the root of publicIo.
local publicIo = pathio(zipIo, "public")

-- This opens public/images/logo.png inside myapp.zip.
local file, err = publicIo:open("images/logo.png")
if file then
   local data = file:read "a"
   file:close()
end

In a Mako Server application, the predefined application io may be directory-backed or ZIP-backed. Calling pathio(io, "public") therefore gives application code the same nested I/O layout in both non-deployed and deployed applications.

Mail (SMTP) Client Library

The SMTP library provides client-side functionality for sending e-mail messages from Lua applications running inside the Barracuda App Server (BAS).

The implementation follows the Simple Mail Transfer Protocol (SMTP) as specified in RFC 5321 and supports common SMTP extensions such as authentication (AUTH). The library can send mail over both plaintext SMTP (where permitted) and encrypted SMTP using TLS.

Relationship to LuaSocket

The BAS SMTP library is based on the LuaSocket SMTP module, but it is enhanced and integrated with the BAS's socket API. Key enhancements include:

For additional details, see the BAS LuaSocket compatibility library.

Secure Transport Support

Most SMTP servers today require encryption. The SMTP client supports two secure modes:

STARTTLS behavior

When starttls = true, STARTTLS is required before authentication or message submission. If the server does not advertise STARTTLS, sending closes the connection and returns nil, "STARTTLS not supported". A rejected STARTTLS command or failed TLS upgrade also closes the connection and returns nil, error. If the shark field is not explicitly set, the library uses the value returned by ba.sharkclient().

Implicit TLS behavior (SMTPS)

When using implicit TLS (direct TLS from connect), you must explicitly provide a SharkSSL object in the shark field. If the shark field is not set, the library treats the connection as non-secure, and the server typically rejects the session.

Blocking Behavior and Threading Requirements

The SMTP library uses blocking sockets and therefore:

If you are calling the SMTP library from outside an LSP request context (for example, from cosocket), run the SMTP logic in the context of the Lua Thread Library to isolate blocking I/O. See the Thread Library documentation for details.

Simplified SMTP Client Library

We provide a wrapper library that simplifies use of the LuaSocket SMTP library. You can choose to use the LuaSocket SMTP library directly or use the simplified SMTP library provided by Real Time Logic. The simplified SMTP client library does not require detailed knowledge of MIME encoding or of how to use LTN12 filters, sources, and sinks. The following documentation covers the simplified SMTP library. See the LuaSocket SMTP documentation if you prefer to use the more technical API.

The simplified SMTP client library, called mail, makes it easy to send text and HTML emails. The library also makes it easy to embed images in HTML messages and add file attachments. The following example shows how to send an email using a standard SMTP server.

Notice that require is used slightly differently in the example below when loading the SMTP library and the LuaSocket compatibility layer. In standard Lua usage, require loads a module and returns it. However, in this case, the module initializes itself and registers socket.mail in the global namespace instead of returning a module table. Thus, calling require has the side effect of creating the global socket.mail function rather than returning a value that must be assigned. This behavior is intentional and aligns with the LuaSocket compatibility API included with BAS.

require "socket.mail" -- Load mail lib (create the global 'socket.mail')

local mail=socket.mail{server="smtp.example.com", shark=ba.sharkclient()} -- Create a mail object

-- Send one email
local ok,err=mail:send{
   subject="Hello",
   from='Bob <bob@example.com>',
   to='Alice <alice@example.com>',
   body=[[
     Hello Alice,
     This is a test.
   ]]
}

Simplified SMTP Client Library API

See also:
socket.mail{
server = string,
[port = number,]
[user = string,]
[password = string,]
[shark=SharkSSL object,]
[starttls=true]
}

Creates a simplified SMTP client object without opening a connection.

Parameters

Return values

Throws

Throws for an invalid settings object or a missing server field. Settings used for connection and submission are checked when send runs.

mail:send{
subject=string,
from=string,
to=string|table,
[replyto=string,]
[cc=string|table,]
[bcc=string|table,]
[txtbody=string|fp,]
[htmlbody=string|fp,]
[htmlimg=table,]
[attach=table,]
[encoding="QUOTED" | "B64" | "8BIT" | "NONE"]
[charset=string]
}

In the syntax above, string|fp means either a string with text/binary data or a file pointer. A file pointer can be from opening a file using the standard Lua I/O function or from a Barracuda I/O. Examples: fp=io.open("/path/img.gif"), fp=ba.openio("disk"):open("/path/img.gif").

Parameters

Return values

Throws

Missing required fields and invalid message data can throw during message assembly, before SMTP sending starts. The underlying SMTP send catches errors in its send operation and returns nil, error, including errors from message sources. See LuaSocket compatibility for the detailed SMTP error contract.

File sources are read from their current position and closed when reading ends. If sending stops before a source finishes, the caller is responsible for closing that file. The underlying ltn12 file source discards returned read and close errors, so a read failure can appear to be normal end-of-file. See the File Sources section in LuaSocket compatibility for this limitation.

Email addresses:
Email addresses can be any of the following:

Mail Examples

GitHub

In addition to the examples below, ready-to-run examples can be downloaded from GitHub. These examples show a practical progression for sending email with socket.mail()

Creating the mail object

The following example configures and creates a mail object for the secure Google SMTP server:

local mail=socket.mail{
   shark=ba.sharkclient(), -- Use TLS
   server="smtp.gmail.com",
   user="YOUR GOOGLE EMAIL ADDRESS",
   password="YOUR GOOGLE PASSWORD",
}

Notice that we use the SharkSSL object returned by ba.sharkclient() in this example.

The following example configures and creates a mail object for the STARTTLS version of the hotmail SMTP server:

local mail=socket.mail{
   shark=ba.sharkclient(), -- Use TLS
   starttls=true, -- Start as a normal socket and then later upgrade to TLS
   server="smtp.live.com",
   user="YOUR HOTMAIL EMAIL ADDRESS",
   password="YOUR HOTMAIL PASSWORD",
   port="587",
}
Sending emails

The following example sends an HTML email to Alice by opening and reading the file "emails/alice.html":

local ok,err=mail:send{
   from='Bob <bob@example.com>',
   to='Alice <alice@example.com>',
   subject="Hello",
   htmlbody=io:open"emails/alice.html"
}

It is good practice to embed a plain text version of the HTML email. The following example sends a combined text and HTML email:

local ok,err=mail:send{
   from='Bob <bob@example.com>',
   to='Alice <alice@example.com>',
   subject="Hello",
   txtbody="This is the text body",
   htmlbody=[[
        <html>
          <body>
            <h1>This is the html body</h1>
          </body>
        </html>
   ]]
}

The following email shows how to send a combined text and HTML image. The HTML includes an embedded image. The email also includes one attachment:

local ok,err=mail:send{
   from='Bob <bob@example.com>',
   to='Alice <alice@example.com>',
   subject="Hello",
   txtbody="This is the text body",
   htmlbody=[[
        <html>
          <body>
            <h1>This is the html body</h1>
            <img src="cid:the-unique-id" alt="Text shown by text only clients">
          </body>
        </html>
   ]],
   htmlimg = {
      id="the-unique-id",
      name="logo.svg",
      source=io:open"logo.svg"
   },
   attach={
      description="A document",
      name="my-document.pdf",
      source=io:open"my-document.pdf"
   }
}

Notice how the image source attribute is set to a unique ID. You must create a special HTML file where each embedded image has its own unique ID.

Sending E-Mails Via a Proxy

The SMTP server's domain name can be a name (string) or an already established socket connection. Using a socket connection is useful when the SMTP client requires connecting to the Internet via a proxy. The following example shows how to send an email using Google Mail and how to establish the connection via a local proxy by using the HTTP client library and the "proxycon" setting.

require "socket.mail" -- Load mail lib

local http = require"httpc".create{
      proxy="localhost", -- Using local proxy
      socks=true, -- Enable SOCK5
      proxycon=true, -- Use the HTTP lib for opening a proxy connection
      proxyport=1080, -- SOCKS5 port number
}
-- Connect to google mail at port number 465
local ok,status = http:request{url="http://smtp.googlemail.com:465"}
if status == "prxready" then -- If proxy connection ready
   local mail=socket.mail{
      server=ba.socket.http2sock(http), -- Extract socket object
      shark=ba.sharkclient(), -- Implicit if not set
      user="john.doe@gmail.com",
      password="the-password",
   }
   -- Send email
   local ok,err=mail:send{
      subject="Hello",
      from='John Doe <john.doe@gmail.com>',
      to='Janie Doe <janie@doe.com>',
      body="Hi Janie"
   }
end

Pipe API

The pipe API is supported on POSIX operating systems including Linux and QNX.

ba.pipe.mkfifo(pathname [, mode])

This function maps directly to the POSIX C function mkfifo().

Parameters

Return values

Throws

Throws for a missing pathname or arguments that cannot be converted to the expected string/integer types. Lua allocation errors can propagate. OS failures return nil, error.

ba.pipe.open(pathname [,cosocket [, args...]])

Opens an existing named pipe as a socket object using the cosocket flow control mechanism. This function does not create the FIFO. The OS descriptor is nonblocking. Without a callback, it opens for writing only and fails if no reader has the FIFO open. With a callback, it opens for both reading and writing.

Parameters

Return values

Throws

Throws if pathname is missing or cannot be converted to a string. Lua allocation errors during setup can propagate. OS open failures return nil, error. Errors raised in the cosocket callback do not propagate as exceptions to the caller of open.

Reverse Proxy

Ref Wikipedia:

A reverse proxy is a type of proxy server that retrieves resources on behalf of a client from one or more servers. These resources are then returned to the client as though they originated from the server itself.

The reverse proxy lets a BAS application expose resources from one or more backend servers as if they were part of the same site. This is useful when integrating existing web applications, placing authentication in front of less secure backend services, or presenting multiple internal services through one public URL structure.

The reverse proxy behaves as a Barracuda Server directory object. One or several instances of the reverse proxy can be inserted into the Barracuda virtual file system. The reverse proxy can also implement authentication and authorization just like any other Barracuda directory object. One can for this reason provide security to less secure backend servers. The reverse proxy also offers SSL termination.

ba.create.redirector(name,domain,port[,baseuri][,priority])

Creates a directory that forwards requests to one fixed HTTP backend. Construction copies the destination settings; the backend connection is opened when handling a request.

Parameters

Return values

Throws

Throws for invalid string/integer argument types, a port outside 0 through 65535, or a priority outside -128 through 127. Lua allocation errors can propagate. Backend connection failures occur later during request handling, not as constructor exceptions.

ba.create.redirector(name[,priority])

Creates a proxy directory whose destination is selected from the request path: domain-name[:port]/path. The destination port defaults to 80.

Parameters

Return values

Throws

Throws for an invalid name or priority type, a missing name argument, or a priority outside -128 through 127. Lua allocation errors can propagate. No operational nil/error pair is returned by this constructor form.

Creating a standard reverse proxy:

The most common operation is to create a standard reverse proxy. The following example creates a reverse proxy for a backend server with IP address 127.0.0.1

  rproxy=ba.create.redirector("backend","127.0.0.1",80)
  myRootDir:insert(rproxy)

The backend server 127.0.0.1 can now be access as: http://localserver/backend/ Use the baseuri if you only want to access a subset of the backend server:

  rproxy=ba.create.redirector("backend","127.0.0.1",80,"subdir")

The backend server URL http://127.0.0.1/subdir/ can now be accessed as: http://localserver/backend/

The basic proxy does not provide a rewrite module, thus absolute encoded URLs in web pages retrieved from a backend server such as images will not show up in the browser unless the reverse proxy is installed as a root directory.

  rproxy=ba.create.redirector(nil,"127.0.0.1",80,-1) -- Priority=-1
  rproxy:insert() -- Insert as root directory

The above is the most common case for inserting an existing web application into a barracuda server. The reverse proxy is inserted as a root directory with a priority that is less than the root directory for the existing application. This will make sure the Barracuda server first searches for the requested resource locally and then delegates the request to the reverse proxy only if the requested resource was not found in the Barracuda Server.

Cross site scripting redirector:

  rproxy=ba.create.redirector("gateway")
  myRootDir:insert(rproxy)

Any server and web page can now be accessed as http://localserver/gateway/domain-name/path.

Example: http://localserver/gateway/www.cnn.com

rwfile.lua

rwfile=require"rwfile"

A small module for reading and writing complete files through a BAS IO object. It is useful for configuration files and other small state files where loading or replacing the whole file is acceptable. The module closes an opened file before normal return. An exception from an underlying operation can interrupt cleanup.

rwfile.json(io, name [, tab])

Read a JSON file and decode it to a Lua table, or encode and write a Lua table as JSON.

Parameters

  • IO userdata io - The IO object containing the file.
  • string name - File pathname relative to io.
  • table, nil or false (optional) tab - A table to encode and write, replacing the file. Omitted, nil or false reads the file and strips a leading UTF-8 BOM before decoding it.

Return values

  • table, boolean or nil result - The first decoded table when reading, or true when writing succeeds. Nil on an open, read, write, close, encoding or decoding failure. An empty file returns nil without an error message.
  • string or nil error - The open, read, write, close or JSON encoding error; "jsonerr" for a decoding failure. Nil on success or an empty file. Encoding failure is returned before opening the file.

Writing uses rwfile.file. A close failure returns nil, error. An earlier read or write error takes precedence over a close error.

Throws

Throws for incorrect arguments, such as a truthy tab that is not a table, or invalid arguments passed to the underlying IO methods. Lua allocation errors and exceptions from underlying methods can propagate. Reported file-operation and JSON conversion failures use the return values above.

Example:
local rwfile = require"rwfile"
local io = ba.openio"home"

rwfile.json(io, "settings.json", {color="blue", gradient="linear"})

local settings, err = rwfile.json(io, "settings.json")
if settings then
   trace(settings.color)
else
   trace("Cannot read settings:", err)
end

rwfile.file(io, name [, data])

Read or write a complete file. The file is read if data is omitted, nil, or false. The file is opened in write mode and replaced if data is a truthy value.

Parameters

  • IO userdata io - The IO object containing the file.
  • string name - File pathname relative to io.
  • string, number, nil or false (optional) data - Data to write; the file object's write method converts numbers to strings. An empty string replaces the file with an empty file. Omitted, nil or false reads the entire file using *a.

Return values

  • string, boolean or nil result - The file contents when reading, true when writing succeeds, or nil on an open, read, write or close failure. Reading an empty file returns nil without an error message.
  • string or nil error - The error returned by open, read, write or close, or nil when none was reported. An earlier read or write error takes precedence over a close error. Additional return values from these methods are not forwarded.

The opened file is closed before normal return. A close failure returns nil, error, including after a successful write or read. Read data is not returned if close fails.

Throws

Throws for incorrect arguments passed to the underlying IO methods, such as an invalid io object, pathname or write-data type. Lua allocation errors and exceptions from underlying methods can propagate. Reported open, read, write and close failures are returned as nil, error.

local rwfile = require"rwfile"
local io = ba.openio"home"

local ok, err = rwfile.file(io, "message.txt", "Hello")
local data, err = rwfile.file(io, "message.txt")

Barracuda Web Server Listen Object

ba.create.servcon(port|con [,op])

Create a Barracuda Web Server listen object and install it in the Socket Event Dispatcher. Web server listen object(s) are typically installed by the C startup code, but can optionally be installed by the server startup script (.config). The benefit of managing these objects in Lua is easier runtime configuration.

See the Lua web-server startup script examples/mako/lsp/.openports in the Barracuda Embedded Web Server SDK for an example showing how to dynamically detect free server port numbers and configure multiple secure and non-secure server connection objects.

For security reasons, objects created with this function are self referencing, which means they will not be collected by the garbage collector if you do not keep a reference to these objects. The Barracuda Socket Event Dispatcher cannot operate without at least one connection. If you close all listen objects, the Socket Event Dispatcher will eventually run out of active connections and malfunction. It is, for this reason, vital that you keep at least one listen object active at all times.

Parameters

Return values

Replacement removes the old object's self reference. Existing accepted TLS connections remain associated with the old object until they close, the old object is explicitly closed, or it is garbage collected. New connections use the replacement SharkSSL object. Keep the returned listener for later changes.

Throws

Throws for an invalid port type or a port outside 1 through 65535; an invalid, closed or inactive replacement listener; or an invalid op.shark. HTTPS requires a server SharkSSL object with a certificate. Exceptions from option-table getters and Lua allocation errors can propagate. Native listener creation failures return nil, "bind".

You can open any number of server connection objects as long as the combination of all of the following is unique: port number, interface, and protocol version (IPv4/IPv6). This means that you can have many server connection objects listening on port 80 if you have more than one interface or protocol version.

Objects returned by ba.create.servcon() have the following methods associated with them:

servcon:setport(port [,op])

Change the listening port and binding options. The original listening socket is replaced only if the new socket is created successfully. Accepted connections remain running.

Parameters

  • listen-object userdata self - The object before the colon. Must not be closed.
  • integer port - New port from 1 through 65535. Port 0 is not accepted for HTTP or HTTPS.
  • table (optional) op - Binding options. Omitted, nil or a non-table value selects IPv4 on all interfaces; it does not retain the previous binding options.
  • string (optional) op.intf - Interface name or IP address. Omitted or non-string means all interfaces.
  • boolean (optional) op.ipv6 - True selects IPv6, which requires build support. Omitted or non-boolean means false.
  • SharkSSL userdata (optional) op.shark - Does not change the listener's SharkSSL object or switch between HTTP and HTTPS. If non-nil, it is still checked to be a SharkSSL object.

Return values

  • boolean or nil ok - True after changing the listening socket, or nil if native creation of the new socket fails. On failure the existing listener is retained.
  • string (on failure) error - "bind". This also covers socket creation, address resolution and listen failures.

Throws

Throws for an invalid or closed self, an invalid port type, a port outside 1 through 65535, or a non-nil op.shark that is not a SharkSSL object. Exceptions from option-table getters and Lua allocation errors can propagate. Reported native failures return nil, "bind".

servcon:close()

Close the active server listening object, remove the listening object from the Socket Event Dispatcher, and remove the self reference on the object.

For an HTTPS listen object, this also closes the accepted TLS connections still associated with that object and releases its reference to the SharkSSL object.

Parameters

  • listen-object userdata self - The object before the colon. Must not already be closed. There are no additional arguments.

Return values

  • boolean ok - True after closing the object. No operational error result is returned.

Throws

Throws if self is not a listen object or the object has already been closed. Calling close twice is a programmer error.

The following example shows how to create a secure (SSL) listen object. You may download the complete example and run the example on your server.

-- Servers do not use a 'cert store' unless mutual authentication is required.
local shark=ba.create.sharkssl(nil, {server=true})
-- x509certData and x509KeyData are Lua strings containing cert and key.
local x509cert = ba.create.sharkcert(x509certData,x509KeyData)
shark:addcert(x509cert) -- Certificate required for servers.
ba.create.servcon(port, {shark=shark})

SharkSSL

(Secure SSL/TLS communication management)

SharkSSL is an extremely compact TLS stack. SharkSSL provides security for network communications, such as for securing HTTP and SMTP communication. The SharkSSL stack supports both client-side and server-side connections, enabling the client to confirm the server's identity and vice-versa.

When using secure communication such as secure sockets or the HTTP library in secure mode, a SharkSSL object is required. Various APIs such as the HTTP client library and the socket library take an option table as an argument. This table must have the attribute shark set to a SharkSSL instance when using secure communication.

ba.create.sharkssl([certstore [,op]])

Create a SharkSSL object. Parameter certstore (the CA list) is an object created with ba.create.certstore(); parameter op is an optional configuration table. As an alternative to using a certstore object, you may also use the more compact SharkSslCAList object format. The documentation for ba.sharkclient() includes an example showing how to create a SharkSSL client object using a binary SharkSslCAList object.

Note: for client connections, you may consider using ba.sharkclient(), which returns a ready-to-use SharkSSL object initialized with the certificates in the file .certificate/cacert.shark.

Parameters

Omitted or nil size fields use their defaults. Size checks occur before conversion to native 16-bit values. The buffer range is a conservative Lua API policy, not a native SharkSSL limit or a guarantee that every handshake configuration fits.

Return values

Throws

Throws for an invalid CA-store argument, a binary CA list rejected by the format check, or an invalid size type or range. The binary-list check requires at least the four-byte header and validates its first two format bytes. It does not validate every entry or offset; supply a valid SharkSslCAList produced by the supported tools. Exceptions from option-table getters and Lua allocation errors can propagate. A native CA-store assembly allocation failure returns nil, "mem".

The returned object has the following method:

sharkssl:addcert(sharkcert)

Add a certificate and key before creating connections from this SharkSSL object. You can add multiple distinct certificates, such as RSA and ECC certificates.

Parameters

  • SharkSSL userdata self - The object before the colon.
  • certificate userdata sharkcert - Certificate and key created by ba.create.sharkcert(). Must not already be attached to any SharkSSL object, including self. A successful attachment retains the certificate until the owning SharkSSL object is garbage collected; after that it can be attached again.

Return values

  • boolean ok - True if added. False if the native library refuses the addition, including when connections already exist, allocation fails, or certificate/key processing fails. A failed addition does not attach the certificate. No error string is returned.

Throws

Throws for an invalid self or sharkcert type, or if sharkcert is already attached. Repeated attachment to the same object and sharing one certificate object between different SharkSSL objects are API usage errors. This check occurs before native registration. Lua allocation errors can also propagate.

Typical use cases
  1. Create a client SharkSSL object that verifies the server using the supplied CA
    clientObj=ba.create.sharkssl(certstore)
  2. Create a client SharkSSL object that accepts any server, including self-signed certificates.
    clientObj=ba.create.sharkssl()
  3. Create a standard server object that does not require client certificates.
    serverObj=ba.create.sharkssl(nil, {server=true}); serverObj:addcert(sharkcert)

The above 3 use cases show the typical web client and server configurations.

ba.create.certstore()

Create a Certificate Authority Store. A Certificate Authority or CA for short is an entity that issues digital certificates for use by other parties. In layman's terminology, a CA is like a certified (government) passport issuer. A CA is typically used by a client SharkSSL object, but can also be used by a server SharkSSL object if the server requires client certificate validation.

Parameters

None.

Return values

Throws

Lua allocation errors can propagate. There are no required arguments to validate and no operational nil/error return from this constructor.

The object returned by ba.create.certstore() has the following method:

addcert(io,name)
addcert(data)

This function adds a certificate in PEM or p7b format to the CA store. A convenient way to obtain CAs is to export certificates from a browser in PEM or p7b format. The p7b format is a container format that can contain many CAs.

Parameters

  • CA-store userdata self - Call as store:addcert(...). The store must not have been assembled.
  • IO userdata io - File form only: the IO object containing the certificate file.
  • string name - File form only: pathname relative to io.
  • string data - Data form only: certificate file contents. Supply either io, name or data. The binding copies the data for native processing.

Return values

  • integer or nil count - The native parser's result, normally the number of certificates added. Zero reports no additions or a processing failure. Nil reports failure to load or copy the input.
  • string (when count is nil) error - The file-loading error (including close failure after a successful read), or "malloc" if the temporary buffer cannot be allocated. No detailed error string accompanies a numeric parser result.

Some malformed input currently produces the numeric result 65535 instead of a failure result. This native behavior is pending correction; do not rely on that value as confirmation that certificates were added. Processing a bundle is not transactional: a failure can leave earlier additions in the store.

Add all certificates before the store is assembled. Assembly occurs when passing the store to ba.create.sharkssl(), or when calling store:data() in builds that provide that method. After successful assembly, addcert throws for either input form. The restriction remains after all SharkSSL objects using the store are garbage collected. Create a new store to use a different certificate set. If assembly itself fails, the store remains editable; if assembly succeeds but a later operation fails, the store remains assembled.

Throws

Throws if the store has already been assembled, before reading a file or parsing certificate data. Also throws for invalid receiver, IO-object, pathname or data argument types. Lua allocation errors can propagate. Failure to allocate the binding's temporary data buffer is returned as nil, "malloc".

The following example is designed to demonstrate how to create a certificate store and use it to validate SSL certificates.

-- Checks if RTL's website certificate is valid.
-- Arg sharkObj: an object created using function ba.create.sharkssl()
-- Returns true if OK; otherwise, nil, "sslnottrusted" is returned.
local function checkRtlCert(sharkObj)
    local url="https://realtimelogic.com"
    local http = require"httpc".create()
    return http:request{trusted=true,url=url,method="HEAD",shark=sharkObj}
end

-- Downloads and returns the content from a given URL
local function downloadFile(url)
    local http=require"httpc".create()
    http:request{method="GET",url=url}
    local data,err=http:read"a"
    if not data then print("Download failed",err) end
    return data
end

print(checkRtlCert()) -- Implicit use of ba.sharkclient(); prints true
print(checkRtlCert(ba.sharkclient())) -- prints true
local store = ba.create.certstore()
-- Empty store
print(checkRtlCert(ba.create.sharkssl(store))) -- Prints nil,"sslnottrusted"

-- RTL's cert signed by Let's Encrypt
local rootCert=downloadFile"https://letsencrypt.org/certs/isrgrootx1.pem"
if rootCert then
    local store = ba.create.certstore()
    store:addcert(rootCert)
    print(checkRtlCert(ba.create.sharkssl(store))) -- Prints true
end

-- DigiCert is a well known CA
local rootCert=downloadFile"https://cacerts.digicert.com/DigiCertGlobalRootCA.crt.pem"
if rootCert then
    local store = ba.create.certstore()
    store:addcert(rootCert)
    -- RTL's cert is not signed by DigiCert
    print(checkRtlCert(ba.create.sharkssl(store))) -- Prints nil,"sslnottrusted"
end

-- Download the "CA Root Certificates bundle" maintained by CURL
local rootCertBundle=downloadFile"https://curl.se/ca/cacert.pem"
if rootCertBundle then
    local store = ba.create.certstore()
    print("Number of CA certs installed:",store:addcert(rootCertBundle))
    print(checkRtlCert(ba.create.sharkssl(store))) -- Prints true
end

See also ba.sharkclient().

ba.create.sharkcert(io, certfile, keyfile[, password])
ba.create.sharkcert(certdata, keydata[, password])

A SharkSSL certificate is a combination of the certificate (the public part) and the certificate key (the private part).

SharkSSL certificate/key object relationships

Parameters

Return values

Throws

Throws for invalid IO-object, pathname, certificate-data, key-data or password argument types, including missing required arguments. Lua allocation errors can propagate. File-loading and reported PEM conversion failures return nil, error.

ba.create.key([{op}])

Create an ECC or RSA key. An ECC key is created if no options are provided. Note that creating RSA keys is extremely time consuming.

Parameters

Return values

Throws

Throws for invalid option field types, an RSA bit size outside 512 through 4096 or not divisible by 256, an unknown or disabled ECC curve, disabled ECC key creation, or insufficient op.rnd bytes. Size, ECC curve and random-input-length checks occur before temporary-buffer allocation. Exceptions from option-table getters and Lua allocation errors can propagate. Reported ECC allocation and generator failures return nil, error. Lua PEM output-allocation errors propagate after temporary native buffers are released.

RSA certificates should use keys of at least 2048 bits. Smaller RSA keys remain accepted for compatibility. This recommendation does not apply to ECC key sizes.

ba.create.csr(privkey, dn [, san], certtype, keyusage [, hashid ])

Generate a Certificate Signing Request (CSR).

Parameters

Certificate-type, key-usage and digest names are case-insensitive.

The certtype values select the Netscape Certificate Type extension:

Netscape Certificate Type and RFC Extended Key Usage are separate certificate extensions. When a deployment requires a specific Extended Key Usage, verify that the issued certificate contains the profile required by the target software.

The keyusage values select the X.509 Key Usage extension:

Keep CA and end-entity purposes in separate profiles. For example, a CA normally uses KEY_CERT_SIGN and optionally CRL_SIGN, while a TLS server normally uses DIGITAL_SIGNATURE plus only the algorithm-specific usages required by the supported TLS modes.

Return values

Throws

Throws for invalid argument or field types, a missing dn.commonname, an unrecognized certificate-type or key-usage option, or an unsupported digest name. These argument checks occur before native key parsing and allocation. Exceptions from table getters and Lua allocation errors can propagate. Reported native operation failures return nil, error. PEM output-buffer allocation failure returns nil, "malloc"; Lua output-allocation errors propagate after temporary native buffers and keys are released.

RSA TLS-server CSR example:
local privkey = ba.create.key{key="rsa", bits=2048}
local certtype = {"SSL_SERVER"}
local keyusage = {"DIGITAL_SIGNATURE", "KEY_ENCIPHERMENT"}
local d = "realtimelogic.com"
local csr=ba.create.csr(
   privkey, {commonname=d}, d..";www."..d,
   certtype, keyusage, "sha256")
print(csr)
ba.create.certificate(csr [, cert], privkey, validfrom, validto, serial [,hashid])

Parameters

Date fields are formatted literally, without calendar normalization or timezone conversion. Use os.date("!*t") when constructing a table for the current UTC time.

Return values

Throws

Throws for invalid argument or date-field types, missing year or month fields, a missing or nonpositive serial number, or an unsupported digest name. Argument checks and date formatting precede native key parsing. Exceptions from table getters and Lua allocation errors can propagate. Reported input-processing and native signing failures return nil, error. PEM output-buffer allocation failure returns nil, "malloc"; Lua output-allocation errors propagate after temporary native buffers are released.

The issued certificate incorporates the supported extension requests from the CSR. An application that signs CSRs from another source should first verify the subject, Subject Alternative Names, certificate type, and key usage against the issuer's certificate policy.

Example:
local csr = ba.create.csr(.......) -- See example above
local validFrom=os.date"!*t"
local validTo=os.date"!*t"
validTo.year = validTo.year + 10
local cert = ba.create.certificate(
   csr, caCert, caKey, validFrom, validTo, 0x000001)
ba.parsecert(asn1)

Parses raw ASN.1 certificate data and returns its fields. Parsing does not establish that a certificate is trusted.

Parameters

Return values

Missing distinguished-name values are represented by empty strings.

Throws

Throws if asn1 is not a string or a value accepted by Lua's string conversion. Lua allocation errors can propagate. A reported native parse failure returns no values.

Example:
local http = require"httpc".create()
local ok,err=http:request{
   url="https://letsencrypt.org/certs/lets-encrypt-e2.pem"}
if ok then
   local cert=http:read"a"
   local t = ba.parsecert(
      ba.b64decode(cert:match".-BEGIN.-\n%s*(.-)\n%s*%-%-"))
   if t then print("Cert info:", ba.json.encode(t)) end
end
ba.parsecerttime(string)

Parameters

Return values

Throws

Throws for a missing argument or a value not accepted by Lua's string conversion. Lua allocation errors can propagate. Unsupported string lengths and reported native conversion failures return zero.

ba.sharkclient()

Parameters

None. Additional arguments are ignored by the built-in function.

Return values

Throws

Throws if the default CA file cannot be opened or read, is empty, or is rejected by the SharkSSL constructor. This is treated as an installation/configuration error. An opened file is closed before a reported read failure or empty result is rejected. Failed initialization is not cached, so a later call retries. Lua allocation errors can propagate. The built-in function does not return nil, error.

These rules describe the built-in implementation. A replacement function, such as the example below, defines its own loading and error behavior.

Trust is the single most important aspect when it comes to establishing an encrypted TLS connection. A TLS client such as HTTPS, SMTP, secure MQTT, etc, cannot establish trusted communication with the server unless the client trusts the server's certificate. Trust can only be established by keeping a copy of the signer of the server's certificate. See the tutorial Introduction to Public Key Infrastructure for details.

This function returns a SharkSSL client object initialized with the certificates in the file vmio/.certificate/cacert.shark.

The following example shows the internal ba.sharkclient() implementation:

-- This code is only executed one time,
-- when the function is initially called.
-- A cached copy is otherwise returned.
local f=ba.openio('vm'):open'.certificate/cacert.shark'
local c=f:read'a'
f:close()
return ba.create.sharkssl(c,{cachesize=40})

The function throws an exception if .certificate/cacert.shark is not found in the VM I/O or if the file is corrupt/invalid. The Barracuda App Server does not automatically include cacert.shark, and you may need to assemble the file yourself. The Mako Server's resource file mako.zip includes a ready-to-use cacert.shark assembled from curl CA certificates. The Mako Server's cacert.shark is large and not suitable for memory-constrained devices. The following example shows how to assemble a cacert.shark that trusts only certificates signed by Let's Encrypt:

curl https://letsencrypt.org/certs/isrgrootx1.pem > cacert.pem
curl https://letsencrypt.org/certs/isrg-root-x2.pem >> cacert.pem
SharkSSLParseCAList -b cacert.shark cacert.pem

The above command line example shows how to create a compact SharkSslCAList object (cacert.shark), which can be created by using the SharkSSLParseCAList command line tool. However, a SharkSSL client object can also be created by using a SharkSSL Certificate Authority Store (ba.create.certstore). The following example shows how to replace the original ba.sharkclient() with your own version.

do -- New scope
   local sharkClient -- closure
   -- replace ba.sharkclient
   function ba.sharkclient()
      if not sharkClient then
         -- Create when function is initially called
         local vmio = ba.openio("vm")
         local sn=".certificate/cacert.pem"
         if not vmio:stat(sn) then
            error("Cannot find "..sn)
         end
         local store = ba.create.certstore()
         store:addcert(vmio,sn)
         sharkClient=ba.create.sharkssl(store)
      end
      return sharkClient
   end
end

Socket API

The TCP and UDP socket API provide easy to use functions for creating client and server TCP connections and for working with UDP sockets. The TCP socket API is integrated with our own SSL stack SharkSSL™, thus making it very easy to create secure custom protocols. The TCP socket API enables interceptions of HTTP client and server connections at any point, making it possible to morph an HTTP(S) connection into an HTTP(S) tunnel, which can be used for (secure) custom protocols that can bypass proxies and firewalls.

Note that UDP socket is not supported on all embedded/RTOS platforms.

The socket API can be used in three modes:

See the section Designing Socket Protocols in Lua for an introduction to the Lua socket API.

The Barracuda socket API is not identical to the standard LuaSocket, but a compatibility layer is included that makes it possible to use code designed for the Lua socket API such as the mail (SMTP) client.

Functions

ba.socket.bind(port [,op])

Creates a TCP listener. Outside a cosocket, activate it with socket:event before accepting clients. Inside an unused or closed cosocket, binds that cosocket and returns the same socket.

Parameters

Return values

Throws

Throws for an invalid port or an invalid/incompatible SharkSSL object. Lua allocation errors may propagate. Listener setup failures return nil, error.

ba.socket.connect(address, port [,op])

Connects a TCP client to a remote host. Outside a cosocket, connection setup is synchronous. Inside an unused or closed cosocket, setup is asynchronous and returns the same socket when connected. TLS trust must be checked using socket:trusted() when required by the application.

Parameters

Return values

Throws

Throws for invalid address, port or timeout arguments, or an invalid/incompatible SharkSSL object. Lua allocation errors may propagate. Connection failures return nil, error, optionally followed by detail.

Examples:

The two examples below connect to Real Time Logic's web server on port 443 (HTTPS/TLS). The first example validates Real Time Logic's certificate using the list of Certificate Authority (CA) certificates that the SharkSSL object returned by ba.sharkclient() is initialized with. The second example creates a new SharkSSL object with an empty certificate store, so the example cannot validate the certificate. See sock:trusted() for more information on the return values.

local s = ba.socket.connect(
  "realtimelogic.com", 443, {shark=ba.sharkclient()})
print(s:trusted()) -- prints: true
print(s:trusted("sharkssl.com")) -- prints: nil	cert
s:close()

local s = ba.socket.connect(
  "realtimelogic.com", 443, {shark=ba.create.sharkssl()})
print(s:trusted()) -- prints: nil	cn
print(s:trusted("sharkssl.com")) -- prints nil	none
s:close()

Using ba.socket.connect() from within a cosocket

A cosocket function (a function started with ba.socket.event) can call ba.socket.connect when the socket is unbound. The following example shows how two cosockets open a client connection, where cosocket2 is started from within cosocket1.

local function cosocket2(sock)
   if ba.socket.connect("google.com",80) then
      trace"cosocket2 connected"
   end
   trace("cosocket2", sock)
end

local function cosocket1(sock)
   -- 'sock' is in state 'not connected'
   local s = ba.socket.connect("google.com",80)
   if s then -- s == sock
      assert(s == sock) -- Will never fail
      -- The following fails since the socket is connected
      -- Prints: nil incorrectuse
      trace(ba.socket.connect("google.com",80))
   end
   trace("cosocket1", sock)
   -- Start cosocket2
   ba.socket.event(cosocket2)
end

-- Start cosocket1
ba.socket.event(cosocket1)
ba.socket.udpcon([address] [,port] [, op])

Creates a UDP socket when datagram support is enabled in the build. Use udpcon(address, port [,op]) for a default remote destination, udpcon(port [,op]) to bind a connectionless socket locally, or udpcon([op]) for a connectionless socket with an automatically selected local port. An empty address also selects connectionless mode, with the following port used locally. Inside an unused or closed cosocket, configures and returns that same socket.

Parameters

Return values

Throws

Throws for invalid port arguments or invalid options consumed by the binding. Lua allocation errors may propagate. Native setup failures return nil, error, optionally followed by detail.

ba.socket.req2sock(request [,raw])

Parameters

Return values

Returns socket, data on success, or nil, error on a reported failure.

Throws

Throws for an invalid or expired request object, a non-boolean raw argument, or a Lua allocation failure. Reported upgrade and native allocation failures return nil, error.

Extracts the active socket connection from the request object. This function, which can only be called from an LSP page or a directory function, makes it possible to morph an HTTP(S) connection into an HTTP(S) tunnel, which can be used for (secure) custom protocols. This function can also upgrade a WebSocket request to a WebSocket connection. The LSP page response object cannot be used after this function is called. Thus if you want to send an HTTP response, the response must be flushed by calling response:flush prior to calling this function. Note: an HTTP response is sent automatically by this function when upgrading a WebSocket request to a WebSocket connection.

This function returns the socket object and any additional data the server received following the HTTP header when upgrading regular HTTP to a socket connection. The second return value is nil if no additional data is available.

This example shows how to convert an HTTP request into a persistent Server-Sent Events (SSE) response. The server-side code creates a timer that sends an event to the client every second. The timer keeps running until the browser closes the connection. To run this example, save the file as index.lsp.

See Socket Design: WebSockets for how to use this function for converting an HTTP request to a persistent WebSocket connection.

<?lsp
if request:header"Accept" == "text/event-stream" then
   trace"New stream event request"
   response:reset()
   response:setcontenttype"text/event-stream"
   response:flush()
   local s = ba.socket.req2sock(request)
   if s then
      local timer = ba.timer(function()
         local cnt=0
         local fmt=string.format
         while true do
            cnt = cnt + 1
            local msg = fmt("data: This is message #%d\n\n",cnt)
            trace(msg)
            if not s:write(msg) or cnt > 9 then
               s:write"data: We are done!\n\n"
               s:close() -- Browser auto reconnects if still on page
               break
            end
            coroutine.yield(true)
         end
      end)
      timer:set(1000)
      s:event(function()
         s:read() -- No data from browser; only socket close
         timer:cancel()
         trace"Event stream closed"
      end, "s")
   end
   response:abort()
end
?>
<html>
<script>
var source = new EventSource(location.href);
source.onmessage = function (event) {
  console.log(event.data);
  document.body.innerHTML=event.data;
};
</script>
<body></body>
</html>
ba.socket.http2sock(httpclient)

Parameters

Return values

Returns socket, data on success, or nil, error on a reported failure.

Throws

Throws for an invalid HTTP client argument, an invalid raw field in a wrapper, or errors raised by wrapper field access. Lua allocation failures can also throw. Reported connection and native WebSocket-state allocation failures return nil, error.

Extracts the active socket connection from an HTTP client object. The function is typically called after sending the request and calling http:status(). This function can also upgrade a WebSocket request to a WebSocket connection.

This function returns the socket object and any additional data the server received following the HTTP header when upgrading regular HTTP to a socket connection. The second return value is nil if no additional data is available.

The following example connects to a web server running on localhost:80, extracts the socket connection from the HTTP client object, and starts an asynchronous coroutine that reads data until end of stream. End of stream is assumed when the receive function receives no data for 2 seconds.

require"http"

local function recData(s)
   local data,err
   data=true
   while data do
      -- s:read returns nil when no data received for 2 seconds
      data,err = s:read(2000)
      trace(data) -- Print response data to trace.
   end
   if err then trace("Read failed", err) end
   -- Socket is closed on return
end

local c = require"httpc".create()
c:request{url="http://localhost",method="GET"}
local s,err=ba.socket.http2sock(c)
if s then
   s:event(recData, "s")
end
ba.socket.toip(address[,ipv6])

Resolves a host name or numeric address to one IP address. DNS lookup is blocking. The binding releases the BAS Lua mutex during resolution and reacquires it before returning, allowing other native threads to run Lua while the lookup waits. This does not make the lookup asynchronous: use a worker thread when resolving names could otherwise block the socket dispatcher, and cache results where appropriate.

Parameters

Return values

Throws

Throws if address cannot be converted to a string, or ipv6 is not a boolean in an IPv6-enabled build. Lua allocation errors can propagate. Resolution failure returns nil/error.

ba.socket.event(function [,args])

Parameters

Return values

Throws

Throws if the first argument is not a function or coroutine/socket allocation fails. Errors raised inside the callback are handled by the cosocket error reporter; they are not rethrown to this caller. The socket is closed when the callback terminates.

Callback

The callback receives socket, args. Its return values are ignored.

Runs a Lua callback function as a cosocket. The provided function (parameter one) is immediately resumed with the first parameter being the socket and subsequent parameters being the optional "args". The cosocket is resumed in the context of the native thread running the Socket Event Dispatcher when resuming from calling ba.socket.connect(), socket:read(), or socket:write(). All socket operations performed in the function will appear to be blocking, but all socket methods are asynchronous behind the scene.

A cosocket typically opens a server connection, binds, and listens or opens a client connection. The function then enters a loop where it waits for data using socket:read(). The function must exit when the socket closes. A cosocket does not have to bind or connect a socket but can stay in the unconnected state; however, the cosocket can only yield by either calling socket:disable() or when blocking when calling socket:write() on another connected socket.

A cosocket can exit when in the unconnected state and must exit when going from state connected to state unconnected. BAS automatically closes the coroutine's own socket when the supplied callback returns or ends with an uncaught error. This includes a connection opened with ba.socket.connect inside that callback. Calling socket:close() just before returning is therefore unnecessary; use it to close the connection earlier. Returning from a nested function does not trigger this cleanup if the callback continues executing. Yielding or disabling the coroutine does not finish it. Other socket objects merely referenced by the callback are not covered by this automatic cleanup.

See the cosockets tutorial for additional information. See also socket:event().

ba.socket.getsock()

Gets the socket associated with the current Lua coroutine. See cosockets and socket:owner().

Parameters

None. Additional arguments are ignored.

Return values

Throws

No argument or connection-state errors. Lua allocation errors can propagate. A missing association returns nil without an error string.

ba.socket.h2n(size,number)

Encodes a number as bytes in network (big-endian) order.

Parameters

Return values

Throws

Throws for invalid argument types or an unsupported size. Numeric strings are accepted. Lua allocation errors can propagate. No operational nil/error pair is returned.

ba.socket.fh2n(size,number)

Encodes a number as bytes in network (big-endian) order.

Parameters

Return values

Throws

Throws for invalid argument types or an unsupported size. Numeric strings are accepted. Lua allocation errors can propagate. No operational nil/error pair is returned.

ba.socket.n2h(size,string [, start])

Decodes bytes in network (big-endian) order.

Parameters

Return values

Throws

Throws for invalid argument types, an unsupported size, or a source range outside the string. Numeric strings are accepted for numeric arguments. Lua allocation errors can propagate. No operational nil/error pair is returned.

ba.socket.fn2h(size,string [, start])

Decodes bytes in network (big-endian) order.

Parameters

Return values

Throws

Throws for invalid argument types, an unsupported size, or a source range outside the string. Numeric strings are accepted for numeric arguments. Lua allocation errors can propagate. No operational nil/error pair is returned.

ba.socket.stat()

Reports socket-object counts for this BAS socket library.

Parameters

None. Additional arguments are ignored.

Return values

Throws

No argument or operational errors are raised by this binding.

Socket Object Methods

Socket objects returned by ba.socket.bind(), ba.socket.connect(), ba.socket.req2sock(), and ba.socket.http2sock() have the following methods associated with them:

TCP: socket:read([timeout])

Reads from the socket. Coroutine sockets yield while waiting and must be read from their owning coroutine. WebSocket data already buffered by an earlier synchronous read remains available after switching to coroutine mode; reading it does not wait for new network traffic. An unconnected, not-closed coroutine socket can use a positive timeout to sleep, returning nil, "timeout". With zero, it waits until explicitly closed.

Parameters

Return values

Throws

Throws for an invalid socket, a listening socket, reading a coroutine socket from the wrong coroutine, or a fractional or out-of-range numeric timeout for the selected mode. A read on an invalid connection returns nil/error the first time; repeating it throws. Invalid UDP addressInfo types can throw in the coroutine path. Lua allocation failure can also throw. Ordinary read failures return nil and an error string.

UDP: socket:read([timeout][,addressInfo])

Reads from the socket. Coroutine sockets yield while waiting and must be read from their owning coroutine. An unconnected, not-closed coroutine socket can use a positive timeout to sleep, returning nil, "timeout". With zero, it waits until explicitly closed.

Parameters

Return values

Throws

Throws for an invalid socket, a listening socket, reading a coroutine socket from the wrong coroutine, or a fractional or out-of-range numeric timeout for the selected mode. A read on an invalid connection returns nil/error the first time; repeating it throws. Invalid UDP addressInfo types can throw in the coroutine path. Lua allocation failure can also throw. Ordinary read failures return nil and an error string.

unconnected:sendto(datagram, ip, port)

Sends one UDP datagram. Requires datagram support in the build. The destination can be a numeric address or a hostname; name resolution can add latency.

Parameters

Return values

Throws

Throws for an invalid socket, a socket that is not UDP, invalid address/port types, or a failing data conversion. Lua allocation failure can also throw. Ordinary resolution and send failures return nil/error. Datagram size and routing constraints depend on the native networking stack.

socket:write(data [,i [,j]] [,utf8])

Writes to TCP, connected UDP, or WebSocket. Blocking writes can wait for output capacity; coroutine writes can yield. Success can mean queued bytes rather than delivery to the peer.

Parameters

Return values

Throws

Throws for an invalid or listening socket, invalid substring indices, failing string conversion, or a selected WebSocket payload larger than 65535 bytes. Lua allocation errors can also throw. Ordinary transport failures return nil/error; asynchronous queue rejection can return nil/queuedBytes. An empty selected range returns false, including for WebSockets.

socket:write(data, utf8)

Writes to TCP, connected UDP, or WebSocket. Blocking writes can wait for output capacity; coroutine writes can yield. Success can mean queued bytes rather than delivery to the peer.

Parameters

Return values

Throws

Throws for an invalid or listening socket, invalid substring indices, failing string conversion, or a selected WebSocket payload larger than 65535 bytes. Lua allocation errors can also throw. Ordinary transport failures return nil/error; asynchronous queue rejection can return nil/queuedBytes. An empty selected range returns false, including for WebSockets.

socket:accept()

Waits for an incoming connection on a listening socket. Call from the listener's own asynchronous coroutine, as configured by ba.socket.bind/socket:event. This call yields until resumed by the accept event.

Parameters

None. Additional arguments are ignored.

Return values

Throws

Throws if the object is not a socket or is not a listening socket, if accept is repeated after the closed-listener error, or if called from a context in which Lua cannot yield. Allocation failures can also throw. The caller must use the listener's own coroutine; the binding does not explicitly validate ownership.

socket:disable()

Disables receive events for an asynchronous/coroutine socket. Called by its own coroutine, it suspends that coroutine. Another coroutine may disable it only while it waits in read without an active timeout. Retain a Lua reference while disabled because the automatic reference is released.

Parameters

None. Additional arguments are ignored.

Return values

Throws

Throws for an invalid socket or a socket without an associated coroutine. Yielding from a prohibited Lua context can throw. State/timer rejection returns nil/error. An unconnected owner coroutine may disable itself; it then needs another part of the application to enable it.

<?lsp
response:setstatus(204)
local sock=ba.socket.event(function(sock)
   -- We assume the connection is successful, thus the return should
   -- be the socket object
   assert(sock == ba.socket.connect("google.com",80))
   while true do
      sock:disable()
      local state,connected = sock:state()
      if not connected then
         trace"Socket closed"
         -- on close housekeeping
         return
      end
      trace"Resuming cosocket"
   end
end)

ba.sleep(2000)
sock:enable()
sock:close()
?>
socket:enable([args])

Re-enables a disabled asynchronous/coroutine socket. If the owner suspended itself in disable(), it is resumed immediately. If another coroutine disabled it during read(), receive events are restored and it waits for data.

Parameters

Return values

Throws

Throws for an invalid socket or a socket without an associated coroutine. Lua allocation failure can also throw. Coroutine execution errors are handled by the socket runtime and reflected by false rather than rethrown by enable().

socket:event(callback [,mode [,args]])

Creates and immediately starts a coroutine for an already bound or connected socket. Subsequent socket events resume it through the dispatcher.

Parameters

Return values

Throws

Throws for an invalid socket object, a non-function callback on a valid connection, invalid mode type, or attempting to reconfigure another coroutine's socket. Lua allocation failure can also throw. Callback execution errors are logged/handled by the socket runtime and reported as nil, "failed" during this initial call. Calling event from the socket's owning coroutine transfers the connection to a new socket object for the new callback and closes the old wrapper; use the socket passed to that callback.

socket:maxsize(size)

Sets the socket send-chunk size used by asynchronous and WebSocket send paths. This is not a maximum application-message or upload size, and it does not change the receive limit.

Parameters

Return values

Throws

For an accepted size, throws if the socket object is invalid. Rejected sizes return false without validating the socket object. The method has no operational nil/error return.

socket:queuelen([len])

Reads or changes the asynchronous send-queue threshold. Available when asynchronous response support is enabled.

Parameters

Return values

Throws

Throws for an invalid socket or a supplied len that cannot be converted to an integer.

For a caller that cannot wait asynchronously, the queue check happens before appending a write. An accepted write can take the queue above the threshold; this is not a strict memory cap. Zero prevents adding queued writes for such callers, but does not prevent an immediate send. Changing the threshold does not discard already queued bytes. See cosocket flow control.

socket:owner()

Checks whether the calling Lua coroutine owns this socket.

Parameters

None. Additional arguments are ignored.

Return values

Throws

Throws for an invalid socket object.

socket:peername()

Queries the remote endpoint address.

Parameters

None. Additional arguments are ignored.

Return values

Throws

Throws for an invalid socket object. Lua allocation failure while constructing results can also throw. Native endpoint-query failures return nil, error.

socket:sockname()

Queries the local endpoint address.

Parameters

None. Additional arguments are ignored.

Return values

Throws

Throws for an invalid socket object. Lua allocation failure while constructing results can also throw. Native endpoint-query failures return nil, error.

socket:websocket()

Checks whether the socket has WebSocket state.

Parameters

None. Additional arguments are ignored.

Return values

Throws

Throws for an invalid socket object.

socket:ping([data])

Sends a WebSocket Ping frame. A successful send does not mean that a Pong has been received.

Parameters

Return values

Throws

Throws for an invalid socket, a socket without WebSocket state, an invalid data type, or a payload longer than 125 bytes. Lua allocation errors and invalid coroutine yields can propagate. Native send failures use return values. The payload limit follows the WebSocket control-frame rules.

socket:setoption("tcp-nodelay", value)

Enables or disables TCP_NODELAY. True disables Nagle buffering. The binding always returns true after invoking the native setter; it does not receive a setter error status.

Parameters

Return values

Throws

Throws for an invalid socket, non-boolean value, invalid option argument types, or an unknown/disabled option name. Lua allocation failure can also throw. Native setting failures return nil, error, code. Unsupported basic or extended keepalive returns nil, "noimplementation"; optional timing values are not examined if basic keepalive is unavailable.

socket:setoption("keepalive", value [,keepidle, keepintv])

Enables or disables TCP keepalive. Idle/interval overrides are used only when both converted values are positive; otherwise the basic keepalive option is used.

Parameters

Return values

Throws

Throws for an invalid socket, non-boolean value, invalid option argument types, or an unknown/disabled option name. Lua allocation failure can also throw. Native setting failures return nil, error, code. Unsupported basic or extended keepalive returns nil, "noimplementation"; optional timing values are not examined if basic keepalive is unavailable.

socket:setoption("dontroute", value)

Requests that outgoing traffic bypass normal routing. Available only with datagram support.

Parameters

Return values

Throws

Throws for an invalid socket, non-boolean value, invalid option argument types, or an unknown/disabled option name. Lua allocation failure can also throw. Native setting failures return nil, error, code. Unsupported basic or extended keepalive returns nil, "noimplementation"; optional timing values are not examined if basic keepalive is unavailable.

socket:setoption("broadcast", value)

Enables or disables permission to send broadcast datagrams. Available only with datagram support.

Parameters

Return values

Throws

Throws for an invalid socket, non-boolean value, invalid option argument types, or an unknown/disabled option name. Lua allocation failure can also throw. Native setting failures return nil, error, code. Unsupported basic or extended keepalive returns nil, "noimplementation"; optional timing values are not examined if basic keepalive is unavailable.

socket:setoption("ip-membership", value, address [,interface])

Joins or leaves a multicast group. Available only with datagram support.

Parameters

Return values

Throws

Throws for an invalid socket, non-boolean value, invalid option argument types, or an unknown/disabled option name. Lua allocation failure can also throw. Native setting failures return nil, error, code. Unsupported basic or extended keepalive returns nil, "noimplementation"; optional timing values are not examined if basic keepalive is unavailable.

socket:state()

Returns coroutine state and native socket validity as separate values.

Parameters

None. Additional arguments are ignored.

Return values

Throws

Throws for an invalid socket object. Lua allocation failure can also throw.

State strings:

For example, an executing coroutine can report "exec", false before it establishes a connection.

socket:close()

Closes the socket and initiates cleanup. For an active WebSocket, first attempts to send a close frame.

Parameters

None. Additional arguments are ignored.

Return values

Throws

Throws for an invalid socket object. The close method ignores a returned WebSocket close-frame send error and does not return a nil/error pair. Errors raised by Lua allocations during callback/cleanup processing can still propagate.

socket:upgrade(shark [,alpn])

Upgrades an established plain TCP connection to TLS, for example after a STARTTLS exchange. Use the owning coroutine for a cosocket; the handshake can yield until it completes.

Parameters

Return values

Throws

Throws for an invalid socket object or missing/non-userdata shark argument. When an upgrade is attempted, also throws if shark is not a SharkSSL context or a server context has no certificate. Lua allocation errors and an invalid attempt to yield can propagate. Native handshake and ALPN-copy failures return nil/error.

local s, err=ba.socket.connect(myaddress, myport)
if not s then return nil, err end
-- Complete the protocol's plain-text STARTTLS exchange here.
local ok, err=s:upgrade(ba.sharkclient())
if not ok then s:close(); return nil, err end
socket:isresumed()

Reports whether the TLS connection resumed a previous session.

Parameters

None. Additional arguments are ignored.

Return values

Throws

Throws for an invalid socket object. A plain socket is reported through return values. Lua allocation errors can propagate.

socket:certificate()

Returns the peer certificate information retained by TLS. The chain uses linked parent tables, not an array. Use trusted() for the combined chain, name, and date checks.

Parameters

None. Additional arguments are ignored.

Return values

Each certificate table contains:

Throws

Throws for an invalid socket object. A plain socket is reported through return values. Lua allocation errors can propagate.

socket:cipher()

Returns the negotiated cipher suite and TLS version.

Parameters

None. Additional arguments are ignored.

Return values

Throws

Throws for an invalid socket object. A plain socket is reported through return values. Lua allocation errors can propagate.

Cipher suites:
socket:trusted([host-name])

Checks the retained peer certificate chain, expected name, and dates. Name matching uses the certificate's common name and subject-alternative-name data through the native matcher.

Parameters

Return values

The native date check rejects expired or unparseable dates. It intentionally allows a certificate to start up to 24 hours after the current clock time to accommodate time-zone uncertainty. It checks the peer certificate and intermediate certificates, skipping the final certificate when it is a parent of another certificate. Date-check support and an accurate device clock are required for a true result.

Throws

Throws for an invalid socket object or a host-name argument that cannot be converted to a string. Failed certificate checks and a plain socket use return values. Lua allocation errors can propagate.

See ba.socket.connect() for example code.

Lua Thread Library

The Lua Thread Library allows Lua code to schedule work on native threads when a task may block for a long time in C code. Its main purpose is to protect the BAS socket dispatcher and the rest of the Lua event system from being stalled by long-running blocking operations.

Using native threads here does not make Lua itself preemptive. BAS still serializes Lua execution through its Lua mutex, and bindings must be written so they release that mutex while performing lengthy native work. All BAS bindings that are expected to block for some time, including the SQLite bindings and blocking socket calls, are designed for this model. When possible, prefer cosockets, which often remove the need for a native thread entirely.

Let's revisit the diagram from section Thread Mapping and Coroutines.

BAS event container and thread interaction overview

The Lua Thread Library is shown in the bottom-right corner of the diagram above. The thread library is typically used when running Lua code that is not part of a typical web-server request/response. For example, you can design code to run at regular intervals using a timer and trigger a function that runs for a prolonged time using the thread library.

ba.thread.run(function)
thread:run(function)

Queues a function for execution on a native thread. ba.thread.run() uses the shared pool; thread:run() uses the dedicated thread returned by ba.thread.create(). Jobs are taken from each queue in FIFO order. Multiple shared-pool workers can finish jobs in a different order.

Parameters

Return values

Throws

Throws if the last argument is not a function, or thread:run() has an invalid receiver. Lua allocation errors can propagate. Job-storage allocation failure returns nil/error. A later callback error is passed to the BAS error handler and is not thrown back to the caller of run().

The callback has no execution-time limit. Capture required values in its closure. Retain a dedicated thread object while its jobs are needed; see its lifetime rules. See also Lua Socket Example 3.b.

ba.thread.configure([maxthreads])

Reads or increases the shared pool's thread count. Additional native threads are created immediately when the count increases. This function is available only when the host enables dynamic thread creation.

Parameters

Return values

Throws

Throws when maxthreads is supplied but cannot be converted to a number. Lua allocation errors can propagate when adding threads. Failure to create a native thread is fatal and invokes the BAS fatal-error handler on Windows and POSIX. It is not a Lua exception and cannot be caught with pcall(). Semaphore creation follows the platform's failure handling.

ba.thread.create()

Creates a userdata object with run() and dbg() methods and one dedicated native thread. The native thread is created immediately; run() submits work to it. Jobs execute one at a time on this thread. This function is available only when the host enables dynamic thread creation.

Parameters

None. Additional arguments are ignored.

Return values

Throws

There are no checked input arguments. Throws on failure to allocate the manager or required Lua objects. Failure to create a native thread is fatal and invokes the BAS fatal-error handler on Windows and POSIX. It is not a Lua exception and cannot be caught with pcall(). Semaphore creation follows the platform's failure handling.

When the object is collected, its worker is requested to terminate. Pending jobs can be discarded, so queueing a callback does not replace the need to retain the thread object.

local function myThreadFunc()
   for i=1,10 do
      print("Hello")
      ba.sleep(1000)
   end
end

local thread=ba.thread.create()
thread:run(myThreadFunc)
ba.thread.dbg()
thread:dbg()

Returns Lua thread states for inspecting worker stacks, for example with debug.traceback(). ba.thread.dbg() inspects the shared pool; thread:dbg() inspects that dedicated manager. BAS serializes Lua execution, so this is a snapshot taken while other workers are outside Lua or waiting for the mutex.

Parameters

None. Additional arguments are ignored.

Return values

Throws

thread:dbg() throws for an invalid receiver. Lua allocation errors can propagate. These functions do not return an operational nil/error pair.

local function mysleep() ba.sleep(500) end
ba.thread.run(function() mysleep() end)
ba.sleep(2) -- Yield and start above thread
for _,thread in ipairs(ba.thread.dbg()) do
    print(debug.traceback(thread))
end

Software Implemented Trusted Platform Module API

The Software Implemented Trusted Platform Module (softTPM) API provides BAS Lua with a protected key-management layer for ECC/X.509 operations, persistent symmetric keys, and TPM-protected user databases.

The main purpose of the softTPM is to prevent device cloning and protect secrets at rest. Instead of exposing private keys and sensitive password material directly to the application, the TPM API keeps the key material inside a protected software boundary and exposes handles that Lua code can use to request operations such as signing, CSR creation, or key derivation.

The API is available in products such as standalone Xedge and Mako Server. For initialization details and deployment guidance, see How the TPM Works.

A TPM handle is a string name that refers to a key without revealing the key itself. This means BAS Lua code can ask the TPM to use a key without extracting that key into normal Lua memory. That is the central security difference between the TPM API and the non-TPM crypto APIs. The protection is aimed at resisting firmware or filesystem cloning; it does not prevent trusted Lua code on the same device from requesting operations that use the handle.

Another important behavior is that TPM keys are recreated by name after restart rather than restored from an exported private-key blob. Calling ba.tpm.createkey() for the same handle recreates the same protected key material, which lets an application re-establish its identity after reboot without ever storing the private key in plaintext. The following example shows the typical pattern used before calling the other TPM APIs.

if not ba.tpm.haskey"mykey" then
    ba.tpm.createkey"mykey"
end

The caller must create the named ECC key before using it. On both Mako and Xedge, signing, reading key parameters, creating a CSR, and converting a certificate throw if the key is missing. TPM certificate creation also throws for a missing key. These operations do not create keys implicitly. Repeat the check and creation after restarting, using the original key name and options.

TPM Examples

TPM API

ba.tpm.createkey(keyname, [{op}])

Creates a named ECC key and keeps its private key inside the TPM wrapper. Recreate keys after restart using the original name, options and TPM initialization inputs.

Parameters

Return values

Throws

Throws if the name already exists, op.key is not "ecc", or the options are invalid for ba.create.key(). Derivation and Lua allocation errors can propagate.

ba.tpm.haskey(keyname)

Checks whether a named ECC key exists in the current runtime.

Parameters

Return values

Throws

Does not throw for a missing key.

ba.tpm.createcsr(keyname, dn [, san], certtype, keyusage [, hashid ])

Generates a Certificate Signing Request (CSR) using the named ECC key.

Parameters

Certificate-type, key-usage and digest names are case-insensitive.

The certtype values select the Netscape Certificate Type extension:

Netscape Certificate Type and RFC Extended Key Usage are separate certificate extensions. When a deployment requires a specific Extended Key Usage, verify that the issued certificate contains the profile required by the target software.

The keyusage values select the X.509 Key Usage extension:

Keep CA and end-entity purposes in separate profiles. For example, a CA normally uses KEY_CERT_SIGN and optionally CRL_SIGN, while a TLS server normally uses DIGITAL_SIGNATURE plus only the algorithm-specific usages required by the supported TLS modes.

Return values

Throws

Throws if the named key is missing. Throws for invalid argument or field types, a missing dn.commonname, an unrecognized certificate-type or key-usage option, or an unsupported digest name. These argument checks occur before native key parsing and allocation. Exceptions from table getters and Lua allocation errors can propagate. Reported native operation failures return nil, error. PEM output-buffer allocation failure returns nil, "malloc"; Lua output-allocation errors propagate after temporary native buffers and keys are released.

ba.tpm.createcertificate(keyname, csr [, cert], validfrom, validto, serial [, hashid])

Creates a self-signed certificate or signs a CSR using the named ECC key. Available in Mako and standalone Xedge. Use the CSR's key for a self-signed certificate, or the issuer's key when supplying cert.

Parameters

Date fields are formatted literally, without calendar normalization or timezone conversion. Use os.date("!*t") when constructing a table for the current UTC time.

Return values

Throws

Throws if the named key is missing. Throws for invalid argument or date-field types, missing year or month fields, a missing or nonpositive serial number, or an unsupported digest name. Argument checks and date formatting precede native key parsing. Exceptions from table getters and Lua allocation errors can propagate. Reported input-processing and native signing failures return nil, error. PEM output-buffer allocation failure returns nil, "malloc"; Lua output-allocation errors propagate after temporary native buffers are released.

The issued certificate incorporates the supported extension requests from the CSR. An application that signs CSRs from another source should first verify the subject, Subject Alternative Names, certificate type, and key usage against the issuer's certificate policy.

ba.tpm.sign(hash, keyname [,op])

Signs a precomputed digest with the named ECC private key.

Parameters

Return values

Throws

Throws if the named key is missing or the forwarded arguments are invalid for ba.crypto.sign(). Lua allocation errors can propagate. Reported signing failures return nil, error.

ba.tpm.jwtsign(payload, keyname, header)

Signs a JWT using the named ECC key. The payload is the first argument.

Parameters

Return values

Caller requirement: Always pass header.alg with the matching ECDSA algorithm. The wrapper does not validate this requirement. Omitting header selects the underlying JWT module's HS256 default; in the current implementation this uses an empty HMAC key and bypasses the named TPM key. Do not use that result for authentication.

Throws

Throws if the ECC signing callback cannot find the named key, or if the payload or header is invalid for jwt.sign(). JSON-encoding and Lua allocation errors can propagate. Reported signing failures return nil, error.

ba.tpm.keyparams(keyname)

Returns the public coordinates of the named ECC key.

Parameters

Return values

Throws

Throws if the named key is missing. Lua allocation errors can propagate. Reported extraction failures return nil, "failed". See ba.crypto.keyparams().

ba.tpm.sharkcert(keyname, certdata)

Combines certificate contents with the named ECC private key.

Parameters

Return values

Throws

Throws if the named key is missing or certdata has an invalid type. Lua allocation errors can propagate. Reported conversion failures return nil, error.

ba.tpm.globalkey(keyname,keylen)
ba.tpm.uniquekey(keyname,keylen)

Derives binary key material from the supplied name and a TPM master key. These functions do not look up or create a named ECC key; ba.tpm.createkey() is not required.

globalkey() reproduces the same bytes when the name, requested length, global master key and selected digest are the same. Devices must share the same product secret initialization for the global master key to match. A matching name alone is insufficient.

uniquekey() uses the device master key. Results differ between devices when initialization supplies distinct device-specific input. Repeating the call after restart reproduces the result when the initialization inputs and selected digest remain unchanged.

Parameters

Return values

Throws

Throws for missing or invalid argument types, or an unavailable digest. Lua allocation errors can propagate. This wrapper forwards the underlying ba.crypto.PBKDF2() behavior.

Implementation note: These functions inherit the pending native PBKDF2 issue for requests longer than one digest. See the PBKDF2 implementation note. No native correction has been made as part of this documentation update.

Example:
The globalkey and uniquekey() functions are commonly used for symmetric encryption. The example below, taken from the CryptoIO module, demonstrates how a device-specific key can be used for AES-GCM file encryption of stored data. A hash function is applied to normalize the key length to 32 bytes, as required by ba.crypto.symmetric().

local keyname="qwerty"
local key = ba.crypto.hash("sha256")(ba.tpm.uniquekey(keyname, 32))(true)
local s = ba.crypto.symmetric("GCM", key, iv)
ba.tpm.jsonuser(keyname, global)

TPM protected user database: This function returns a TPM-protected wrapper around the object created by ba.create.jsonuser(). Use it when you want the convenience of the JSON user-database API, but do not want the resulting database to be stored as plaintext or with an application-managed encryption key.

Parameters

Return values

Throws

Throws if keyname cannot be used to derive the key. Derivation and Lua allocation errors can propagate.

database.setuser(name [,pwd])

Adds, replaces or deletes a user in memory. Persist the returned encrypted bytes to save the change.

Parameters

Return values

Throws

Throws if the underlying user database rejects the supplied records. JSON encoding, encryption and Lua allocation errors can propagate.

database.setdb(encrypteddb)

Decrypts and imports a previously saved database.

Parameters

Return values

Throws

Invalid argument types can throw. Decryption/JSON-decoding exceptions within the decode operation are caught and reported as nil, "Data corrupt". Errors outside that protected operation can propagate.

database.getauth()

Returns the authenticator database managed by this wrapper.

Parameters

None.

Return values

Throws

Does not throw during normal use.

database.users()

Lists the user names currently held by the wrapper.

Parameters

None.

Return values

Throws

Lua allocation errors can propagate.

Example:
local io = ba.openio"home" or ba.openio"disk" -- mako or xedge
local rw=require"rwfile"

-- Read/write encrypted db. Write if 'encdb' provided
local function rwdb(encdb)
   trace(encdb and "Writing" or "Reading","userdb.encrypted")
   return rw.file(io,"userdb.encrypted",encdb)
end

-- Create the wrapper and make the encrypted DB global
local tju=ba.tpm.jsonuser("myhandle",true)
local encdb=rwdb() -- Load the encrypted DB, if any
if not encdb then
   trace"No DB, creating and saving 3 test users"
   tju.setuser("john",{pwd="qwerty",roles={}})
   -- Example using HA1 hashed pwd
   tju.setuser("alice",{pwd={ba.crypto.hash"md5"("alice")":"(realm)":"(password)(true,"hex")}},roles={}})
   -- Add the last user and persistently save the database by sending the return value to rwdb()
   rwdb(tju.setuser("bob","123456"))
else
   -- Set the DB
   local ok,err=tju.setdb(encdb)
   if not ok then trace("User DB error:",err) end
end

-- A dir we will attach an authenticator to
local privdir=ba.create.dir"private"
-- Create the authenticator and use the user DB provided by the wrapper
local authenticator=ba.create.authenticator(tju.getauth(),{type="basic"})
privdir:setauth(authenticator)
-- 'dir' is a Barracuda Resource Reader created for LSP apps by Mako and Xedge
dir:insertprolog(privdir,true) -- Protects anything in the sub-dir private/
trace("Authenticator installed @ "..dir:baseuri().."private/")

The example starts by creating an IO object for storing the encrypted user database. When using the Mako Server, the current directory is used; for Xedge, "disk" is used.

The rwdb function reads from or writes to an encrypted database using the rwfile module. If no database is found, the example creates a test user database. In a real-world scenario, user management would typically be handled through a web interface, with the authenticator installed only if a user database is available. The Light Dashboard Example provides a web interface for managing a TPM-protected user database.

The example then creates a privdir instance, installs the authenticator on this directory, and links it to the main directory of the LSP application. This setup demonstrates protecting only a subset of the web application, leaving room for a public and a protected section, which is a common pattern in web applications.

You can test this example using either the Mako Server or Xedge. To do so, create an application and add a .preload file in the app root directory. Insert the provided code into the .preload script, then run the application.

TraceLogger

TraceLogger

TraceLogger is a browser-based viewer for the BAS trace buffer. It lets you decide what the trace buffer records and then watch the resulting output in real time from a web page. The default web interface URI is /rtl/tracelogger/, where the client-side JavaScript opens a persistent connection to ../tracelogger.service/. Output from trace() and _G.print() also flows into this buffer. TraceLogger is optional and must be installed by the C startup code; the Mako Server includes it by default.

Tutorial:

The tutorial Logging for Testing and Production Mode includes recommendations for using the TraceLogger, including an option that enables a more permanent option for receiving and saving TraceLogger data. When using the Mako Server, the TraceLogger data is saved in mako.log (Linux: /var/log/mako.log).

API:

local dir=ba.create.tracelogger([name])

Create a TraceLogger directory object. The returned object exposes the TraceLogger web service used by the browser client in index.html.

Example code: study the Mako Server's .config script.

Note: the TraceLogger directory object keeps an internal self-reference and will not be garbage collected until dir:close() is called. Because it is wired to the global trace buffer, only one TraceLogger instance can exist at a time.

Parameters

Return values

Throws

For a new instance, throws if name cannot be converted to a string and is not nil. Lua allocation errors can propagate. Native thread/synchronization creation follows the platform's fatal-error policy and is not a catchable Lua exception. No operational nil/error pair is returned.

The created directory object adds the following TraceLogger-specific method:

dir:configure([op])

Reads or updates the server's trace settings. Omit op to read them. When setting options, missing fields retain their current settings rather than resetting to startup defaults.

Parameters

  • table (optional) op - Options to update. Explicit nil is not accepted. The function fills missing or unsupported field values in this table with their current settings.
  • boolean (optional) op.request - Trace HTTP request methods and URLs.
  • boolean (optional) op.requestheaders - Trace HTTP request headers.
  • boolean (optional) op.responseheaders - Trace HTTP response headers.
  • boolean (optional) op.responsebody - Trace response data generated by dynamic pages such as LSP and CSP.
  • boolean (optional) op.http11state - Trace the internal HTTP/1.1 state machine. For all boolean fields, a nonboolean value retains the current setting and is replaced in op.
  • number or numeric string (optional) op.priority - Trace level from 0 through 255. Messages whose priority is no greater than this level pass the filter. Fractional values within the range are truncated to an integer. Missing or nonnumeric values retain the current level and are replaced in op. The native startup level is 5.

Return values

  • table settings - A new table of current settings when called without op; otherwise the supplied table, with omitted/unsupported fields filled in. Supplied numeric strings or fractional numbers remain unchanged in that table; a subsequent read reports the effective integer level.

A closed/inactive TraceLogger instance returns no values.

Throws

Throws for an invalid TraceLogger object, a supplied op that is not a table, or a numeric priority outside 0 through 255, including NaN and infinities. An invalid priority leaves the server's settings unchanged, although default fields may already have been filled in op. Table metamethod errors and Lua allocation errors can propagate. No operational nil/error pair is returned.

dir:syslog([op])

Enable, disable, or query RFC 5424 syslog forwarding over UDP. This method is available when the BAS C code is compiled with USE_SYSLOG=1.

Parameters

  • table, string, or boolean (optional) op - Omit to query whether forwarding is enabled. False disables forwarding. A table configures the fields below. A string selects the positional form dir:syslog(address [, port [, ipv6 [, priority]]]). Explicit nil does not query; it returns nil, "address required".
  • string address / op.address - Nonempty collector hostname or IP address. Numeric values are converted to strings. Missing, empty, or unsupported values return nil, "address required", without changing the current forwarding state.
  • integer (optional) port / op.port - Collector UDP port. Values at or below zero select 514; values above 65535 are clamped to 65535. Numeric strings are accepted. The table form defaults to 514. Omitted positional values retain the last successfully configured port, initially 514.
  • boolean (optional) ipv6 / op.ipv6 - True selects IPv6. The table form defaults to false. Omitted positional values retain the last successfully configured address family, initially false.
  • integer (optional) priority / op.priority - Syslog PRI value encoding facility and severity, clamped to 0 through 191. Numeric strings are accepted. The table form defaults to 134 (local0, informational). Omitted positional values retain the last successfully configured value, initially 134.

In the positional form, nonnumeric port/priority arguments and nonboolean ipv6 arguments are ignored. The table form checks these field types. Disabling forwarding preserves the last successfully configured options.

Return values

  • boolean or nil status - For a query, true if enabled or false if disabled. For disable, always true. For setup, true confirms local UDP socket setup; nil indicates a missing address or setup failure.
  • string (on failure) error - "address required", a native connection error description, or "Cannot open syslog UDP socket".

A closed/inactive TraceLogger instance returns no values. A setup attempt closes the previous syslog connection before opening the new one, so an open failure leaves forwarding disabled. A later send failure also disables forwarding; it is not returned to the earlier setup call. Each datagram is limited to 1472 bytes including its syslog header; excess message bytes are omitted.

Throws

Throws for an invalid TraceLogger object, table port/priority fields that cannot be converted to Lua integers, or a nonboolean table ipv6 field. Table metamethod errors and Lua allocation errors can propagate. Missing addresses and UDP setup failures return nil, error.

Calling dir:syslog() without arguments returns true if syslog forwarding is enabled, otherwise false.

Calling dir:syslog(false) disables syslog forwarding and returns true.

Syslog forwarding can be enabled by passing a table or by passing the address and optional settings as arguments:

local ok,err = dir:syslog{
   address = "127.0.0.1",
   port = 514,
   ipv6 = false,
   priority = 134
}

local ok,err = dir:syslog("127.0.0.1", 514, false, 134)

The priority value is the syslog PRI value. The default is 134, which is facility local0 and severity informational. The method returns true on success or nil,error if the UDP syslog connection cannot be opened.

dir:ontrace([callback])

Installs a callback for trace output. Trace chunks are queued for the Lua thread manager; each callback receives one string. Delivery is not guaranteed: chunks are skipped when eight callback jobs are already pending or job allocation fails.

Parameters

  • function (optional) callback - Called asynchronously as callback(msg). Replaces the previous callback. Omitted or nil uninstalls it. Callback return values are ignored.
  • string (callback argument) msg - Trace output chunk. A chunk need not correspond to one complete line.

Return values

  • boolean changed - True when a callback is installed, replaced, or removed. False when uninstalling an already absent callback.

Throws

Throws for an invalid directory, a closed/inactive instance, an unavailable Lua thread manager, or a callback that is neither a function nor nil. Lua allocation errors can propagate. Callback errors go through the thread manager's error handler rather than propagating to this registration call. Avoiding callback loops remains the programmer's responsibility, as described below.

This method is designed for non-production systems and specialized development tools that need direct access to TraceLogger output.

dir:ontrace(function(msg) -- Install
   -- Store the message in a buffer or forward it to another subsystem.
   -- Do not call trace() or print() from this callback.
   myTraceBuffer[#myTraceBuffer + 1] = msg
end)
dir:ontrace() -- uninstall

Warning: use this callback with extreme caution. Errors raised by the callback, or trace output produced by the callback, are written to the trace buffer and can recursively trigger the callback again, creating an endless loop of error messages. Keep the callback small, defensive, and avoid using trace() or print() from inside the callback.

dir:onclient([callback])

Install a callback that gets called when the TraceLogger browser client connects or disconnects.

The callback receives one or two arguments. The first argument is 0 when no client is connected and 1 when a client is connected. When a client is connected and authenticated, the second argument is the session ID. The session object can be retrieved by calling ba.session(sessionId).

Parameters

  • function (optional) callback - Called asynchronously as callback(connected [, sessionId]). Replaces the previous callback; omitted or nil uninstalls it. Callback return values are ignored.
  • integer (callback argument) connected - 1 for a connected browser client, or 0 for a disconnect notification.
  • integer (optional callback argument) sessionId - Session identifier when supplied with the connection notification; otherwise absent.

Return values

  • boolean changed - True when a callback is installed, replaced, or removed. False when uninstalling an already absent callback.

Throws

Throws for an invalid directory, a closed/inactive instance, an unavailable Lua thread manager, or a callback that is neither a function nor nil. Lua allocation errors can propagate. Callback errors go through the thread manager's error handler rather than propagating to this registration call.

dir:onclient(function(connected, sessionId) -- install
   if connected == 1 and sessionId then
      local session = ba.session(sessionId)
      -- Use the session object as needed.
   end
end)
dir:onclient() -- uninstall
dir:close()

Shuts down all active connections, removes the directory from the virtual file system (if installed), and releases the reference so the garbage collector can remove the directory object.

Parameters

None.

Return values

None.

Throws

Throws for an invalid TraceLogger directory object. Calling close again on an already closed instance has no effect. The call waits for its native worker thread to exit; it does not return an operational error pair.