Prior to start developing a new CSA application, the very first thing you need to do is to create the proper structure of files and directories where your application will be located on the Web server. The following explanation will show how to setup and play with an application that we will call "example", consisting of just a few programs (or methods), one of which is the classical "hello-world". As you will see in the rest of this manual, CSA makes use of a number of naming conventions to identify its files and objects. One of such convention is to identify a single program by its file-name, prefixed by the name of the application it belongs to. In our example, the application is example, and the program will be hello-world. So the fully-qualified name of our program will be example.hello-world. The purpose of our program is to display the usual "Hello World" message on a Web page, whenever it is called by the user through a Web browser, or through some other Web client program.
Let's assume that the non-privileged system account under which our application will run is goofy, and that its HOME directory (that we will usually refer to as $HOME) is /home/goofy/.
First, you need to copy the CSA shell script from /usr/local/csa/bin/CSA into $HOME/cgi-bin/. Then edit $HOME/cgi-bin/CSA and change "logname = jsmith" into "logname = goofy". As already stated, your knowledge of rc shell syntax is assumed here. For better security, you should also create the directory that CSA will use to store temporary workfiles, /tmp/goofy-csa/, and set it owned by goofy and with mode 700. If this is not done, the CSA will try and create that directory upon the first invocation, but it is more secure to do it beforehand. If you decide to use a non-publicly writable directory (probably a sensible thing), say /home/goofy/tmp/, simply create it and change the "TMPDIR" definition in $HOME/cgi-bin/CSA accordingly.
By default, our CSA example application will go in $HOME/.csa/example/
and a few subdirectories thereof. Should you prefer to place it
in a different location, edit the "libstem =" definition in
$HOME/cgi-bin/CSA accordingly. Finally, if your system stores commands
and utilities in non-standard locations, you may want to
edit also the "PATH =" definition in the same script. Note,
however, that the PATH defined here is necessary only to find
what CSA needs to start-up and load the proper per-application
configuration file, namely test and mawk,
so you sould not normally need to change it.
The application-specific PATH setting is done in the actual
per-application profile, as shown later. The application-specific
path "$HOME/.csa/example
" will often be identified
simply as $CSA_ROOT
throughout this Programming Guide.
Next, we MUST create all what is needed under $CSA_ROOT. To make your life a bit easier, I have provided a file that does this. First, create directory /home/goofy/.csa/, cd into it and un-tar the "example-app.tar.gz" file contained in the "extra/" subdirectory of the CSA distribution tarball. As with anything else that comes with CSA, I take no responsibility if a given script or component causes you troubles, drills holes in your server or sets your house on fire. If you are a security-consciuos person you may want to first have a look at things beforehand, prior to blindly do whatever someone else tells you to do with them, including un-tarring an object on the file-system before having at least taken a look at it by simply listing its contents. Anyway, I've been running CSA for some time now, and my house is still up and my Web servers are still working, so I think you sould be sort of safe too :-)
Why did I choose a hidden directory name, such as .csa/, for the user-level components of CSA ? Well, for no particular reason really, but I see CSA as an extension of the shell command-line to the Web, a way of taking to the Web common shell-level UNIX commands and applications. It seems therefore logical to me to consider such enablement a per-user configuration matter, and user profiling stuff is usually written into hidden files and folders in one's $HOME directory.
Back to our "example.hello-world" (note the loosely Object-Oriented naming scheme here, see further down), the actual script can be found in /home/goofy/.csa/example/rpclib/. This is where all the Web "methods" that will make up our example application will have to be. "rpclib" means "Remote Procedure Call Library", as calling a program over the Web is a form of RPC operation, at least this is the way I see it. Before things are really ready to run, you still need to set up a few local definitions in the "example" application main configuration file, /home/goofy/.csa/example/csa.rc. Here you will probably only need to set CSA_ALLOW_USER to your UNIX account ("goofy" in our example, yours will almost certainly be different and equal to your Unix login account), and define the correct values for CSA_URL, CSA_RPC_URL and CSA_DOCROOT. The example settings in the supplied csa.rc assume that your CGI setuid wrapper is "cgiwrap". Adapting those values to suEXEC should be trivial.
For the rest of this document I will often call an application a "class", and its individual scripts "methods". Although CSA isn't Object Oriented Programming in any true sense, I like naming things in this way. So let's call our first example script the "hello-world" method in class "example". Note that the "csa" class SHOULD be considered reserved for CSA own use, and SHOULD NOT be used to identify a CSA user-level appication.
First, take a look at the actual "rpclib/hello-world" script. Well, there isn't eally much to it; all it contans is:
# Sample "hello-world" CSA application. csaPrintMsg 1000 $CSA_PGM csaTemplate csaOk.html csaExit # End of program.
Just three lines of code, plus comments. Of course real-world scripts will usually be more than that, but even such a simple script can already show us how the CSA basics work. Open your Web browser and type the script URL (replace the relevant parts with your local values):
http://www.example.com/cgi-bin/cgiwrap/goofy/CSA?0=example.hello-world
Tell your browser to load that URL, and if everything was set up as explained you should see a response page with an "Hello World!" message on it. If that didn't work, please double-check all the steps that I have outlined, because it must work, believe me :-)
Some explanations are needed on the URL syntax first. The first part is pretty obvious:
http://www.example.com
The next part is necessary if you use cgiwrap:
/cgi-bin/cgiwrap/goofy/CSA
If you rather use suEXEC this will most likely become
/cgi-bin/CSA
Whatever the case, CSA
is the CGI program that you
copied to your $HOME/cgi-bin/CSA. What comes after the "?"
are CGI parameters, as usual, and they are passed to CSA:
?0=example.hello-world
Since the CGI specs do not mandate that argument names use letters, I decided to use numbers for CSA own purposes, to make the syntax recall the familiar C-style argv[] argument list, where argv[0] is the program name, and argv[1] through to argv[n] are arguments. The adopted style is similar to the one suggested by spec n. 6 of the MIME-RPC proposed protocol.
Our "example.hello-world" script does not take arguments, but if it did they would therefore extend the above URL to become something like:
?0=example.hello-world&1=value1&2=value2&a=abc&b=123&...
I like to see this remote program invocation style ar a kind of Web Command Line Interface, which I dubbed CSA-RPC.
Back to "example.hello-world", let's examine it a bit more in depth.
All our program does is to call three CSA library functions:
csaPrintMsg
, csaTemplate
and csaExit
.
Here are the gory details:
csaPrintMsg
is the message processor.
It is passed two arguments:
1000
is the number of the response message to be printed$CSA_PGM
contains the program name, hello-world
in this case. Message code and severity are always mandatory, while
additional arguments depend on the spcific message that is requested.
See section
Message Processing
for details.csaTemplate
is the actual response processor.
It is passed the name
of the template page to use to display the response to the client. The
requested template, csaOk.html
in this case, is sought for
by csaTemplate
in the $CSA_ROOT/forms/
directory.
See section
Template Processing
for details.csaExit
. This function always ends a CSA program, and is
necessary to perform return-code processing and garbage-collection
upon program termination. It can optionally be passed a numerical
error code. If the code is not specified it defaults to zero (i.e.:
it is equivalent to call csaExit 0
).Although our "example.hello-world" script was specially crafted for tutoring purposes, its function can be more elegantly performed by "collapsing" its code into one single line, like this:
# Sample "hello-world" CSA application ("squeezed" version). csaExit.ok 1000 $CSA_PGM # End of program.
The csaExit.ok
statement is CSA generic "positive completion"
exit function. When called as shown, it will first call csaPrintMsg
with the passed arguments, then call csaTemplate
against
the generic csaOk.html
template (that MUST always be present
in the template directory), and then csaExit 0
will be called
and the program terminates.
Likewise, the csaExit.fault
function can be used to display
the generic csaError.html
template to the client and then
quit the program. For detailed usage info on CSA library functions,
please refer to the relevant library source files in the CSA installation
directory, usually /usr/local/csa/lib
. I will eventually provide
detailed documentation for them, but it will probably take time.
In the meantime, the best source of documentation remains the code
itself, and the relevant comment lines.
Our example application contains a second script, "hello-folks". To call it from your Web browser type:
http://www.example.com/cgi-bin/cgiwrap/goofy/CSA?0=example.hello-folks
replacing the relevant parts with your local values, as usual.
The "hello-folks" script source code, from
$CSA_ROOT/rpclib/hello-folks
is:
# Sample "hello-folks" CSA application. csaPrintMsg 1000 $CSA_PGM csaExit.ok hello-link.html # End of program.
This time a template other than the default csaOk.html
is displayed, namely hello-link.html
, also stored in the
HTML template directory $CSA_ROOT/forms/en_US/
.
Since the template name is a custom one, it cannot be implied by
csaExit.ok
and MUST be passed to the function as an argument.
Beside the message-capture tag $[ISO_CSA_MSG_TEXT]
, the
template now has also the additional URL tag $[CSA_RPC_URL]
,
that is used to "resolve" the clickable hyperlink on the page.
The value associated with CSA_RPC_URL
is a local configuration
parameter, defined in the application profile $CSA_ROOT/csa.rc
.
The $[CSA_URL]
tag, which value is also defined in
$CSA_ROOT/csa.rc
,
is useful to resolve relative URL's
to images or other external HTML obiects, in spite of the fact that
the page was served through the path of the CSA CGI driver, and not
directly as a static object by the Web server.
By clicking on the relevant anchor-point on the response page, the "example.hello-folks" script can be re-run over and over.
Let's see how program messages are handled. In general, when either
the application program or a shell library function need to produce
a message, they call the csaPrintMsg
CSA primitive.
Messages are identified by a message number (the "1000" of
the "hello-world" example), an associated explanatory text, and
some of them can be passed optional arguments (the $CSA_PGM
in said example). Message numbers are always a four-digit code. Codes
beginning with "0" (0000, 0001, ... 0999) are reserved for basic CSA
messages. They can be used by both CSA library functions and application
programs, and refer to generic error or informational conditions
(such as errors reading/writing files, generic error messages, generic
positive completion messages, etc.). Messages from 1000 to 9999 are
available to application programmers, and can be re-defined differently
for each specific CSA application. Messages are organized in flat-file tables,
composed by fields (or columns) which are separated from each other by an horizontal TAB character. This is an example entry for message
0008
:
0008<TAB>ERR_SYSTEM<TAB>could not read from file '%s'
In the example I have represented with "<TAB>" the otherwise non-printable horizontal TAB character. The message code uniquely identifies the message table entry, and further down I'll explain the meaning of the ERR_SYSTEM field. The sample message shown takes one parameter, the name of the file that the program failed to read, so to actually use the message a program would do:
csaPrintMsg 0008 /path/to/my/file
The ERR_SYSTEM field in the message table is the message group identifier. This is a symbolic string, all upper-case, that identifies the class of messages that a particular message belongs to.
The class string is actually made up by two substrings, separated by an underscore: class_subclass. The class part can be one of:
The CSA class is reserved for CSA use, and MUST NOT be used by application-defined messages. The subclass part can be one of:
The generic SYSTEM subclass is reserved for CSA use, and MUST NOT be used by application-defined messages. If an application needs to define a general subclass, it can use the LOCAL one.
Therefore, a message group of ERR_REFUSED belongs to the ERR class, and its subclass (REFUSED) indicates that it is related to something that the issuer refused to do, for some reason. An ERR_SYSTEM string means that an error occured in a generic component of the CSA stack, while ERR_LOCAL indicates a generic error at the application level.
To get an idea of how message groups are used, have a look at $CSA_INSTALL/message.d/0/en_US.data
The purpose of this classification scheme is to identify, in a machine-readable format, the general condition that caused a message to be issued.
If an application program wants to issue a certain message, but
it is not satisfied with the default message group associated
with that message, it may override that value by passing the
new group name to csaPrintMsg
. For instance, the default
group associated with message 0008 is ERR_SYSTEM, since it
is usually an indication that an error occurred and that the
program SHOULD stop. However, a particular application may consider
that condition just a warning, and continue processing. In that
case it SHOULD override the original message group with a new
value indicating a less severe condition, like WARN_SYSTEM, for
example. Choosing the appropriate message group is not mandatory,
but will help when reading message logs and debugging applications.
Back to the previous example, to issue message 0008 with a message group of WARN_SYSTEM instead of the default ERR_SYSTEM, an application would do:
csaPrintMsg 0008 WARN_SYSTEM /path/to/my/file
As previously shown, message 0008 takes a string argument '%s'. Messages are processed with the printf(1) shell utility, so please refer to the relevant man page for more on the parameter substitution rules.
For the complete list of CSA pre-defined messages 0xxx
, see
the relevant tables in the CSA installation directory, usually
/usr/local/csa/message.d/
. In the directory there are one message
file for each message language supported by CSA. To date, only en_US
(US-English) and it
(Italian) are provided, but other will
follow in the future. The language selection can be done by setting the
$CSA_LANG_DEFAULT
variable to the desired language code in the
$CSA_ROOT/csa.rc
application profile. If unset, this variable
defaults to en_US
. Application programs may specify a different
language code through variable $CSA_LANG. The language code is used to
identify not
only the correct message table, but also the associated template
subdirectory of forms/
.
For instance, HTML (or XML) templates for
the en_US
language are expected to be
in forms/en_US/
. The language code
is also used by CSA library routines that handle date values,
which will represent dates consistently with the
code used (DD/MM/YYYY, MM/DD/YYYY, and so on).
Beside CSA predefined messages, each application will usually need to define
its own. Those can be assigned any code between 1000 and 9999, MUST be
organized in language-code files as already explained, and are expected
to be found in $CSA_ROOT/message.d/
.
Each message MUST be given a suitable message group, in the form of
the symbolic labels previously explained.
You only need to provide
message files for those languages that your application actually uses.
The message issued by our "example.hello-world" has code 1000, meaning
that it is an application-defined message, and it can be found in
$CSA_ROOT/message.d/
. Beside being grouped into
language code tables, messages are further grouped into sub-directories
according to the first digit of the message code. The complete path to
the file containing our sample message (1000) will therefore be
$CSA_ROOT/message.d/1/en_US.data
. The reason why I
further classified
messages in this way was to allow different programmers, working at
different parts of an application, to handle their own messages
without interefering with each-other. This may or may not be a problem,
depending on whether you use other cooperative development tools, like
the Concurrent Versioning System (CVS), to coordinate your
development team. But anyway the facility is there and, even if you don't
need it, it will not hurt.
See section
Path-Based Clustering
for more details on this table-record grouping scheme, as it applies
also to other CSA data tables, not just to messages.
Provided that both CSA- and application-defined messages are organized
in directories and files as shown, the csaPrintMessage
primitive will be able to fetch and process them transparently,
removing the associated hassle from the application program.
Whenever csaPrintMsg
is called with the proper arguments it
does the following main things:
$CSA_MSG_NUM
to the message code.$CSA_MSG_GRP
to the
symbolic message group name.$CSA_MSG_TEXT
to the message
text (after substitutions).$CSA_ROOT/var/message.log
.Since csaPrintMsg
writes log entries to
$CSA_ROOT/var/message.log
,
that file will eventually grow in size, and on a busy server it will
need to be rotated at regular intervals (for instance daily, or weekly),
by properly configuring your local logfile rotation facility.
On Debian GNU/Linux see /etc/logrotate.conf
. Other Linux
distributions may provide different rotation facilities.
Let's refer back to the
Classical Hello World
section, in particular to the csaTemplate csaOk.html
line
of the "example.hello-world" program.
The specified template, csaOk.html
, is fetched by
csaTemplate
from
$CSA_ROOT/forms/en_US/csaOk.html
and sent to
the client browser in response to the original request. Before the
template is sent, a number of things occur inside csaTemplate
.
The most important one is tag substitution. That is, templates
may contain special tags, that whenever are met by csaTemplate
are replaced by the
content of the corresponding environment variables, before the
template is sent to the client. Such tags are ordinary names, surrounded
by special markers, $[
and ]
by default. If you look
at the example csaOk.html
, you will see that it contains one
such tag, $[ISO_CSA_MSG_TEXT]
. If no value has been associated
with a certain tag, it will be replaced with a null string in the template
that is sent to the client. Note that substitutions are done only against
the data that travels towards the client, and that no changes occur
to the original file on disk.
As an example, let's say the program environment of "example.hello-world"
contains the variable $HOME
, having the value /home/goofy
,
and that we want such value to be displayed on the output template.
For this to occur, the template must contain a $[HOME]
tag,
and upon serving the template to the client, csaTemplate
will do the proper substitution. Since csaTemplate
is meant
to work on HTML templates, for every variable in the program environment
the function prepares also two extra names, with the ISO and URI encodings
of that variable. That is, given our $HOME
variable,
csaTemplate
will also provide $ISO_HOME
and
$URI_HOME
, so that in our template we can refer to any one
of the three versions of the same variable, depending on our needs.
The point is that for a substitution not to break HTTP and HTML
rules, some special characters in the original value need to be
encoded. Strings that are to appare in the various HTML elements should
be ISO-encoded, while those that are to be included in URL's need
to be URI-encoded. It will then be up to the template programmer to
refer to the correct encoding, depending on the specific location
in the template where the substitution is due to occur.
Back to the $HOME
example we will then have:
/home/goofy
/home/goofy
%2Fhome%2Fgoofy
Now it becomes clear why, in our csaOk.html
template,
the substitution tag is $[ISO_CSA_MSG_TEXT]
: it is the
ISO-encoded version of the $CSA_MSG_TEXT
variable
that was set in the environment by csaPrintMsg
, containing
the message text to be displayed.
Since the rc shell exports every variable to the environment by default, substitution tags may refer to any variable that is known to the program name space. Anything that is present in the program environment can be captured by an equally-named tag on a page template. And every variable will take on two additional names, prefixed by URI_ and ISO_, with the relevant encodings, as already explained. Although rc also axports function definitions to the environment, CSA by design does not make them available to the substitution process.
Tag names inside $[...] in templates SHOULD always be simple strings,
formed only
by those character that are acceptable as shell variable names.
That is, tags having names like $[HOME]
, $[my_tag]
and
$[goofy_123]
are ok, while $[minnie!]
,
$[my.tag]
and $[my@tag]
are not.
As explained, substitution tags are marked by the default "$[
"
and "]
" markers. The advantage of such defaults is that they
remain visible on the HTML page while it is being created, for the
Web author's convenience. However, such tags can be changed if desired,
by setting the relevant CSA environment variables $CSA_TPLTAGO
(tag opening marker) and $CSA_TPLTAGC
(tag closing marker).
For instance, one may want to mark tags in a way that parses correctly
with an XML or HTML parser. Back to our $HOME
example, if
we set $CSA_TPLTAGO
to "<csa:
" and $CSA_TPLTAGC
to "/>
", the csaTemplate
function will expect the
replacement point for HOME
in the template
to be "<csa:HOME/>
".
Of course such tag parses as well-formed XML before the substitution
occurs. Anyway, whatever tags you choose, the opening and closing
markers are always expected to be different, and you MUST NOT use
the same string for both.
Beside marking a variable substitution point, tags can also cause
whole files to be inserted in the template at the point where a
tag occurs. For this to happen, a tag must be interpreted as a file
path by the substitution engine. For instance, a tag like
$[/etc/passwd]
would cause csaTemplate
to
include your password file in the template, before the latter
is sent to the client. Not very safe, but it would work :-)
So keep on the safe side, files are encoded in Base64 by default
before being included at the point where the relevant tag occurrs.
If a different encoding, or no encoding at all, is desired, it
can be done by suffixing the tag with an appropriate encoding
option, separated from the tag name by a colon ":".
As an example, we can request our /etc/passwd
to be included
as-is (that is without any encoding) by using the tag
into $[/etc/passwd:c]
(here "c" stands for ``CDATA''). The
available encoding fleags are:
CDATA (character-data). That is, the requested file can be included verbatim in the specified context, with no need for encoding.
PCDATA (parsed character-data). The file needs to have a few special characters encoded before the inclusion, or the resulting templatemay be broken.
Here CDATA and PCDATA are taken from the XML terminology, and are used with the same meaning.
So far, so good. In reality, however, the name of the file to
be inserted is not known in advance, and it's name cannot therefore
be hard-coded into the tag. Usually, the file name is contained in a
program variable, say varname=/path/to/file
, and the
template will contain the tag $[varname]
. But given
the standard substitution mechanism, $[varname]
would
be replaced with the literal value /path/to/file
by
the substitution process, not by the file contents. To cope with
this, the csaTemplate
function can be called as follows:
csaTemplate --eval varname template.html
This tells csaTemplate
that whenever it encounters a
$[varname]
tag in template.html
, the value
associated with varname
must be substituted first, as usual,
but then a second substitution pass must be done on the resulting
tag. Since varname
contains /path/to/file
, the
first pass will turn $[varname]
into $[/path/to/file]
,
and this value will then be interpreted by the second pass as
a file-inclusion directive.
If multiple file-inclusion tags must be eval'ed in this way,
they can be passed to
csaTemplate
in a comma-separated list:
csaTemplate --eval varname1,varname2,... template.html
If a file tag contains a relative file path,
that is $[path/to/file]
,
the specified file is considered to be relative to the HTML template
directory $CSA_ROOT/forms/$CSA_LANG
by default,
or to whatever directory is specified with the --file-root
option of csaTemplate
.
An important part of the application response process, strictly
related to templates, is the possibility for the application programmer
to define a local AWK function, or hook, to perform additional
complex processing of environment variables before they are used by
csaTemplate
to make substitutions on the response template.
Prior to doing the substitutions, csaTemplate
first looks
for a special AWK library file associated with the application
program. In the case of our rpclib/hello-world
example script,
such file is sought for in lib/rpcio/hello-world.awk
.
Here rpcio
stands for "RPC I/O" library.
If that file exists, it is expected to be a valid AWK function
library, and to contain the AWK function outvar().
Each name/value pair which is present in the program environment
is first passed by csaTemplate
to the outvar()
function, which can process it as desired and then return the
modified value (or a null string) to the caller.
Since outvar() may need to store status information
between one call and the next one, it is allowed to use the
global CSA AWK array RPCOBUF[] (RPC Output Buffer) for
that purpose. After the
function has been called on the last name/value pair available, it is
called once more and passed the special name ENOMSG,
that signals that there are no more "messages" to process.
In this way the function knows that it will receive no more
stuff, and that it is time for it to do any closing actions that depend
on status information that has been accumulated so far in RPCOBUF[].
Please see section
C-like stuff
for more on the ENOMSG special name.
The standard CSA library provides functions that are needed to
handle concurrent access to the data by multiple programs, with the
objective of preserving
file integrity during update operations. Concurrency is
handled through the use of lock-files, or semaphores.
These can be created/destroyed with the relevant CSA library functions
csaLock
and csaUnlock
. Handling locks is a tricky subject,
due to the possibility of race-conditions that may lead to
data corruption. These can occur whenever we have a test operation
followed by an action based on the result of the test. This test-action
sequence is non-atomical, hence the potential for races.
The low-level driver used by csaLock
to perform
locking is lockfile, a utility especially conceived for
this purpose and that can safely be used also when the files to
be locked leave on NFS-mounts. By calling
lockfile with the proper command-line options,
the csaLock
function
tries also to cope gently with stale-locks, i.e. semaphores left
in place by processes that died prematurely.
Preventing dead-locks
is another issue that the library functions try to cope with, although
for this to be effective the CSA application program must have been
designed sensibly in the first place.
As a rule-of-thumb, semaphores MUST be set whenever any of the following conditions are met:
Whatever the locking policy, it is left entirely up to the CSA application program. The CSA library merely provides the locking primitives, but whether to lock individual files, whole directories, or other application-dependent collection of resources is an application-level affair from the CSA point of view.
Explicitly releasing lock-files is usually not necessary, as they
are removed automatically when a program calls csaExit
or
one of its variants (csaExit.ok
, csaExit.fault
,
etc.). Removing locks with an explicit call to csaUnlock
may be
necessary only if a program needs to iterate multiple times before
exiting, and in general when it is expected to go on for a while before
calling csaExit
. In such cases we SHOULD release the locks when
we no longer need them, or others may time-out while waiting to try
and set their own locks on the same resources.
There is a lot more to be said to use lock-files properly. I'll eventually expand this section a bit, to try and make it more useful.
An important REST-related feature of CSA is its capability to expose to clients a "logical representation" of CSA Web applications and resources. This is done through a URL rewriting mechanism, based on AWK regular expressions. For more details see the rest.map file distributed with CSA. Here I will give only a few examples to show what this mechanism can be useful for.
Let's say we have a CSA Web application (let's call it "depot"), that deals with querying a remote repository of items and enter purchase orders. The "depot" application is made up by three "methods":
According to the non-RESTful CSA-RPC model, the three methods will be invoked through URLs similar to the following:
By using suitable rewriting rules in the CSA rest.map file, we can present to clients a logical representation of such URLs, in terms of entities (names) rather than methods (verbs). For example:
Issuing a GET request against that URL will be rewritten inside CSA into
http://www.example.com/cgi-bin/cgiwrap/goofy/CSA?0=depot.list-parts
and will produce a response message containing a list of URLs to parts that are available for ordering, for instance:
Then, if a GET request is issued against one of the returned URLs, CSA will internally rewrite it as, say,
http://www.example.com/cgi-bin/cgiwrap/goofy/CSA?0=depot.show-part&partid=AB450
and the relevant catalogue entry (part detail) will be shown to the client. On the part detail page, an order form will be present, with a button to submit it through a POST operation. The URL associated with the POST will still be in RESTful format, for instance
http://www.example.com/cgi-bin/cgiwrap/goofy/CSA/Parts/AB450/
but by using POST against it, instead of GET, the action will be rewritten by CSA as
http://www.example.com/cgi-bin/cgiwrap/goofy/CSA?0=depot.order-part&partid=AB450
and this time it will result in an order for part AB450 to be entered.
The URL rewriting facilities provided by CSA are rather flexible, and it should be possible to rewrite almost anything into anything else.
The part of the URL up to the CGI name, that is
http://www.example.com/cgi-bin/cgiwrap/goofy/CSA
, cannot
be rewritten by CSA, which has no control over it, but only by the Web
server program, if the
one being used provides URL rewriting facilities (Apache does).
That is, if you want a URL like
http://www.example.com/cgi-bin/cgiwrap/goofy/CSA?0=depot.list-parts
to be addressable as http://www.example.com/Depot/
you
will need to have the Web server configured to rewrite it into
http://www.example.com/cgi-bin/cgiwrap/goofy/CSA/Parts/
and then CSA will be able to apply its own rewriting rules, as shown.