Getting Started With the Atmos C API

Introduction

This document provides an overview on how to use the new 2.1+ version of the Atmos C API.  The API has been completely overhauled to provide a clean, object-oriented interface for C99 compilers.

 

Installation

To get started, ensure your system has the following components installed:

  • A C99-compliant compiler.  This should include most compilers except MSVC.  On Windows, you should be able to download gcc from cygwin or MinGW and compile with that instead of MSVC.  If you're using managed code, you can also use the Atmos .NET library instead.
  • libcurl >= 7.12
  • libxml2 >= 2.4
  • openssl >= 0.9.8

 

Download the latest source tarball and extract:

 

Shell

MBP2:~ cwikj$ wget http://atmos-c.googlecode.com/files/atmos-c-2.1.0.tar.gz

MBP2:~ cwikj$ tar xzf atmos-c-2.1.0.tar.gz

MBP2:~ cwikj$ cd atmos-c-2.1.0

 

 

Run the configuration script, compile, and install:

 

Shell

MBP2-2:atmos-c-2.1.0 cwikj$ sh configure

checking for gcc... gcc

checking whether the C compiler works... yes

checking for C compiler default output file name... a.out

checking for suffix of executables...

checking whether we are cross compiling... no

 

...<snip>...

 

MBP2-2:atmos-c-2.1.0 cwikj$ make

make  all-recursive

Making all in xmlbind

make[2]: Nothing to be done for `all'.

Making all in dep/rest-client-c

 

...<snip>...

 

MBP2-2:atmos-c-2.1.0 cwikj$ sudo make install

Password:

Making install in xmlbind

../build-aux/install-sh -c -d '/usr/local/bin'

  /bin/sh ../libtool   --mode=install /usr/bin/install -c xmlbind '/usr/local/bin'

libtool: install: /usr/bin/install -c xmlbind /usr/local/bin/xmlbind

make[2]: Nothing to be done for `install-data-am'.

Making install in dep/rest-client-c

Making install in lib

 

...<snip>...

 

 

The headers and libraries will be installed on your platform's standard location for 3rd party libraries (generally /usr/local/include and /usr/local/lib).  You can override this by passing --prefix=/some/other/dir to the configure script.

 

OO API

The new C api uses an Object-Oriented pattern implemented using structs and some naming conventions.  The first member in every struct is the parent class's struct.  This allows you to freely cast a structure to any of its parent classes and access it directly.  Also, every class will have constructors and destructors named <class>_init() and <class>_destroy() respectively.  Every method for a class will be prefixed by the class name and the first argument will be a pointer to the class's struct.

 

The object structures themselves are designed to be either allocated on the stack or on the heap.  As a convienience, all constructors return the class object so you can malloc() the object inline when using heap objects.

 

Example: stack_object.c

stack_object.c

#include <atmos.h>

#include <stdio.h>

 

int main(int argc, char **argv) {

    AtmosResponse response;

 

    AtmosResponse_init(&response);

 

    // Cast it to superclass

    printf("Class name: %s\n", ((Object*)&response)->class_name);

 

    // Alternate using parent members

    printf("Class name again: %s\n", response.parent.parent.class_name);

 

    // Call a parent class's functions

    RestResponse_add_header((RestResponse*)&response,

            "Sample-Header: Sample-Value");

    printf("Value of Sample-Header: %s\n",

            RestResponse_get_header_value((RestResponse*)&response,

                    "Sample-Header"));

 

    AtmosResponse_destroy(&response);

 

    return 0;

}

 

Compile & execute

 

Shell

MBP2:atmostest cwikj$ gcc stack_object.c -I/usr/local/include -I/usr/include/libxml2/ \

-L/usr/local/lib -latmos -lrest -lxml2 -o stack_object

MBP2:atmostest cwikj$ ./stack_object

Class name: RestResponse

Class name again: RestResponse

Value of Sample-Header: Sample-Value

 

Example: heap_object.c

heap_object.c

#include <atmos.h>

#include <stdio.h>

 

int main(int argc, char **argv) {

    AtmosResponse *response;

 

    response = AtmosResponse_init(malloc(sizeof(AtmosResponse)));

 

    // Cast it to superclass

    printf("Class name: %s\n", ((Object*)response)->class_name);

 

    // Alternate using parent members

    printf("Class name again: %s\n", response->parent.parent.class_name);

 

    // Call a parent class's functions

    RestResponse_add_header((RestResponse*)response,

            "Sample-Header: Sample-Value");

    printf("Value of Sample-Header: %s\n",

            RestResponse_get_header_value((RestResponse*)response,

                    "Sample-Header"));

 

    AtmosResponse_destroy(response);

    free(response);

 

    return 0;

}

 

