cSOAP Implementation Guide $Revision: 1.3 $ FerhatAyaz 2004csoap
Introduction 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 09189 MyCity 12456 snowdrop passphrase64 ]]>