cson  Update of "cson_cgi_HowTo"

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview

Artifact ID: 47833bc5f2261ae6cc27c675f51ace852ce8c6f4
Page Name:cson_cgi_HowTo
Date: 2011-05-09 17:58:44
Original User: stephan
Parent: f78086f0c17a6aa3490c72bbc6271dd1d07ee4c8
Content

ACHTUNG: THIS PAGE IS NOW MAINTAINED IN THE NEW WIKI: 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.

Initialization

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 NULL 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 cgi-test.c, 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 not interpretted by the library, only converted as-is to JSON form.
  • System environment data (i.e.. what you can get via getenv()).
  • 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...

Using the Environment Data

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 case-insensitive 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 cson_cgi_getenv(), 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 cson_cgi_getenv(). 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.

BE VERY AWARE 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.

Generating Output Data

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 cson_object 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.

Setting Cookies

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 (normally, but not always) clean it up himself. Note that the library reference-counts cson_value 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 critical that these data structures not form any circular references, however - those are Strictly Illegal in this library and will cause endless loops and/or double free()s.

Configuring

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

Cleaning up

The library installs an atexit() 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 main() returns, call:

cson_cgi_cleanup_lib();

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

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