Compile & execute

 

Shell

MBP2:atmostest cwikj$ gcc heap_object.c -I/usr/local/include -I/usr/include/libxml2/ \

-L/usr/local/lib -latmos -lrest -lxml2 -o heap_object

MBP2:atmostest cwikj$ ./heap_object

Class name: RestResponse

Class name again: RestResponse

Value of Sample-Header: Sample-Value

 

Connecting to Atmos

The main entrypoint for users into the API is the AtmosClient class.  You will construct an instance of this class that points to your Atmos installation.  Once this object has been created, the easiest way to check your connection to Atmos is to use the AtmosClient_get_service_information() method.

 

Example: connect.c

 

Replace ATMOS_UID and ATMOS_SECRET below with your atmosonline credentials.

 

connect.c

#include <atmos.h>

#include <stdio.h>

 

#define ATMOS_UID "6c91fae4fc1f427a9b101ae92d35b0b7/A130672722730429efbb"

#define ATMOS_SECRET "itsasecrettoeverybody="

 

int main(int argc, char **argv) {

    AtmosClient atmos;

    AtmosServiceInfoResponse service_info;

 

    // Construct the AtmosClient

    AtmosClient_init(&atmos, "http://api.atmosonline.com", -1,

            ATMOS_UID, ATMOS_SECRET);

 

    // Init the response object.

    AtmosServiceInfoResponse_init(&service_info);

 

    // Call

    AtmosClient_get_service_information(&atmos, &service_info);

 

    // Check error

    if(!service_info.parent.atmos_error == 0) {

        printf("Error connecting to Atmos.  HTTP %d, Atmos %d: %s\n",

                service_info.parent.parent.http_code,

                service_info.parent.atmos_error,

                service_info.parent.atmos_error_message);

    } else {

        printf("Connected to Atmos version %s\n", service_info.version);

    }

 

    // Cleanup

    AtmosServiceInfoResponse_destroy(&service_info);

    AtmosClient_destroy(&atmos);

 

    return 0;

}

 

compile & execute

 

Shell

MBP2:atmostest cwikj$ gcc connect.c -I/usr/local/include -I/usr/include/libxml2/ \

-L/usr/local/lib -latmos -lrest -lxml2 -lcurl -o connect

MBP2:atmostest cwikj$ ./connect

Connected to Atmos version 2.0.1

 

If your secret key was incorrect, you would see the following:

 

Shell

MBP2:atmostest cwikj$ ./connect

Error connecting to Atmos.  HTTP 403, Atmos 1032: There was a mismatch between

the signature in the request and the signature computed by the server.

 

Writing and Reading an Object

The next sample will create a simple object with some content and read it back.

 

Example: object_test.c

 

Replace ATMOS_UID and ATMOS_SECRET below with your atmosonline credentials.

 

object_test.c

#include <atmos.h>

#include <stdio.h>

 

#define ATMOS_UID "6c91fae4fc1f427a9b101ae92d35b0b7/A130672722730429efbb"

#define ATMOS_SECRET "itsasecrettoeverybody="

 

#define OBJECT_CONTENT "Hello World!"

 

