Note: SOAP (Simple Object Access Protocol) is an older web service protocol that uses XML for structured messaging. While it's no longer the preferred approach for most modern web applications, SOAP is still used in certain enterprise and legacy systems. We include support and documentation to assist those who need to maintain or integrate with existing SOAP-based services.
Barracuda SOAP services are defined and written in Lua, in a simple text format.
These service definitions would normally be embedded in your server, but can be loaded dynamically or uploaded remotely if required, without rebuilding the server.
Barracuda dynamically generates and publishes the appropriate WSDL (Web Service Description Language) document from your installed service definitions. This means that the WSDL always reflects the currently installed services, so there is no possibility of a mismatch between the service implementation and its WSDL.
The following is all that is required to define a SOAP service called Info, with an operation called Date that returns the server's date:
soap_services = {
Info = {
Date = {
output = {name = "return", type = "date"},
call = function() return os.date("!%Y-%m-%d") end,
},
}
}
Note the type="date" declaration. The WSDL uses the declared XML Schema type. The Lua conversions are listed below; the SOAP module does not perform full XML Schema validation.
Here is a VBScript program that accesses the service defined above, assuming that Barracuda SOAP is configured to publish SOAP services on localhost/soap:
dim SOAPClient
set SOAPClient = createobject("MSSOAP.SOAPClient")
SOAPClient.mssoapinit("http://localhost/soap/info.wsdl")
wscript.echo "SOAP Server date is :", SOAPClient.Date()
Barracuda SOAP services are defined as a Lua table called soap_services. This table defines one or more services; each service can support any number of operations.
An operation is a single remote procedure call (rpc).
Each named element in the soap_services table is a SOAP service definition, and must
contain a list of operations supported by this service.
note: SOAP service, operation and parameter names
must be valid XML names, as per the W3C XML
spec.
Each operation is a table with the following defined members : input, output, call, and lifetime
input; results are returned in the order listed by output.
The parameter definition's name names the corresponding XML value.
Lua values follow the conversion table below. Arrays use sequence tables;
structures use tables with fields named by their member definitions. An omitted
type defaults to string. With no input, the callback
receives no arguments; with no output, return no values on success.nil, error, where error is a string, to report an
operation failure without raising a Lua error.| Declared type | Lua type | Description |
|---|---|---|
| integer | number | Input uses Lua numeric conversion; output uses integer formatting. Supply whole numbers within the Lua integer range. |
| decimal, float, double | number | Use Lua numbers. Decimal values therefore have Lua numeric precision, not arbitrary decimal precision. |
| boolean | boolean | XML input accepts true, false, 1 and 0. XML output uses 1 or 0. |
| string, or omitted type | string | XML text and CDATA are combined in document order without inserted spaces. Whitespace-only values are preserved. Output text is XML-escaped. Empty text is an empty Lua string. |
| Other simple types | string | Passed as XML text, including date/time, base64Binary, hexBinary and derived numeric types such as int and unsignedInt. Supply the type's XML representation; these types are not automatically decoded to binary or converted to Lua numbers. |
| Simple type followed by Array | table | A sequence starting at index 1, with each element converted using the simple type. Empty sequences are supported, including array members of structures. |
| Structure definition table | table | Fields use the declared member names and types. Input also provides the members at sequential numeric indices. Nested structures are not supported. |
Note: two dashes (--) indicate a comment in Lua; strings are enclosed in quotes (" "); tables are enclosed in braces ({ }); and table entries are separated by commas (,). Trailing commas are permitted. For the full Lua syntax, see the Lua documentation.
-- File: soap/.services/date.lua
soap_services = {
Info = {
HTTPVersion = { -- returns the HTTP request version as a string.
output = {name = "return",type = "string"},
lifetime = 0, -- never cache
call = function() return request:version() end,
},
Header = { -- returns a string with the value of the named HTTP header.
input = {name = "name",type = "string"},
output = {name = "value",type = "string"},
lifetime = 0, -- never cache
-- return empty string if no header
call = function(s) return request:header(s) or "" end,
},
Date = { -- returns the current server date, in UTC
lifetime = 5, -- date is valid for 5 seconds
output = {name = "return",type = "date"},
call = function() return os.date("!%Y-%m-%d") end,
},
}
}
-- File: soap/.services/math.lua
soap_services = {
Math = { -- service definition
Add = { -- operation
input = {{name = "x", type = "double"},{name = "y", type = "double"}},
output = {name = "return",type = "double"},
call = function(x,y) return x+y end
},
Subtract = { -- operation
input = {{name = "x", type = "double"},{name = "y", type = "double"}},
output = {name = "return",type = "double"},
call = function(x,y) return x-y end
},
Multiply = { -- operation
input = {{name = "x", type = "double"},{name = "y", type = "double"}},
output = {name = "return",type = "double"},
call = function(x,y) return x*y end
},
Divide = { -- operation
input = {{name = "x", type = "double"},{name = "y", type = "double"}},
output = {name = "return",type = "double"},
call = function(x,y) return x/y end
},
Sum = { -- operation to sum an array of doubles
input = {name = "t", type = "doubleArray"},
output = {name = "return",type = "double"},
call = function(t)
local tot = 0
for i,v in ipairs(t) do tot = tot+v end
return tot
end
},
RandomInt = { -- range must be a nonnegative integer
input = {name = "range", type = "integer"},
output = {name = "return",type = "integer"},
call = function(range) return math.random(0,range) end,
},
} -- end of Math service definition
} -- end of soap_services definition
SOAP is implemented in Barracuda via a directory object, constructed
by calling ba.create.soapdir(). The directory object can then be attached
to the directory tree like any other Barracuda directory, with the same
semantics and address resolution rules.
Parameters
ba.openio("vm"). A loader function is called as serviceloader(env) and returns a function that, when executed, sets env.soap_services. Set the returned function's environment to env, as shown below. The filename form loads the file in this environment automatically.lifetime is used, or 5 seconds if neither is set. Error responses use zero.Loader callback
soap_shared field is a table shared by this directory's requests; print is the trace function and response is false.env. The chunk sets soap_services to a table; its return values are ignored.Both forms defer loading until a request needs the service definitions. A cached WSDL response does not reload them. RPC requests reload them on each request. Use soap_shared to retain service state between requests. For a file in an application's own IO rather than the VM resource IO, use the loader function form.
require"basoap" -- Install the SOAP constructor.
-- services/math.lua must exist in ba.openio("vm").
local mathdir = ba.create.soapdir("math", "services/math.lua")
dir:insert(mathdir, true) -- Attach to the application's directory.
local function serviceDefinitionLoader()
-- io is the application's IO
return function(env)
local fnsource,err = io:loadfile("math.lua",env)
if not fnsource then error("failed to load SOAP services: "..err) end
return fnsource
end
end
Return values
userspace field is the table exposed to service definitions as soap_shared.Throws
Throws for an invalid directory name or loader argument. Construction does not load or execute the service definitions, so it does not report missing files or file syntax errors. These failures occur later while handling a request: the filename loader raises a load error, which the server's request error handler reports. A custom loader may also raise an error. Errors raised by the service definition chunk, or a missing or non-table soap_services value, are reported as HTTP 500 responses.
For each defined service, Barracuda publishes a WSDL file at name/service name and name/service name.wsdl.
SOAP requests are sent to name/service name.rpc.
name is relative to the parent directory object.
SOAP message format: The generated WSDL uses SOAP 1.1 RPC/literal
messages. The operation element must use the service namespace published in
the WSDL's soap:body namespace attribute. Either a prefix or a
default namespace may identify the operation; its parameter elements are
unqualified. Responses use the same namespace and the operation name followed
by Response, for example AddResponse. Unqualified
operations and operations in another namespace are rejected with a SOAP fault.
Use the current WSDL when configuring or generating a client.
Malformed envelopes, empty bodies and missing required input values are reported as SOAP faults. Errors raised by service callbacks, including non-string Lua error values, are converted to fault messages.
To implement the sample services above, create a text file with the
service definitions as described above.
Then, in the Barracuda config file, create a SOAP service directory using
ba.create.soapdir("directory name", service-definition-loader),
and attach it to the directory tree like any other Barracuda http dir.
ba.create.soapdir() creates a SOAP service directory. The service definitions
are loaded when a request needs them, rather than during construction.
The SOAP directory will have an entry for each service defined in the service configuration file.
In the case of this sample config, and assuming that the directory name was "soap",
Barracuda will publish a WSDL file at "soap/Math" and "soap/Math.wsdl",
and the address for soap requests will be at "soap/Math.rpc".
note :Barracuda SOAP rpc and wsdl requests are handled the same regardless
of case, ie soap/Math and soap/math will return the same reponse.
You can test for the existence of the SOAP service by pointing a web browser at the WSDL URL.
The following complete example loads the two service definition files above, "date.lua" and "math.lua", creates two soap directories, and installs the directories in the server:
-- The following code creates the two SOAP services from our documentation:
-- https://realtimelogic.com/ba/doc/en/lua/soap.html
require"basoap" -- Install ba.create.soapdir
-- The applications I/O: We set the io as local variable since the
-- variable is used as an upvalue by function serviceloader
local io=io
--Lua SOAP service loader
local function serviceloader(fname)
-- io is the applications IO
return function(env) -- Create Closure
local fnsource,err = io:loadfile(fname,env)
if not fnsource then error("failed to load soap services : "..err) end
return fnsource
end
end
local soapdir=ba.create.dir"soap"
local datedir=ba.create.soapdir("date",serviceloader"soap/.services/date.lua")
local mathdir=ba.create.soapdir("math",serviceloader"soap/.services/math.lua")
-- Install SOAP services in the Barracuda virtual file system
dir:insert(soapdir, true)
soapdir:insert(datedir, true) -- ./soap/date/
soapdir:insert(mathdir, true) -- ./soap/math/
Excel, MSWord, VB Script and .NET are copyright(C) Microsoft Corporation.