cSOAP Implementation Guide $Revision: 1.3 $FerhatAyaz2004csoapIntroduction
This document shows in short examples how to use cSOAP
for the client and server side implementation.
cSOAP is a simple client/server SOAP library to implement standalone
and embedded SOAP applications. cSOAP was implemented in pure C to
support platforms as far as possible.
The underlying xml layer is libxml2 (http://xmlsoft.org). We used
this library because it is fast and still maintained. You can find also
some documentations about using libxml2.
The transport of SOAP messages will be established via HTTP. cSOAP
contains the subproject "nanohttp" which will do all the HTTP specific
stuff. It implements a simple HTTP client and a server which can also
be used outside the cSOAP project.
Nameconvention in cSOAP
cSOAP contains the following simple modules:
+ client : soap-client.h
+ server : soap-server.h
+ ctx : soap-ctx.h
+ env : soap-env.h
+ fault : soap-fault.h
+ router : soap-router.h
+ service : soap-service.h
Each module declares the functions in the following name convention:
_();
]]>
For example: The function to invoke a SOAP call from the client side:
herror_t soap_client_invoke(....);
Error handling
Almost all function will return a "herror_t" object. If the function
returns with success this object is H_OK. Another herror_t object
will be returned otherwise. Following functions can be used with a
returned herror_t object:
herror_code() : Returns the error code
herror_func() : Returns the function name, where the error occured
herror_message() : Returns the human readable error message
herror_release() : Frees the herror_t object.
herror_t err = soap_client_invoke(...);
if (err != H_OK) {
printf("Message: %s\n", herror_message(err));
printf("Error code: %d\n", herror_code(err));
printf("In function: %s\n", herror_func(err));
herror_release(err);
}
Note that you "must" call herror_release() to free the resources.
The error codes are defined in "nanohttp-common.h". Here some examples:
#define HSOCKET_ERROR_CREATE 1001
#define URL_ERROR_UNKNOWN_PROTOCOL 1101
#define XML_ERROR_PARSE 1601
You can also create your own herror_t object with herror_new().
#define MY_ERROR_TEST 5001
herror_t err = herror_new("my_func()",
MY_ERROR_TEST,
"Size %d is greater then %d", 17, 13);
User defined error codes must be greater then 5000.
Implementing a simple client
One of the advantages of cSOAP is its simplicity. You can implement
a client or a server in a few minutes. The steps are always indentical.
1. Initialize cSOAP client
soap_client_init_args()
2. Create a SoapCtx object
soap_ctx_new_with_method()
3. Fill the envelope
soap_env_add_item()
soap_env_add_itemf()
soap_env_add_attachment()
soap_env_add_custom()
soap_env_push_item()
soap_env_pop_item()
4. Invoke
soap_client_invoke()
5. Process returned SoapCtx object
6. Clean up cSOAP client
static const char *url = "http://localhost:10000/csoapserver";
static const char *urn = "urn:examples";
static const char *method = "sayHello";
int main(int argc, char *argv[])
{
SoapCtx *ctx, *ctx2;
herror_t err;
/* 1. Initialize cSOAP client */
soap_client_init_args(argc, argv);
/* 2. Create a SoapCtx object */
soap_ctx_new_with_method(urn, method, &ctx);
/* 3. Fill the envelope */
soap_env_add_item(ctx->env, "xsd:string", "name", "Jonny B. Good");
/* 4. Invoke */
soap_client_invoke(ctx, &ctx2, url, "");
if (err != H_OK) {
log_error4("[%d] %s(): %s ",
herror_code(err),
herror_func(err),
herror_message(err));
herror_release(err);
soap_ctx_free(ctx);
return 1;
}
/* 5. Process returned SoapCtx object */
soap_xml_doc_print(ctx2->env->root->doc);
/* 6. Clean up cSOAP client */
soap_ctx_free(ctx2);
soap_ctx_free(ctx);
soap_client_destroy();
return 0;
}
]]>
Important: Note that we have omitted error handling. The complete
code can be found under examples/csoap/simpleclient.c
Implementing a simple server
Before you start to implement a SOAP server, you must understand
the architecture how you publish you web service .
* Each URL represent a router service.
* Each router service contains one or more user services.
* Each user service is a C function with the following signature:
typedef herror_t (*SoapServiceFunc)(SoapCtx*, SoapCtx*);
The first SoapCtx object is the request, the second SoapCtx object
is the response, which must be filled by the service function
"SoapServiceFunc". The function must return H_OK if success.
The steps to implement a SOAP server described below:
1. Init cSOAP server
2. Create and register a router
3. Register you service (C function)
4. Enter server loop
5. Clean up cSOAP server
The above steps can be shown in the the following example:
env, &res->env);
return H_OK;
}
int main(int argc, char *argv[])
{
herror_t err;
SoapRouter *router;
/* 1. Init cSOAP server */
soap_server_init_args(argc, argv);
/* 2. Create and register a router */
router = soap_router_new();
soap_server_register_router(router, url);
/* 3. Register you service (C function) */
soap_router_register_service(router, say_hello, method, urn);
/* 4. Enter server loop */
soap_server_run();
/* 5. Clean up cSOAP server */
soap_server_destroy();
return 0;
}
]]>
You can see how to implement a soap server easily!
Important: Note that we have omitted error handling. The complete
code can be found under examples/csoap/simpleserver.c
Implementing a simple client with attachment
File are added to the context using soap_ctx_add_file().
The following example is a little bit modified version of the above
simpleclient example.
env,"source", href);
/* Send soap request to the server */
soap_client_invoke(ctx, &ctx2, url, "");
/* Handle response (just print to the screen) */
fault = soap_env_get_fault(ctx2->env);
if (fault) {
...
} else {
...
}
/* Clean up */
soap_ctx_free(ctx2);
soap_ctx_free(ctx);
soap_client_destroy();
return 0;
}
]]>
The difference between soap_ctx_add_file() and soap_env_add_attachment()
is, that soap_ctx_add_file() adds the file to the context. In our case,
this is a MIME message. soap_ctx_add_file() fills the "href" field with
a generated reference id. (Content-ID: [href]) This id can be used to
point to the added file from a SOAP message using soap_env_add_attachment().
Here a simple example to understand. Look at the following MIME message:
]]>
Implementing a simple server with attachment
Attachments are represented as an "attachments_t" object. An
"attachments_t" object is a list of parts. A part is the physical file
on the filesystem.
typedef struct _attachments
{
part_t *parts;
part_t *last;
part_t *root_part;
}attachments_t;
You don't have to care about this object becaus it is a part of
SoapCtx.
typedef struct _SoapCtx
{
SoapEnv *env;
attachments_t *attachments;
}SoapCtx;
The following example shows how to use attachment in a service function
(C function).
env, &res->env);
if (err != H_OK) {
return err;
}
if (req->attachments)
{
for (part = req->attachments->parts; part != NULL; part = part->next)
{
soap_ctx_add_file(res, part->filename, part->content_type, href);
soap_env_add_attachment(res->env, "echoFile", href);
}
}
return H_OK;
}
]]>Building an envelope using the libxml2 API
One of the ways building a xml tree into an SOAP envelope is to use
directly the libxml2 API. You can obtain the xmlNodePtr of an envelope
using the SoapEnv structure.
typedef struct _SoapEnv
{
xmlNodePtr root;
xmlNodePtr cur;
}SoapEnv;
Here is "root" your xml node to ]]>.
Building an envelope using envelope functions
You can build a xml tree using following functions
xmlNodePtr
soap_env_add_item(SoapEnv* env, const char *type,
const char *name, const char *value);
xmlNodePtr
soap_env_add_itemf(SoapEnv* env, const char *type,
const char *name, const char *value, ...);
xmlNodePtr
soap_env_push_item(SoapEnv *env, const char *type,
const char *name);
void
soap_env_pop_item(SoapEnv* env);
soap_env_add_itemf() does the same thing like soap_env_add_item()
but with a C style argument list. (Max buffer for value is 1054)
The next example shows, how to use the stack pattern of cSOAP.
env;
soap_env_push_item(env, "my:user", "User");
soap_env_add_item(env, "xsd:string", "id", "09189");
soap_env_push_item(env, "my:adress", "Adress");
soap_env_add_item(env, "xsd:string", "City", "MyCity");
soap_env_add_item(env, "xsd:int", "Zip", "%d", 12456);
soap_env_pop_item(env);
soap_env_add_item(env, "xsd:string", "name", "snowdrop");
soap_env_add_item(env, "xsd:string", "passwd", "passphrase64");
soap_env_pop_item(env);
]]>
This will create the following xml structure
09189MyCity12456snowdroppassphrase64
]]>