int main(int argc, char **argv) {

    AtmosClient atmos;

    AtmosCreateObjectResponse create_response;

    AtmosReadObjectResponse read_response;

    RestResponse delete_response;

 

    // Construct the AtmosClient

    AtmosClient_init(&atmos, "http://api.atmosonline.com", -1,

            ATMOS_UID, ATMOS_SECRET);

 

    // Create an Object

    AtmosCreateObjectResponse_init(&create_response);

    AtmosClient_create_object_simple(&atmos, OBJECT_CONTENT,

            strlen(OBJECT_CONTENT), "text/plain", &create_response);

 

    if(create_response.parent.atmos_error != 0) {

        printf("Error creating object.  HTTP %d, Atmos %d: %s\n",

                create_response.parent.parent.http_code,

                create_response.parent.atmos_error,

                create_response.parent.atmos_error_message);

        AtmosCreateObjectResponse_destroy(&create_response);

        AtmosClient_destroy(&atmos);

        return 1;

    } else {

        printf("Created Object %s\n", create_response.object_id);

    }

 

    // Read the Object back

    AtmosReadObjectResponse_init(&read_response);

    AtmosClient_read_object_simple(&atmos, create_response.object_id,

            &read_response);

    if(read_response.parent.atmos_error != 0) {

        printf("Error creating object.  HTTP %d, Atmos %d: %s\n",

                read_response.parent.parent.http_code,

                read_response.parent.atmos_error,

                read_response.parent.atmos_error_message);

        AtmosCreateObjectResponse_destroy(&create_response);

        AtmosReadObjectResponse_destroy(&read_response);

        AtmosClient_destroy(&atmos);

        return 1;

    } else {

        printf("Object content %s\n", read_response.parent.parent.body);

    }

    AtmosReadObjectResponse_destroy(&read_response);

 

    // Delete the object

    RestResponse_init(&delete_response);

    AtmosClient_delete_object(&atmos, create_response.object_id,

            &delete_response);

    // note that DELETE and PUT operations do not have a response body, so

    // you'll only get HTTP error codes.  Deletes will return HTTP 204 (No

    // content) on success.

    if(delete_response.http_code != 204) {

        printf("Error deleting object, HTTP %d: %s\n",

                delete_response.http_code, delete_response.http_status);

        AtmosCreateObjectResponse_destroy(&create_response);

        AtmosClient_destroy(&atmos);

        RestResponse_destroy(&delete_response);

        return 1;

    }

    RestResponse_destroy(&delete_response);

 

    // Cleanup

    AtmosCreateObjectResponse_destroy(&create_response);

    AtmosClient_destroy(&atmos);

 

    return 0;

}

 

Compile & Execute

 

Shell

MBP2:atmostest cwikj$ gcc object_test.c -I/usr/local/include -I/usr/include/libxml2/ \

-L/usr/local/lib -latmos -lrest -lxml2 -lcurl -o object_test

MBP2:atmostest cwikj$ ./object_test

Created Object 4ee696e4a31f549604f0b753961faa050d4d936e7bf5

Object content Hello World!

 

 

Uploading Object From a Local File

 

The last sample will upload a file from the local system and print out a "shareable URL" for the object that you can paste into your browser to view.  Note that when using shareable URLs, it's important to set the Content-Type of an object correctly so the browser will know how to render the object.

 

Example: upload.c

 

Replace ATMOS_UID and ATMOS_SECRET below with your atmosonline credentials.

 

 

upload.c

#include <atmos.h>

#include <stdio.h>

#include <errno.h>

#include <time.h>

 

#define ATMOS_UID "6c91fae4fc1f427a9b101ae92d35b0b7/A130672722730429efbb"

#define ATMOS_SECRET "itsasecrettoeverybody="

 

int main(int argc, char **argv) {

    AtmosClient atmos;

    AtmosCreateObjectResponse create_response;

    FILE *f;

    off_t filesz;

    time_t one_year;

    char *url;

 

    if(argc != 3) {

        printf("Usage: %s {filename} {content type}\n", argv[0]);

        return 1;

    }

 

    // Open the file

    f = fopen(argv[1], "r");

    if(!f) {

        printf("Failed to open file %s: %s\n", argv[1], strerror(errno));

        return 1;

    }

 

    // get the file size

    if(fseeko(f, 0, SEEK_END)) {

        printf("Failed to find end of file %s: %s\n", argv[1], strerror(errno));

        fclose(f);

        return 1;

    }

 

    filesz = ftello(f);

    if(filesz == -1) {

        printf("Failed to find end of file %s: %s\n", argv[1], strerror(errno));

        fclose(f);

        return 1;

    }

 

    rewind(f);

 

    // Construct the AtmosClient

    AtmosClient_init(&atmos, "http://api.atmosonline.com", -1,

            ATMOS_UID, ATMOS_SECRET);

 

    // Upload the file

    AtmosCreateObjectResponse_init(&create_response);

    AtmosClient_create_object_file(&atmos, f, filesz, argv[2],

            &create_response);

    if(create_response.parent.atmos_error != 0) {

        printf("Error creating object.  HTTP %d, Atmos %d: %s\n",

                create_response.parent.parent.http_code,

                create_response.parent.atmos_error,

                create_response.parent.atmos_error_message);

        AtmosCreateObjectResponse_destroy(&create_response);

        AtmosClient_destroy(&atmos);

        fclose(f);

        return 1;

    } else {

        printf("Created Object %s\n", create_response.object_id);

    }

    fclose(f);

 

    // Generate a shareable URL for it, valid for one year.

    time(&one_year);

    one_year += 3600*24*365;

    url = AtmosClient_get_shareable_url(&atmos, create_response.object_id, one_year, NULL);

    printf("Access your object for one year at: %s\n", url);

    free(url);

 

    // Cleanup

    AtmosCreateObjectResponse_destroy(&create_response);

    AtmosClient_destroy(&atmos);

 

    return 0;

}

 

 

