cson  Artifact Content

Artifact 47833bc5f2261ae6cc27c675f51ace852ce8c6f4:

Wiki page [cson_cgi_HowTo] by stephan 2011-05-09 17:58:44.
D 2011-05-09T17:58:44.395
L cson_cgi_HowTo
P f78086f0c17a6aa3490c72bbc6271dd1d07ee4c8
U stephan
W 10075
<strong>ACHTUNG: THIS PAGE IS NOW MAINTAINED IN THE NEW WIKI:</strong> [http://whiki.wanderinghorse.net/wikis/cson/?page=cson_cgi_HowTo]

The [cson_cgi] workflow looks something like this:

   *  Initialize the library.
   *  Client app uses the data provided by the library and generates output in the form of a JSON object tree.
   *  Client tells the library to generate the output (i.e. HTTP headers and JSON content).

The rest of this page demonstrates how to do those things.


Before the library is used it must be initialized, which is done like this:

int main( int, argc, char const * const * argv ) {
    int rc = cson_cgi_init( argc, argv,
                            NULL /* this can be used to specify several options,
                                    including the configuration file. */);
    if( 0 != rc ) {
        Only fatal errors are returned (non-fatals are not reported to
        the client). Do not use the cson_cgi_xxx() APIs after this!
        The most significant fatal error is a memory allocation error
        (which is highly unlikely here unless the computer is completely
        overloaded). Some errors, like a problem while loading a stored
        session, are not reported as errors here because the client app can
        conceivably continue without it. We may someday add a mechanism to
        report such problems back to the client.
        return rc;
    return 0;

The <tt>NULL</tt> parameter (not) shown above can be used to specify
various options used by the library, such as the configuration file
(currently needed if you want to enable sessions support) and various
JSON formatting options for generated output. See the sample application
<tt>cgi-test.c</tt>, in the source tree, for a detailed example.

The initialization reads in various external sources of data and formats them
as JSON object/array trees using the core cson API. These data include:

   *  Command line arguments, as a JSON array of strings. They are <em>not</em> interpretted by the library, only converted as-is to JSON form.
   *  System environment data (i.e.. what you can get via <tt>getenv()</tt>).
   *  HTTP request cookies, if available.
   *  HTTP GET parameters, if available.
   *  HTTP POST data, if it is unencoded JSON text. (TODO: parse form-urlencoded data in the same way the GET parameters are handled.)

The various data (generally referred to as "environment data") is
available to the client application, as demonstrated in the next section...

<h1>Using the Environment Data</h1>

Fetching the environment data is really easy (we write it in long form here
only to more clearly describe the parameters):

char const * key = "myKey";
char const * environmentList = "gcp"; // described below
cson_value * v = cson_cgi_getenv( environmentList, key );
// v == NULL if no entry found

The first parameter is a string which contains the one-letter alias(es) of
the various available environments. Each data set is associated with a single
<em>case-insensitive</em> letter, as follows:

   *  "c" = HTTP cookies, as a JSON Object.
   *  "g" = HTTP GET, as a JSON Object.
   *  "p" = HTTP POST, read in from raw JSON (Object or Array).
   *  "e" = System environment variables, as a JSON Object.
   *  "s" = Application session (if sessions support is enabled).
   *  "f" = Application configuration file.
   *  "a" = Application-specific data. This is a reserved placeholder which the API guarantees not to use for itself. Clients may store arbitrary JSON data here.

When using <tt>cson_cgi_getenv()</tt>, the environments are searched in the order
provided in the first argument to that function. Thus "gcp" would first search the GET
parameters, then the cookie data, then the POST data, stopping at the first one
which contains a value with the given key.

It is also possible to fetch the individual environment objects, like so:

cson_value * cookies = cson_cgi_env_get_val( 'c', 0/*==do not create*/ );
// cookies might be NULL here because of the second argument.
cson_value * get = cson_cgi_env_get_val( 'g', 1/*==create if it doesn't already exist*/ );
// get will only be NULL here on an allocation error.
cson_object * getObj = cson_value_env_get_obj( 'g', 0 );
assert( cson_value_get_object( get ) == getObj ); // just to demonstrate the relationship

The (single, case-insensitive) letter passed here can be any of those described for <tt>cson_cgi_getenv()</tt>. The second parameter specifies whether the underlying object should be created if needed (it will be of type Object). If the environment object already exists, that parameter is ignored, otherwise the object will be created if the second parameter is true (non-zero). If the second parameter is non-zero and NULL is returned then it means either the first argument was invalid or a memory allocation error occurred.

<em>BE VERY AWARE</em> that creating an environment object this way might not have the same effect as if it had been created by the library during initialization. A good example is the 'f' (config file) environment. Passing non-zero as the second argument (as shown above) would create a JSON object in which to hold the config information, but it does not actually create a new file (it does not save the contents of the new JSON object anywhere). Likewise for the 's' (session) environment. That said, such "extraneous creation" is typically harmless, and can simplify usage of the library for cases where the client doesn't care if the JSON object representing the configuration file really came from a file or not.

<h1>Generating Output Data</h1>

There are two approaches. The first (simplest) is to allow the library to create a root JSON object, and then populate that object from client code:

cson_value * v = cson_cgi_response_root_get( 1 /*==create if needed*/);
cson_object * obj = cson_value_get_object( v );

The root object is owned by the API, and must not be destroyed by the caller.
The <tt>cson_object</tt> API can be used to fetch data from or modify the root
output object.

The second approach is to create your own Object or Array and set it as the output root:

cson_value * v = cson_value_new_array();
cson_array * ar = cson_value_get_array( v );
int rc = cson_cgi_response_root_set( v );
if( 0 != rc ) {
        // Error! This means we still own v.
        cson_value_free( v );
        ... return ...
// cson_cgi now owns v, and will clean it up properly when the app exits
// or when cson_cgi_response_root_set() is called with a different value.

... now populate the array ...

Once the root is in place, we can output it, along with any HTTP headers we need to generate (e.g. setting cookies), like this:

int rc = cson_cgi_response_output_all();
// there is no generic recovery strategy from an output error, so the error code
// can probably be ignored, except possibly to log it.

<h1>Setting Cookies</h1>

Cookies can be set like this:

// Value for our cookie (currently only POD types, not Objects/Arrays):
cson_value * v = cson_value_new_integer(42);

// The simple approach: create a session cookie with no extra parameters:
int rc = cson_cgi_cookie_set( "myKey", v );

// The advanced approach:

rc = cson_cgi_cookie_set2( "myKey", v,
                           "mydomain.com", "/path",
                           time(NULL)+(60*60*24/*==1 day*/) /* expiry time */,
                           0 /*non-0 == "secure" (HTTPS only) */,
                           1 /*non-0 == "HttpOnly" */ );

These functions can fail (with a non-0 return value) on allocation error or if any arguments are invalid (e.g. the key is NULL or empty). If it succeeds then (like the rest of the cson container-related API), it passes ownership of the given value to the underlying container (a JSON object holding the cookie key/value pairs). On error the caller still owns the value and must (<em>normally, but not always</em>) clean it up himself. Note that the library reference-counts <tt>cson_value</tt> instances as they are added to/removed from objects and arrays, so a single value instance can be in multiple places at one time, as in this contrived example:

cson_object * session = cson_cgi_env_get_obj( 's', 1 );
cson_object * config = cson_cgi_env_get_obj( 'f', 1 );

cson_value * v = cson_value_new_array();

cson_object_set( session, "reference1", v );
cson_object_set( config, "reference2", v );

In such a case, any changes made to the underlying JSON Array object will be visible in both the session and the config environments (the 's' and 'f' arguments, respectively). It is <em>critical</em> that these data structures not form <em>any</em> circular references, however - those are <em>Strictly Illegal</em> in this library and <em>will</em> cause endless loops and/or double <tt>free()</tt>s.


TODO: show an example. Until then, see the file <tt>cgi-test.json</tt> for the one used by the <tt>cgi-test.c</tt> sample application.

<h1>Cleaning up</h1>

The library installs an <tt>atexit()</tt> handler when it is initialized, which will clean up
all resources used by the library, save the application session, etc.

If you want to force the library to clean (and save the session) up before <tt>main()</tt> returns, call:


but it is not necessary to do so. The installed <tt>atexit()</tt>
handler will clean up, provided the program terminates normally.

After calling <tt>cson_cgi_cleanup_lib()</tt> the client <em>must not make any further
use of the cson_cgi API</em>. Violating this rule leads to undefined results, very possibly
a crash caused by dereferencing cleaned-up objects.

Z d3f4aef0c8195c140c4bff30f569d2cb