Getting Started with gSOAP

Introduction to XML data bindings with gSOAP {#bindings} --- XML is a universally accepted data format to push structured information along, just as many other structured data formats are designed to do. But XML has an advantage over so-called "schemaless" data formats: XML schemas describe types, elements, and attributes with their properties to validate XML content. Validation gives us a strong safety net that ensures that invalid content is automatically rejected, before the data is processed by the application. XML schemas also allow us to build larger and more complex data structures and systems from other schemas as building blocks, without having to worry about name and type clashes. To benefit most from XML (and benefit from using Web service protocols that are built on top of XML) we should strongly consider using **XML data bindings** when working with XML. XML data bindings for C/C++ bind XML schema types to C/C++ types. So integers in XML are bound to C integers, strings in XML are bound to C or C++ strings, complex types in XML are bound to C structs or C++ classes, and so on. So we know for sure that the structured data you create and accept will fit the data model and is type safe. In other words, by leveraging strong typing in C/C++, your XML data meets XML validation requirements **and** satisfies XML interoperability requirements. This article presents the basics to understand the XML data bindings for C and C++ with gSOAP. We give an overview of the gSOAP tools and libraries and present example client and server applications. To download the gSOAP toolkit, see [download and installation.](http://www.genivia.com/downloads.html) To learn more after reading this article, visit [XML data bindings.](http://www.genivia.com/doc/databinding/html/index.html) Consider for example the following XML schema with an `employee` type defined in an `HR` schema namespace: [xml] <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="HR" xmlns:tns="HR"> <complexType name="employee"> <sequence> <element name="manages" type="tns:employee" minOccurs="0" maxOccurs="12"/> </sequence> <attribute name="name" type="string"/> <attribute name="ID" type="int" use="default" default="9999"/> </complexType> <element name="employee" type="tns:employee"/> </schema> This schema describes the following example XML document, also called an instance of the schema: [xml] <ns:employee xmlns:ns="HR" name="Jane Doe" ID="123"> <manages name="John Smith" ID="456"/> <manages name="Mike Johnson" ID="789"/> </ns:employee> The gSOAP XML data binding in C++ for this complex type is a `ns__employee` class with corresponding data members: #import "gsoap/import/stlvector.h" // import std::vector XML data binding //gsoap ns schema namespace: HR class ns__employee { public: @std::string name 1; // required name attribute @int ID 0 = 9999; // optional ID attribute, defaults to 9999 std::vector<ns__employee*> manages 0:12; // up to 12 <manages> elements }; This **data binding interface** is auto-generated from the example schema with the **wsdl2h** tool. But you can also declare your own interface to make your C/C++ types XML serializable. The interface shown above declares a `ns__employee` class with a required name (`1` means that member occurs exactly once in XML content), an integer ID with default value `9999` which is used when the value is omitted (`0` means may be optional in XML content), and up to twelve employees whom he or she manages (`0:12` means up to 12 occurrences). Note the use of a prefix `ns` in the `ns__employee` class. We use this convention to bind XML namespace prefixes to C/C++ types, member names, and to Web service functions. A prefix is not absolutely required to serialize types in XML, but it is strongly recommended to ensure that the type is bound to a schema. - Note 1: a colon can be used to prefix names in data bindings. For example `ns:employee` instead of `ns__employee`. However, the colon notation cannot guarantee the absence of C/C++ name clashes caused by identical names defined in different XML namespaces (e.g. `ns:employee` clashes with `xy:employee` in C/C++, yet they are different tags in XML). - Note 2: the `//gsoap` directives and class member annotations are part of the data binding specification that is auto-generated from WSDLs and schemas with **wsdl2h**. These annotations are used to generate the **data binding implementation** code with **soapcpp2**. You can set various properties of the schema using [directives](http://www.genivia.com/doc/databinding/html/index.html#directives). An XML data binding makes it easy to write code to read, write, and manipulate XML-sourced data. The binding allows you to populate and extract XML data by working on equivalent C/C++ data structures. The C/C++ compiler effectively checks that the types and names of the structures are consistent with their schema definitions. As an example, suppose we create a `boss` instance of the `ns__employee` class and add an employee to the list of employees that the boss manages, where the new employee is read from an XML file: #include "soapH.h" // include auto-generated data bindings #include "ns.nsmap" // include auto-generated XML namespaces struct soap *soap = soap_new(); // a new gSOAP runtime engine context, for memory management, IO, etc. ns__employee boss; boss.name = "Jane Doe"; boss.ID = 123; ns__employee temp; soap_read_ns__employee(soap, &temp); // read temp XML data from stdin (auto-generated function) boss.manages.push_back(&temp); soap_write_ns__employee(soap, &boss); // write boss XML data to stdout (auto-generated function) soap_destroy(soap); // delete temp deserialized data soap_end(soap); // delete other deserialized data soap_free(soap); // we're done First, use the **soapcpp2** tool on the data binding interface `hr.h` shown earlier to generate **data binding implementation** code with efficient (i.e. streaming) **XML serializers** for the C/C++ types in your application. Then compile the generated code `soapC.cpp` with your code and the `stdsoap2.cpp` gSOAP engine: [command] soapcpp2 hr.h c++ -o hr hr.cpp soapC.cpp stdsoap2.cpp The auto-generated functions for each serializable C/C++ type use function names that follow simple naming conventions, see auto-generated [operations on classes and structs.](http://www.genivia.com/doc/databinding/html/index.html#toxsd9-13). There are only a few auto-generated functions per serializable type that you will ever need to use. You don't need to read and understand what is in `soapH.h` and `soapC.cpp` that implement these functions for each serializable type. For example, the auto-generated functions to create, read, write, and deep copy and delete data for each C/C++ type declared in the data binding interface are used below on the `ns__employee` class: // managed instantiation supporting automatic deletion ns__employee *employee = soap_new_ns__employee(soap); // instantiate and set only the required (as per schema) member(s) ns__employee *employee = soap_new_req_ns__employee(soap, "Jane Doe"); // also std::string is a serializable type and has a managed instantiator std::string *name = soap_new_std__string(soap); *name = "Jane Doe"; // instantiate and set all members of an employee std::vector<ns__employee*> manages; ns__employee *employee = soap_new_set_ns__employee(soap, name, 123, manages); // write to std::cout via std::ostream* soap->os (std::istream* soap->is is for reading) soap->os = &std::cout; if (soap_write_ns__employee(soap, employee)) ... // handle IO error soap->os = NULL; // reset to use default streams // read from a file via soap->recvfd (soap->sendfd is for writing) soap->recvfd = open("employee.xml", O_RDONLY); if (soap_read_ns__employee(soap, employee)) ... // handle IO error close(soap->recvfd); soap->recvfd = 0; // reset to stdin // deep copy employee to another managing context (soapcpp2 option -Ec to generate) struct soap *other_soap = soap_new(); ns__employee *employee_copy = employee->soap_dup(other_soap); // reset employee to default member values employee->soap_default(soap); // delete all objects managed by the context, must be called before soap_end(soap) soap_destroy(soap); // free all data allocated with soap_malloc and internal data soap_end(soap); To serialize XML to strings and parse XML from strings you can set the C++ streams `soap->os` and `soap->is` to point to `std::stringstream` objects: #include "soapH.h" #include "ns.nsmap" #include <sstream> std::stringstream ss; struct soap *soap = soap_new(); // write to string via std::ostream* soap->os soap->os = &ss; if (soap_write_ns__employee(soap, employee)) ... // handle IO error soap->os = NULL; ... = stream.str(); // read from string via std::istream* soap->is ss.str("<ns:employee ...>...</ns:employee>"); soap->is = &ss; if (soap_read_ns__employee(soap, employee)) ... // handle IO error soap->is = NULL; For gSOAP 2.8.28 and later, serializing XML to C `char*` strings and parsing XML from C strings and buffers is fairly easy. You can simply set `soap->os` to point to an output string and set `soap->is` to point to an input string to parse from: #include "soapH.h" #include "ns.nsmap" struct soap *soap = soap_new(); // C string we want to set to the NUL-terminated string with XML output const char *cs = NULL; soap->os = &cs; if (soap_write_ns__employee(soap, employee)) ... // handle IO error soap->os = NULL; ... = cs; // use the string (do not free(cs): cs is managed by context and freed with soap_end()) // C string we want to read from soap->is = "<ns:employee ...>...</ns:employee>"; if (soap_read_ns__employee(soap, employee)) ... // handle IO error soap->is = NULL; SOAP/XML and REST-XML service operations essentially work the same way and require very few lines of code to send and receive data serialized in XML. Most of the SOAP/XML processing logic and details are hidden, allowing you to focus on your application logic. Continue reading the next section to learn more. The gSOAP tools also support JSON and XML-RPC data formats and services. See our [tutorials](tutorials.html) and [XML-RPC and JSON/JSONPath.](doc/xml-rpc-json/html/index.html) To learn more about XML data bindings, see [XML data bindings.](http://www.genivia.com/doc/databinding/html/index.html) To learn more about the tools and libraries, see the [overview of gSOAP tools and libraries.](#overview) To get gSOAP, visit [download and installation.](downloads.html) Introduction to Web Services with gSOAP {#services} --- You can implement client and server-side XML Web services in C or in C++ without much coding effort by taking advantage of the gSOAP autocoding tools to implement the Web services API for you. This introduction presents the basics that you will need to know to get started quickly developing your own services and to consume services. Use wsdl2h to convert a set of WSDL and/or XSD files to a gSOAP header file: [command] wsdl2h [options] -o file.h ... XSD and WSDL files ... This command converts WSDL and XSD files to C++ (or pure C with wsdl2h option `-c`) and saves the declarations in `file.h`. This special header file file uses familiar C/C++ syntax extended with directives and annotations that are used by the soapcpp2 tool to implement the Web services API: soapcpp2 [options] file.h This command generates the data binding implementation and the Web services API for the client-side and/or server-side. The default option is to generate both client and server code in `soapClient.c[pp]` and `soapServer.c[pp]`, respectively. These files implement the Web service API declared in `file.h`, meaning that the service API functions in `file.h` of the form: int prefix__func(arg1, arg2, ..., argn, result); are implemented in the auto-generated `soapClient.c[pp]` as client-side stub functions: int soap_call_prefix__func(struct soap*, const char *URL, const char *action, arg1, arg2, ..., argn, result); where `URL` is the Web service endpoint URL to connect to and `action` is a SOAP action header (you can use NULL for the default URL and action defined by the service). The `arg` service request parameters are values serialized in XML and sent with the request to the Web service. The `result` is the service response, usually a struct with data members for the service response parameters. The auto-generated `soapServer.c[pp]` implements a service dispatcher: int soap_serve(struct soap*); You can use this dispatcher directly to deploy CGI-hosted services or put it in a loop to accept incoming requests on a port (see the server examples in this article). You should also implement all of the service API functions declared in `file.h` but with an additional first argument: int prefix__func(struct soap*, arg1, arg2, ..., argn, result); By convention, gSOAP functions take the engine context `struct soap*` as the first argument and return `SOAP_OK` (zero) or an error code. To display an error, use: void soap_print_fault(struct soap*, FILE*); void soap_stream_fault(struct soap*, std::ostream&); To build your client and server, you will also need to compile `soapC.c[pp]` and `stdsoap2.c[pp]` (or link with a library, see [overview of tools and libraries](#overview)). You can generate more powerful C++ proxy and service classes with soapcpp2 option `-j`. Otherwise, your C++ code will be similar to C code with stub functions and a service dispatcher. With option `-j`, the `NameProxy` class is saved in `soapNameProxy.h` and `soapNameProxy.cpp`. The `NameService` class is saved in `soapNameService.h` and `soapNameService.cpp`. The details of the auto-generated class-based APIs are saved in the proxy and service header files, which we will not describe in detail here, but that are used in the C++ client and server examples in this article. We also recommend that you read about the examples below to understand how a [C++ client](#client-cpp) and a [C++ server](#server-cpp) are implemented (both with option `-j`), and how a [C client](#client-c) and a [C server](#server-c) are implemented. Overview of gSOAP tools and libraries {#overview} --- After installation of the software, you will get several development tools and libraries: - The **wsdl2h** tool translates WSDL and XSD files to a gSOAP header file with the **data binding interface**. - The **soapcpp2** tool takes a header file with the data binding interface and generates the **data binding implementation** with **XML serializers** to implement Web services and XML data bindings. The generated code is platform independent and portable. - The **runtime engine** handles HTTP and XML transport over any IO device and sockets and is responsible for memory allocation. The runtime is configured per platform and declared in `stdsoap2.h` and implemented in `stdsoap2.c` (for C) and `stdsoap2.cpp` (for C++). Also, gSOAP installs `libgsoap`, `libgsoassl`, `libgsoap++`, and `libgsoapssl++` libraries, see further below. - The **XML DOM API** and **DOM parser** are implemented in `dom.c` (for C) and `dom.cpp` (for C++). XML DOM is used by the WS-Security plugin and to store xsd:anyType and xsd:any XML data. The gSOAP DOM API offers a hybrid **DOM + data binding approach** that allows you to embed serializable C/C++ data types in a DOM node graph. See [XML DOM and XPath.](http://www.genivia.com/doc/dom/html/index.html) - The new **domcpp** tool generates C or C++ source code to parse, search, manipulate, and write raw XML using the DOM API and DOM parser. The tool takes an XML file or XPath query. The domcpp tool is part of the XML DOM examples in `gsoap/samples/dom` in the download package. See [XML DOM and XPath.](http://www.genivia.com/doc/dom/html/index.html) - The **XML-RPC** and **JSON** libraries and examples are located in `gsoap/samples/xml-rpc-json` in the download package. See [XML-RPC and JSON/JSONPath.](http://www.genivia.com/doc/xml-rpc-json/html/index.html) - The new **jsoncpp** tool generates C or C++ source code to parse, manipulate, and write JSON data. The tool takes a JSON file or JSONPath query. See [XML-RPC and JSON/JSONPath.](http://www.genivia.com/doc/xml-rpc-json/html/index.html) - Many other demonstration examples are located in `gsoap/samples` in the download package to get you started. The wsdl2h tool does exactly what the name suggests: it translates WSDL files into .h header files with data binding interface declarations of services and their C/C++ data. The wsdl2h tool is a gSOAP application itself and uses data binding code generated for the the schemas of WSDL specifications and the W3C XML schema-of-schemas. The soapcpp2 tool runs as a C/C++ preprocessor on a .h header file with the data binding interface to generate the source code "glue" that implements services and XML data bindings with XML serializers for your projects. It is perfectly possible to use soapcpp2 without wsdl2h by declaring services and XML data binding types directly in familiar C/C++ syntax. In this case, the soapcpp2 tool also generates WSDL and XSD files that describe the services and data bindings. The gSOAP runtime engine is also installed as a library compiled from `stdsoap2.c` + `dom.c` and `stdsoap2.cpp` + `dom.cpp` that you can link to your project code: - `gsoap/libgsoap.a` the C runtime engine (plain and compact, no HTTPS) - `gsoap/libgsoapssl.a` the C runtime engine with DOM support, cookies, zlib, and SSL - `gsoap/libgsoap++.a` the C++ runtime engine (plain and compact, no HTTPS) - `gsoap/libgsoapssl++.a` the C++ runtime engine with DOM support, cookies, zlib, and SSL The extended SSL versions of the library are recommended to use in your project, since HTTPS and HTTP compression are widely in use (but cookies are often ignored). If you want to use the source code of the runtime engine and DOM parser instead of the extended SSL versions of the library, then you must compile all sources with compiler flags `-DWITH_DOM`, `-DWITH_GZIP`, `-DWITH_OPENSSL`, `-DWITH_COOKIES`, and optionally with `-DWITH_IPV6` for IPv6 support. Example gSOAP client (C++) {#client-cpp} --- Let's put the gSOAP tools to work to convert a calculator WSDL to a working C++ client that invokes the calculator service: [command] wsdl2h -o calc.h http://www.genivia.com/calc.wsdl The `calc.h` file includes the following declarations (showing only one operation here for brievety): //gsoap ns2 schema namespace: urn:calc //gsoap ns2 schema form: unqualified //gsoap ns2 service name: calc //gsoap ns2 service type: calcPortType //gsoap ns2 service port: http://websrv.cs.fsu.edu/~engelen/calcserver.cgi //gsoap ns2 service namespace: urn:calc //gsoap ns2 service transport: http://schemas.xmlsoap.org/soap/http //gsoap ns2 service method-protocol: add SOAP //gsoap ns2 service method-style: add rpc //gsoap ns2 service method-encoding: add http://schemas.xmlsoap.org/soap/encoding/ //gsoap ns2 service method-action: add "" //gsoap ns2 service method-output-action: add Response int ns2__add( double a, // Input parameter double b, // Input parameter double &result // Output parameter ); As you can see, gSOAP uses three basic forms of source code annotations in the auto-generated data binding interface file: - [directives](doc/databinding/html/index.html#directives), such as `//gsoap ns2 schema namespace: urn:calc` - [identifier naming conventions](doc/databinding/html/index.html#toxsd2), such as `ns2__add` - other annotations (not shown in this example) for declaring XML attributes and for enforcing XML validation rules. The directives in this example describe the Web service API properties of this calculator service: this is a SOAP RPC style messaging service over HTTP. Other possible protocols include REST protocols (POST, GET, PUT, and DELETE) and `SOAP-GET`. By convention, the parameters of the service consist of input arguments except for the last parameter which is a reference (or a pointer for C) to the result. The result argument is set by the response data of the service. We picked this example because it has simple parameters to illustrate the operational aspects. Next, you run the soapcpp2 tool to generate the data binding implementation that "glues" your code to the service operations: [command] soapcpp2 -j -CL -I/path/to/gsoap/import calc.h Option `-j` produces C++ proxy classes with `-CL` indicating client-side (non-libs), using an import path for the `#import` files in `calc.h`. Several files are generated, among those are: [command] Saving soapStub.h annotated copy of the source input Saving soapH.h declarations to #include Saving soapcalcProxy.h client proxy class Saving soapcalcProxy.cpp client proxy class Saving calc.nsmap namespace mapping table Saving soapC.cpp serializers Use the generated proxy in your main program `calcclient.cpp` to invoke the calculator service: #include "calc.nsmap" // XML namespace mapping table (only needed once at the global level) #include "soapcalcProxy.h" // the proxy class, also #includes "soapH.h" and "soapStub.h" int main() { calcProxy calc; double sum; if (calc.add(1.23, 4.56, sum) == SOAP_OK) std::cout << "Sum = " << sum << std::endl; else calc.soap_stream_fault(std::cerr); calc.destroy(); // same as: soap_destroy(calc.soap); soap_end(calc.soap); } To compile the program and run: [command] c++ -o calcclient calcclient.cpp soapC.cpp soapcalcProxy.cpp stdsoap2.cpp ./calcclient Sum = 5.79 It is highly recommended that we change the `n2__` prefix that wsdl2h produced by default to something more maintainable. To do so, we add the following line to `typemap.dat` that is used by wsdl2h (put `typemap.dat` in your build directory or use wsdl2h with option `-t/path/to/gsoap/typemap.dat`): [command] ca = "urn:calc" Then run wsdl2h again to generate a new `calc.h` file with `ns2` permanently replaced by `ca`. Finally, you may want to add timeouts to avoid the client from indefinitely blocking on an unresponsive server: calc.soap->connect_timeout = 10; // connect within 10s calc.soap->send_timeout = 5; // send timeout is 5s calc.soap->recv_timeout = 5; // receive timeout is 5s if (calc.add(1.23, 4.56, sum) == SOAP_OK) ... Some systems (Linux) do no support connection timeouts. Example gSOAP client (C) {#client-c} --- The C client is similar to the C++ client described in the previous section. However, you cannot use a proxy class. Instead, you use an auto-generated client stub function to invoke the service. Use wsdl2h option `-c` to generate pure C code: [command] wsdl2h -c -o calc.h http://www.genivia.com/calc.wsdl Next, execute soapcpp2 on `calc.h` to generate the data binding implementation: [command] soapcpp2 -CL calc.h Several files are generated, among those are: [command] Saving soapStub.h annotated copy of the source input Saving soapH.h declarations to #include Saving calc.nsmap namespace mapping table Saving soapClient.c client proxy class Saving soapC.c serializers The generated "client stub function" `soap_ns2__add` defined in `soapClient.c` implements the interface function `ns2__add` that is declared in `calc.h`. We use this stub function to invoke the service: #include "calc.nsmap" // XML namespace mapping table (only needed once at the global level) #include "soapH.h" // client stubs, serializers, etc. int main() { struct soap soap = soap_new(); // allocate and initalize a context double sum; if (soap_call_ns2__add(soap, NULL, NULL, 1.23, 4.56, &sum) == SOAP_OK) printf("Sum = %g\n", sum); else soap_print_fault(soap, stderr); soap_destroy(soap); // delete deserialized objects soap_end(soap); // delete allocated data soap_free(soap); // free the soap struct context data } To compile the program and run it: [command] cc -o calcclient calcclient.c soapC.c soapClient.c stdsoap2.c ./calcclient Sum = 5.79 To change the meaningless `n2__` prefix that wsdl2h produced by default to something more maintainable, we add the following line to `typemap.dat` that is used by wsdl2h (put `typemap.dat` in your build directory or use wsdl2h option `-t/path/to/gsoap/typemap.dat`): [command] ca = "urn:calc" Then run wsdl2h again to generate a new `calc.h` file with `ns2` permanently replaced by `ca`. Finally, you may want to add timeouts to avoid the client from indefinitely blocking on an unresponsive server: soap->connect_timeout = 10; // connect within 10s soap->send_timeout = 5; // send timeout is 5s soap->recv_timeout = 5; // receive timeout is 5s if (soap_call_ca__add(soap, NULL, NULL, 1.23, 4.56, &sum) == SOAP_OK) ... Some systems (Linux) do no support connection timeouts. Example gSOAP server (C++) {#server-cpp} --- For this example we start with a WSDL file: [command] wsdl2h -o calc.h http://www.genivia.com/calc.wsdl This command saves `calc.h`, declaring the interface to the calculator service. Here we only show one of the service operations with the other essential parts: //gsoap ns2 schema namespace: urn:calc //gsoap ns2 schema form: unqualified //gsoap ns2 service name: calc //gsoap ns2 service type: calcPortType //gsoap ns2 service port: http://websrv.cs.fsu.edu/~engelen/calcserver.cgi //gsoap ns2 service namespace: urn:calc //gsoap ns2 service transport: http://schemas.xmlsoap.org/soap/http //gsoap ns2 service method-protocol: add SOAP //gsoap ns2 service method-style: add rpc //gsoap ns2 service method-encoding: add http://schemas.xmlsoap.org/soap/encoding/ //gsoap ns2 service method-action: add "" //gsoap ns2 service method-output-action: add Response int ns2__add( double a, // Input parameter double b, // Input parameter double &result // Output parameter ); In addition to `ns2__add`, the service implements `ns2__sub`, `ns2__mul`, `ns2__div`, and `ns2__pow` functions with `double` parameters. We picked this example because it has simple parameters to illustrate the operational aspects. [command] soapcpp2 -j -SL -I/path/to/gsoap/import calc.h Option `-j` produces C++ service classes. The option `-SL` indicates server-side (non-libs) and `-I` specifies an import path for the `#import` directives located in `calc.h` (if any). Use the generated skeleton service class `calcService` that is declared in `soapcalcService.h` and defined in `soapcalcService.cpp`. The class declares the service operations as methods without actually implementing them. We implement the service operations in a new main program `calcserver.cpp`. We intent to deploy the service as a simple CGI-hosted service using the [Common Gateway Interface](http://en.wikipedia.org/wiki/Common_Gateway_Interface), which means you just need to call the `serve` method of the generated `calcService` class: #include "calc.nsmap" // XML namespace mapping table (only needed once at the global level) #include "soapcalcService.h" // the service class, also #includes "soapH.h" and "soapStub.h" int main() { calcService calc(SOAP_XML_INDENT); if (calc.serve() != SOAP_OK) calc.soap_stream_fault(std::cerr); calc.destroy(); // same as: soap_destroy(calc.soap); soap_end(calc.soap); } int calcService::add(double a, double b, double &result) { result = a + b; return SOAP_OK; } int calcService::sub(double a, double b, double &result) { result = a - b; return SOAP_OK; } int calcService::mul(double a, double b, double &result) { result = a * b; return SOAP_OK; } int calcService::div(double a, double b, double &result) { if (b == 0.0) { char *msg = (char*)soap_malloc(this->soap, 1024); snprintf(msg, 1024, "Trying to divide %f by zero", a); return this->soap_senderfault(msg, NULL); } result = a / b; return SOAP_OK; } int calcService::pow(double a, double b, double &result) { result = ::pow(a, b); // soap_errno is like errno, but compatible with Win32 if (soap_errno == EDOM) { char *msg = (char*)soap_malloc(this->soap, 1024); snprintf(msg, 1024, "<error xmlns=\"http://tempuri.org/\">Can't take power of %f to %f</error>", a, b); return this->soap_senderfault("Power function domain error", s); } return SOAP_OK; } Note that you can use `soap_malloc` to malloc data that will be automatically released when the service is done. For class instances, use the auto-generated `soap_new_X(this->soap)` functions to allocate objects of a class `X` that will be deallocated after the service responded. Here are some examples on how to create temporary data managed by the `soap` engine context and to set default values: // allocate N bytes (managed by the 'soap' context) char *str = soap_malloc(soap, N); // duplicate a char string (managed by the 'soap' context) char *dup = soap_dup(soap, str); // instantiate one object X (managed by the 'soap' context) X *object = soap_new_X(soap); // instantiate an array of N objects X (managed by the 'soap' context) X *objects = soap_new_X(soap, N); // instantiate and set only the required (as per schema) members m1, ..., mk (managed by the 'soap' context) X *object = soap_new_req_X(soap, m1, m2, ..., mk); // instantiate and set all members m1, ..., mk (managed by the 'soap' context) X *object = soap_new_set_X(soap, m1, m2, ..., mk); // reset object to default member values object->soap_default_X(soap); After sending the Web service response, invoke `calc.destroy()` as shown in the `calcservice.cpp` code above. This calls `soap_destroy(this->soap)` and `soap_end(this->soap)` to delete context-managed objects and temporary data, respectively. Let's compile the program `calcserver.cpp` together with the auto-generated `soapcalcService.cpp` and `soapC.cpp` XML data bindings. Then run it on the request message `calc.add.req.xml` that was auto-generated by soapcpp2 in the previous step: [command] c++ -o calcserver calcserver.cpp soapC.cpp soapcalcService.cpp stdsoap2.cpp ./calcserver < calc.add.req.xml Status: 200 OK Server: gSOAP/2.8 Content-Type: text/xml; charset=utf-8 Content-Length: 466 Connection: close <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns2="urn:calc"> <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <ns2:addResponse> <result>0</result> </ns2:addResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope> To host the service on the Web with CGI, install `calcserver` in `cgi-bin` on your Web server and set the permissions appropriately (this won't work if the file permissions are wrong or if CGI is not enabled). For FastCGI, compile the generated source code and `stdsoap2.cpp` with `-DWITH_FASTCGI`. To host the service as a stand-alone HTTP server, change the main program to invoke the `run` method with a port number as follows: int main() { calcService calc(SOAP_XML_INDENT); if (calc.run(8080) != SOAP_OK) calc.soap_stream_fault(std::cerr); calc.destroy(); } This runs the service as an iterative Web server on port 8080, but any glitch or fault will terminate it. You may want to put this in an infinite loop to restart a `run()`. You also may want to add timeouts to avoid the server from indefinitely blocking on an unresponsive client: calc.soap->send_timeout = 5; // send timeout is 5s calc.soap->recv_timeout = 5; // receive timeout is 5s while (calc.run(8080) != SOAP_OK && calc.error != SOAP_TCP_ERROR) calc.soap_stream_fault(std::cerr); When deploying the Web server, we strongly recommend to use threads and set IO timeouts to harden the server. See the `samples/webserver` example in the gSOAP package. You can also deploy gSOAP services as fast and robust [Apache modules](http://www.genivia.com/doc/apache/html/index.html) and [ISAPI extensions.](http://www.genivia.com/doc/isapi/html/index.html) Example gSOAP server (C) {#server-c} --- For implementing a C service similar to the C++ service described in the previous section we cannot use a service class. Instead, we use wsdl2h option `-c` to generate pure C code: [command] wsdl2h -c -o calc.h http://www.genivia.com/calc.wsdl soapcpp2 -SL calc.h Several files are generated, among those are: [command] Saving soapStub.h annotated copy of the source input Saving soapH.h declarations to #include Saving calc.nsmap namespace mapping table Saving soapServer.c server request dispatcher Saving soapC.c serializers The C implementation of the service requires global functions for the service operations: #include "calc.nsmap" // XML namespace mapping table (only needed once at the global level) #include "soapH.h" // server stubs, serializers, etc. int main() { struct soap *soap = soap_new1(SOAP_XML_INDENT); if (soap_serve(soap) != SOAP_OK) soap_print_fault(soap, stderr); soap_destroy(soap); // delete deserialized objects soap_end(soap); // delete allocated (deserialized) data soap_free(soap); // free the soap struct context data } int ns2__add(struct soap *soap, double a, double b, double &result) { result = a + b; return SOAP_OK; } int ns2__sub(struct soap *soap, double a, double b, double &result) { result = a - b; return SOAP_OK; } int ns2__mul(struct soap *soap, double a, double b, double &result) { result = a * b; return SOAP_OK; } int ns2__div(struct soap *soap, double a, double b, double &result) { if (b == 0.0) { char *msg = (char*)soap_malloc(soap, 1024); snprintf(msg, 1024, "Trying to divide %f by zero", a); return soap_sender_fault(soap, msg, NULL); } result = a / b; return SOAP_OK; } int ns2__pow(struct soap *soap, double a, double b, double &result) { result = ::pow(a, b); // soap_errno is like errno, but compatible with Win32 if (soap_errno == EDOM) { char *msg = (char*)soap_malloc(soap, 1024); snprintf(msg, 1024, "<error xmlns=\"http://tempuri.org/\">Can't take power of %f to %f</error>", a, b); return soap_sender_fault(soap, "Power function domain error", s); } return SOAP_OK; } Note the use of `soap_malloc` to allocate a temporary message string (in C we have no classes and thus we cannot use `soap_new_X` functions). Here are some examples on how to create temporary data managed by the `soap` engine context and to set default values: // allocate N bytes (managed by 'soap' context) char *str = soap_malloc(soap, N); // duplicate a char string (managed by 'soap' context) char *dup = soap_dup(soap, str); // allocate struct X (managed by 'soap' context) struct X *object = soap_malloc(soap, sizeof(struct X)); // reset struct X to default soap_default_X(soap, object); After sending the Web service response, we call `soap_destroy(soap)` and `soap_end(soap)` to delete all context-managed objects and data, respectively. Let's compile the program and run it on the request message `calc.add.req.xml` that was auto-generated by soapcpp2 in the previous step: [command] cc -o calcserver calcserver.c soapC.c soapcalcService.c stdsoap2.c ./calcserver < calc.add.req.xml Status: 200 OK Server: gSOAP/2.8 Content-Type: text/xml; charset=utf-8 Content-Length: 466 Connection: close <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns2="urn:calc"> <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <ns2:addResponse> <result>0</result> </ns2:addResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope> To host the service on the Web with CGI, install `calcserver` in `cgi-bin` on your Web server and set the permissions appropriately (this won't work if the file permissions are wrong or if CGI is not enabled). For FastCGI, compile the generated source code and `stdsoap2.c` with `-DWITH_FASTCGI`. To host the service as a stand-alone HTTP server, change the main program: int main() { struct soap *soap = soap_new1(SOAP_XML_INDENT); if (!soap_valid_socket(soap_bind(soap, NULL, 8080, 100))) exit(EXIT_FAILURE); while (soap_valid_socket(soap_accept(soap))) { if (soap_serve(soap) != SOAP_OK) break; soap_destroy(soap); // delete deserialized objects soap_end(soap); // delete allocated (deserialized) data } soap_print_fault(soap, stderr); soap_free(soap); // free the soap struct context data } This runs the service as an iterative Web server on port 8080. You may want to add timeouts to avoid the server from indefinitely blocking on an unresponsive client: soap->send_timeout = 5; // send timeout is 5s soap->recv_timeout = 5; // receive timeout is 5s if (!soap_valid_socket(soap_bind(soap, NULL, 8080, 100))) ... When deploying the Web server, we strongly recommend to use threads and set IO timeouts to harden the server. See the `samples/webserver` example in the gSOAP package. You can also deploy gSOAP services as fast and robust [Apache modules](http://www.genivia.com/doc/apache/html/index.html) and [ISAPI extensions.](http://www.genivia.com/doc/isapi/html/index.html) [![Go up](images/go-up.png) To top](dev.html)