Compile & Execute

 

 

Shell

MBP2:atmostest cwikj$ gcc upload.c -I/usr/local/include -I/usr/include/libxml2/ \

-L/usr/local/lib -latmos -lrest -lxml2 -lcurl -o upload

usenwhitej22l1:atmostest cwikj$ ./upload upload.c text/plain

Created Object 4ee696e4a21f549804f0b909b26c96050d4e7e66f2c6

Access your object for one year at: http://api.atmosonline.com/rest/objects/4ee696e4a21f549804f0b909b26c96050d4e7e66f2c6?uid=6c91fae4fc1f427a9b101ae92d35b0b7%2FA130672722730429efbb&expires=1387666283&signature=JnwNAjSgpoosIJ6eImqVrPLZX4E%3D

 

 

Pasting the link into your browser will show the uploaded file

 

Screen Shot 2012-12-21 at 4.51.37 PM.png

Low-Level Error Handling

If the http_status of a request is zero, this generally indicates that some low-level transport error occured like a DNS resolution failure, connection failure, etc.  In this case, there's two extra fields in RestResponse that contain the curl_error and curl_error_message.

Debugging

There are two notable options for debugging.  You can enable curl's "verbose mode" by adding the following handler:

 

Curl Verbose

    RestClient_add_curl_config_handler((RestClient*)&atmos,

            rest_verbose_config);

 

Also, you can turn on signature debugging on the AtmosClient.  This will print the "canonicalized request" string that is used to generate the signature:

 

Signature Debug

   atmos.signature_debug = 1;

 

Running upload.c with both these options enabled generates the following output:

 

Shell

MBP2:atmostest cwikj$ ./upload upload.c text/plain

Fri Dec 21 17:10:13 2012 - DEBUG atmos_client.c:34: String to Sign: POST

text/plain

 

 

Fri, 21 Dec 2012 23:10:13 GMT

/rest/objects

x-emc-uid:6c91fae4fc1f427a9b101ae92d35b0b7/A130672722730429efbb

Fri Dec 21 17:10:13 2012 - DEBUG atmos_client.c:35: With key: itsasecrettoeverybody=

Fri Dec 21 17:10:13 2012 - DEBUG atmos_client.c:42: Signature: brkRGeJeY8GFcIzkbNkoNdEgZzs=

* About to connect() to api.atmosonline.com port 80 (#0)

*   Trying 74.112.146.139...

* connected

* Connected to api.atmosonline.com (74.112.146.139) port 80 (#0)

> POST /rest/objects HTTP/1.1

Host: api.atmosonline.com

Accept: */*

Date:Fri, 21 Dec 2012 23:10:13 GMT

x-emc-uid:6c91fae4fc1f427a9b101ae92d35b0b7/A130672722730429efbb

Content-Type: text/plain

x-emc-signature:brkRGeJeY8GFcIzkbNkoNdEgZzs=

Content-Length: 2406

 

 

< HTTP/1.1 201 Created

< Server: Apache

< Content-Type: text/plain; charset=UTF-8

< Date: Fri, 21 Dec 2012 23:10:09 GMT

< location: /rest/objects/4ee696e4a11f549604f0b753939ef2050d4ec5162980

< x-emc-delta: 2406

< x-emc-policy: default

< Connection: Keep-Alive

< Content-Length: 0

<

* Connection #0 to host api.atmosonline.com left intact

* Closing connection #0

Created Object 4ee696e4a11f549604f0b753939ef2050d4ec5162980

Fri Dec 21 17:10:14 2012 - DEBUG atmos_client.c:34: String to Sign: GET

/rest/objects/4ee696e4a11f549604f0b753939ef2050d4ec5162980

6c91fae4fc1f427a9b101ae92d35b0b7/A130672722730429efbb

1387667414

Fri Dec 21 17:10:14 2012 - DEBUG atmos_client.c:35: With key: itsasecrettoeverybody=

Fri Dec 21 17:10:14 2012 - DEBUG atmos_client.c:42: Signature: ImmjmGAhyytx2NCgnONUHThKeJg=

Access your object for one year at: http://api.atmosonline.com/rest/objects/4ee696e4a11f549604f0b753939ef2050d4ec5162980?uid=6c91fae4fc1f427a9b101ae92d35b0b7%2FA130672722730429efbb&expires=1387667414&signature=ImmjmGAhyytx2NCgnONUHThKeJg%3D