Introduction
Where to Store Your API Hook Module
Associating an API Hook with a Resource
Encryption
Implement a new encryption algorithm.
Encoding
Implement a new encoding algorithm for packing data.
TraceLog
Store trace information where and how you want to.PowerWeb Server API Reference
ServerInitialisation
Perform initialisation tasks when the server starts up.
ServerTermination
Perform termination tasks when the server shuts down.
ResourceNameTranslation
Translates a virtual URL into a physical filename.
ErrorLog
Store error log information where and how you want to.
AuditLog
Store audit trail information where and how you want to.PowerWeb Resource API Reference
UserAuthentication
Authenticates the user.
AccessControl
Determines whether the client has access permission.
ResourceType
Determines the MIME type and MIME encoding of the resource.
CommandProcessing
Executes the extended equivalent of a CGI script.
ServerInclusions
Processes unrecognised Web Macros and server-side includes.PowerWeb API Functions
A simple example is that of writing an equivalent of a CGI program, except that the API extension is far more efficient because it removes the overhead of the CGI interface which is typically many times greater than the overall time taken by PowerWeb Server++ to service a normal request. For example, PowerWeb Server++ might service an entire request within 30 milliseconds, whereas a CGI script could take 3 seconds just to load!
If your server is linked to a database to perform queries, an even better speedup can be achieved by using the API extensions. CGI scripts are forced to log on to the database server every time a request arrives. With an API extension, you can log on once when the PowerWeb Server++ starts up and then keep the connection open to service queries until such time as the PowerWeb Server++ shuts down, in which case you can log off from the database server. This results in far less network traffic as well as requiring less powerful hardware to sustain a given throughput.
The APIs are broken up into three groups: Global, Server and Resource, according to the object to which they attach. Global APIs apply across all servers and requests, while Server APIs apply to a specific server and its operation. Resource APIs modify the behaviour of the PowerWeb Server++ when accessing or manipulating a particular resource, such as a document.
This fine level of granularity gives powerful control and flexibility so that the WebMaster can change the method of operation for a single document, while leaving all others unchanged, or the WebMaster could define an API extension that applies to all resources across the entire server.
The management of all these API extensions is performed through the Administration and Configuration Pages with a web browser. There is no need to manually modify operating system configuration files with arcane details.
API extensions can be written in Rexx or in any compiled language compatible with the C language calling convention (such as COBOL or FORTRAN). The ability to write extensions in Rexx provides quick prototyping and in most cases will give satisfactory speed (achieved through caching the Rexx script in tokenised form to reduce overhead).
The CGI interface requires the server to load a new process for every request. If the server doesn't have to run such CGI programs all that often, there is typically no performance problem, but if CGI is being used often, the overhead of simply creating the new process to run the CGI program is normally far higher than servicing the request itself (usually 10 to 100 times longer).
CGI is also limited to the extent with which it can interact with the server. A CGI program is passed a fixed set of a few server status variables and has no way of obtaining more information, or of returning information, other than documents, to the server. This severely limits the types of applications that can be created with CGI.
CGI is targeted at one specific purpose: the task of returning a document to the server to be relayed to the client's browser. There is no facility to modify aspects of the server's operation, such as customising the authentication procedure or altering the format or destination of the audit log (to store it in a relational database for example).
All these limitations and efficiency issues are overcome through the PowerWeb Server++ APIs. The lowest possible overheads, full access to the entire Server status and data structures, and the ability to customise any aspect of the server's operation are key benefits of the API.
All C-compatible functions called by PowerWeb Server++ have the following definition:
typedef long (*pfnAPI)(void* parcel);These functions should return one of the following values:
If an API extension encounters an error, it should log it in the error log (by writing to the variable "Server:/ErrorOutput"), set a protocol-dependent status code (by writing to the variable "Request:/StatusCode") and return HOOK_ERROR or HOOK_ABORT_xx. Custom error message text to be sent to the client browser can be stored in "Request:/StatusText".
HOOK_OK 0 HOOK_NO_ACTION 1 HOOK_CALL_AGAIN 2 HOOK_ERROR 3 HOOK_ABORT_CONNECTION 4 HOOK_ABORT_SERVER 5
PowerWeb Server++ function calls and variables are multi-thread safe, so there will be no problems with partially interleaved text being sent to the error log, for example.
When an API extension needs to call PowerWeb back, it uses one of a family of library functions. All these functions have standardised return values:
Under Rexx, if there is an error, then Rexx error 40 is always raised, because there is no facility within Rexx for specific return values.
ERR_INTERNAL -3 ERR_MEMORY -2 ERR_BAD_ARGUMENTS -1 ERR_NONE 0 ERR_NOT_FOUND 1 ERR_NO_LONGER_EXISTS 2 ERR_ACCESS_DENIED 3 ERR_BUSY 4 ERR_INVALID_TYPE 5 ERR_NOT_A_LIST 6 ERR_VALUE_TRUNCATED 7 ERR_INCOMPATIBLE_VERSION 8 ERR_BAD_NAME 9
For example, IBM's CSet++ compiler uses the compiler switches:
/Gd+ /Ge- /Gm+
You should also ensure that "\powerweb\include" is on your INCLUDE path and that "\powerweb\lib" is on your LIB path. Refer to the file "\powerweb\tour\source\tour.nmk" for a sample NMAKE script.
Rexx modules do not need compiling prior to use. PowerWeb Server++ takes advantage of caching of the tokenised image of the Rexx module to improve the speed of execution.
Suggested Location for Rexx Modules:
Put your module in the directory "\powerweb\rexx-bin", so that you can load it with the URL "/rexx-bin/module.cmd" where "module.cmd" is the name of your Rexx module.Example URL: "/rexx-bin/svrpush.cmd"
Suggested Location for C Modules:
Put your library DLL in the directory "\powerweb\bin", so that you can load it with the URL "/bin/module!function" where "module" is the name of your DLL (without the file extension) and "function" is the name of the function within your DLL to call.Example URL: "/bin/property!ShowMenu"
URL
API Hook Modules can be called directly by using a URL, and do not need to be associated with a specific Resource.
Rexx Examples:
/rexx-bin/svrpush.cmdC Examples:
/bin/tour!TourDirect
/bin/property!ShowMenu
API Hook Modules can be called directly from within PowerWeb Macros. The Macro is named exec and is used as follows:
Rexx Examples:
<!--#exec rexx="/rexx-bin/svrpush.cmd" -->C Examples:
<!--#exec api="/bin/tour!TourDirect" -->
<!--#exec api="/bin/property!ShowMenu" -->
In each case, you will be able to specify the implementation language and the location of your module, as well as the name of the function to call. The default is to cache calls to API hooks, so you may wish to switch caching off during debugging, so that you don't have to restart PowerWeb Server++ to test each new version of your module.Global API Hooks
Define these by selecting "Global" from the "/admin" page and then selecting "Hook".
Server API Hooks
Define these by selecting "Servers" from the "/admin" page, selecting the "Advanced Settings" for the relevant Server, and then selecting "Hook".
Resource API Hooks
Define these by selecting "Resources" from the "/admin" page and then selecting the "API Hook" for the relevant Resource.
PowerWeb Server++ is not limited to its built-in encryption algorithms. With the Encryption hook, you can define new methods, making PowerWeb an excellent platform for testing and evaluating new security models.
Encryption
Arguments In: To Be Documented with PowerWeb Secure Server++.
Arguments Out: To Be Documented with PowerWeb Secure Server++.
Data transmission can be encoded as well as encrypted. Encoding is defined as a mechanism for compressing or translating data, such as used by popular ZIP programs. The ability to define a hook to handle custom encoding methods means you can write specialised client-server software without losing the advantages of the HTTP framework and server.
Encoding
Arguments In: To Be Documented with PowerWeb Secure Server++.
Arguments Out: To Be Documented with PowerWeb Secure Server++.
Informational and debugging messages are sent to the Trace Log. This hook allows such messages to be redirected, ignored, or formatted differently.
TraceLog
Arguments In:
Arguments Out: None.Argument:/Message
The text that is to be output to the trace log.
This hook is called when the service is started and allows for the extension library to initialise itself. A typical initialisation might be the establishment of a link to a database server, to avoid the overhead of logging in and out for every service request.
ServerInitialisation
Arguments In: None.
Arguments Out: None.
This hook is called when the service shuts down. It is not guaranteed to be called if the PowerWeb Server++ is aborted abnormally. The main purpose of this hook is to allow cleanup and relinquishing of resources obtained during the "ServerInitialisation" hook.
ServerTermination
Arguments In: None.
Arguments Out: None.
Resource Name Translation converts a logical (or virtual) name into a physical resource name. PowerWeb Server++ has a built-in function which does this according to the Aliases defined in its server configuration. With this hook, you can override or modify this behaviour. If you need to access the Alias information, it is stored in the variable directory "Config:/Security/Alias".
ResourceNameTranslation
Arguments In:
Arguments Out:Argument:/VirtualName
The name of the resource, as specified by the remote client.Argument:/PhysicalName
The translated physical name of the resource, typically a filename accessible via the server's file system.
Argument:/Type
The object type of the resource. Refer to the list of object types.
Information sent to the "Server:/ErrorOutput" variable is written to disk by the default version of this hook. By defining your own hook, you can output different information (such as status from your own library module) or redirect the log to a different location or storage mechanism (such as a database).
ErrorLog
Arguments In:
Arguments Out: None.Argument:/Message
The text that is to be output to the Error Log.
Information sent to the "Server:/Audit/AuditOutput" variable is written to disk by the default version of this hook. By defining your own hook, you can output different information (such as status from your own library module) or redirect the log to a different location or storage mechanism (such as a database).
AuditLog
Arguments In:
Arguments Out: None.Server:/Audit/AuditFile
The destination of the audit log information. The API hook writer may choose to override this parameter.
Authentication and Access Control are two separate phases in PowerWeb Server++. Authentication refers to the checking of the validity of the user's identity, whereas Access Control defines whether the specified user is allowed access to a given resource from a given machine.
UserAuthentication
The Authentication hook splits apart the authorization string provided by the client browser and stores the authenticated results back in other server status variables.
Arguments In:
Arguments Out:Request:/Header/In/Authorization
The authorization string provided by the browser (if any).Request:/AuthenticateType
The type of authentication that was performed.
Request:/AuthenticateUser
The authenticated user name.
Request:/AuthenticatePassword
The password that was supplied (optional).
Access Control defines whether the specified user is allowed access to a given resource from a given machine. By defining your own hook, you can implement other access control policies such as allowing access only during specified time periods during the day.
AccessControl
Arguments In:
Arguments Out:Request:/Resource
The physical resource being accessed.
Request:/Protocol
The name of the protocol being used to access this resource.
Request:/Method
The protocol-specific method, such as GET or POST.
Request:/SubMethod
The protocol and method-specific sub-method, such as INCLUDE or EXEC.
Request:/AuthenticateUser
The authenticated user name.
Connect:/RemoteAddress
The remote client's IP address.
Connect:/RemoteHost
The remote client's machine host name, as determined by reverse domain name server lookup.Request:/StatusCode
The protocol-specific status code to return to the client if access is denied.
The ResourceType hook tells PowerWeb Server++ the MIME type and encoding of the resource. The default handler bases its decisions on the filename extension of the resource.
ResourceType
Arguments In:
Arguments Out:Argument:/Resource
The physical name of the resource to be accessed after resource name translation.Argument:/ContentType
The MIME type of the resource.
Argument:/ContentEncoding
The MIME encoding of the resource.
The CommandProcessing hook is the closest equivalent to CGI. It provides a superset of CGI functionality because all server variables can be both queried and set (subject to access control permissions).
CommandProcessing
Custom applications can use this hook to perform tasks other than serving of documents or data files. Database searches, gateways to other systems and services, administration tasks, etc can all be performed by this hook.
Arguments In: None.
Arguments Out: None.
The ServerInclusions hook allows the developer to define customised PowerWeb Macros, extending the existing set provided with PowerWeb Server++. Any unrecognised macro is passed to this hook for execution.
ServerInclusions
Arguments Out:Argument:/WebMacro
The name of the PowerWeb Macro to be interpreted.
Argument:/Attributes
The full text of the PowerWeb Macro to be interpreted, excluding the name. Attributes are typically space-delimited and are composed of "name=value" pairs where values can be contained in quotes.Request:/Result
The processed output of the PowerWeb Macro should be appended to this variable. It will be automatically sent to the client's browser.
Please read the PowerWeb Macro Reference Manual for details on the directory structure of PowerWeb's variables. You can also browse the server configuration and status variables to gain familiarity with the "Config:/" set of variables.Navigation Within the Variable Directories:
ServerGetConfig
Get a handle to the "Config:/" variable sub-directory.C Example:
rc = ServerGetConfig(parcel, &handleConfig);
Rexx Example:
handleConfig = ServerGetConfig(parcel)
ServerGetServer
Get a handle to the "Server:/" variable sub-directory.C Example:
rc = ServerGetServer(parcel, &handleServer);
Rexx Example:
handleServer = ServerGetServer(parcel)
ServerGetConnect
Get a handle to the "Connect:/" variable sub-directory.C Example:
rc = ServerGetConnect(parcel, &handleConnect);
Rexx Example:
handleConnect = ServerGetConnect(parcel)
ServerGetRequest
Get a handle to the "Request:/" variable sub-directory.C Example:
rc = ServerGetRequest(parcel, &handleRequest);
Rexx Example:
handleRequest = ServerGetRequest(parcel)
ServerGetParameters
Get a handle to the "Parameter:/" variable sub-directory.C Example:
rc = ServerGetParameters(parcel, &handleParam);
Rexx Example:
handleParameters = ServerGetParameters(parcel)
ServerGetArguments
Get a handle to the "Argument:/" variable sub-directory.C Example:
rc = ServerGetArguments(parcel, &handleArgument);
Rexx Example:
handleArguments = ServerGetArguments(parcel)
ServerFind
Get a handle to the variable with a specified name. The name can be absolute or relative to the handle passed over as a first parameter.C Example 1:
rc = ServerGetRequest(parcel, &handleRequest);
rc = ServerFind(handleRequest, "Resource", &handleVariable);C Example 2:
rc = ServerGetConfig(parcel, &handleConfig);
rc = ServerFind(handleConfig, "/Servers/HTTP", &handleHTTP);
rc = ServerFind(handleHTTP, "ErrorFile", &handleFile);Rexx Example 1:
handleRequest = ServerGetRequest(parcel)
handleVariable = ServerFind(handleRequest, "Resource")Rexx Example 2:
handleConfig = ServerGetConfig(parcel)
handleHTTP = ServerFind(handleConfig, "/Servers/HTTP")
handleFile = ServerFind(handleHTTP, "ErrorFile")Querying a Variable's Attributes:
ServerKind
Returns the data type of the indicated variable:
- 32-bit signed integer
- Double-precision floating point
- Variable-length text
- Variable-length binary buffer
- List that contains other Variables
- Pointer to another Variable
C Example:
rc = ServerFind(parcel, "Config:/Servers", &handleServers);
rc = ServerKind(handleServers, &iKind);
ServerSize
Returns the size in bytes of the indicated variable, unless it is a List variable, in which case it returns the number of variables contained in that list.C Example:
rc = ServerFind(parcel, "Config:/Servers", &handleServers);
rc = ServerSize(handleServers, &cServers);
ServerName
Returns the name of the indicated variable. Often used in conjunction with the hierarchy browsing functions described below.C Example:
rc = ServerGetConfig(parcel, &handleConfig);
rc = ServerChild(handleConfig, &handleFirst);
rc = ServerName(handleFirst, buffer, sizeof(buffer));
Reading the Contents of a Variable:
ServerReadInteger
Reads the indicated variable and converts it (if necessary) into a 32 bit signed integer.C Example:
rc = ServerReadInteger(parcel, "Server:/Stats/Clients", &iClients);
ServerReadFloat
Reads the indicated variable and converts it (if necessary) into a double precision floating point number.C Example:
rc = ServerReadFloat(parcel, "Config:/VersionConfig", &dVersion);
ServerReadText
Reads the indicated variable and converts it (if necessary) into a text buffer.C Example:
rc = ServerReadText(parcel, "Config:/ConfigFile", buffer, sizeof(buffer));
ServerReadBinary
Reads the indicated variable and converts it (if necessary) into an unformatted binary buffer.C Example:
rc = ServerReadBinary(parcel, "Request:/ArgumentText", buffer, sizeof(buffer));
Overwriting the Contents of a Variable:
ServerWriteInteger
Writes the integer value to the indicated variable, performing datatype conversions if necessary.C Example:
rc = ServerWriteInteger(parcel, "Request:/StatusCode", 401);
ServerWriteFloat
Writes the double precision floating point value to the indicated variable, performing datatype conversions if necessary.
ServerWriteText
Writes the text buffer to the indicated variable, performing datatype conversions if necessary.C Example:
rc = ServerWriteText(parcel, "Request:/StatusText", "Authorisation is Required");
ServerWriteBinary
Writes the binary buffer to the indicated variable, performing datatype conversions if necessary.C Example:
rc = ServerWriteBinary(parcel, "Request:/Result", buffer, cbResult);
Appending to the Existing Contents of a Variable:
ServerAppendText
Appends a text buffer to the existing content of the indicated variable, performing datatype conversions if necessary.C Example:
rc = ServerAppendText(parcel, "Request:/Result", "All Rights Reserved");
ServerAppendBinary
Appends a binary buffer to the existing content of the indicated variable, performing datatype conversions if necessary.C Example:
rc = ServerAppendText(parcel, "Request:/Result", buffer, cbExtra);
Creating New Variables:
ServerNewInteger
Creates a new integer variable, initialised to a given value, and given a specified name within the indicated variable directory.C Example:
rc = ServerFind(parcel, "Request:/Argument", &handleArgument);
rc = ServerNewInteger(handleArgument, "MyNewFormVariable", 100);
ServerNewFloat
Creates a new floating point variable, initialised to a given value, and given a specified name within the indicated variable directory.C Example:
rc = ServerFind(parcel, "Request:/Argument", &handleArgument);
rc = ServerNewFloat(handleArgument, "MyNewFormVariable", 3.1415);
ServerNewText
Creates a new text variable, initialised to a given value, and given a specified name within the indicated variable directory.C Example:
rc = ServerFind(parcel, "Request:/Argument", &handleArgument);
rc = ServerNewText(handleArgument, "MyNewFormVariable", "MyForm");
ServerNewBinary
Creates a new binary variable, initialised to a given value, and given a specified name within the indicated variable directory.C Example:
rc = ServerFind(parcel, "Request:/Argument", &handleArgument);
rc = ServerNewBinary(handleArgument, "MyNewFormVariable", buffer, cbData);
ServerNewList
Creates a new list variable, given a specified name within the indicated variable directory. A list variable is the same as a "variable directory" - it is simply a variable that can contain other variables (of any type, including nested lists).C Example:
rc = ServerGetConfig(parcel, &handleConfig);
rc = ServerNewList(handleConfig, "ClientList");Browsing Through the Variable Directory Hierarchy:
ServerParent
Returns the handle of the parent of the indicated variable.C Example:
rc = ServerParent(handleVariable, &handleParent);
ServerChild
Returns the handle of the first child of the indicated variable.C Example:
rc = ServerChild(handleVariable, &handleChild);
ServerSibling
Returns the handle of the next sibling of the indicated variable. Commonly used in conjunction with ServerChild in order to walk through the entire tree of variables.C Example:
rc = ServerChild(handleVariable, &handleChild);
rc = ServerSibling(handleChild, &handleNext);
Another source of information is the help contained in the /admin/help/ URL which contains a list of HTML pages, starting with the letters "var", which describe the variables and their usages.
You will need to read the function descriptions, before writing code to call these functions.
The PowerWeb Server++ C/C++ Interface Function Definitions Are:
Returns Function Parameters long ServerGetConfig (void* parcel, void** handle); long ServerGetServer (void* parcel, void** handle); long ServerGetConnect (void* parcel, void** handle); long ServerGetRequest (void* parcel, void** handle); long ServerGetParameters (void* parcel, void** handle); long ServerGetArguments (void* parcel, void** handle); long ServerFind (void* parent, const char* pszName, void** item); long ServerKind (void* parent, unsigned long* type); long ServerSize (void* parent, unsigned long* size); long ServerName (void* parent, char* value, unsigned long size); long ServerReadInteger (void* parent, const char* pszName, long* value); long ServerReadFloat (void* parent, const char* pszName, double* value); long ServerReadText (void* parent, const char* pszName, char* value, unsigned long size); long ServerReadBinary (void* parent, const char* pszName, unsigned char* value, unsigned long size); long ServerWriteInteger (void* parent, const char* pszName, long value); long ServerWriteFloat (void* parent, const char* pszName, double value); long ServerWriteText (void* parent, const char* pszName, const char* value); long ServerWriteBinary (void* parent, const char* pszName, const unsigned char* value, unsigned long size); long ServerAppendText (void* parent, const char* pszName, const char* value); long ServerAppendBinary (void* parent, const char* pszName, const unsigned char* value, unsigned long size); long ServerNewInteger (void* parent, const char* pszName, long value); long ServerNewFloat (void* parent, const char* pszName, double value); long ServerNewText (void* parent, const char* pszName, const char* value); long ServerNewBinary (void* parent, const char* pszName, const unsigned char* value, unsigned long size); long ServerNewList (void* parent, const char* pszName); long ServerParent (void* here, void** handle); long ServerChild (void* here, void** handle); long ServerSibling (void* here, void** handle);
Parameters:
void* parcel
The parcel pointer is the same one that was passed to the API hook from PowerWeb Server++. It should be treated as a "magic cookie" which identifies the current context. The parcel is unique within the system, and allows for easy and safe multi-threading.
void** handle
The handle pointer is another "magic cookie", this time one that refers to a specific PowerWeb variable directory or individual variable. It is used to query a specific variable or to navigate through the hierarchy. It is a pointer to a pointer to allow PowerWeb to write the value of the handle pointer into it.
void* parent
The parent pointer is either the "parcel" pointer or a "handle" pointer. When the pszName contains a fully qualified global name (ie. has a prefix of "Request:/", "Server:/", etc) then the "parent" pointer is the "parcel" pointer, otherwise the pszName is a relative reference to a "handle" pointer.
Examples:
Perform Access Control via the Remote User's IP Address:
This particular example limits access to a single IP address. It is obviously easier to set up a security rule in the configuration to provide this type of protection, but the example shows the basic components of writing an API Extension.long AccessControlHook(void *parcel) { char ip[32]; ServerReadText(parcel, "Connect:/RemoteAddress", ip, sizeof(ip)); if (strcmp(ip, "12.34.56.78") != 0) { ServerWriteInteger(parcel, "Request:/StatusCode", 403); return HOOK_ERROR; } return HOOK_OK; }Guided Tour Sample Code:
The Guided Tour has a complete example of API hooks for forms processing, dynamic document translation, simple direct-call hooks, and a tree view of your server settings.
You will need to read the function descriptions, before writing code to call these functions.
The PowerWeb Server++ Rexx Interface Function Definitions Are:
Returns Function Parameters handle ServerGetConfig parcel handle ServerGetServer parcel handle ServerGetConnect parcel handle ServerGetRequest parcel handle ServerGetParameters parcel handle ServerGetArguments parcel handle ServerFind parent, name type ServerKind handle size ServerSize handle name ServerName handle value ServerReadText parent, name value ServerReadBinary parent, name - ServerWriteText parent, name, value - ServerWriteBinary parent, name, value - ServerAppendText parent, name, value - ServerAppendBinary parent, name, value - ServerNewInteger parent, name, value - ServerNewFloat parent, name, value - ServerNewText parent, name, value - ServerNewBinary parent, name, value - ServerNewList parent, name, value handle ServerParent handle handle ServerChild handle handle ServerSibling handle
Examples:
Guided Tour Sample Code:
The Guided Tour has a complete Rexx example of an API hook for dynamic translation of a database into an HTML document. It has equivalent functionality to the C example, so you can compare the two language implementations.There is also has an example of server push in the file "\powerweb\rexx-bin\svrpush.cmd" for those browsers capable of handling this feature which allows for periodic updating of the client's page or image.