![]() |
Home | Documentation |
gSOAP user guide
updated Tue Jul 4 2017 by Robert van Engelen
|
The gSOAP tools provide an automated SOAP and XML data binding for C and C++ based on compiler technologies. The tools simplify the development of SOAP/XML Web services and XML application in C and C++ using autocode generation and advanced mapping methods. Most toolkits for Web services adopt a WSDL/SOAP-centric view and offer APIs that require the use of class libraries for XML-specific data structures. This forces a user to adapt the application logic to these libraries because users have to write code to populate XML and extract data from XML using a vendor-specific API. This often leads to fragile solutions with little or no assurances for data consistency, type safety, and XML validation. By contrast, gSOAP provides a type-safe and transparent solution through the use of compiler technology that hides irrelevant WSDL-, SOAP-, REST-, and XML-specific protocol details from the user, while automatically ensuring XML validity checking, memory management, and type-safe serialization. The gSOAP tools automatically map native and user-defined C and C++ data types to semantically equivalent XML data types and vice-versa. As a result, full SOAP/REST XML interoperability is achieved with a simple API relieving the user from the burden of WSDL/SOAP/XML details, thus enabling him or her to concentrate on the application-essential logic.
The gSOAP tools support the integration of (legacy) C/C++ codes (and other programming languages when a C interface is available), embedded systems, and real-time software in SOAP/XML applications that share computational resources and information with other SOAP applications, possibly across different platforms, language environments, and disparate organizations located behind firewalls.
The gSOAP tools are also popular to implement XML data binding in C and C++. This means that application-native data structures can be encoded in XML automatically, without the need to write conversion code. The tools also produce XML schemas for the XML data binding, so external applications can consume the XML data based on the schemas.
To start building Web services applications or automate XML data bindings with gSOAP, you will need:
The gSOAP software is self-contained, so there is no need to download any third-party software, except when you want to use OpenSSL for HTTPS and/or Zlib compression.
The gSOAP distribution package includes:
wsdl2h
WSDL/schema converter and data binding tool.soapcpp2
stub/skeleton compiler and code generator.Binaries of these two tools are included in the gSOAP package in gsoap/bin
for Windows and Mac OS plarforms, see also the README files in the package for more details.
Although gSOAP tools are available in binary format for several platforms, the code generated by these tools are all equivalent. This means that the generated source codes can be transferred to other platforms and locally compiled.
If you don't have the binaries or if you want to rebuild them, you need
soapcpp2
.soapcpp2
.wsdl2h
.Bison and Flex are preferred. Both are released under open source licenses that are compatible with gSOAP's licenses:
You can also build soapcpp2
without Bison and Flex installed, see installation instructions on the gSOAP web site.
The gSOAP engine is built as a library libgsoap.a
and libgsoap++.a
with separate versions of these two libgsoapssl.a
and libgsoapssl++.a
that support SSL. See the README.txt
instructions on how to build these libraries with the platform-independent gSOAP package's autoconf and automake. Alternatively, you can compile and link the engine's source code stdsoap2.c
(or stdsoap2.cpp
for C++) directly with your code.
The gSOAP packages contain numerous examples in the samples
directory. Run make
to build the example applications. The examples are also meant to demonstrate different features of gSOAP. A streaming MTOM attachment server and client application demonstrate efficient file exchanges in samples/mtom-stream
. An SSL-secure Web server application demonstrates the generation of dynamic content for Web browsing and Web services functionality at the same time, see samples/webservice
. And much more.
The gSOAP tools minimize application adaptation efforts for building Web Services by using a XML data binding for C and C++ implemented by advanced XML schema analyzers and source-to-source code generation tools. The gSOAP wsdl2h
tool imports one or more WSDLs and XML schemas and generates a gSOAP header file with familiar C/C++ syntax to define the Web service operations and the C/C++ data types. The gSOAP soapcpp2
compiler then takes this header file and generates XML serializers for the data types (soapH.h
and soapC.cpp
), the client-side stubs (soapClient.cpp
), and server-side skeletons (soapServer.cpp
).
The gSOAP soapcpp2
compiler can also generate WSDL definitions for implementing a service from scratch, i.e. without defining a WSDL first. This "closes the loop" in that it enables Web services development from WSDL or directly from a set op C/C++ operations in a header file without the need for users to analyze Web service details.
You only need to follow a few steps to execute the tools from the command line or Makefile (see also MSVC++ project examples in the samples
directory with tool integration in the MSVC++ IDE). For example, to generate code for the calculator Web service, we run the wsdl2h
tool from the command line on the URL of the WSDL and use option -o
to specify the output file:
> wsdl2h -o calc.h http://www.genivia.com/calc.wsdl
This generates the calc.h
service definition header file with service operation definitions and types for the operation's data. This header file is then to be processed with soapcpp2
to generate the stub and/or skeleton code and XML serialization routines. The calc.h
file includes all documentation, so you can use Doxygen (http://www.doxygen.org) to automatically generate the documentation pages for your development.
The wsdl2h
-generated service definitions header file also contains information on the use of the service, such as WS-Policy assertions when applicable.
In this example we are developing a C++ API for the calculator service. By default, gSOAP assumes you will use C++ with STL. To build without STL, use option -s
:
> wsdl2h -s -o calc.h http://www.genivia.com/calc.wsdl
To build a pure C application, use option -c
:
> wsdl2h -c -o calc.h http://www.genivia.com/calc.wsdl
.vcproj
, please set *CompileAs="2"
* in your .vcproj
file.We have not yet generated the stubs for the C/C++ API. To do so, run the soapcpp2
compiler:
> soapcpp2 -i -C -Iimport calc.h
Option -i
(and alternatively option -j
) indicates that we want C++ proxy and server objects that include the client (and server) code, -C
indicates client-side only files (soapcpp2
generates both client and server stubs and skeletons by default). Option -I
is needed to import the stlvector.h
file from the import
directory in the gSOAP package to support serialization of STL vectors.
Suppose we develop a C++ client for the calculator service using wsdl2h -o calc.h http://www.genivia.com/calc.wsdl
and soapcpp2 -i -C calc.h
.
We use the generated soapcalcProxy
class and calc.nsmap
XML namespace mapping table to access the Web service. The soapcalcProxy
class is a proxy to invoke the service:
To complete the build, compile the code above and compile and link this with the generated soapC.cpp
, soapcalcProxy.cpp
, and the run-time gSOAP engine -lgsoap++
(or use source stdsoap2.cpp
in case libgsoap++.a
is not installed) with your code.
Suppose we develop a client in C using wsdl2h -c -o calc.h http://www.genivia.com/calc.wsdl
and soapcpp2 -C calc.h
. In this case our code uses a simple C function call to invoke the service and we also need to explicitly delete data and the context with soap_end
and soap_free
:
The calculator example is fairly simple and used here to illustrate the development process. The development process for large-scale XML applications is similar. More extensive examples can be found in the samples
directory in the gSOAP package.
Developing a service application is easy too. We will use CGI here because it is a simple mechanism. This is not the preferred deployment mechanism. Because CGI is slow and stateless. Faster services can be developed as a stand-alone gSOAP HTTP/HTTPS server (but see comments at the end of this section) or, as preferred, the use of Apache module or IIS with the mod_gsoap ISAPI extension (included in the gSOAP package under gsoap/mod_gsoap
with instructions).
Suppose we implement a CGI-based service that returns the time in GMT. The Common Gateway Interface (CGI) is a simple mechanism to publish services on your Web site.
For this example we start with a gSOAP header file, currentTime.h
which contains the service definitions. Such a file can be obtained from a WSDL using wsdl2h
when a WSDL is available. When a WSDL is not available, you can define the service in C/C++ definitions in a newly created header file and let the gSOAP tools generate the source code and WSDL for you.
Our currentTime
service only has an output parameter, which is the current time defined in our currentTime.h
gSOAP service specification:
Note that we associate an XML namespace prefix ns
and namespace name urn:currentTime
with the service WSDL and SOAP/XML messages. The gSOAP tools use a special convention for identifier names that are part of a namespace: a namespace prefix (ns
in this case) followed by a double underscore __
. This convention is used to resolve namespaces and to avoid name clashes. The ns
namespace prefix is bound to the urn:currentTime
namespace name with the //gsoap
directive. The //gsoap
directives are used to set the properties of the service, in this case the name, namespace, and location endpoint.
The service implementation for CGI requires a soap_serve
call on a soap context created with soap_new
. The service operations are implemented as functions, which are called by the RPC dispatcher soap_serve
:
Note that we pass the soap struct with the gSOAP context information to the service routine. This can come in handy to determine properties of the connection and to dynamically allocate data with soap_malloc(soap, num_bytes)
that will be automatically deleted when the service is finished.
We run the soapcpp2
compiler on the header file to generate the server-side code:
> soapcpp2 -S currentTime.h
and then compile the CGI binary:
> c++ -o currentTime.cgi currentTime.cpp soapC.cpp soapServer.cpp stdsoap2.cpp
You will find stdsoap2.cpp
in the gsoap
dir. Or after installation you can link with libgsoap++
instead of using the stdsoap2.cpp
source:
> c++ -o currentTime.cgi currentTime.cpp soapC.cpp soapServer.cpp -lgsoap++
To activate the service, copy the currentTime.cgi
binary to your bin-cgi
directory using the proper file permissions.
The soapcpp2
tool generated the WSDL definitions currentTime.wsdl
. You can use the WSDL to advertize your service. You don't need to use this WSDL to develop a gSOAP client. You can use the currentTime.h
file with soapcpp2
option -C
to generate client-side code.
A convenient aspect of CGI is that it exchanges messages over standard input/output. Therefore, you can run the CGI binary on the auto-generated example request XML file to test your server:
> ./currentTime.cgi < currentTime.currentTime.req.xml
and this displays the server response in SOAP XML.
The above approach works also for C. Just use soapcpp2
option -c
in addition to the -S
option to generate ANSI C code. Of course, in C we use pointers instead of references and the currentTime.h
file should be adjusted to use C-only types.
A more elegant server implementation in C++ can be obtained by using the soapcpp2
option -i
(or -j
) to generate C++ client-side proxy and server-side service objects as classes that you can use to build clients and services in C++. The option removes the generation of soapClient.cpp
and soapServer.cpp
, since these are no longer needed when we have classes that implement the client and server logic:
> soapcpp2 -i -S currentTime.h
This generates soapcurrentTimeService.h
and soapcurrentTimeService.cpp
files, as well as auxiliary files soapStub.h
(included by default by all codes) and currentTime.nsmap
.
Using the currentTimeService
object we rewrite the CGI server as:
Compile with
> c++ -o currentTime.cgi currentTime.cpp soapC.cpp soapcurrentTimeService.cpp -lgsoap++
and install the binary as CGI. To install the CGI binary please consult your Web server documentation on how to deploy CGI applications.
To run the server as a stand-alone iterative (non-multithreaded) server on port 8080:
To implement threaded services, please see Section How to Create a Multi-Threaded Stand-Alone Service . These stand-alone Web Services that handle multiple SOAP requests by spawning a thread for each request. Thread pooling is also an option. The use of Apache and IIS modules to deploy gSOAP services is preferred to ensure load balancing, access control, tracing, and so on.
For more information on server-side service classes, see Section How to Generate C++ Server Object Classes . For more information on client-side proxy classes, see Section How to Generate C++ Client Proxy Classes .
Or in other words, how to map schemas (XSD files) to C/C++ bindings for automatically reading and writing XML data.
The XML C/C++ data binding in gSOAP provides and automated mechanism to encode any C and C++ data type in XML (and decode XML back to C/C++ data). An XML schema (XSD file) can be transformed into a set of C or C++ definitions that can be readily incorporated into your application to manipulate XML with much more ease than DOM or SAX. Essentially, each XML component definition in an XML schema has a C/C++ data type representation that has equivalent type properties. The advantage of this approach is immediately apparent when we look at an XSD complexType that maps to a class as shown:
In this way, an automatic mapping between XML elements of the XML schema and members of a class is created. No DOM traversals and SAX events are needed. In addition, the XML C/C++ data binding makes XML manipulation type safe. That is, the type safety of C/C++ ensures that only valid XML documents can be parsed and generated.
The wsdl2h
tool performs the mapping of WSDL and XML schemas to C and/or C++ automatically. The output of wsdl2h
is an annotated header file that includes comments and documentation on the use of the C/C++ declarations in a SOAP/XML client/server or in a generic application for reading and writing XML using the auto-generated serializers.
We will illustrate this further with an example. Suppose we have an XML document with a book record:
An example XML schema that defines the book element and type could be
Using wsdl2h
we translate the XML schema that defines the book type and root element to a class definition:
Note that annotations such as @
are used to distinguish attributes from elements. These annotations are gSOAP-specific and are handled by the soapcpp2
tool to generate serializers for the data type(s) for reading and writing XML.
The soapcpp2
tool generates all the necessary code to parse and generate XML for book
objects. Validation constraints such as *minOccurs="1"
* and *use="required"
* are included in the generated code as checks.
To write the XML representation of a book, we first create a soap
engine context and use it with soap_write_book
(generated by soapcpp2
) to write the object in XML to standard output:
The ctx
gSOAP engine context (type struct soap
) controls settings and holds state, such as XML formatting, (e.g. SOAP_XML_INDENT
), serialization options, current state, and I/O settings. Simply set the output stream (std::ostream) ctx->os
of the context to redirect the content, e.g. to a file or string. Also, when serializing a graph rather than an XML tree (SOAP_XML_TREE
option forces trees) the XML output conforms to SOAP encoding for object graphs based on id-ref, see Section How to Use the SOAP Serializers and Deserializers to Save and Load Application Data using XML Data Bindings for details.
To read the XML representation from standard input into a book object, use:
Automatic built-in strict XML validation (enabled with SOAP_XML_STRICT
) ensures that data members are present so we can safely print them in this example, thus ensuring consistency of data with the XML schema. Set the ctx->is
input stream to read from a file/string stream instead of stdin.
The soap_destroy
and soap_end
calls deallocate the deserialized content, so use with care. In general, memory management is automatic in gSOAP to avoid leaks.
The above uses a very simple example schema. The gSOAP toolkit handles all XML schema constructs defined by the XML schema standard. The toolkit is also able to (de)serialize pointer-based C/C++ data structures (including cyclic graphs), structs/classes, unions, enums, STL containers, and even special data types such as struct tm
. Therefore, the toolkit works in two directions: from WSDL/schema to C/C++ and from C/C++ to WSDL/schema.
The gSOAP toolkit also handles multiple schemas defined in multiple namespaces. Normally the namespace prefixes of XML namespaces are added to the C/C++ type definitions to ensure type uniqueness. For example, if we would combine two schemas in the same application where both schemas define a book
object, we need to resolve this conflict. In gSOAP this is done using namespace prefixes, rather than C++ namespaces (research has pointed out that XML namespaces are not equivalent to C++ namespaces). Thus, the book
class might actually be bound to an XML namespace and the class would be named ns__book
, where ns
is bound to the corresponding namespace.
The following options are available to control serialization:
Other flags can be used to format XML, see Section Run Time Flags .
For more details on XML databinding support for C and C++, see Section How to Use the SOAP Serializers and Deserializers to Save and Load Application Data using XML Data Bindings .
The highlights of gSOAP are:
samples/xml-rpc-json
folder in the package.wsdl2h
tool supports WS-Policy. Policy assertions are included in the generated service description header file with recommendations and usage hints.soapcpp2
compiler includes a WSDL and schema generator for convenient Web Service publishing.soapcpp2
compiler generates sample input and output messages for verification and testing (before writing any code). An option (-T
) can be used to automatically implement echo message services for testing.wsdl2h
tool converts WSDL and XSD files to gSOAP header files for automated client and server development.void*
need to be modified, but only if the data structures that adopt them are required to be serialized or deserialized as part of a service operation invocation.href
* attributes in SOAP).The typographical conventions used by this document are:
Courier
denotes C and C++ source code, XML data, JSON data, file names, and shell/batch commands.[optional: ...]
denotes an optional construct.The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC-2119.
To comply with WS-I Basic Profile 1.0a, gSOAP 2.5 and higher adopts SOAP document/literal by default. There is no need for concern, because the WSDL parser wsdl2h
automatically takes care of the differences when you provide a WSDL document, because SOAP RPC encoding, literal, and document style are supported. A new soapcpp2 compiler option was added -e
for backward compatibility with gSOAP 2.4 and earlier to adopt SOAP RPC encoding by default in case you want to develop a service that uses SOAP encoding. You can also use the gSOAP soapcpp2
compiler directives to specify SOAP encoding for individual operarations, when desired.
You should read this section only if you are upgrading from gSOAP 2.1 to 2.2 and later.
Run-time options and flags have been changed to enable separate recv/send settings for transport, content encodings, and mappings. The flags are divided into four classes: transport (IO), content encoding (ENC), XML marshalling (XML), and C/C++ data mapping (C). The old-style flags soap_disable_X
and soap_enable_X
, where X
is a particular feature, are deprecated. See Section Run Time Flags for more details.
Before a client can invoke service operations or before a service can accept requests, a runtime context needs to be allocated and initialized.
soap_init(struct soap *soap)
& Initializes a context (required only once)struct soap *soap_new()
& Allocates, initializes, and returns a pointer to a runtime contextstruct soap *soap_copy(struct soap *soap)
& Allocates a new runtime context and copies contents of the context such that the new environment does not share any data with the original contextA context can be reused as many times as necessary and does not need to be reinitialized in doing so. A dynamically allocated context is deallocated with soap_free(soap)
.
A new context is only required for each new thread to guarantee exclusive access to a new runtime context by each thread. For example, the following code stack-allocates the runtime context which is used for multiple service operation calls:
The runtime context can also be heap allocated:
A service needs to allocate and initialize an context before calling soap_serve
:
Or alternatively:
The soap_serve
dispatcher handles one request or multiple requests when HTTP keep-alive is enabled (with the SOAP_IO_KEEPALIVE
flag see Section TCP and HTTP Keep-Alive ).
A service can use multi-threading to handle requests while running some other code that invokes service operations:
In the example above, two runtime contexts are required. In comparison, gSOAP 1.X statically allocates the runtime context, which prohibits multi-threading (only one thread can invoke service operations and/or accept requests due to the single runtime context).
Section How to Create a Multi-Threaded Stand-Alone Service presents a multi-threaded stand-alone Web Service that handles multiple SOAP requests by spawning a thread for each request.
gSOAP interoperability has been verified with the following SOAP implementations and toolkits:
Apache 2.2, Apache Axis, ASP.NET, Cape Connect, Delphi, easySOAP++, eSOAP, Frontier, GLUE, Iona XMLBus, kSOAP, MS SOAP, Phalanx, SIM, SOAP::Lite, SOAP4R, Spray, SQLData, WCF, White Mesa, xSOAP, ZSI
This user guide offers a quick way to get started with gSOAP. This section requires a basic understanding of the SOAP protocol and some familiarity with C and/or C++. In principle, SOAP clients and SOAP Web services can be developed in C and C++ with the gSOAP soapcpp2
compiler without a detailed understanding of the SOAP protocol when gSOAP client-server applications are built as an ensamble and only communicate within this group (i.e. meaning that you don't have to worry about interoperability with other SOAP implementations). This section is intended to illustrate the implementation of gSOAP Web services and clients that connect to and interoperate with other SOAP implementations such as Apache Axis, SOAP::Lite, and .NET. This requires some details of the SOAP and WSDL protocols to be understood.
In general, the implementation of a SOAP client application requires a stub (also called service proxy) for each service operation that the client invokes. The primary stub's responsibility is to marshall the parameter data, send the request with the parameters to the designated SOAP service over the wire, to wait for the response, and to demarshall the parameter data of the response when it arrives. The client application invokes the stub routine for a service operation as if it would invoke a local function. To write a stub routine in C or C++ by hand is a tedious task, especially if the input and/or output parameters of a service operation contain elaborate data structures such as objects, structs, containers, arrays, and pointer-linked graph structures. Fortunately, the gSOAP wsdl2h
WSDL parser tool and soapcpp2
stub/skeleton and serialization code generator tool automate the development of SOAP/XML Web service client and server applications.
The soapcpp2
tool generates the necessary gluing code (also called stubs and skeletons) to build web service clients and services. The input to the soapcpp2
tool consists of an service definition-annotated C/C++ header file. The header file can be generated from a WSDL (Web Service Description Language) documentation of a service with the gSOAP wsdl2h
WSDL parser tool.
Consider the following command (entered at the Linux/Unix/Windows command line prompt):
> wsdl2h -o calc.h http://www.genivia.com/calc.wsdl
This generates the file Web service description calc.h
in an annotated C++ header file. The WSDL specification (possibly consisting of multiple imported WSDL files and XSD schema files) is mapped to C++ using C++ databindings for SOAP/XML. The generated header file contains data types and messages to operate the service, and meta information related to WSDL and XML schemas.
To generate a service definition header file to develop a pure C client application, use the -c
option:
> wsdl2h -c -o calc.h http://www.genivia.com/calc.wsdl
For more details on the WSDL parser and its options, see The wsdl2h WSDL and Schema Importer .
The service definition calc.h
header file is further processed by the gSOAP soapcpp2
compiler to generate the gluing code's logic to invoke the Web service from a client.
Looking into the file calc.h
we see that the SOAP service methods are specified as function prototypes. For example, the add
function to add two double floats:
The ns2__add
function uses an XML namespace prefix to distinguish it from operations defined in other namespaces, thus preventing name clashes. The convention to add an XML namespace prefix to the names of operations, types, and struct
and class
members is universally used by the gSOAP tools and automatically created by wsdl2h
, but it is not mandatory when translating existing C/C++ types and operations to web services. Thus, the prefix notation can be omitted from type names defined in an header file with to run soapcpp2
to create clients and services that exchange existing (i.e. application-native) data types.
These function prototypes are translated by the gSOAP soapcpp2
tool to stubs and proxies for remote calls:
soapStub.h
annotated copy of the input definitionssoapH.h
serializerssoapC.cpp
serializerssoapClient.cpp
client calling stubsThus, the logic of the generated stub routines allow C and C++ client applications to seamlessly interact with existing SOAP Web services as illustrated by the client code example in the next section.
The input and output parameters of a SOAP service operation may be primitive data types or complex compound data types such as containers and pointer-based linked data structures. These are defined in the header file that is either generated by the WSDL parser or specified by hand. The gSOAP soapcpp2
tool automatically generates XML serializers and XML deserializers for the data types to enable the generated stub routines to encode and decode the contents of the parameters of the service operations in SOAP/XML.
Note that the gSOAP soapcpp2
tool also generates skeleton routines soapServer.cpp
for each of the service operations specified in the header file. The skeleton routines can be readily used to implement one or more of the service operations in a new SOAP Web service. These skeleton routines are not used for building SOAP clients in C++, although they can be used to build mixed SOAP client/server applications (peer applications).
The add
service operation (declared in the calc.h
file obtained with the wsdl2h
tool in the previous section) adds two float values. The WSDL description of the service provides the endpoint to invoke the service operations and the XML namespace used by the operations:
http://websrv.cs.fsu.edu/~engelen/calcserver.cgi
*urn:calc
*Each service operation has a SOAP action, which is an optional string to identify the operation (mainly used with WS-Addressing), an operation request message and a response message. The request and response messages for SOAP RPC-encoded services are simply represented by C functions with input and output parameters. For the add
operation, the SOAP binding details are:
""
* (empty string)This information is translated to the wsdl2h
-generated header file with the service definitions. The calc.h
header file for C++ generated by wsdl2h
contains the following directives and declarations: (the actual contents may vary depending on the release version and the options used to control the output):
The other calculator operations are similar and elided here for clarity.
The //gsoap
directives are required for the soapcpp2
tool to generate code that is compliant to the SOAP protocol. For this service the SOAP protocol with the common "RPC encoding style" is used. For //gsoap
directive details, see Section Customizing the WSDL and Namespace Mapping Table File Contents With gSOAP Directives .
The service operations are declared as function prototypes, with all non-primitive parameter types needed by the operation declared in the header file (all parameter types are primitive in this case).
The calculator add
operation takes two double floats a
and b
, and returns the sum in result
. By convention, all parameters are input parameters except the last. The last parameter is al**ways the output parameter. A struct
or class
is used to wrap multiple output parameters, see also Section How to Specify Multiple Output Parameters . This last parameter must be a pointer or reference. By contrast, the input parameters support pass by value or by pointer, but not pass by C++ reference.
The function prototype associated with a service operation always returns an int
. The value indicates success (0
or equivalently SOAP_OK
) or failure (any nonzero value). See Section Error Codes for the nonzero error codes.
The role of the namespace prefix (ns2__
) in the service operation name in the function prototype declaration is discussed in detail in XML Namespace Considerations . Basically, a namespace prefix is added to a function name or type name with a pair of underscores, as in ns2__add
, where ns2
is the namespace prefix and add
is the service operation name. This mechanism ensures uniqueness of operations and types associated with a service.
It is strongly recommended to set the namespace prefix to a name of your choice. This avoids problems when running wsdl2h
on multiple WSDLs where the sequence of prefixes ns1
, ns2
, and so on are arbitrarily assigned to the services. To choose a prefix name for all the operations and types of a service, say prefix c__
for the calculator service, add the following line to typemap.dat
:
and rerun wsdl2h
. The typemap.dat
configures wsdl2h
to use specific bindings and data types for services. The result is that c__add
is used to uniquely identify the operation rather than the more arbitrary name ns2__add
.
Note on the use of underscores in names: a single underscore in an identifier name will be translated into a dash in XML, because dashes are more frequently used in XML compared to underscores, see Section C/C++ Identifier Name to XML Tag Name Mapping .
Next, the gSOAP soapcpp2
tool is invoked from the command line to process the calc.h
service definitions:
> soapcpp2 calc.h
The tool generates the stub routines for the service operations. Stub routines can be invoked by a client program to invoke the remote service operations. The interface of the generated stub routine is identical to the function prototype in the calc.h
service defintion file, but with additional parameters to pass the gSOAP engine's runtime context soap
, an endpoint URL (or NULL for the default), and a SOAP action (or NULL for the default):
This stub routine is saved in soapClient.cpp
. The file soapC.cpp
contains the serializer and deserializer routines for the data types used by the stub. You can use option -c
for the soapcpp2
tool to generate pure C code, when needed.
Note: the soap
parameter must be a valid pointer to a gSOAP runtime context. The URL
can be set to override the default endpoint address (the endpoint defined by the WSDL). The action
parameter can be set to override the default SOAP action.
The following example C/C++ client program uses the stub:
The call returns SOAP_OK
(zero) on success and a nonzero error on failure. When an error occurred the fault is displayed with the soap_print_fault
function. Use soap_sprint_fault(struct soap*, char *buf, size_t len)
to print the error to a string, and use soap_stream_fault(struct soap*, std::ostream&)
to send it to a stream (C++ only).
The following functions can be used to explicitly setup a gSOAP runtime context (struct soap
):
soap_init(struct soap *soap)
Initializes a runtime contextsoap_init1(struct soap *soap, soap_mode iomode)
Initializes a runtime context and set in/out mode flagssoap_init2(struct soap *soap, soap_mode imode, soap_mode omode)
Initializes a runtime context and set in/out mode flagsstruct soap *soap_new()
Allocates, initializes, and returns a pointer to a runtime contextstruct soap *soap_new1(soap_mode iomode)
Allocates, initializes, and returns a pointer to a runtime context and set in/out mode flagsstruct soap *soap_new2(soap_mode imode, soap_mode omode)
Allocates, initializes, and returns a pointer to a runtime context and set in/out mode flagsstruct soap *soap_copy(struct soap *soap)
Allocates a new runtime context and copies a context (deep copy, i.e. the new context does not share any data with the other context)soap_done(struct soap *soap)
Reset, close communications, and remove callbackssoap_free(struct soap *soap)
Reset and deallocate the context created with soap_new
or soap_copy
A runtime context can be reused as many times as necessary for client-side remote calls and does not need to be reinitialized in doing so. A new context is required for each new thread to guarantee exclusive access to runtime context by threads. Also the use of any client calls within an active service method requires a new context.
The soapcpp2
code generator tool also generates a service proxy class for C++ client applications (and service objects for server applications) with the -i
(or -j
) option:
> soapcpp2 -i calc.h
The proxy is defined in:
soapcalcProxy.h
client proxy classsoapcalcProxy.cpp
client proxy classNote: without the -i
option only old-style service proxies and objects are generated, which are less flexible and no longer recommended. Use -j
as an alternative to -i
to generate classes with the same functionality, but that are not inherited from struct soap
and use a pointer to a struct soap
engine context that can be shared with other proxy and service class instances. This choice is also important when services are chained, see Section How to Chain C++ Server Classes to Accept Messages on the Same Port .
The generated C++ proxy class initializes the gSOAP runtime context and offers the service interface as a collection of methods:
The proxy class is derived from the gSOAP runtime context structure struct soap
and thus inherits (option -i
) all state information of the runtime. The proxy constructor takes context mode parameters to initialize the context, e.g. SOAP_XML_INDENT
in this example.
The code is compiled and linked with soapcalcProxy.cpp
, soapC.cpp
, and stdsoap2.cpp
(or use libgsoap++.a
).
The proxy class name is extracted from the WSDL content and may not always be in a short format. Feel free to change the entry
and rerun soapcpp2
to generate code that uses the new name.
When the example client application is invoked, a SOAP request is performed:
The SOAP response message:
A client can invoke a sequence of service operations:
In the above, no data is deallocated until the proxy is deleted. To deallocate deserialized data between the calls, use:
Deallocation is safe here, since the float values were copied and saved in sum
. In other scenarios one must make sure data is copied or removed from the deallocation chain with:
which is to be invoked on each data item to be preserved, before destroying deallocated data. When the proxy is deleted, also all deserialized data is deleted. To delegate deletion to another runtime context for later removal, use:
For example
In C (use wsdl2h -c
) the example program would be written as:
The code above is compiled and linked with soapClient.c
, soapC.c
, and stdsoap2.c
(or use libgsoap.a
).
The declaration of the ns2__add
function prototype (discussed in the previous section) uses the namespace prefix ns2__
of the service operation namespace, which is distinguished by a pair of underscores in the function name to separate the namespace prefix from the service operation name. The purpose of a namespace prefix is to associate a service operation name with a service in order to prevent naming conflicts, e.g. to distinguish identical service operation names used by different services.
Note that the XML response of the service example uses a namespace prefix that may be different (e.g. *ns
*) as long as it bound to the same namespace name *urn:calc
* through the *xmlns:ns="{
*urn:calc} binding. The use of namespace prefixes and namespace names is also required to enable SOAP applications to validate the content of SOAP messages. The namespace name in the service response is verified by the stub routine by using the information supplied in a namespace mapping table that is required to be part of gSOAP client and service application codes. The table is accessed at run time to resolve namespace bindings, both by the generated stub's data structure serializer for encoding the client request and by the generated stub's data structure deserializer to decode and validate the service response. The namespace mapping table should not be part of the header file input to the gSOAP soapcpp2
tool. Service details including namespace bindings may be provided with gSOAP directives in a header file, see Section Customizing the WSDL and Namespace Mapping Table File Contents With gSOAP Directives .
The namespace mapping table is:
The first four namespace entries in the table consist of the standard namespaces used by the SOAP 1.1 protocol. In fact, the namespace mapping table is explicitly declared to enable a programmer to specify the SOAP encoding style and to allow the inclusion of namespace-prefix with namespace-name bindings to comply to the namespace requirements of a specific SOAP service. For example, the namespace prefix *ns2
*, which is bound to *urn:calc
* by the namespace mapping table shown above, is used by the generated stub routine to encode the add
request. This is performed automatically by the gSOAP soapcpp2
tool by using the ns2
prefix of the ns2__add
method name specified in the calc.h
header file. In general, if a function name of a service operation, struct
name, class
name, enum
name, or field name of a struct
or class
has a pair of underscores, the name has a namespace prefix that must be defined in the namespace mapping table.
The namespace mapping table will be output as part of the SOAP Envelope by the stub routine. For example:
The namespace bindings will be used by a SOAP service to validate the SOAP request.
The incorporation of namespace prefixes into C++ identifier names is necessary to distinguish service operations that share the same name but are provided by separate Web services and/or organizations. It avoids potential name clashes, while sticking to the C syntax. The C++ proxy classes generated with soapcpp2 -i
(or -j
) drop the namespace prefix from the method names
The namespace prefix convention is also be applied to non-primitive types. For example, class
names are prefixed to avoid name clashes when the same name is used by multiple XML schemas. This ensures that the XML databinding never suffers from conflicting schema content. For example:
The namespace prefix is separated from the name of a data type by a pair of underscores (__
).
An instance of e__Address
is encoded by the generated serializer for this type as an Address element with namespace prefix e
:
While an instance of s__Address
is encoded by the generated serializer for this type as an Address element with namespace prefix s
:
The namespace mapping table of the client program must have entries for e
and s
that refer to the XML Schemas of the data types:
This table is required to be part of the client application to allow access by the serializers and deserializers of the data types at run time.
Proxy classes for C++ client applications are automatically generated by the gSOAP soapcpp2
tool, as was shown in Section Example .
There is a new and improved code generation capability for proxy classes, which is activated with the soapcpp2 -i
(or j
) option. These new proxy classes are derived from the soap structure, have a cleaner interface and offer more capabilites.
With C++, you can also use wsdl2h
option -q
*name* to generate the proxy in a C++ namespace name. This is very useful if you want to create multiple proxies for services by repeated use of wsdl2h
and combine them in one code. Alternatively, you can run wsdl2h
just once on all service WSDLs and have soapcpp2
generate multiple proxies for you. The latter approach does not use C++ namespaces and may reduce the overall amount of code.
To illustrate the generation of a "standard" (old-style) proxy class, the calc.h
header file example of the previous section is augmented with the appropriate directives to enable the gSOAP soapcpp2
tool to generate the proxy class. Directives are included in the generated header file by the wsdl2h
WSDL importer:
The first three directives provide the service details, which is used to name the proxy class, the service location port (endpoint), and the XML namespace. The subsequent groups of three directives per method define the operation's SOAP style (RPC) and encoding (SOAP encoded), and SOAP action string. These directives can be provided for each service operation when the SOAPAction is required, such as with SOAP1.1 RPC encoded and when WS-Addressing is used. In this example, the service protocol is set by default for all operations to use SOAP 1.1 RPC encoding. For //gsoap
directive details, see Section Customizing the WSDL and Namespace Mapping Table File Contents With gSOAP Directives .
The soapcpp2
tool takes this header file and generates a proxy soapcalcProxy.h
with the following contents (not using option -i
):
The gSOAP context and endpoint are declared public to enable access.
This generated proxy class can be included into a client application together with the generated namespace table as shown in this example:
The constructor allocates and initializes a gSOAP context for the instance.
You can use soapcpp2
option -n
together with -p
to create a local namespaces table to avoid link conflicts when you need multiple namespace tables or need to combine multiple clients, see also Sections soapcpp2 Options and How to Create Client/Server Libraries , and you can use a C++ code namespace
to create a namespace qualified proxy class, see Section How to Build a Client or Server in a C++ Code Namespace .
The soapcpp2 -i
option to generate proxy classes derived from the base soap structure. In addition, these classes offer more functionality as illustrated in Section Example .
Many SOAP services require the explicit use of XML Schema types in the SOAP payload. The default encoding, which is also adopted by the gSOAP soapcpp2
tool, assumes SOAP RPC encoding which only requires the use of types to handle polymorphic cases. Nevertheless, the use of XSD typed messages is advised to improve interoperability. XSD types are introduced with typedef
definitions in the header file input to the gSOAP soapcpp2
tool. The type name defined by a typedef
definition corresponds to an XML Schema type (XSD type). For example, the following typedef
declarations define various built-in XSD types implemented as primitive C/C++ types:
This easy-to-use mechanism informs the gSOAP soapcpp2
tool to generate serializers and deserializers that explicitly encode and decode the primitive C++ types as built-in primitive XSD types when the typedef
ed type is used in the parameter signature of a service operation (or when used nested within structs, classes, and arrays). At the same time, the use of typedef
does not force any recoding of a C++ client or Web service application as the internal C++ types used by the application are not required to be changed (but still have to be primitive C++ types, see Section How to use C++ Wrapper Classes to Specify Polymorphic Primitive Types for alternative class implementations of primitive XSD types which allows for the marshalling of polymorphic primitive types).
Reconsider the calculator example, now rewritten with explicit XSD types to illustrate the effect:
When processed by the gSOAP soapcpp2
tool it generates source code for the function soap_call_ns2__add
, which is identical to the C-style SOAP call:
The client application does not need to be rewritten and can still call the proxy using the "old" C-style function signatures. In contrast to the previous implementation of the stub however, the encoding and decoding of the data types by the stub has been changed to explicitly use the XSD types in the message payload.
For example, when the client application calls the proxy, the proxy produces a SOAP request with an *xsd:double
* (the *xsi:type
* is shown when the soapcpp2 -t
option is used):
The service response is:
The validation of this service response by the stub routine takes place by matching the namespace names (URIs) that are bound to the *xsd
* namespace prefix. The stub also expects the *addResponse
* element to be associated with URI *urn:calc
* through the binding of the namespace prefix *ns2
* in the namespace mapping table. The service response uses namespace prefix *n
* for the *addResponse
* element. This namespace prefix is bound to the same URI *urn:calc
* and therefore the service response is valid. When the XML is not well formed or does not pass validation, the response is rejected and a SOAP fault is generated. The validation level can be increased with the SOAP_XML_STRICT
flag, but this is not advised for SOAP RPC encoded messaging.
There is no standardized convention for the response element name in a SOAP RPC encoded response message, although it is recommended that the response element name is the method name ending with "*`Response`*". For example, the response element of *add
* is *addResponse
*.
The response element name can be specified explicitly using a struct
or class
declaration in the header file. This name must be qualified by a namespace prefix, just as the operation name should use a namespace prefix. The struct
or class
name represents the SOAP response element name used by the service. Consequently, the output parameter of the service operation must be declared as a field of the struct
or class
. The use of a struct
or a class
for the service response is fully SOAP 1.1 compliant. In fact, the absence of a struct
or class
indicates to the soapcpp2
tool to automatically generate a struct
for the response which is internally used by a stub.
Reconsider the calculator service operation specification which can be rewritten with an explicit declaration of a SOAP response element as follows:
The SOAP request and response messages are the same as before:
The difference is that the service response is required to match the specified addResponse
name and its namespace URI:
This use of a struct
or class
enables the adaptation of the default SOAP response element name and/or namespace URI when required.
The gSOAP soapcpp2
tool compiler uses the convention that the last parameter of the function prototype declaration of a service operation in a header file is also the only single output parameter of the method. All other parameters are considered input parameters of the service operation. To specify a service operation with multiple output parameters, a struct
or class
must be declared for the service operation response, see also How to Change the Response Element Name . The name of the struct
or class
must have a namespace prefix, just as the service method name. The fields of the struct
or class
are the output parameters of the service operation. Both the order of the input parameters in the function prototype and the order of the output parameters (the fields in the struct
or class
) is not significant. However, the SOAP 1.1 specification states that input and output parameters may be treated as having anonymous parameter names which requires a particular ordering, see Section How to Specify Anonymous Parameter Names .
As an example, consider a hypothetical service operation getNames
with a single input parameter SSN
and two output parameters first
and last
. This can be specified as:
The gSOAP soapcpp2
tool takes this header file as input and generates source code for the function soap_call_ns3__getNames
. When invoked by a client application, the proxy produces the SOAP request:
The response by a SOAP service:
where *first
* and *last
* are the output parameters of the getNames
service operation of the service.
As another example, consider a service operation copy
with an input parameter and an output parameter with identical parameter names (this is not prohibited by the SOAP 1.1 protocol). This can be specified as well using a response struct
:
The use of a struct
or class
for the service operation response enables the declaration of service operations that have parameters that are passed both as input and output parameters.
The gSOAP soapcpp2
compiler takes the copy.h
header file as input and generates the soap_call_X_rox__copy_name
proxy. When invoked by a client application, the proxy produces the SOAP request:
The response by a SOAP copy service could be something like:
The name will be parsed and decoded by the proxy and returned in the name
field of the struct X_rox__copy_nameResponse &r
parameter.
If the single output parameter of a service operation is a complex data type such as a struct
or class
it is necessary to specify the response element of the service operation as a struct
or class
at all times. Otherwise, the output parameter will be considered the response element (!), because of the response element specification convention used by gSOAP, as discussed in How to Change the Response Element Name .
This is best illustrated with an example. The Flighttracker service by ObjectSpace provides real time flight information for flights in the air. It requires an airline code and flight number as parameters. The service operation name is getFlightInfo
and the method has two string parameters: the airline code and flight number, both of which must be encoded as *xsd:string
* types. The method returns a getFlightResponse
response element with a return
output parameter that is of complex type FlightInfo
. The type FlightInfo
is represented by a class
in the header file, whose field names correspond to the FlightInfo
accessors:
The response element ns1__getFlightInfoResponse
is explicitly declared and it has one field: return_
of type ns2__FlightInfo
. Note that return_
has a trailing underscore to avoid a name clash with the \return
keyword, see Section C/C++ Identifier Name to XML Tag Name Mapping for details on the translation of C++ identifiers to XML element names.
The gSOAP soapcpp2
compiler generates the soap_call_ns1__getFlightInfo
proxy. Here is an example fragment of a client application that uses this proxy to request flight information:
When invoked by a client application, the proxy produces the SOAP request:
The Flighttracker service responds with:
The proxy returns the service response in variable r
of type struct ns1__getFlightInfoResponse
and this information can be displayed by the client application with the following code fragment:
This code displays the service response as:
A320 flight UAL184 traveling 497 mph at 37000 ft, is located 188 mi W of Lincoln, NE
Note: the flight tracker service is no longer available since 9/11/2001. It is kept in the documentation as an example to illustrate the use of structs/classes and response types.
The SOAP RPC encoding protocol allows parameter names to be anonymous. That is, the name(s) of the output parameters of a service operation are not strictly required to match a client's view of the parameters names. Also, the input parameter names of a service operation are not strictly required to match a service's view of the parameter names. The gSOAP soapcpp2
compiler can generate stub and skeleton routines that support anonymous parameters. Parameter names are implicitly anonymous by omitting the parameter names in the function prototype of the service operation. For example:
To make parameter names explicitly anonymous on the receiving side (client or service), the parameter names should start with an underscore (_
) in the function prototype in the header file.
For example:
In this example, the _a
, _b
, and _return
are anonymous parameters. As a consequence, the service response to a request made by a client created with gSOAP using this header file specification may include any name for the output parameter in the SOAP payload. The input parameters may also be anonymous. This affects the implementation of Web services in gSOAP and the matching of parameter names by the service.
To specify a service operation that has no input parameters, just provide a function prototype with one parameter which is the output parameter (some C/C++ compilers will not compile and complain about an empty struct
: use compile flag -DWITH_NOEMPTYSTRUCT
to compile the generated code for these cases). This struct
is generated by gSOAP to contain the SOAP request message. To fix this, provide one input parameter of type void*
(gSOAP can not serialize void*
data). For example:
The ns__getAllSOAPServices
method has one void*
input parameter which is ignored by the serializer to produce the request message.
Most C/C++ compilers allow empty struct
s and therefore the void*
parameter is not required.
To specify a service operation that has no output parameters, just define a function prototype with a response struct that is empty. For example:
Since the response struct is empty, no output parameters are specified.
Some SOAP resources refer to SOAP RPC with empty responses as one way SOAP messaging. However, we refer to one-way massaging by asynchronous explicit send and receive operations as described in Section Asynchronous One-Way Message Passing . The latter view of one-way SOAP messaging is also in line with Basic Profile 1.0.
The gSOAP soapcpp2
compiler generates skeleton routines in C++ source form for each of the service operations specified as function prototypes in the header file processed by the gSOAP soapcpp2
compiler. The skeleton routines can be readily used to implement the service operations in a new SOAP Web service. The compound data types used by the input and output parameters of service operations must be declared in the header file, such as structs, classes, arrays, and pointer-based data structures (graphs) that are used as the data types of the parameters of a service operation. The gSOAP soapcpp2
compiler automatically generates serializers and deserializers for the data types to enable the generated skeleton routines to encode and decode the contents of the parameters of the service operations. The gSOAP soapcpp2
compiler also generates a service operation request dispatcher routine that will serve requests by calling the appropriate skeleton when the SOAP service application is installed as a CGI application on a Web server.
The following example specifies three service operations to be implemented by a new SOAP Web service:
The add
and sub
methods are intended to add and subtract two double floating point numbers stored in input parameters a
and b
and should return the result of the operation in the result
output parameter. The sqrt
method is intended to take the square root of input parameter a
and to return the result in the output parameter result
. The xsd__double
type is recognized by the gSOAP soapcpp2
compiler as the *xsd:double
* XSD Schema data type. The use of typedef
is a convenient way to associate primitive C types with primitive XML Schema data types.
To generate the skeleton routines, the gSOAP soapcpp2
compiler is invoked from the command line with:
> soapcpp2 calc.h
The compiler generates the skeleton routines for the add
, sub
, and sqrt
service operations specified in the calc.h
header file. The skeleton routines are respectively, soap_serve_ns__add
, soap_serve_ns__sub
, and soap_serve_ns__sqrt
and saved in the file soapServer.cpp
. The generated file soapC.cpp
contains serializers and deserializers for the skeleton. The compiler also generates a service dispatcher: the soap_serve
function handles client requests on the standard input stream and dispatches the service operation requests to the appropriate skeletons to serve the requests. The skeleton in turn calls the service operation implementation function. The function prototype of the service operation implementation function is specified in the header file that is input to the gSOAP soapcpp2
compiler.
Here is an example Calculator service application that uses the generated soap_serve
routine to handle client requests:
Note that the service operations have an extra input parameter which is a pointer to the gSOAP runtime context. The implementation of the service operations MUST return a SOAP error code. The code SOAP_OK
denotes success, while SOAP_FAULT
denotes an exception with details that can be defined by the user. The exception description can be assigned to the soap->fault->faultstring
string and details can be assigned to the soap->fault->detail
string. This is SOAP 1.1 specific. SOAP 1.2 requires the soap->fault->SOAP_ENV__Reason
and the soap->fault->SOAP_ENV__Detail
strings to be assigned. Better is to use the soap_receiver_fault
function that allocates a fault struct and sets the SOAP Fault string and details regardless of the SOAP 1.1 or SOAP 1.2 version used. The soap_receiver_fault
function returns SOAP_FAULT
, i.e. an application-specific fault. The fault exception will be passed on to the client of this service.
This service application can be readily installed as a CGI application. The service description would be:
urn:simple-calc
*add
*a
* of type *xsd:double
* and *b
* of type *xsd:double
*result
* of type *xsd:double
*sub
*a
* of type *xsd:double
* and *b
* of type *xsd:double
*result
* of type *xsd:double
*sqrt
*a
* of type *xsd:double
*result
* of type *xsd:double
* or a SOAP FaultThe soapcpp2
compile generates a WSDL file for this service, see Section How to Generate WSDL Service Descriptions .
Unless the CGI application inspects and checks the environment variable SOAPAction
which contains the SOAP action request by a client, the SOAP action is ignored by the CGI application. SOAP actions are specific to the SOAP protocol and provide a means for routing requests and for security reasons (e.g. firewall software can inspect SOAP action headers to grant or deny the SOAP request. Note that this requires the SOAP service to check the SOAP action header as well to match it with the service operation.)
The header file input to the gSOAP soapcpp2
compiler does not need to be modified to generate client stubs for accessing this service. Client applications can be developed by using the same header file as for which the service application was developed. For example, the soap_call_ns__add
stub routine is available from the soapClient.cpp
file after invoking the gSOAP soapcpp2
compiler on the calc.h
header file. As a result, client and service applications can be developed without the need to know the details of the SOAP encoding used.
soapcpp2
compiler on a gSOAP header file. In this way you can incrementally build a new service by adding new operations and data types to the header file. To specify a custom build step, select the "Project" menu item "Settings" and select the header file in the File view pane. Select the "Custom Build" tab and enter 'soapcpp2.exe "$(inputPath)"
' in the "Command" pane. Enter 'soapStub.h soapH.h soapC.cpp soapClient.cpp soapServer.cpp
'. Don't forget to add the soapXYZProxy.h soapXYZObject.h
files that are generated for C++ class proxies and server objects named XYZ. Click "OK". Run soapcpp2
once to generate these files (you can simply do this by selecting your header file and select "Compile"). Add the files to your project. Each time you make a change to the header file, the project sources are updated automatically.mod_gsoap
directory of the gSOAP package to simplify Internet access and deal with encryption, proxies, and authentication. API instructions are included in the source.wsdl2h
WSDL parser with option -s
to prevent the generation of STL code. In addition, time_t
serialization is not supported, which means that you should add the following line to typemap.dat
indicating a mapping of xsd__dateTime
to char*
: *xsd__dateTime = | char* | char*
*.The deployment of a Web service as a CGI application is an easy means to provide your service on the Internet. However, the performance of CGI is not great. Also, gSOAP services can be run as stand-alone services on any port by utilizing the built-in HTTP and TCP/IP stacks. However, the preferred mechanism to deploy a service is through an Apache module or IIS module. These servers and modules are designed for server load balancing and access control.
To create a stand-alone service, only the main
routine of the service needs to be modified as follows. Instead of just calling the soap_serve
routine, the main
routine is changed into:
The soap_serve
dispatcher handles one request or multiple requests when HTTP keep-alive is enabled (with the SOAP_IO_KEEPALIVE
flag see Section TCP and HTTP Keep-Alive ).
The gSOAP functions that are frequently used for server-side coding are:
soap_new()
Allocates and Initializes gSOAP contextsoap_init(struct soap *soap)
Initializes a stack-allocated gSOAP context (required once)soap_bind(struct soap *soap, char *host, int port, int backlog)
Returns master socket (backlog = max. queuehost==NULL
: host is the machine on which the service runssoap_accept(struct soap *soap)
Returns slave socketsoap_end(struct soap *soap)
Clean up deserialized data (except class instances) and temporary datasoap_free_temp(struct soap *soap)
Clean up temporary data onlysoap_destroy(struct soap *soap)
Clean up deserialized class instances (note: this function will be renamed with option -n
soap_done(struct soap *soap)
Reset and detach context: close master/slave sockets and remove callbackssoap_free(struct soap *soap)
Detach and deallocate context (soap_new
())The host name in soap_bind
may be NULL to indicate that the current host should be used.
The soap.accept_timeout
context attribute of the gSOAP runtime context specifies the timeout value for a non-blocking soap_accept(&soap)
call. See Section Timeout Management for Non-Blocking Operations for more details on timeout management.
See Section Memory Management for more details on memory management.
A client application connects to this stand-alone service with the endpoint machine.genivia.com:18083
. A client may use the http://
prefix. When absent, no HTTP header is sent and no HTTP-based information will be communicated to the service.
Stand-alone multi-threading a Web Service is essential when the response times for handling requests by the service are (potentially) long or when keep-alive is enabled, see Section TCP and HTTP Keep-Alive . In case of long response times, the latencies introduced by the unrelated requests may become prohibitive for a successful deployment of a stand-alone service. When HTTP keep-alive is enabled, a client may not close the socket on time, thereby preventing other clients from connecting.
However, the preferred mechanism to deploy a service is through an Apache module or IIS module. These servers and modules are designed for server load balancing and access control.
The following example illustrates the use of threads to improve the quality of service by handling new requests in separate threads:
Note: the code does not wait for threads to join the main thread upon program termination.
The soap_serve
dispatcher handles one request or multiple requests when HTTP keep-alive is set with SOAP_IO_KEEPALIVE
. The soap.max_keep_alive
value can be set to the maximum keep-alive calls allowed, which is important to avoid a client from holding a thread indefinitely. The send and receive timeouts are set to avoid (intentionally) slow clients from holding a socket connection too long. The accept timeout is used to let the server terminate automatically after a period of inactivity.
The following example uses a pool of servers to limit the machine's resource utilization:
The following functions can be used to setup a gSOAP runtime context (struct soap
):
soap_init(struct soap *soap)
Initializes a runtime context (required only once)struct soap *soap_new()
Allocates, initializes, and returns a pointer to a runtime contextstruct soap *soap_copy(struct soap *soap)
Allocates a new runtime context and copies a context (deep copy, i.e. the new context does not share any data with the other context)the argument context such that the new context does not share data with the argument context
soap_done(struct soap *soap)
Reset, close communications, and remove callbacksA new context is initiated for each thread to guarantee exclusive access to runtime contexts.
For clean termination of the server, the master socket can be closed and callbacks removed with soap_done(struct soap *soap)
.
The advantage of the code shown above is that the machine cannot be overloaded with requests, since the number of active services is limited. However, threads are still started and terminated. This overhead can be eliminated using a queue of requests (open sockets) as is shown in the code below.
Note: the plugin/threads.h
and plugin/threads.c
code can be used for a portable implementation. Instead of POSIX calls, use MUTEX_LOCK
, MUTEX_UNLOCK
, and COND_WAIT
. These are wrappers for Win API calls or POSIX calls.
The void *soap.user
field can be used to pass application data to service methods. This field should be set before the soap_serve()
call. The service method can access this field to use the application-dependent data. The following example shows how a non-static database handle is initialized and passed to the service methods:
Another way to pass application data around in a more organized way is accomplished with plugins, see Section gSOAP Plug-ins .
The same client header file specification issues apply to the specification and implementation of a SOAP Web service. Refer to
Server object classes for C++ server applications are automatically generated by the gSOAP soapcpp2
compiler.
There are two modes for generating classes. Use soapcpp2
option -i
(or -j
) to generate improved class definitions where the class' member functions are the service methods.
The older examples (without the use of soapcpp2
option -i
and -j
) use a C-like approach with globally defined service methods, which is illustated here with a calculator example:
The first three directives provide the service name which is used to name the service class, the service location (endpoint), and the schema. The fourth directive defines the optional SOAPAction for the method, which is a string associated with SOAP 1.1 operations. Compilation of this header file with soapcpp2 -i
creates a new file soapCalculatorObject.h
with the following contents:
This generated server object class can be included into a server application together with the generated namespace table as shown in this example:
You can use soapcpp2
option -n
together with -p
to create a local namespace table to avoid link conflict when you need to combine multiple tables and/or multiple servers, see also Sections soapcpp2 Options and How to Create Client/Server Libraries , and you can use a C++ code namespace
to create a namespace qualified server object class, see Section How to Build a Client or Server in a C++ Code Namespace .
The example above serves requests over stdin/out. Use the bind and accept calls to create a stand-alone server to service inbound requests over sockets, see also How to Create a Stand-Alone Server .
A better alternative is to use the soapcpp2
option -i
. The C++ proxy and server objects are derived from the soap context struct, which simplifies the proxy invocation and service operation implementations.
Compilation of the above header file with the gSOAP compiler soapcpp2
option -i
creates new files soapCalculatorService.h
and soapCalculatorService.cpp
(rather than the C-style soapServer.cpp
).
This generated server object class can be included into a server application together with the generated namespace table as shown in this example:
Note that the service operation does not need a prefix (ns__
) and there is no soap context struct passed to the service operation since the service object itself is the context (it is derived from the soap struct).
When combining multiple services into one application, you can run wsdl2h
on multiple WSDLs to generate the single all-inclusive service definitions header file. This header file is then processed with soapcpp2
, for example to generate server class objects with option -i
and -q
to separate the service codes with C++ namespaces, see Section How to Build a Client or Server in a C++ Code Namespace .
This works well, but the problem is that we end up with multiple classes, each for a collection of service operations the class is supposed to implement. But what if we need to provide one endpoint port for all services and operations? In this case invoking the server object's serve
method is not sufficient, since only one service can accept requests while we want multiple services to listen to the same port.
The approach is to chain the service dispatchers, as shown below:
The dispatch
method parses the SOAP/XML request and invokes the service operations, unless there is no matching operation and SOAP_NO_METHOD
is returned. The soap_copy_stream
ensures that the service object uses the currently open socket. The copied streams are freed with soap_free_stream
. Do not enable keep-alive support, as the socket may stay open indefinitely afterwards as a consequence. Also, the dispatch
method does not send a fault to the client, which has to be explicitly done with the soap_send_fault
operation when an error occurs.
In this way, multiple services can be chained to accept messages on the same port. This approach also works with SSL for HTTPS services.
However, this approach is not recommended for certain plugins, because plugins must be registered with all service objects and some plugins require state information to be used across the service objects, which will add significantly to the complexity.
When plugin complications arise, it is best to have all services share the same context. This means that soapcpp2
option -j
should be used instead of option -i
. Each service class has a pointer member to a soap struct context. This member pointer should point to the same soap context.
With option -j
and -q
the code to chain the services is as follows, based on a single struct soap
engine context:
The gSOAP stub and skeleton compiler soapcpp2
generates WSDL (Web Service Description Language) service descriptions and XML Schema files when processing a header file. The tool produces one WSDL file for a set of service operations, which must be provided. The names of the function prototypes of the service operations must use the same namespace prefix and the namespace prefix is used to name the WSDL file. If multiple namespace prefixes are used to define service operations, multiple WSDL files will be created and each file describes the set of service operations belonging to a namespace prefix.
In addition to the generation of the *ns.wsdl
* file, a file with a namespace mapping table is generated by the gSOAP compiler. An example mapping table is shown below:
This file can be incorporated in the client/service application, see Section Namespace Mapping Table for details on namespace mapping tables.
To deploy a Web service, copy the compiled CGI service application to the designated CGI directory of your Web server. Make sure the proper file permissions are set (*chmod 755 calc.cgi
* for Unix/Linux). You can then publish the WSDL file on the Web by placing it in the appropriate Web server directory.
The gSOAP soapcpp2
compiler also generates XML Schema files for all C/C++ complex types (e.g. struct
s and class
es) when declared with a namespace prefix. These files are named *ns.xsd
*, where *ns
* is the namespace prefix used in the declaration of the complex type. The XML Schema files do not have to be published as the WSDL file already contains the appropriate XML Schema definitions.
To customize the WSDL output, it is essential to use //gsoap
directives to declare the service name, the endpoint port, and namespace:
These are minimal settings. More details and settings for the service operations should be declared as well. See Section Customizing the WSDL and Namespace Mapping Table File Contents With gSOAP Directives for more details.
For example, suppose the following methods are defined in the header file:
Then, one WSDL file will be created with the file name *ns.wsdl
* that describes all three service operations:
The above uses all default settings for the service name, port, and namespace which should be set in the header file with //gsoap
directives (Section Customizing the WSDL and Namespace Mapping Table File Contents With gSOAP Directives ).
A gSOAP service implemented with CGI may make direct client calls to other services from within its service operations, without setting up a new context. A stand-alone service application must setup a new soap struct context, e.g. using soap_copy
and delete it after the call.
The server-side client call is best illustrated with an example. The following example is a more sophisticated example that combines the functionality of two Web services into one new SOAP Web service. The service provides a currency-converted stock quote. To serve a request, the service in turn requests the stock quote and the currency-exchange rate from two XMethods services (these services are no longer available by XMethods, but are used here as an example).
In addition to being a client of two XMethods services, this service application can also be used as a client of itself to test the implementation. As a client invoked from the command-line, it will return a currency-converted stock quote by connecting to a copy of itself installed as a CGI application on the Web to retrieve the quote after which it will print the quote on the terminal.
The header file input to the gSOAP soapcpp2
compiler is given below. The example is for illustrative purposes only (the XMethods services are not operational):
The quotex.cpp
client/service application source is:
To compile:
soapcpp2 quotex.h c++ -o quotex.cgi quotex.cpp soapC.cpp soapClient.cpp soapServer.cpp stdsoap2.cpp -lsocket -lxnet -lnsl
Note: under Linux and Mac OS X you can often omit the *-l
* libraries.
The quotex.cgi
executable is installed as a CGI application on the Web by copying it in the designated directory specific to your Web server. After this, the executable can also serve to test the service. For example
> quotex.cgi IBM uk
returns the quote of IBM
in uk
pounds by communicating the request and response quote from the CGI application. See http://xmethods.com/detail.html?id=5 for details on the currency abbreviations.
When combining clients and service functionalities, it is required to use one header file input to the compiler. As a consequence, however, stubs and skeletons are available for all service operations, while the client part will only use the stubs and the service part will use the skeletons. Thus, dummy implementations of the unused service operations need to be given which are never called.
Three WSDL files are created by gSOAP: *ns1.wsdl
*, *ns2.wsdl
*, and *ns3.wsdl
*. Only the *ns3.wsdl
* file is required to be published as it contains the description of the combined service, while the others are generated as a side-effect (and in case you want to develop these separate services).
SOAP RPC client-server interaction is synchronous: the client blocks until the server responds to the request. gSOAP also supports asynchronous one-way message passing and the interoperable synchronous one-way message passing over HTTP. The two styles are similar, but only the latter is interoperable and is compliant to Basic Profile 1.0. The interoperable synchronous one-way message passing style over HTTP is discussed in Section Implementing Synchronous One-Way Message Passing over HTTP below.
SOAP messaging routines are declared as function prototypes, just like service operations for SOAP RPC. However, the output parameter is a void
type to indicate the absence of a return value.
For example, the following header file specifies an event message for SOAP messaging:
The gSOAP soapcpp2
tool generates the following functions in soapClient.cpp
:
The soap_send_ns__event
function transmits the message to the destination URL by opening a socket and sending the SOAP encoded message. The socket will remain open after the send and has to be closed with soap_closesock()
. The open socket connection can also be used to obtain a service response, e.g. with a soap_recv
function call.
The soap_recv_ns__event
function waits for a SOAP message on the currently open socket (soap.socket
) and fills the struct ns__event
with the ns__event
parameters (e.g. int eventNo
). The struct ns__event
is automatically created by gSOAP and is a mirror image of the ns__event
parameters:
The gSOAP generated soapServer.cpp
code includes a skeleton routine to accept the message. (The skeleton routine does not respond with a SOAP response message.)
The skeleton routine calls the user-implemented ns__event(struct soap *soap, int eventNo)
routine (note the absence of the void parameter!).
As usual, the skeleton will be automatically called by the service operation request dispatcher that handles both the service operation requests (RPCs) and messages:
One-way SOAP message passing over HTTP as defined by the SOAP specification and Basic Profile 1.0 is synchrounous, meaning that the server must respond with an HTTP OK header (or HTTP 202 Accepted) and an empty body. To implement synchrounous one-way messaging, the same setup for asynchrounous one-way messaing discussed in Section Asynchronous One-Way Message Passing is used, but with one simple addition at the client and server side for HTTP transfer.
At the server side, we have to return an empty HTTP OK response. Normally with one-way messaging the gSOAP engine closes the socket when the service operation is finished, which is not desirable for synchronous one-way message exchanges over HTTP: an HTTP response should be send. This is accomplished as follows. For each one-way operation implemented in C/C++, we replace the return SOAP_OK
with:
At the client side, the empty response header must be parsed as follows:
The synchronous (and asynchronous) one-way messaging supports HTTP keep-alive and chunking.
Note: soap_send_empty_response
returns the error code SOAP_STOP
to force the engine to stop producing a response message after the service operation completed, which allows soap_send_empty_response
to be used with any service operation that should return HTTP 202.
The gSOAP XML databindings for C and C++ allow a seamless integration of XML in C and C++ applications. Data can be serialized in XML and vice versa. WSDL and XML schema files can be converted to C or C++ definitions. C and C++ definitions can be translated to WSDL and schemas to support legacy ANSI C applications for example.
Learn more about XML data binding for C and C++ with gSOAP by visiting the Developer Center https://www.genivia.com/dev.html and the new and most up-to-date XML data binding documentation https://www.genivia.com/doc/databinding/html.
Command:
> wsdl2h [options] XSD and WSDL files ...
The WSDL 1.1 and 2.0 standards are supported. If you have trouble with WSDL 2.0 please contact the author. The entire XML schema 1.1 standard is supported, except XPath expressions and assertions. This covers all of the following schema components with their optional [ attributes ] shown:
The supported facets are:
Other:
All primitive XSD types are supported. A subset of the default type mappings is shown below. User-defined mappings can be added to typemap.dat
to let wsdl2h
(re)map XSD types to C/C++ types.
Note: automatic validation of *xs:pattern
* restricted content is possible with a hook to a regex pattern matching engine, see the fsvalidate
and fwvalidate
callbacks in Section Function Callbacks for Customized I/O and HTTP Handling .
Note: string targets are defined in the typemap.dat
file used by wsdl2h
to map XSD types. This allows the use of char*
, wsha_t*
, std::string
, and std::wstring
string types for all XSD types mapped to strings.
All non-primitive XSD types are supported (with the default mapping shown):
There are several initialization flags to control XML serialization at runtime:
SOAP_XML_STRICT
.SOAP_XML_IGNORENS
.SOAP_XML_CANONICAL
.xmlns="..."
namespace bindings are enforced with SOAP_XML_DEFAULTNS
.SOAP_XML_INDENT
.xsi:nil
for NULL elements is serialized with SOAP_XML_NIL
.Strict validation catches all structural XML validation violations. For primitive type values, it depends on the C/C++ target type that XSD types are mapped to, to catch primitive value content pattern violations. Primitive value content validation is performed on non-string types such as numerical and time values. String values are not automatocally validated, unless a *xs:pattern
* is given and the fsvalidate
and fwvalidate
callbacks are implemented by the user. Alternatively, deserialized string content can be checked at the application level.
To obtain C and/or C++ type definitions for XML schema components, run wsdl2h
on the schemas to generate a header file. This header file defines the C/C++ type representations of the XML schema components. The header file is then processed by the soapcpp2
tool to generate the serializers for these types. See Section XML data bindings for an overview to use wsdl2h
and soapcpp2
to map schemas to C/C++ types to obtain XML data bindings.
To generate serialization code, execute:
> soapcpp2 [options] header_file.h
The following C/C++ types are supported in the header file:
Additional features and C/C++ syntax requirements:
#import "file.h"
instead of #include
to import other header files. The #include
and #define
directives are accepted, but deferred to the generated code.soapcpp2
-generated code nor changed/augmented by the soapcpp2
tool:The SOAP_ENV__Header
struct is mutable as well as the SOAP_ENV__Fault
, SOAP_ENV__Detail
, SOAP_ENV__Reason
, and SOAP_ENV__Code
structs.
SOAP_C_UTFSTRING
flag. When enabled, all std::string
and char*
strings MUST contain UTF8.The soapcpp2 tool generates serializers and deserializers for all wsdl2h-generated or user-defined data structures that are specified in the header file input to the compiler. The serializers and deserializers can be found in the generated soapC.cpp
file. These serializers and deserializers can be used separately by an application without the need to build a full client or service application. This is useful for applications that need to save or export their data in XML or need to import or load data stored in XML format.
We assume that the wsdl2h
tool was used to map XML schema types to C/C++ data types. The soapcpp2
tool then generates the (de)serializers for the C/C++ types. You can also use soapcpp2
directly on a header file that declares annotated C/C++ data types to serialize.
The following context attributes can be set to control the destination and source for serialization and deserialization:
int soap.socket
socket file descriptor for input and output (or set to SOAP_INVALID_SOCKET
)ostream *soap.os
C++ only: output stream used for send operationsconst char **soap.os
C only: points to a string pointer to be set with the managed string contentistream *soap.is
C++ only: input stream used for receive operationsconst char *soap.is
C only: string with input to parse (this pointer advances)int soap.sendfd
when soap.socket
=SOAP_INVALID_SOCKET
, this fd is used for send operationsint soap.recvfd
when soap.socket
=SOAP_INVALID_SOCKET
, this fd is used for receive operationsThe following initializing and finalizing functions can be used:
void soap_begin_send(struct soap*)
start a send/write phaseint soap_end_send(struct soap*)
flush the bufferint soap_begin_recv(struct soap*)
start a rec/read phase (if an HTTP header is present, parse it first)int soap_end_recv(struct soap*)
perform a id/href consistency check on deserialized dataThese operations do not open or close the connections. The application should open and close connections or files and set the soap.socket
, soap.os
or soap.sendfd
, soap.is
or soap.recvfd
streams or descriptors. When soap.socket
<0 and none of the streams and descriptors are set, then the standard input and output will be used.
The following options are available to control serialization:
See also Section Run Time Flags to control the I/O buffering and content encoding such as compression and DIME encoding.
We assume that the wsdl2h
tool was used to map XML schema types to C/C++ data types. The soapcpp2
tool then generates the (de)serializers for the C/C++ types.
To serialize data to an XML stream, two functions should be called to prepare for serialization of the data and to send the data, respectively. The first function, soap_serialize
, analyzes pointers and determines if multi-references are required to encode the data and if cycles are present the object graph. The second function, soap_put
, produces the XML output on a stream.
The soap_serialize
and soap_put
(and both combined by soap_write
) functions are statically generated specific to a data type. For example, soap_serialize_float(&soap, &d)
is called to serialize an float
value and soap_put_float(&soap, &d, "number", NULL)
is called to output the floating point value in SOAP tagged with the name *<number>
*. The soap_write_float(&soap, &d)
conveniently combines the initialization of output, writing the data, and finalizing the output.
To initialize data, the soap_default
function of a data type can be used. For example, soap_default_float(&soap, &d)
initializes the float to 0.0. The soap_default
functions are useful to initialize complex data types such as arrays, struct
s, and class
instances. Note that the soap_default
functions do not need the gSOAP runtime context as a first parameter.
The following table lists the type naming conventions used by gSOAP:
Type | Name |
---|---|
char* | string |
wchar_t* | wstring |
std::string | std__string |
std::wstring | std__wstring |
char | byte |
bool | bool |
double | double |
int | int |
float | float |
long | long |
long long | LONG64 |
short | short |
time_t | time |
unsigned char | unsignedByte |
unsigned int | unsignedInt |
unsigned long | unsignedLong |
unsigned long long | ULONG64 |
unsigned short | unsignedShort |
T [N] | ArrayN OfType where Type is the type name of T |
T * | PointerToType where Type is the type name of T |
std::vector<T> | TemplateOfType where Type is the type name of T |
struct Name | Name |
class Name | Name |
enum Name | Name |
Consider for example the following C code with a declaration of p
as a pointer to a struct ns__Person
:
To serialize p
, its address is passed to the function soap_serialize_PointerTons__Person
generated for this type by the gSOAP soapcpp2
compiler:
The address of p
is passed, so the serializer can determine whether p
was already serialized and to discover co-referenced objects and cycles in graph data structures that require SOAP encoding with id-ref serialization. To generate the output, the address of p
is passed to the function soap_put_PointerTons__Person
together with the name of an XML element and an optional type string (to omit a type, use NULL
):
or the shorthand for the above (without the xsi type):
This produces:
The serializer is initialized with the soap_begin_send(soap)
function and closed with soap_end_send(soap)
. All temporary data structures and data structures deserialized on the heap are destroyed with the soap_destroy
and soap_end
functions (in this order).
The soap_done
function should be used to reset the context, i.e. the last use of the context. To detach and deallocate the context, use soap_free
.
To remove the temporary data only and keep the deserialized data on the heap, use soap_free_temp
. Temporary data structures are only created if the encoded data uses pointers. Each pointer in the encoded data has an internal hash table entry to determine all multi-reference parts and cyclic parts of the complete data structure.
You can assign an output stream in C++ to soap.os
and in C an output string soap.os
, or a file descriptor to soap.sendfd
.
For example, to assign a file descriptor:
The above can be abbreviated to
The soap_serialize
function is optional. It MUST be used when the object graph contains cycles. It MUST be called to preserve the logical coherence of pointer-based data structures, where pointers may refer to co-referenced objects. By calling soap_serialize
, data structures shared through pointers are serialized only once and referenced in XML using id-refs attributes. The actual id-refs used depend on the SOAP encoding. To turn off SOAP encoding, remove or avoid using the SOAP-ENV and SOAP-ENC namespace bindings in the namespace table. In addition, the SOAP_XML_TREE
and SOAP_XML_GRAPH
flags can be used to control the output by restricting serialization to XML trees or by enabling multi-ref graph serialization with id-ref attribuation.
To save the data as an XML tree (with one root) without any id-ref attributes, use the SOAP_XML_TREE
flag. The data structure MUST NOT contain pointer-based cycles.
To preserve the exact structure of the data object graph and create XML with one root, use the SOAP_XML_GRAPH
output-mode flag (see Section Run Time Flags ). Use this flag and the soap_serialize
function to prepare the serialization of data with in-line id-ref attributes. Using the SOAP_XML_GRAPH
flag assures the preservation of the logical structure of the data
For example, to encode the contents of two variables var1
and var2
that may share data through pointer structures, the serializers are called before the output routines:
where Type1 is the type name of T1 and Type2 is the type name of T2 (see table above). The strings [optional: namespace-prefix:]type-name1
and [optional: namespace-prefix:]type-name2
describe the schema types of the elements. Use NULL
to omit this type information.
For serializing class instances, method invocations MUST be used instead of function calls, for example obj.soap_serialize(&soap)
and obj.soap_put(&soap, "elt", "type")
. This ensures that the proper serializers are used for serializing instances of derived classes.
You can serialize a class instance to a stream as follows:
For gSOAP 2.8.28 and later, in C we use soap.os
to obtain a string with the XML serialized data:
When you declare a soap struct pointer as a data member in a class, you can overload the << operator to serialize the class to streams:
Of course, when you construct an instance you must set its soap struct to a valid context. Deserialized class instances with a soap struct data member will have their soap structs set automatically, see Section Intra-Class Memory Management .
In principle, XML output for a data structure can be produced with soap_put
without calling the soap_serialize
function first. In this case, the result is similar to SOAP_XML_TREE
which means that no id-refs are output. Cycles in the data structure will crash the serialization algorithm, even when the SOAP_XML_GRAPH
is set.
Consider the following struct
:
The following fragment initializes the pointer fields p
and q
to the value of field n
:
What is special about this data structure is that n
is 'fixed' in the Tricky
structure, and p
and q
both point to n
. The gSOAP serializers strategically place the id-ref attributes such that n
will be identified as the primary data source, while p
and q
are serialized with ref/href attributes.
The resulting output is:
which uses an independent element at the end to represent the multi-referenced integer, assuming the SOAP-ENV
and SOAP-ENC
namespaces indicate SOAP 1.1 encoding.
With the SOAP_XML_GRAPH
flag the output is:
In this case, the XML is self-contained and multi-referenced data is accurately serialized. The gSOAP generated deserializer for this data type will be able to accurately reconstruct the data from the XML (on the heap).
We assume that the wsdl2h
tool was used to map XML schema types to C/C++ data types. The soapcpp2
tool then generates the (de)serializers for the C/C++ types. You can also use soapcpp2
directly on a header file that declares annotated C/C++ data types to serialize.
To deserialize a data type from XML, the soap_get
(or the simpler soap_read
) function for the data type to be deserialized is used. The outline of a program that deserializes two variables var1
and var2
is for example:
The strings [optional: namespace-prefix:
type-name1] and [optional: namespace-prefix:
type-name2] are the schema types of the elements and should match the *xsi:type
* attribute of the receiving message. To omit the match, use NULL
as the type. For class instances, method invocation can be used instead of a function call if the object is already instantiated, i.e. obj.soap_get(&soap, "...", "...")
.
The soap_begin
call resets the deserializers. The soap_destroy
and soap_end
calls remove the temporary data structures and the decoded data that was placed on the heap.
To remove temporary data while retaining the deserialized data on the heap, the function soap_free_temp
should be called instead of soap_destroy
and soap_end
.
One call to the soap_get_Type
function of a type Type
scans the entire input to process its XML content and to capture SOAP 1.1 independent elements (which contain multi-referenced objects). As a result, soap.error
will set to SOAP_EOF
. Also storing multiple objects into one file will fail to decode them properly with multiple soap_get
calls. A well-formed XML document should only have one root anyway, so don't save multiple objects into one file. If you must save multiple objects, create a linked list or an array of objects and save the linked list or array. You could use the soap_in_Type
function instead of the soap_get_Type
function. The soap_in_Type
function parses one XML element at a time.
You can deserialize class instances from a stream as follows:
This can be abbreviated to:
When declaring a soap struct pointer as a data member in a class, you can overload the >> operator to parse and deserialize a class instance from a stream:
For gSOAP 2.8.28 and later, you can parse XML from strings as follows:
When declaring a soap struct pointer as a data member in a class, you can overload the >> operator to parse and deserialize a class instance from a stream or string stream:
As an example, consider the following data type declarations:
The following program uses these data types to write to standard output a data structure that contains the data of a person named "John" living at Downing st. 10 in Londen. He has a mother "Mary" and a father "Stuart". After initialization, the class instance for "John" is serialized and encoded in XML to the standard output stream using gzip compression (requires the Zlib library, compile sources with -DWITH_GZIP):
The header file is processed and the application compiled on Linux/Unix with:
> soapcpp2 person.h > c++ -DWITH_GZIP -o person person.cpp soapC.cpp stdsoap2.cpp -lsocket -lxnet -lnsl -lz
(Depending on your system configuration, the libraries libsocket.a
, libxnet.a
, libnsl.a
are required. Compiling on Linux typically does not require the inclusion of those libraries.) See Zlib Compression for details on compression with gSOAP.
Running the person
application results in the compressed XML output:
The following program fragment decodes this content from standard input and reconstructs the original data structure on the heap:
It is REQUIRED to either pass NULL
to the soap_get
routine, or a valid pointer to a data structure that can hold the decoded content. If the data john
was already allocated then it does not need to be allocated again as the following demonstrates. The following program fragment decodes the SOAP content in a struct ns__Person
allocated on the stack:
Note the use of soap_default_ns__Person
. This routine is generated by the gSOAP soapcpp2
tool and assigns default values to the fields of john
.
C++ applications can define appropriate stream operations on objects for (de)serialization of objects on streams. This is best illustrated with an example. Section Serializing C/C++ Data to XML gives details on serializing types in general. Consider the class
The struct soap
member is used to bind the instances to a gSOAP context for (de)serialization. We use the gSOAP soapcpp2
compiler from the command prompt to generate the class (de)serializers (assuming that person.h
contains the class declaration):
> soapcpp2 person.h
gSOAP generates the (de)serializers and an instantiation function for the class soap_new_ns__person(struct soap *soap, int num)
to instantiate one or more objects and associate them with a gSOAP context for deallocation with soap_destroy(soap)
. To instantiate a single object, omit the num
parameter or set to -1. To instantiate an array of objects, set num >= 0
.
The stream operations are implemented as follows
The gSOAP soapcpp2
compiler generates soap_default
functions for all data types. The default values of the primitive types can be easily changed by defining any of the following macros in the stdsoap2.h
file:
Instead of adding these to stdsoap2.h
, you can also compile with option -DWITH_SOAPDEFS_H
and include your definitions in file soapdefs.h
. The absence of a data value in a receiving SOAP message will result in the assignment of a default value to a primitive type upon deserialization.
Default values can also be assigned to individual struct
and class
fields of primitive type. For example,
Default values are assigned to the fields on receiving a SOAP/XML message in which the data values are absent.
Because method requests and responses are essentially structs, default values can also be assigned to method parameters. The default parameter values do not control the parameterization of C/C++ function calls, i.e. all actual parameters must be present when calling a function. The default parameter values are used in case an inbound request or response message lacks the XML elements with parameter values. For example, a Web service can use default values to fill-in absent parameters in a SOAP/XML request:
When the request message lacks uid and pwd parameters, e.g.:
then the service uses the default values. In addition, the default values will show up in the SOAP/XML request and response message examples generated by the gSOAP compiler.
The wsdl2h
tool is an advanced application that converts one or more WSDLs to C/C++. It can also be used without WSDLs to convert XML schemas (XSD files) to C/C++ to implement XML data bindings in C and C++.
The creation of C and C++ applications from one of more WSDL service descriptions is a two-step process.
To convert a WSDL to C++, use:
> wsdl2h file.wsdl
to generate a C++ header file file
.h. This generated header file is a Web service specification that contains the parameter types and service function definitions in an understandable format in C++ (or ANSI C as shown below). Web service operations are represented as function prototypes. Schema types are represented by semantically equivalent C/C++ types that are convenient and natural to use in a C/C++ application. The generated header file also contains various annotations related to the Web service properties defined in the WSDL.
To generate ANSI C, use option -c
:
> wsdl2h -c file.wsdl
Multiple WSDL specifications can be processed at once and saved to one file with the -o
option:
> wsdl2h -o file.h file1.wsdl file2.wsdl file3.wsdl
You can retrieve WSDLs from one of more URLs:
> wsdl2h -o file.h http://www.example.com/example.wsdl
To convert XML schemas to C or C++ XML data binding code, use:
> wsdl2h -o file.h file1.xsd file2.xsd file3.xsd
The wsdl2h
-generated header file file.h
is processed by the soapcpp2
tool to auto-generate the advanced data binding logic to convert the C/C++ data to XML and vice versa at runtime for your SOAP/XML application.
To process a gSOAP header file file.h
(generated by wsdl2h
) to generate advanced XML data bindings for C++, use:
> soapcpp2 -i -Iimport file.h
When the header file file.h
was generated for C++, then this command generates a couple of C++ source files (more details will follow in Section Using the soapcpp2 Compiler and Code Generator ) that implement XML encoders for the data binding. Option -i
generates a client proxy objects and service objects to invoke and serve SOAP/XML operations, respectively. Option -Iimport
sets the import directory for imported files from the package's import
, such as stlvector.h
for STL vector serialization support.
When the header file file.h
was generated for ANSI C, then the above command generates a couple of C files that implement XML encoders, client stubs for remote invocation, and service skeletons for service operations.
Consider for example the following commands to implement a c++ client of a service:
> wsdl2h -o calc.h http://www.genivia.com/calc.wsdl > soapcpp2 -i -Iimport calc.h
The first command generates calc.h
from the WSDL at the specified URL. The header file is then processed by the soapcpp2
tool to generate the proxies (and service objects that we will not use) for the client application.
The C++ client application uses the auto-generated soapcalcProxy.h
class and calc.nsmap
XML namespace table to access the Web service. Both need to be #include
-d in your source. Then compile and link the soapcalcProxy.cpp
, soapC.cpp
and stdsoap2.cpp
sources to complete the build.
The wsdl2h
tool is an advanced XML data binding tool for converting WSDLs and XML schemas (XSD files) to C or C++. The tool takes WSDL and/or XSD files or URLs and converts these to a C or C++ specification in one easy-to-read C/C++ header file. The header file is not intended to be included in your code directly!. It should be converted by soapcpp2
to generate the logic for the data bindings. It can however be safely converted by a documentation tool such as Doxygen to analyze and represent the service operations and data in a convenient layout. To this end, the header file is self-explanatory.
The wsdl2h
tool generates only one file, the header file that includes all of the information obtained from all WSDL and schema files provided to the tool at the command-line prompt. The default output file name of wsdl2h
is the first WSDL/schema input file name but with extension .h
instead of .wsdl
(or .xsd
). When an input file is absent or a WSDL file from a Web location is accessed, the header output will be produced on the standard output unless option -o
is used to direct the output to a file.
The wsdl2h
command-line options are:
option | result |
---|---|
-a | generate indexed struct names for local elements with anonymous types |
-b | bi-directional operations to serve one-way response messages (duplex) |
-c | generate C source code |
-d | use DOM to populate xs:any and xsd:anyType elements |
-e | don't qualify enum names |
-f | generate flat C++ class hierarchy for schema extensions |
-g | generate global top-level element declarations |
-h | print help information |
-I path | use path to locate source files for #import |
-i | don't import (advanced option) |
-j | don't generate SOAP_ENV__Header and SOAP_ENV__Detail definitions |
-k | don't generate SOAP_ENV__Header mustUnderstand qualifiers |
-l | include license information in output |
-m | use xsd.h module to import primitive types |
-N name | use name for service prefixes to produce a service for each binding |
-n name | use name as the base namespace prefix name instead of ns |
-o file | output to file |
-P | don't create polymorphic types inherited from xsd__anyType |
-p | create polymorphic types inherited from base xsd__anyType (this is automatically performed when WSDL contains polymorphic definitions) |
-q name | use name for the C++ namespace of all declarations |
-r host[:port[:id:pw]] | connect via proxy host, port, and proxy credentials |
-r:uid:pwd | connect with authentication credentials (digest auth requires SSL) |
-R | generate REST operations for REST bindings in the WSDL |
-s | don't generate STL code (no std::string and no std::vector) |
-t file | use type map file instead of the default file typemap.dat |
-U | map Unicode XML names to UTF8-encoded Unicode C/C++ identifiers |
-u | don't generate unions |
-V | display the current version and exit |
-v | verbose output |
-W | suppress warnings |
-w | always wrap response parameters in a response struct |
-x | don't generate _XML any/anyAttribute extensibility elements |
-y | generate typedef synonyms for structs and enums |
-z1 | compatibility with 2.7.6e: generate pointer-based arrays |
-z2 | compatibility with 2.7.15: qualify element/attribute referenced members |
-z3 | compatibility with 2.7.16 to 2.8.7: qualify element/attribute references |
-z4 | compatibility up to 2.8.11: don't generate union structs in std::vector |
-z5 | compatibility up to 2.8.15 |
-z6 | compatibility up to 2.8.17 |
-_ | don't generate _USCORE (replace with Unicode _x005f) |
Note: see README.txt
in the wsdl
directory for the latest information on installation and options to of the wsdl2h
WSDL/schema importer.
The typemap.dat
file for the wsdl2h
tool is intended to customize or optimize the type bindings by mapping schema types to C/C++ types. It contains custom XML Schema to C/C++ type bindings and a few bindings are defined for convenience.
Here is an example typemap file's content:
Type bindings can be provided to bind XML schema types to C/C++ types for your project. Type bindings have four parts:
where 'prefix__type' is the C/C++-translation of the schema type, 'declaration' introduces the C/C++ type in the header file, the optional 'use' specifies how the type is used directly, and the optional 'ptr-use' specifies how the type is used as a pointer type.
The i
and s
prefixes are declared such that the header file output by the WSDL parser will use these to produce C/C++ code. XML Schema types are associated with an optional C/C++ type declaration, a use reference, and a pointer-use reference. The pointer-use reference of the xsd__byte
type for example, is int*
because char*
is reserved for strings.
When a type binding requires only the usage to be changed, the declaration part can be given by an elipsis ..., as in:
This ensures that the wsdl2h-generated type definition is preserved, while the use and ptr-use are remapped.
This method is useful to serialize dynamic types in C, where elements types int XML carry the *xsi:type
* attribute.
The following example illustrates an "any" type mapping for the *ns:sometype
* XSD type in a schema. This type will be replaced with a "any" type wrapper that supports dynamic serialization with *xsi:type
*:
where *__type
* and *__item
* are used to (de)serialize any data type in the wrapper, including base and its derived types based on *xsi:type
* attribuation.
To support complexType extensions that are dynamically bound in C code, i.e. polymorphic types based on inheritance hierarchies, we can redeclare the base type of a hierarchy as a wrapper type and use the __type
to serialize base or derived types. One addition is needed to support base type serialization without the use of *xsi:type
* attributes. The absence of this attribute requires the serialization of the base type.
Basically, we need to be able to both handle a base type and its extensions as per schema extensibility. Say base type *ns:base
* is a complexType that is extended by several other complexTypes. To implement dynamic binding in C to serialize the base type and derived types, we define:
The *__self
* field refers to the element tag (basically a struct member name) to which the *ns:base
* type is associated. So for example, we see in the soapcpp2-generated output:
where __item
represents name
when the __ns__base
is serialized with an *xsi:type
* attribute, and __self
represents name
when the __ns__base
is serialized wwithout an *xsi:type
* attribute. Therefore, the dynamic binding defaults to struct ns__base *__self
when no dynamic type information in XML is available.
Additional data and function members can be provided to extend a generated struct or class. Class and struct extensions are of the form:
For example, to add a constructor and destructor to class myns__record:
Type remappings can be given to map a type to another type:
which replaces *prefix__type1
* by *prefix__type2
* in the wsdl2h output. For example:
where *SOAP_ENC__boolean
* is mapped to *xsd__boolean
*, which in turn may be mapped to a C *enum xsd__boolean
* type or C++ *bool
* type.
The soapcpp2
compiler and code generator is invoked from the command line and optionally takes the name of a header file as an argument or, when the file name is absent, parses the standard input:
> soapcpp2 headerfile.h
where aheaderfile.h
is a C/C++ header file generated by wsdl2h
or developed manually to specify the SOAP/XML service operations as function prototypes and the C/C++ data types to be auto-mapped to XML.
The soapcpp2
tool produces C/C++ source files. These files are used to implement SOAP/XML clients and services, and to implement the advanced XML data binding logic to convert C/C++ data into XML and vice versa.
The type of files generated by soapcpp2
are:
soapStub.h
A modified and annotated header file produced from the input header filesoapH.h
Main header file to be included by all client and service sourcessoapC.cpp
Serializers and deserializers for the specified data structuressoapClient.cpp
Client stub routines for remote operationssoapServer.cpp
Service skeleton routinessoapClientLib.cpp
Client stubs combined with local static (de)serializerssoapServerLib.cpp
Service skeletons combined with local static (de)serializerssoapXYZProxy.h
A C++ proxy object (link with soapC.cpp soapClient.cpp)soapXYZProxy.h
With option -i: proxy object (link with soapC.cpp and soapXYZProxy.cpp)soapXYZProxy.cpp
With option -i: proxy codesoapXYZObject.h
A C++ server object (link with soapC.cpp and soapServer.cpp)soapXYZService.h
With option -i: server object (link with soapC.cpp and soapXYZService.cpp)soapXYZService.cpp
With option -i: server code.xsd
An ns.xsd
file is generated with an XML Schema for each namespace prefix ns
used by a data structure in the header file input to the compiler, see Section How to Generate WSDL Service Descriptions.wsdl
A ns.wsdl
file is generated with an WSDL description for each namespace prefix ns
used by a service operation in the header file input to the compiler, see Section How to Generate WSDL Service Descriptions.xml
Several SOAP/XML request and response files are generated. These are example message files are valid provided that sufficient schema namespace directives are added to the header file or the generated .nsmap namespace table for the client/service is not modified by hand.nsmap
A ns.nsmap
file is generated for each namespace prefix ns
used by a service operation in the header file input to the compiler, see Section How to Generate WSDL Service Descriptions . The file contains a namespace mapping table that can be used in the client/service sourcesBoth client and service applications are developed from a header file that specifies the service operations. If client and service applications are developed with the same header file, the applications are guaranteed to be compatible because the stub and skeleton routines use the same serializers and deserializers to encode and decode the parameters. Note that when client and service applications are developed together, an application developer does not need to know the details of the internal SOAP encoding used by the client and service.
The soapClientLib.cpp
and soapServerLib.cpp
can be used to build (dynamic) client and server libraries. The serialization routines are local (static) to avoid link symbol conflicts. You must create a separate library for SOAP Header and Fault handling, as described in Section How to Create Client/Server Libraries .
The following files are part of the gSOAP package and are required to build client and service applications:
stdsoap2.h
Header file of stdsoap2.cpp
runtime librarystdsoap2.c
Runtime C library with XML parser and run-time support routinesstdsoap2.cpp
Runtime C++ library identical to stdsoap2.c
The soapcpp2
source-to-source compiler supports the following command-line options:
option | result |
---|---|
-1 | generate SOAP 1.1 bindings |
-2 | generate SOAP 1.2 bindings |
-0 | no SOAP bindings, use REST |
-C | generate client-side code only |
-S | generate server-side code only |
-T | generate server auto-test code |
-Ec | generate extra routines for deep data copying |
-Ed | generate extra routines for deep data deletion |
-Et | generate extra routines for data traversals with walker functions |
-L | do not generate soapClientLib/soapServerLib |
-a | use SOAPAction with WS-Addressing to invoke server-side operations |
-A | require SOAPAction to invoke server-side operations |
-b | serialize byte arrays char[N] as string |
-c | generate pure C code |
-d <path> | save sources in directory specified by <path> |
-e | generate SOAP RPC encoding style bindings |
-f N | multiple soapC files, with N serializer definitions per file (N>=10) |
-h | print a brief usage message |
-i | generate service proxies and objects inherited from soap struct |
-j | generate C++ service proxies and objects that can share a soap struct |
-I <path> | use <path> for #import (paths separated with ':' or ';' for windows) |
-l | generate linkable modules (experimental) |
-m | generate Matlab code for MEX compiler |
-n | when used with -p , enables multi-client and multi-server builds: sets compiler option WITH_NONAMESPACES , see Section Library Build Flags saves the namespace mapping table with name <name>_namespaces instead of namespaces |
| renames soap_serve()
into <name>_serve()
and soap_destroy()
into <name>_destroy()
-p <name>
| save sources with file name prefix <name>
instead of "`soap`" -q <name>
| use name
for the C++ namespace of all declarations -r
| generate soapReadme.md report -s
| generates deserialization code with strict XML validation checks -t
| generates code to send typed messages (with the *xsi:type
* attribute) -u
| uncomment comments in WSDL/schema output by suppressing XML comments -V
| display the current version and exit -v
| verbose output -w
| do not generate WSDL and schema files -x
| do not generate sample XML message files -y
| include C/C++ type access information in sample XML messages -z1
| compatibility: generate old-style C++ service proxies and objects -z2
| compatibility with 2.7.x: omit XML output for NULL pointers -z3
| compatibility with 2.8.30 and earlier: _param_N is indexed globally
For example
> soapcpp2 -c -d 'projects' -pmy file.h
Saves the sources:
projects/myH.h
projects/myC.c
projects/myClient.c
projects/myServer.c
projects/myStub.h
MS Windows users can use the usual "`/`" for options, for example:
soapcpp2 /cd '..\projects' /pmy file.h
Compiler options c, i, n, l, w
can be set in the gSOAP header file using the //gsoapopt
directive. For example,
gSOAP supports SOAP 1.1 by default. SOAP 1.2 support is automatically turned on when the appropriate SOAP 1.2 namespace is used, which shows up in the namespace mapping table:
Normally the soapcpp2
-generated namespace table allows dynamic switching between SOAP 1.1 to SOAP 1.2 by providing the SOAP 1.2 namespace as a pattern in the third column of a namespace table:
where the "`*`" in the third column of the namespace URI pattern is a meta wildcard. This is used to match and accept inbound namespaces.
This way, gSOAP Web services can respond to either SOAP 1.1 or SOAP 1.2 requests. gSOAP will automatically return SOAP 1.2 responses for SOAP 1.2 requests.
The gSOAP soapcpp2
tool generates a .nsmap
file with SOAP-ENV
and SOAP-ENC
namespace patterns similar to the above. Since clients issue a send first, they will always use SOAP 1.1 for requests when the namespace table is similar as shown above. Clients can accept SOAP 1.2 responses by inspecting the response message.
To use SOAP 1.2 by default and allow SOAP 1.1 messages to be received, use the soapcpp2 -2
option to generate SOAP 1.2 conformant .nsmap
and .wsdl
files. Alternatively, add the following line to your service definitions header file (generated by wsdl2h
) for soapcpp2
:
__offset
field of a dynamic array is meaningless.SOAP_ENV__Code
, SOAP_ENV__Reason
, and SOAP_ENV__Detail
fields in a SOAP_ENV__Fault
fault struct, while SOAP 1.1 uses faultcode
, faultstring
, and detail
fields. Use soap_receiver_fault_subcode(struct soap *soap, const char *subcode, const char *faultstring, const char *detail)
to set a SOAP 1.1/1.2 fault at the server-side with a fault subcode (SOAP 1.2). Use soap_sender_fault_subcode(struct soap *soap, const char *subcode, const char *faultstring, const char *detail)
to set a SOAP 1.1/1.2 unrecoverable Bad Request fault at the server-side with a fault subcode (SOAP 1.2).The soapdefs.h
header file is included in stdsoap2.h
when compiling with option -DWITH_SOAPDEFS_H
:
> c++ -DWITH_SOAPDEFS_H -c stdsoap2.cpp
The soapdefs.h
file allows users to include definitions and add includes without requiring changes to stdsoap2.h
. You can also specify the header file name to include as a macro SOAPDEFS_h
to override the name soapdefs.h
:
> c++ -DSOAPDEFS_H=mydefs.h -c stdsoap2.cpp
For example,
The following header file can now refer to ostream
:
See also Section Transient Data Types .
The #module
directive is used to build modules. A library can be built from a module and linked with multiple Web services applications. The directive should appear at the top of the header file and has the following formats:
and
where name must be a unique short name for the module. The name is case insensitive and MUST not exceed 4 characters in length. The fullname, when present, represents the full name of the module.
The rest of the content of the header file includes type declarations and optionally the declarations of service operations and SOAP Headers/Faults. When the gSOAP soapcpp2
compiler processes the header file module, it will generate the source codes for a library. The Web services application that uses the library should use a header file that imports the module with the #import
directive.
For example:
The module.h
header file declares a long, char*, and a struct ns__X
. The module name is "test", so the gSOAP soapcpp2
compiler produces a testC.cpp
file with the (de)serializers for these types. The testC.cpp
library can be separately compiled and linked with an application that is built from a header file that imports "module.h" using #import "module.h"
. You should also compile testClient.cpp
when you want to build a library that includes the service operations that you defined in the module header file.
There are some limitations on a sequence of module imports. A module MUST be imported into another header to use the module content and you MUST place this import statement before all other statements in the file, including other imports (except when these are also modules). It is also advised to put all basic data type definitions in the root module of a module import hierarchy, e.g. using typedef
to declare XSD types (see also Section How to Represent Primitive C/C++ Types as XSD Types ).
You cannot use a module alone to build a SOAP or XML application. That is, the final gSOAP header file in the import chain SHOULD NOT be a module.
When multiple modules are linked, the types that they declare MUST be declared in one module only to avoid name clashes and link errors. You cannot create two modules that share the same type declaration and link the modules. When necessary, you should consider creating a module hierarchy such that types are declared only once and by only one module when these modules must be linked.
The #import
directive is used to include gSOAP header files into other gSOAP header files for processing with the gSOAP compiler soapcpp2
. The C #include
directive cannot be used to include gSOAP header files. The #include
directive is reserved to control the post-gSOAP compilation process, see How to Use #include and #define Directives .
The #import
directive is used for two purposes: you can use it to include the contents of a header file into another header file and you can use it to import a module, see How to Build Modules and Libraries with the #module Directive .
An example of the #import
directive:
where "mydefs.gsoap"
is a gSOAP header file that defines xsd__string
and xsd__int
:
When importing a module, where the module content is declared with #module
, then note that this module MUST place the import statement before all other statements in the header file, including other imports (except when these are also modules).
The #include
and #define
directives are normally ignored by the gSOAP soapcpp2
compiler and just passed on to the generated code. Thus, the gSOAP compiler will not actually parse the contents of the header files provided by the #include
directives in a header file. Instead, the #include
and #define
directives will be added to the generated soapH.h
header file before any other header file is included. Therefore, #include
and #define
directives can be used to control the C/C++ compilation process of the sources of an application. However, they have no effect on soapcpp2
.
The following example header file refers to ostream
by including <ostream>
:
This example also uses #define
directives for various settings in the target source code.
#define
in the header file does not automatically result in compiling stdsoap2.cpp
with these directives. You MUST use the -DWITH_COOKIES
and -DWITH_OPENSSL
(or -DWITH_GNUTLS
options when compiling stdsoap2.cpp
before linking the object file with your codes. As an alternative, you can use #define WITH_SOAPDEFS_H
and put the #define
directives in the soapdefs.h
file.After invoking the gSOAP soapcpp2
tool on a header file description of a service, the client application can be compiled on a Linux machine as follows:
> c++ -o myclient myclient.cpp stdsoap2.cpp soapC.cpp soapClient.cpp
Or on a Unix machine:
> c++ -o myclient myclient.cpp stdsoap2.cpp soapC.cpp soapClient.cpp -lsocket -lxnet -lnsl
(Depending on your system configuration, the libraries libsocket.a
, libxnet.a
, libnsl.a
or dynamic *.so
versions of those libraries are required.)
The myclient.cpp
file must include soapH.h
and must define a global namespace mapping table. A typical client program layout with namespace mapping table is shown below:
A mapping table is generated by the gSOAP soapcpp2
compiler that can be used in the source, see Section How to Generate WSDL Service Descriptions .
After invoking the gSOAP soapcpp2
tool on a header file description of the service, the server application can be compiled on a Linux machine as follows:
> c++ -o myserver myserver.cpp stdsoap2.cpp soapC.cpp soapServer.cpp
Or on a Unix machine:
> c++ -o myserver myserver.cpp stdsoap2.cpp soapC.cpp soapServer.cpp -lsocket -lxnet -lnsl
(Depending on your system configuration, the libraries libsocket.a
, libxnet.a
, libnsl.a
or dynamic *.so
versions of those libraries are required.)
The myserver.cpp
file must include soapH.h
and must define a global namespace mapping table. A typical service program layout with namespace mapping table is shown below:
When the gSOAP service is compiled and installed as a CGI application, the soap_serve
function acts as a service dispatcher. It listens to standard input and invokes the method via a skeleton routine to serve a SOAP client request. After the request is served, the response is encoded in SOAP and send to standard output. The method must be implemented in the server application and the type signature of the method must be identical to the service operations specified in the header file. That is, the function prototype in the header file must be a valid prototype of the method implemented as a C/C++ function.
The gSOAP soapcpp2
compiler can be used to create pure C Web services and clients. The gSOAP stub and skeleton compiler soapcpp2
generates .cpp
files by default. The compiler generates .c
files with the -c
option. However, these files only use C syntax and data types if the header file input to soapcpp2
uses C syntax and data types. For example:
> soapcpp2 -c quote.h > cc -o quote quote.c stdsoap2.c soapC.c soapClient.c
Warnings will be issued by the compiler when C++ class declarations occur in the header file.
gSOAP is SOAP 1.1 and SOAP 1.2 compliant and supports SOAP RPC and document/literal operations.
From the perspective of the C/C++ language, a few C++ language features are not supported by gSOAP and these features cannot be used in the specification of SOAP service operations.
There are certain limitations for the following C++ language constructs:
soapcpp2
compiler supports C++ strings std::string
and std::wstring
(see Section C++ Strings ) and the STL containers std::deque
, std::list
, std::vector
, and std::set
, (see Section STL Containers ).soapcpp2
compiler is a preprocessor that cannot determine the template instantiations used by the main program, nor can it generate templated code. You can however implement containers similar to the STL containers.#include
and #define
are interpreted by the gSOAP soapcpp2
compiler. However, the interpretation is different compared to the usual handling of directives, see Section How to Use #include and #define Directives . If necessary, a traditional C++ preprocessor can be used for the interpretation of directives. For example, Unix and Linux users can use "*`cpp -B`*" to expand the header file, e.g. *cpp -B myfile.h | soapcpp2
*. Use the gSOAP #import
directive to import gSOAP header files, see How to use the #import Directive .The following data types require some attention to ensure they are serialized:
union
types: A union
data type can not be serialized unless run-time information is associated with a union
in a struct/class as discussed in Section Union Serialization . An alternative is to use a struct
with a pointer type for each field. Because NULL
pointers are not encoded, the resulting encoding will appear as a union type if only one pointer field is valid (i.e. non-NULL
) at the time that the data type is encoded.void
and void *
types: The void
data type cannot be serialized unless run-time type information is associated with the pointer using a int __type
field in the struct/class that contains the void*
. The void *
data type is typically used to point to some object or to some array of some type of objects at run-time. The compiler cannot determine the type of data pointed to and the size of the array pointed to. A struct or class with a void*
field can be augmented to support the (de)serialization of the void*
using a int __type
field as described in Section Void Pointers .float a[]
cannot be used. gSOAP supports dynamic arrays using a special type convention, see Section Dynamic Arrays .NULL
to enable serialization of the data structure at run time.There are a number of programming solutions that can be adopted to circumvent these limitations. Instead of using void *
, a program can in some cases be modified to use a pointer to a known type. If the pointer is intended to point to different types of objects, a generic base class can be declared and the pointer is declared to point to the base class. All the other types are declared to be derived classes of this base class. For pointers that point to a sequence of elements in memory dynamic arrays should be used instead, see Dynamic Arrays .
The following macros (#define
s) can be used to enable certain optional features by compiling all of the source code files with compiler option -D
to set the macro:
define | result |
---|---|
WITH_SOAPDEFS_H | includes the soapdefs.h file for custom settings, see Section The soapdefs.h Header File |
SOAPDEFS_H | the header file to include, if different from soapdefs.h (see above) |
WITH_COOKIES | enables HTTP cookies, see Sections Client-Side Cookie Support Server-Side Cookie Support |
WITH_OPENSSL | enables OpenSSL, see Sections Secure Clients with HTTPS/SSL Secure Web Services with HTTPS/SSL~ |
WITH_GNUTLS | enables GNUTLS, see Sections Secure Clients with HTTPS/SSL Secure Web Services with HTTPS/SSL~ |
WITH_IPV6 | enables IPv6 support (compile ALL sources with this macro set) |
WITH_IPV6_V6ONLY | IPv6-only server option (compile ALL sources with this macro set) |
WITH_NO_IPV6_V6ONLY | permits IPv4 and IPv6 (compile ALL sources with this macro set) |
WITH_TCPFIN | use TCP FIN after sends when socket is ready to close |
WITH_FASTCGI | enables FastCGI, see Sections FastCGI Support |
WITH_GZIP | enables gzip and deflate compression, see Section Zlib Compression |
WITH_ZLIB | enables deflate compression only, see Section Zlib Compression |
WITH_NOIO | eliminates need for file IO and BSD socket library, see Section How to Eliminate BSD Socket Library Linkage |
WITH_NOIDREF | eliminates href/ref and id attributes to (de)serialize multi-ref data, or use the SOAP_XML_TREE runtime flag |
WITH_NOHTTP | eliminates HTTP stack to reduce code size |
WITH_NOZONE | removes and ignores the timezone in xsd:dateTime |
WITH_LEAN | creates a small-footprint executable, see Section How to Create gSOAP Applications With a Small Memory Footprint |
WITH_LEANER | creates an even smaller footprint executable, see Section How to Create gSOAP Applications With a Small Memory Footprint |
WITH_FAST | use faster memory allocation when used with WITH_LEAN /WITH_LEANER |
WITH_COMPAT | removes dependency on C++ stream libraries, eliminating C++ exceptions |
WITH_NONAMESPACES | removes dependence on global namespaces table, MUST set it explicitly with soap_set_.namespaces() see also Section Namespace Mapping Table |
WITH_PURE_VIRTUAL | to generate C++ abstract service classes with pure virtual methods |
WITH_NOEMPTYSTRUCT | inserts a dummy member in empty structs to allow compilation |
WITH_NOGLOBAL | omit SOAP Header and Fault serialization code, prevents duplicate definitions with generated soapXYZLib code |
WITH_CDATA | retain the parsed CDATA sections in literal XML strings (no conversion, default) |
WITH_C_LOCALE | use locale functions when available to ensure locale-independent number conversions (force the use of C locale) |
WITH_CASEINSENSITIVETAGS | enable case insensitive XML parsing |
WITH_REPLACE_ILLEGAL_UTF8 | strict UTF-8: replaces UTF8 content that is outside the allowed range, with U+FFFD |
SOCKET_CLOSE_ON_EXIT | prevents a server port from staying in listening mode after exit by internally setting fcntl(sock, F_SETFD, FD_CLOEXEC) |
Compile-time flags to change the default engine settings:
define | result |
---|---|
SOAP_BUFLEN | the length of the internal message buffer (affects socket comms) |
SOAP_TAGLEN | maximum length of XML tags and URL domain names (buffering) |
SOAP_SSL_RSA_BITS | the length of the RSA key (2048 by default) |
SOAP_UNKNOWN_CHAR | an 8 bit code that represents a character that could not be converted to an ASCII char (e.g. from Unicode, applicable when SOAP_C_UTFSTRING is off) |
stdsoap2.cpp
, soapC.cpp
, soapClient.cpp
, soapServer.cpp
, and all application sources that include stdsoap2.h
or soapH.h
. If the macros are not consistently used, the application will crash due to a mismatches in the declaration and access of the gSOAP context.gSOAP provides flags to control the input and output mode settings at runtime. These flags are divided into four categories: transport (IO), content encoding (ENC), XML marshalling (XML), and C/C++ data mapping (C).
Although gSOAP is fully SOAP 1.1 compliant, some SOAP implementations may have trouble accepting multi-reference data and/or require explicit nil data so these flags can be used to put gSOAP in "safe mode". In addition, the embedding (or inlining) of multi-reference data is adopted in the SOAP 1.2 specification, which gSOAP automatically supports when handling with SOAP 1.2 messages.
To set and clear flags for inbound message processing use:
To set and clear the flags for outbound message processing use:
To allocate and initialize a gSOAP context with inbound and outbound flags use:
To initialize an unitialized gSOAP context with inbound and outbound flags use:
The input-mode and output-mode flags for inbound and outbound message processing are:
flag | in, out, or in+out result |
---|---|
SOAP_IO_FLUSH | in: disable buffering and flush output (default for all file-based output) |
SOAP_IO_BUFFER | in: enable buffering (default for all socket-oriented connections) |
SOAP_IO_STORE | in: store entire message to calculate HTTP content length |
SOAP_IO_CHUNK | out: use HTTP chunking |
SOAP_IO_LENGTH | out: (internal flag) require apriori calculation of content length |
SOAP_IO_KEEPALIVE | in+out: attempt to keep socket connections alive (open) |
SOAP_IO_UDP | in+out: use UDP (datagram) transport, maximum message length is SOAP_BUFLEN |
SOAP_ENC_PLAIN | in+out: use plain messages without parsing or emitting HTTP headers |
SOAP_ENC_XML | deprecated, alias for SOAP_ENC_PLAIN |
SOAP_ENC_DIME | out: use DIME encoding (automatic when DIME attachments are used) |
SOAP_ENC_MIME | out: use MIME encoding (activate using soap_set_mime ) |
SOAP_ENC_MTOM | out: use MTOM XOP attachments (instead of DIME) |
SOAP_ENC_ZLIB | out: compress encoding with Zlib (deflate or gzip format) |
SOAP_ENC_SSL | in+out: encrypt with SSL (automatic with "https:" endpoints) |
SOAP_XML_INDENT | out: produces indented XML output |
SOAP_XML_CANONICAL | out: produces canonical XML output |
SOAP_XML_DEFAULTNS | out: forces output of xmlns="..." default namespace declarations |
SOAP_XML_IGNORENS | in: ignores the use of XML namespaces in input |
SOAP_XML_STRICT | in: XML strict validation |
SOAP_XML_TREE | out: serialize data as XML trees (no multi-ref, duplicate data when necessary); in: ignore id attributes (do not resolve id-ref) |
SOAP_XML_GRAPH | out: serialize data as an XML graph with inline multi-ref (SOAP 1.2 default) |
SOAP_XML_NIL | out: serialize NULL data as xsi:nil attributed elements |
SOAP_XML_NOTYPE | out: disable *xsi:type * attributes |
SOAP_C_NOIOB | in: do not fault with SOAP_IOB |
SOAP_C_UTFSTRING | in+out: (de)serialize 8-bit strings "as is" (strings MUST have UTF-8 encoded content) |
SOAP_C_MBSTRING | in+out: enable multibyte character support (depends on locale) |
SOAP_C_NILSTRING | out: serialize empty strings as nil (ommited element) |
The flags can be selectively turned on/off at any time, for example when multiple Web services are accessed by a client that require special treatment.
All flags are orthogonal, except SOAP_IO_FLUSH
, SOAP_IO_BUFFER
, SOAP_IO_STORE
, and SOAP_IO_CHUNK
which are enumerations and only one of these I/O flags can be used. Also the XML serialization flags SOAP_XML_TREE
and SOAP_XML_GRAPH
should not be mixed.
The flags control the inbound and outbound message transport, encoding, and (de)serialization. The following functions are used to set and reset the flags for input and output modes:
soap_init2(struct soap *soap, int imode, int omode)
Initialize the runtime and set flagssoap_imode(struct soap *soap, int imode)
Set all input mode flagssoap_omode(struct soap *soap, int omode)
Set all output mode flagssoap_set_imode(struct soap *soap, int imode)
Enable input mode flagssoap_set_omode(struct soap *soap, int omode)
Enable output mode flagssoap_clr_imode(struct soap *soap, int omode)
Disable input mode flagssoap_clr_omode(struct soap *soap, int omode)
Disable output mode flagsThe default setting is SOAP_IO_DEFAULT
for both input and output modes.
For example
sends a compressed client request with keep-alive enabled and all data serialized as canonical XML trees.
In many cases, setting the input mode will have no effect, especially with HTTP transport because gSOAP will determine the optimal input buffering and the encoding used for an inbound message. The flags that have an effect on handling inbound messages are SOAP_IO_KEEPALIVE
, SOAP_ENC_SSL
(but automatic when "https:" endpoints are used or soap_ssl_accept
), SOAP_C_NOIOB
, SOAP_C_UTFSTRING
, and SOAP_C_MBSTRING
.
SOAP_XML_TREE
serialization flag can be used to improve interoperability with SOAP implementations that are not fully SOAP 1.1 compliant. However, a tree serialization will duplicate data when necessary and will crash the serializer for cyclic data structures.Additional run-time flags to control sockets.
Use the following selection of flags that are OS dependent to control sockets for send/sendto/recv/recvfrom operations:
MSG_NOSIGNAL
disables sigpipe (check your OS, this is not portable)MSG_DONTROUTE
bypass routing, use direct interfaceUse the following selection of flags to set client-side socket connection flags (setsockopt):
SO_NOSIGPIPE
disables sigpipe (check your OS, this is not portable)SO_DEBUG
turns on recording of debugging information in the underlying protocol modulesSO_BROADCAST
permits sending of broadcast messages (e.g. with UDP) when permittedSO_LINGER
set soap.linger_time
(set this value as needed)Use the following selection of flags to set server-side socket connection accept flags (setsockopt):
SO_NOSIGPIPE
disables sigpipe (check your OS, this is not portable)SO_DEBUG
turns on recording of debugging information in the underlying protocol modulesSO_REUSEADDR
reuse bind address immediately (prevents bind reject)SO_LINGER
set soap.linger_time
(set this value as needed)For example, soap.accept_flags = (SO_NOSIGPIPE | SO_LINGER)
disables sigpipe signals and set linger time value given by soap.linger_time
(zero by default).
The SO_SNDBUF
and SO_RCVBUF
socket options can be set by assigning soap.sndbuf
and soap.rcvbuf
after the context initialization, respectively. The default value is SOAP_BUFLEN
, which is the same as the size of the internal buffer. A zero value omits the internal setsockopt
call to set these options. Setting these values to zero enables autotuning with Linux 2.4 and up.
Understanding gSOAP's run-time memory management is important to optimize client and service applications by eliminating memory leaks and/or dangling references.
There are two forms of dynamic (heap) allocations made by gSOAP's runtime for serialization and deserialization of data. Temporary data is created by the runtime such as hash tables to keep pointer reference information for serialization and hash tables to keep XML id/href information for multi-reference object deserialization. Deserialized data is created upon receiving SOAP messages. This data is stored on the heap and requires several calls to the malloc
library function to allocate space for the data and new
to create class instances. All such allocations are tracked by gSOAP's runtime by linked lists for later deallocation. The linked list for malloc
allocations uses some extra space in each malloc
ed block to form a chain of pointers through the malloc
ed blocks. A separate malloc
ed linked list is used to keep track of class instance allocations.
If you want to preserve the deserialized data before deleting a soap context, you can assign management of the data and delegate responsibility of deletion to another soap context using soap_delegate_deletion(struct soap *soap_from, struct soap *soap_to)
. This moves all deserialized and temporary data to the other soap context soap_to
, which will delete its data and all the delegated data it is responsible for when you call soap_destroy
and soap_end
. This can be particularly useful for making client calls inside a server operation, i.e. a mixed server/client. The client call inside the server operation requires a new soap context, e.g. copied from the server's with soap_copy
. Before destroying the client context with soap_free
, the data can be delegated to the server's context with soap_delegate_deletion
. See samples/mashup/machupserver.c
code for an example.
Note that gSOAP does not per se enforce a deallocation policy and the user can adopt a deallocation policy that works best for a particular application. As a consequence, deserialized data is never deallocated by the gSOAP runtime unless the user explicitly forces deallocation by calling functions to deallocate data collectively or individually.
The deallocation functions are:
soap_destroy(struct soap *soap)
Remove all dynamically allocated C++ objects. must be called before soap_end()
soap_end(struct soap *soap)
Remove temporary data and deserialized data except class instancessoap_free_temp(struct soap *soap)
Instead of soap_destroy
and soap_end
: remove temporary data onlysoap_dealloc(struct soap *soap, void *p)
Remove malloced data at p
. When p==NULL
: remove all dynamically allocated (deserialized) data except class instancessoap_delete(struct soap *soap, void *p)
Remove class instance at p
. When p==NULL
: remove all dynamically allocated (deserialized) class instances (this is identical to calling soap_destroy(struct soap *soap))soap_unlink(struct soap *soap, void *p)
Unlink data/object at p
from gSOAP's deallocation chain so gSOAP won't deallocate itsoap_done(struct soap *soap)
Detach context (reset runtime context)soap_free(struct soap *soap)
Detach and free context (allocated with soap_new
)Temporary data (i.e. the hash tables) are automatically removed with calls to the soap_free_temp
function which is also made by soap_end
and soap_done
or when the next call to a stub or skeleton routine is made to send a message or receive a message. Deallocation of non-class based data is straightforward: soap_end
removes all dynamically allocated deserialized data (data allocated with soap_malloc
. That is, when the client/service application does not use any class instances that are (de)marshalled, but uses structs, arrays, etc., then calling the soap_end
function is safe to remove all deserialized data. The function can be called after processing the deserialized data of a service operation call or after a number of service operation calls have been made. The function is also typically called after soap_serve
, when the service finished sending the response to a client and the deserialized client request data can be removed.
Individual data objects can be unlinked from the deallocation chain if necessary, to prevent deallocation by the collective soap_end
or soap_destroy
functions.
There are three situations to consider for memory deallocation policies for class instances:
soap_end
and soap_destroy
functions,soap_end
and soap_destroy
functions can be called to remove the data.soap_unlink
function. This allows soap_destroy
and soap_end
to remove the remaining instances and data without causing duplicate deallocations.It is advised to use pointers to class instances that are used within other structs and classes to avoid the creation of temporary class instances during deserialization. The problem with temporary class instances is that the destructor of the temporary may affect data used by other instances through the sharing of data parts accessed with pointers. Temporaries and even whole copies of class instances can be created when deserializing SOAP multi-referenced objects. A dynamic array of class instances is similar: temporaries may be created to fill the array upon deserialization. To avoid problems, use dynamic arrays of pointers to class instances. This also enables the exchange of polymorphic arrays when the elements are instances of classes in an inheritance hierarchy. In addition, allocate data and class instances with soap_malloc
and soap_new_X
functions (more details below).
To summarize, it is advised to pass class data types by pointer to a service operation. For example:
Response elements that are class data types can be passed by reference, as in:
But dynamic arrays declared as class data types should use a pointer to a valid object that will be overwritten when the function is called, as in:
Or a reference to a valid or NULL
pointer, as in:
The gSOAP memory allocation functions can be used in client and/or service code to allocate temporary data that will be automatically deallocated. These functions are:
void *soap_malloc(struct soap *soap, size_t n)
return pointer to n
bytesType *soap_new_Type(struct soap *soap)
allocate and instantiateType *soap_new_Type(struct soap *soap, int n)
instantiate array of n
objectsType *soap_new_set_Type(struct soap *soap, m1, ..., mn)
instantiate and set membersType *soap_new_req_Type(struct soap *soap, m1, ..., mn)
instantiate and set required-only membersThe soap_new_X
functions are generated by the gSOAP soapcpp2
compiler for every type in the header file.
Space allocated with soap_malloc
will be released with the soap_end
and soap_dealloc
functions. All objects instantiated with soap_new_X(struct soap*)
are removed altogether with soap_destroy(struct soap*)
.
For example, the following service uses temporary data in the service operation implementation:
An example service operation that allocates a temporary string is:
This temporary allocation can also be used to allocate strings for the SOAP Fault data structure. For example:
Use soap_receiver_fault(struct soap *soap, const char *faultstring, const char *detail)
to set a SOAP 1.1/1.2 fault at the server-side. Use soap_sender_fault(struct soap *soap, const char *faultstring, const char *detail)
to set a SOAP 1.1/1.2 unrecoverable Bad Request fault at the server-side. Sending clients are not supposed to retry messages after a Bad Request, while errors at the receiver-side indicate temporary problems.
The above functions do not include a SOAP 1.2 Subcode element. To include Subcode element, use soap_receiver_fault_subcode(struct soap *soap, const char *subcode, const char *faultstring, const char *detail)
to set a SOAP 1.1/1.2 fault with Subcode at the server-side. Use soap_sender_fault_subcode(struct soap *soap, const char *subcode, const char *faultstring, const char *detail)
to set a SOAP 1.1/1.2 unrecoverable Bad Request fault with Subcode at the server-side.
gSOAP provides a function to duplicate a string into gSOAP's memory space:
The function allocates space for s
with soap_malloc
, copies the string, and returns a pointer to the duplicated string. When s
is NULL, the function does not allocate and copy the string and returns NULL.
When a class declaration has a struct soap *
field, this field will be set to point to the current gSOAP runtime context by gSOAP's deserializers and by the soap_new_Class
functions. This simplifies memory management for class instances. The struct soap*
pointer is implicitly set by the gSOAP deserializer for the class or explicitly by calling the soap_new_X
function for class X
. For example:
The constructor and destructor for class Sample
are:
The soap_unlink()
call removes the object from gSOAP's deallocation chain. In that way, soap_destroy
can be safely called to remove all class instances. The following code illustrates the explicit creation of a Sample
object and cleanup:
Here is another example:
Calls to soap_new_ns__myClass(soap)
will set the soap
field in the class instance to the current gSOAP context. Because the deserializers invoke the soap_new
functions, the soap
field of the ns__myClass
instances are set as well. This mechanism is convenient when Web Service methods need to return objects that are instantiated in the methods. For example
Calling soap_destroy
right after soap_serve
in the Web Service will destroy all dynamically allocated class instances.
To activate debugging and message logging, set the #define DEBUG
macro on the compiler's command line (typically as a compiler option -DDEBUG
) or in stdsoap2.h
, and recompile your code together with stdsoap2.c
or stdsoap2.cpp
(instead of libgsoap
). When using libgsoap
and libgsoap++
, reinstall the software with ./configure --enable-debug
.
When your client and server applications run, they will log their activity in three separate files:
SENT.log
the SOAP content transmitted by the applicationRECV.log
the SOAP content received by the applicationTEST.log
a log containing various activities performed by the applicationDEBUG_STAMP
instead of DEBUG
to add time stamps to TEST.log
. This works on platforms supporting the gettimeofday
function.You can test a service CGI application without deploying it on the Web. To do this, create a client application for the service and activate message logging by this client. Remove any old SENT.log
file and run the client (which connects to the Web service or to another dummy, but valid address) and copy the SENT.log
file to another file, e.g. SENT.tst
. Then redirect the SENT.tst
file to the service CGI application. For example,
> ./myservice.cgi < SENT.tst
This should display the service response on the terminal.
The file names of the log files and the logging activity can be controlled at the application level. This allows the creation of separate log files by separate services, clients, and threads. For example, the following service logs all SOAP messages (but no debug messages) in separate directories:
Likewise, messages can be logged for individual client-side service operation calls.
The soapcpp2 -T
option generates an auto-test server application in soapTester.cpp
, which is to be compiled and linked with the code generated for a server implementation, i.e. soapServer.cpp
(or with the generated server object class) and soapC.cpp
. The feature also supports C, so use the soapcpp2 -c
option to generate C.
The auto-test server can be used to test a client application. Suppose the generated code is compiled into the executable named tester
(compile soapServer.cpp
, soapC.cpp
, and stdsoap2.cpp
or link libgsoap++
). We can use the IO redirect to "send" it a message saved in a file, for example one of the sample request messages generated by soapcpp2
:
> ./tester < example.req.xml
which then returns the response with default XML values displayed on the terminal.
To run the auto test service on a port to test a client against, use two command-line arguments. The first argument is the OR-ed values of the gSOAP runtime context flags such as SOAP_IO_KEEPALIVE
(0x10 = 16) and the second argument is the port number:
> ./tester 16 8080
This starts an iterative stand-alone server on port 8080. This way, messages can be sent to *http://localhost:8080
* to test the client. The data in the response messages are copied from the request messages when possible, or XML default values, or empty otherwise.
The soapcpp2 -Ec
option generates deep copy code for each type T
:
T * soap_dup_T(struct soap*, T *dst, const T *src)
deep copy src
into dst
, replicating all deep cycles and shared pointers when a managing soap context is provided as argument. When dst
is NULL, allocates space for dst
. Deep copy is a tree when argument is NULL, but the presence of deep cycles will lead to non-termination. Use flag SOAP_XML_TREE
with managing context to copy into a tree without cycles and pointers to shared objects. Returns dst
(or allocated space when dst
is NULL).
For classes T
, also a deep copy method is generated with option -Ec
:
virtual T * T::soap_dup(struct soap*) const
returns a duplicate of this object by deep copying, replicating all deep cycles and shared pointers when a managing soap context is provided as argument. Deep copy is a tree when argument is NULL, but the presence of deep cycles will lead to non-termination. Use flag SOAP_XML_TREE
with managing context to copy into a tree without cycles and pointers to shared objects.
The soapcpp2 -Ed
option generates deep deletion code for each type T
:
void soap_del_T(const T*)
deletes all heap-allocated members of this object by deep deletion ONLY IF this object and all of its (deep) members are not managed by a soap context AND the deep structure is a tree (no cycles and co-referenced objects by way of multiple (non-smart) pointers pointing to the same data). Can be safely used after soap_dup(NULL)
to delete the deep copy. Does not delete the object itself.
For classes T
, also a deep deletion method is generated with option -Ed
:
virtual void T::soap_del() const
deletes all heap-allocated members of this object by deep deletion ONLY IF this object and all of its (deep) members are not managed by a soap context AND the deep structure is a tree (no cycles and co-referenced objects by way of multiple (non-smart) pointers pointing to the same data).Can be safely used after soap_dup(NULL)
to delete the deep copy. Does not delete the object itself.
> c++ -o myclient myclient.cpp stdsoap2.cpp soapC.cpp soapClient.cpp -lsocket -lxnet -lnsl
These library loading options are not required with Linux.
NaN
*, *INF
*, and *-INF
* floating point representations. The library is not strictly necessary and the <math.h>
header file import can be commented out from the stdsoap2.h
header file. The application can be linked without the -lm
math library e.g. under Sun Solaris: > c++ -o myclient myclient.cpp stdsoap2.cpp soapC.cpp soapClient.cpp -lsocket -lxnet -lnsl
A service operation is specified as a C/C++ function prototype in a header file. The function is REQUIRED to return int
, which is used to represent a SOAP error code, see Section Error Codes . Multiple service operations MAY be declared together in one header file.
The general format of a service operation specification is:
where
namespace_prefix__
is the optional namespace prefix of the method (see identifier translation rules C/C++ Identifier Name to XML Tag Name Mapping )method_name
it the service operation name (see identifier translation rules C/C++ Identifier Name to XML Tag Name Mapping )inparam
is the declaration of an input parameter of the service operationoutparam
is the declaration of the output parameter of the service operationThis simple form can only pass a single, non-struct
and non-class
type output parameter. See Service Operation Parameter Passing for passing multiple output parameters. The name of the declared function namespace_prefix__method_name
must be unique and cannot match the name of a struct
, class
, or enum
declared in the same header file.
The method request is encoded in SOAP as an XML element and the namespace prefix, method name, and input parameters are encoded using the format:
where the *inparam-name
* accessors are the element-name representations of the inparam
parameter name declarations, see Section C/C++ Identifier Name to XML Tag Name Mapping . (The optional parts are shown enclosed in [optional: ].)
The XML response by the Web service is of the form:
where the *outparam-name
* accessor is the element-name representation of the outparam
parameter name declaration, see Section C/C++ Identifier Name to XML Tag Name Mapping . By convention, the response element name is the method name ending in *Response
*. See Service Operation Parameter Passing on how to change the declaration if the service response element name is different.
The gSOAP soapcpp2
tool generates a stub routine for the service operation. This stub is of the form:
This proxy can be called by a client application to perform the service operation call.
The gSOAP soapcpp2
tool generates a skeleton routine for the service operation. The skeleton function is:
The skeleton routine, when called by a service application, will attempt to serve a request on the standard input. If no request is present or if the request does not match the method name, SOAP_NO_METHOD
is returned. The skeleton routines are automatically called by the generated soap_serve
routine that handles all requests.
The input parameters of a service operation MUST be passed by value. Input parameters cannot be passed by reference with the &
reference operator, but an input parameter value MAY be passed by a pointer to the data. Of course, passing a pointer to the data is preferred when the size of the data of the parameter is large. Also, to pass instances of (derived) classes, pointers to the instance need to be used to avoid passing the instance by value which requires a temporary and prohibits passing derived class instances. When two input parameter values are identical, passing them using a pointer has the advantage that the value will be encoded only once as multi-reference (hence, the parameters are aliases). When input parameters are passed using a pointer, the data pointed to will not be modified by the service operation and returned to the caller.
The output parameter MUST be passed by reference using &
or by using a pointer. Arrays are passed by reference by default and do not require the use of the reference operator &
.
The input and output parameter types have certain limitations, see Section Limitations of gSOAP
If the output parameter is a struct
or class
type, it is considered a service operation response element instead of a simple output parameter value. That is, the name of the struct
or class
is the name of the response element and the struct
or class
fields are the output parameters of the service operation, see also How to Change the Response Element Name . Hence, if the output parameter has to be a struct
or class
, a response struct
or class
MUST be declared as well. In addition, if a service operation returns multiple output parameters, a response struct
or class
MUST be declared. By convention, the response element is the service operation name ending with "*`Response`*".
The general form of a response element declaration is:
where
namespace_prefix__
is the optional namespace prefix of the response element (see identifier translation rules C/C++ Identifier Name to XML Tag Name Mapping )response_element_name
it the name of the response element (see identifier translation rules C/C++ Identifier Name to XML Tag Name Mapping )outparam
is the declaration of an output parameter of the service operationThe general form of a service operation specification with a response element declaration for (multiple) output parameters is:
The choice of name for anyparam
has no effect on the SOAP encoding and decoding and is only used as a place holder for the response.
The method request is encoded in SOAP as an independent element and the namespace prefix, method name, and input parameters are encoded using the format:
where the *inparam-name
* accessors are the element-name representations of the inparam
parameter name declarations, see Section C/C++ Identifier Name to XML Tag Name Mapping . (The optional parts resulting from the specification are shown enclosed in [optional: ].)
The method response is expected to be of the form:
where the *outparam-name
* accessors are the element-name representations of the outparam
parameter name declarations, see Section C/C++ Identifier Name to XML Tag Name Mapping . (The optional parts resulting from the specification are shown enclosed in [optional: ].)
The input and/or output parameters can be made anonymous, which allows the deserialization of requests/responses with different parameter names as is endorsed by the SOAP 1.1 specification, see Section How to Specify Anonymous Parameter Names .
The error codes returned by the stub and skeleton routines are listed below.
code | description |
---|---|
SOAP_OK | No error (code is zero) |
SOAP_CLI_FAULT * | The service returned a client fault (SOAP 1.2 Sender fault) |
SOAP_SVR_FAULT * | The service returned a server fault (SOAP 1.2 Receiver fault) |
SOAP_TAG_MISMATCH | Element found didn't correspond to anything expected |
SOAP_TYPE | XML Schema type mismatch |
SOAP_SYNTAX_ERROR | An XML syntax error occurred on the input |
SOAP_NO_TAG | Begin of an element expected, but not found |
SOAP_IOB | Array index out of bounds |
SOAP_MUSTUNDERSTAND * | An element needs to be ignored that need to be understood |
SOAP_NAMESPACE | Namespace name mismatch (validation error) |
SOAP_FATAL_ERROR | Internal error |
SOAP_USER_ERROR | User error (reserved for soap.user usage |
SOAP_FAULT | An exception raised by the service |
SOAP_NO_METHOD | The dispatcher did not find a matching operation for a request |
SOAP_NO_DATA | No data in HTTP message |
SOAP_GET_METHOD | HTTP GET operation not handled, see Section HTTP GET Support |
SOAP_EOM | Out of memory |
SOAP_MOE | Memory overflow/corruption error (DEBUG mode) |
SOAP_NULL | An element was null, while it is not supposed to be null |
SOAP_DUPLICATE_ID | Element's ID duplicated (multi-ref encoding) |
SOAP_MISSING_ID | Element ID missing for an href/ref (multi-ref encoding) |
SOAP_HREF | Reference to object is incompatible with the object refered to |
SOAP_UTF_ERROR | An UTF-encoded message decoding error occured |
SOAP_UDP_ERROR | Message too large to store in UDP packet |
SOAP_TCP_ERROR | A connection error occured |
SOAP_HTTP_ERROR | An HTTP error occured |
SOAP_NTLM_ERROR | An NTLM authentication handshake error occured |
SOAP_SSL_ERROR | An SSL error occured |
SOAP_ZLIB_ERROR | A Zlib error occured |
SOAP_PLUGIN_ERROR | Failed to register plugin |
SOAP_MIME_ERROR | MIME parsing error |
SOAP_MIME_HREF | MIME attachment has no href from SOAP body error |
SOAP_MIME_END | End of MIME attachments protocol error |
SOAP_DIME_ERROR | DIME formatting error or DIME size exceeds SOAP_MAXDIMESIZE |
SOAP_DIME_END | End of DIME attachments protocol error |
SOAP_DIME_HREF | DIME attachment has no href from SOAP body (and no DIME callbacks were defined to save the attachment) |
SOAP_DIME_MISMATCH | DIME version/transmission error |
SOAP_VERSIONMISMATCH * | SOAP version mismatch or no SOAP message |
SOAP_DATAENCODINGUNKNOWN | SOAP 1.2 DataEncodingUnknown fault |
SOAP_REQUIRED | Attributed is required |
SOAP_PROHIBITED | Attributed is prohibited |
SOAP_LEVEL | XML nesting depth level exceeds SOAP_MAXLEVEL |
SOAP_OCCURS | Element minOccurs/maxOccurs validation error or SOAP_MAXOCCURS exceeded |
SOAP_LENGTH | Element length validation error or SOAP_MAXLENGTH exceeded |
SOAP_FIXED | Element or attribute value is fixed |
SOAP_EMPTY | Element or attribute is empty when a value is expected |
SOAP_FD_EXCEEDED | Too many open sockets (for non-win32 systems not supporting poll() ) |
SOAP_EOM | Out of memory |
SOAP_EOF | Unexpected end of file, no input, or timeout receiving data |
SOAP_ERR | Error (for internal use) |
The error codes that are returned by a stub routine (proxy) upon receiving a SOAP Fault from the server are marked (*). The remaining error codes are generated by the proxy itself as a result of problems with a SOAP payload. The error code is SOAP_OK
when the service operation call was successful (the SOAP_OK
predefined constant is guaranteed to be 0
). The error code is also stored in soap.error
, where soap
is a variable that contains the current runtime context. The function soap_print_fault(struct soap *soap, FILE *fd)
can be called to display an error message on fd
where current value of the soap.error
variable is used by the function to display the error. Use soap_stream_fault(struct soap *soap, std::ostream& os)
in C++. The function soap_print_fault_location(struct soap *soap, FILE *fd)
prints the location of the error if the error is a result from parsing XML. Use soap_print_stream_location(struct soap *soap, std::ostream& os)
in C++. Use soap_sprint_fault(struct soap*, char *buf, size_t len)
to print the error to a string.
A service operation implemented in a SOAP service MUST return an error code as the function's return value. SOAP_OK
denotes success and SOAP_FAULT
denotes an exception. The exception details can be assigned with the soap_receiver_fault(struct soap *soap, const char *faultstring, const char *detail)
which sets the strings soap.fault->faultstring
and soap.fault->detail
for SOAP 1.1, and soap.fault->SOAP_ENV__Reason
and soap.fault->SOAP_ENV__Detail
for SOAP 1.2, where soap
is a variable that contains the current runtime context, see Section SOAP Fault Processing . A receiver error indicates that the service can't handle the request, but can possibly recover from the error. To return an unrecoverable SOAP 1.1/1.2 error, use soap_sender_fault(struct soap *soap, const char *faultstring, const char *detail)
.
To return a HTTP error code a service method can simply return the HTTP error code number. For example, return 404;
returns a "404 Not Found" HTTP error back to the client. The soap.error
is set to the HTTP error code at the client side. The HTTP 1.1 error codes are:
code | description |
---|---|
201 | Created |
202 | Accepted |
203 | Non-Authoritative Information |
204 | No Content |
205 | Reset Content |
206 | Partial Content |
300 | Multiple Choices |
301 | Moved Permanently |
302 | Found |
303 | See Other |
304 | Not Modified |
305 | Use Proxy |
307 | Temporary Redirect |
400 | Bad Request |
401 | Unauthorized |
402 | Payment Required |
403 | Forbidden |
404 | Not Found |
405 | Method Not Allowed |
406 | Not Acceptable |
407 | Proxy Authentication Required |
408 | Request Time-out |
409 | Conflict |
410 | Gone |
411 | Length Required |
412 | Precondition Failed |
413 | Request Entity Too Large |
414 | Request-URI Too Large |
415 | Unsupported Media Type |
416 | Requested range not satisfiable |
417 | Expectation Failed |
500 | Internal Server Error |
501 | Not Implemented |
502 | Bad Gateway |
503 | Service Unavailable |
504 | Gateway Time-out |
505 | HTTP Version not supported |
The error codes are given for informational purposes only. The HTTP protocol requires the proper actions after an error is issued. gSOAP's HTTP 1.0/1.1 handling is automatic.
One of the "secrets" behind the power and flexibility of gSOAP's encoding and decoding of service operation names, class names, type identifiers, and struct or class fields is the ability to specify namespace prefixes with these names that are used to denote their encoding style. More specifically, a C/C++ identifier name of the form
where the prefix and the element name are separated by double underscores will be encoded in XML as
The underscore pair (__
) separates the namespace prefix from the element name. Each namespace prefix has a namespace URI specified by a namespace mapping table Namespace Mapping Table , see also Section XML Namespace Considerations . The namespace URI is a unique identification that can be associated with the service operations and data types. The namespace URI disambiguates potentially identical service operation names and data type names used by disparate organizations.
XML element names are NCNames (restricted strings) that MAY contain hyphens, dots, and underscores. The special characters in the XML element names of service operations, structs, classes, typedefs, and fields can be controlled using the following conventions: A single underscore in a namespace prefix or identifier name is replaced by a hyphen (*-
*) in the XML element name. For example, the identifier name SOAP_ENC__ur_type
is represented in XML as *SOAP-ENC:ur-type
*. The sequence _DOT
is replaced by a dot (*.
*), and the sequence _USCORE
is replaced by an underscore (*_
*) in the corresponding XML element name. For example:
is encoded in XML as:
Trailing underscores of an identifier name are not translated into the XML representation. This is useful when an identifier name clashes with a C++ keyword. For example, *return
* is often used as an accessor name in a SOAP response element. The *return
* element can be specified as return_
in the C++ source code. Note that XML should be treated as case sensitive, so the use of e.g. Return
may not always work to avoid a name clash with the \return
keyword. The use of trailing underscores also allows for defining struct
s and class
es with essentially the same XML Schema type name, but that have to be distinguished as seperate C/C++ types.
For decoding, the underscores in identifier names act as wildcards. An XML element is parsed and matches the name of an identifier if the name is identical to the element name (case insensitive) and the underscores in the identifier name are allowed to match any character in the element name. For example, the identifier name I_want__soap_fun_the_bea___DOTcom
matches the element name *I-want:SOAP4fun@the-beach.com
*.
By default, soapcpp2
generates data bindings in which all XML elements are and attributes are unqualified:
where the name
element and the type
attribute are unqualified in the XML content (for example to facilitate SOAP RPC encoding).
To force qualification of elements and attributes, use the "form" directive:
You can also use "elementForm" and "attributeForm" directives to (un)qualify local element and attributes, respectively.
Because the soapcpp2
-generated serializers follow the qualified/unqualified forms of the schemas, there is normally no need to explicitly qualify struct/class members because automatic encoding rules will be used.
If explicit qualification is needed, this can be done using the prefix convention:
which ensures that there cannot be any name clashes between members of the same name defined in different schemas (consider for example name
and y__name
), but this can clutter the representation when clashes do not occur.
An alternative to the prefix convention is the use of "**colon notation**" in the gSOAP header file. This deviation from the C/C++ syntax allows you to bind type names and struct and class members to qualified and unqualified XML tag names explicitly, thus bypassing the default mechanism that automatically qualifies or unqualifies element and attribute tag names based on the schema element/attribute form.
The colon notation for type names, struct/class names and members overrides the prefix qualification rules explicitly:
where x
and y
are namespace prefixes that MUST be declared with a directive. The xsi:type
member is an XML attribute in the xsi
namespace. The soapcpp2
tool maps this to the following struct without the annotations:
The soapcpp2
tool also generates XML schemas with element and attribute references. That is, y:name
is referenced from the y
schema by the *x:record
* complexType defined in the x
schema.
The colon notation also allows you to override the element/attribute form to unqualified for qualified schemas:
where the colon notation ensures that both type
and name
are unqualified in the XML content, which overrides the default qualified forms of the x
schema.
Note that the use of colon notation to bind namespace prefixes to type names (typedef, enum, struct, and class names) translates to code without the prefixes. This means that name clashes can occur between types with identical unquaified names:
while prefixing with double underscores never lead to clashes:
Also note that colon notation has a very different role than the C++ scope operator ::
. The scope operator cannot be used in places where we need colon notation, such as struct/class member fields.
The default mechanism that associates XML tag names with the names of struct and class member fields can be overriden by "**retagging**" names with the annotation of *tag
* placed next to the member field name. This is particularly useful to support legacy code for which the fixed naming of member fields cannot be easily changed. For example:
This maps the t
member to the *x:type
* XML attribute tag and s
member to the *x:full-name
* XML element tag. Note that both tags are namespace qualified as per schema declaration.
As of gSOAP 2.8.23, Unicode characters in C/C++ identifiers are accepted by soapcpp2
when the source file is encoded in UTF8. C/C++ Unicode names are mapped to Unicode XML tags. For C/C++ source code portability reasons, the wsdl2h
tool still converts Unicode XML tag names to ASCII C/C++ identifiers using the _xHHHH
naming convention for HHHH
character code points. Option wsdl2h -U
maps Unicode letters in XML tag names to UTF8-encoded Unicode letters in C/C++ identifiers.
A namespace mapping table MUST be defined by clients and service applications. The mapping table is used by the serializers and deserializers of the stub and skeleton routines to produce a valid SOAP payload and to validate an incoming SOAP payload. A typical mapping table is shown below:
Each namespace prefix used by a identifier name in the header file specification (see Section C/C++ Identifier Name to XML Tag Name Mapping ) MUST have a binding to a namespace URI in the mapping table. The end of the namespace mapping table MUST be indicated by the NULL
pair. The namespace URI matching is case insensitive. A namespace prefix is distinguished by the occurrence of a pair of underscores (__
) in an identifier.
An optional namespace pattern MAY be provided with each namespace mapping table entry. The patterns provide an alternative namespace matching for the validation of decoded SOAP messages. In this pattern, dashes (-
) are single-character wildcards and asterisks (*
) are multi-character wildcards. For example, to decode different versions of XML Schema type with different authoring dates, four dashes can be used in place of the specific dates in the namespace mapping table pattern:
Or alternatively, asterisks can be used as wildcards for multiple characters:
A namespace mapping table is automatically generated together with a WSDL file for each namespace prefix that is used for a service operation specified in the header file. This namespace mapping table has entries for all namespace prefixes. The namespace URIs need to be filled in. These appear as http://tempuri.org
in the table. See Section Customizing the WSDL and Namespace Mapping Table File Contents With gSOAP Directives on how to specify the namespace URIs in the header file.
For decoding elements with namespace prefixes, the namespace URI associated with the namespace prefix (through the *xmlns
* attribute of an XML element) is searched from the beginning to the end in a namespace mapping table, and for every row the following tests are performed as part of the validation process:
-
is a one-character wildcard and *
is a multi-character wildcardWhen a match is found, the namespace prefix in the first column of the table is considered semantically identical to the namespace prefix used by the XML element to be decoded, though the prefix names may differ. A service will respond with the namespace that it received from a client in case it matches a pattern in the third column.
For example, let's say we have the following structs:
and a namespace mapping table in the program:
Then, the following XML elements will match the structs:
The response of a service to a client request that uses the namespaces listed above, will include *my URI
* for the name space of element *k
*.
It is possible to use a number of different namespace tables and select the one that is appropriate. For example, an application might contact many different Web services all using different namespace URIs. If all the URIs are stored in one table, each service operation invocation will dump the whole namespace table in the SOAP payload. There is no technical problem with that, but it can be ugly when the table is large. To use different namespace tables, declare a pointer to a table and set the pointer to a particular table before service operation invocation. For example:
This section describes the serialization and deserialization of C and C++ data types for SOAP 1.1 and 1.2 compliant encoding and decoding.
The wsdl2h tool automatically generates a header file specialized for SOAP RPC encoding or document/literal style. The serialization and deserialization rules for C/C++ objects is almost identical for these styles, except for the following important issues.
With SOAP RPC encoding style, care must be taken to ensure typed messages are produced for interoperability and compatibility reasons. To ensure that the gSOAP engine automatically generates typed (*xsi:type
* attributed) messages, use soapcpp2
option -t, see also Section soapcpp2 Options . While gSOAP can handle untyped messages, some toolkits fail to find deserializers when the *xsi:type
* information is absent.
When starting the development of a gSOAP application from a header file, the soapcpp2
compiler will generate WSDL and schema files for SOAP 1.1 document/literal style by default (use the //gsoap
directives to control this, see Section Customizing the WSDL and Namespace Mapping Table File Contents With gSOAP Directives ). Use soapcpp2
options -2, -e, and -t to generate code for SOAP 1.2, RPC encoding, and typed messages.
With SOAP RPC encoding, generic *complexTypes
* with *maxOccurs="unbounded"
* are not allowed and SOAP encoded arrays must be used. Also XML attributes and unions (XML schema *choice
*) are not allowed with SOAP RPC encoding.
Also with SOAP RPC encoding, multi-reference accessors are common to encode co-referenced objects and object digraphs. Multi-reference encoding is not supported in document/literal style, which means that cyclic object digraphs cannot be serialized (the engine will crash). Also DAGs are represented as XML trees in document/literal style messaging.
The default encoding rules for the primitive C and C++ data types are given in the table below:
C/C++ type | XML schema type |
---|---|
bool | *boolean * |
char* | *string * |
char | *byte * |
long double | *decimal * with #import "custom/long_double.h" |
double | *double * |
float | *float * |
int | *int * |
long | *long * |
LONG64 | *long * |
long long | *long * |
short | *short * |
time_t | *dateTime * |
std::string | *string * |
std::wstring | *string * |
struct tm | *dateTime * with #import "custom/struct_tm.h" |
struct timeval | *dateTime * with #import "custom/struct_timeval.h" |
unsigned char | *unsignedByte * |
unsigned int | *unsignedInt * |
unsigned long | *unsignedLong * |
ULONG64 | *unsignedLong * |
unsigned long long | *unsignedLong * |
unsigned short | *unsignedShort * |
wchar_t* | *string * |
Objects of type void
and void *
cannot be encoded. Enumerations and bit masks are supported as well, see Enumeration Serialization .
By default, encoding of the primitive types will take place as per SOAP encoding style. The encoding can be changed to any XML Schema type (XSD type) with an optional namespace prefix by using a typedef
in the header file input to the gSOAP soapcpp2
tool. The declaration enables the implementation of built-in XML Schema types (also known as XSD types) such as *positiveInteger
*, *xsd:anyURI
*, and *xsd:date
* for which no built-in data structures in C and C++ exist but which can be represented using standard data structures such as strings, integers, and floats.
The typedef
declaration is frequently used for convenience in C. A typedef
declares a type name for a (complex) type expression. The type name can then be used in other declarations in place of the more complex type expression, which often improves the readability of the program code.
The gSOAP soapcpp2
compiler interprets typedef
declarations the same way as a regular C compiler interprets them, i.e. as types in declarations. In addition however, the gSOAP soapcpp2
compiler will also use the type name in the encoding of the data in SOAP. The typedef
name will appear as the XML element name of an independent element and as the value of the *xsi:type
* attribute in the SOAP payload.
Many built-in primitive and derived XSD types such as *xsd:anyURI
*, *positiveInteger
*, and *decimal
* can be stored by standard primitive data structures in C++, such as strings, integers, floats, and doubles. To serialize strings, integers, floats, and doubles as built-in primitive and derived XSD types, a typedef
declaration can be used to declare an XSD type.
For example, the declaration
creates a named type positiveInteger
which is represented by unsigned int
in C++. For example, the encoding of a positiveInteger
value 3
is
The built-in primitive and derived numerical XML Schema types are listed below together with their recommended typedef
declarations. Note that the SOAP encoding schemas for primitive types are derived from the built-in XML Schema types, so SOAP_ENC__
can be used as a namespace prefix instead of xsd__
.
xsd:anyURI
* Represents a Uniform Resource Identifier Reference (URI). Each URI scheme imposes specialized syntax rules for URIs in that scheme, including restrictions on the syntax of allowed fragment identifiers. It is recommended to use strings to store *xsd:anyURI
* XML Schema types. The recommended type declaration is:xsd:base64Binary
* Represents Base64-encoded arbitrary binary data. For using the *xsd:base64Binary
* XSD Schema type, the use of the base64Binary representation of a dynamic array is strongly recommended, see Section Base64Binary XML Schema Type Encoding . However, the type can also be declared as a string and the encoding will be string-based:With this approach, it is the responsibility of the application to make sure the string content is according to the Base64 Content-Transfer-Encoding defined in Section 6.8 of RFC 2045.
xsd:boolean
* For declaring an *xsd:boolean
* XSD Schema type, the use of a bool is strongly recommended. If a pure C compiler is used that does not support the bool
type, see Section Boolean Enumeration Serialization for C . The corresponding type declaration is:Type xsd__boolean
declares a Boolean (0 or 1), which is encoded as
xsd:byte
* Represents a byte (-128...127). The corresponding type declaration is: Type xsd__byte
declares a byte which is encoded as
xsd:dateTime
* Represents a date and time. The lexical representation is according to the ISO 8601 extended format CCYY-MM-DDThh:mm:ss where "CC" represents the century, "YY" the year, "MM" the month and "DD" the day, preceded by an optional leading "-" sign to indicate a negative number. If the sign is omitted, "+" is assumed. The letter "T" is the date/time separator and "hh", "mm", "ss" represent hour, minute and second respectively. It is recommended to use the time_t
type to store *xsd:dateTime
* XSD Schema types and the type declaration is:However, note that calendar times before the year 1902 or after the year 2037 cannot be represented. Upon receiving a date outside this range, the time_t
value will be set to -1.
Strings (char*
) can be used to store *xsd:dateTime
* XSD Schema types. The type declaration is:
In this case, it is up to the application to read and set the dateTime representation.
xsd:date
* Represents a date. The lexical representation for date is the reduced (right truncated) lexical representation for dateTime: CCYY-MM-DD. It is recommended to use strings (char*
) to store *xsd:date
* XSD Schema types. The type declaration is:xsd:decimal
* Represents arbitrary precision decimal numbers. It is recommended to use the {double} type to store *xsd:decimal
* XSD Schema types and the type declaration is:Type xsd__decimal
declares a double floating point number which is encoded as
xsd:double
* Corresponds to the IEEE double-precision 64-bit floating point type. The type declaration is:Type xsd__double
declares a double floating point number which is encoded as
xsd:duration
* Represents a duration of time. The lexical representation for duration is the ISO 8601 extended format PnYn MnDTnH nMnS, where nY represents the number of years, nM the number of months, nD the number of days, T is the date/time separator, nH the number of hours, nM the number of minutes and nS the number of seconds. The number of seconds can include decimal digits to arbitrary precision. It is recommended to use strings (char*
) to store *xsd:duration
* XSD Schema types. The type declaration is:xsd:float
* Corresponds to the IEEE single-precision 32-bit floating point type. The type declaration is:Type xsd__float
declares a floating point number which is encoded as
xsd:hexBinary
* Represents arbitrary hex-encoded binary data. It has a lexical representation where each binary octet is encoded as a character tuple, consisting of two hexadecimal digits ([0-9a-fA-F]) representing the octet code. For example, "0FB7" is a hex encoding for the 16-bit integer 4023 (binary representation is 111110110111. For using the *xsd:hexBinary
* XSD Schema type, the use of the hexBinary representation of a dynamic array is strongly recommended, see Section hexBinary XML Schema Type Encoding . However, the type can also be declared as a string and the encoding will be string-based:With this approach, it is solely the responsibility of the application to make sure the string content consists of a sequence of octets.
xsd:int
* Corresponds to a 32-bit integer in the range -2147483648 to 2147483647.Type xsd__int
declares a 32-bit integer which is encoded as
xsd:integer
* Corresponds to an unbounded integer. Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:Type xsd__integer
declares a 64-bit integer which is encoded as an unbounded *xsd:integer
*:
Another possibility is to use strings to represent unbounded integers and do the translation in code.
xsd:long
* Corresponds to a 64-bit integer in the range -9223372036854775808 to 9223372036854775807. The type declaration is:Or
Or
Type xsd__long
declares a 64-bit integer which is encoded as
xsd:negativeInteger
* Corresponds to a negative unbounded integer (<0). Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:Type xsd__negativeInteger
declares a 64-bit integer which is encoded as a *xsd:negativeInteger
*:
Another possibility is to use strings to represent unbounded integers and do the translation in code.
xsd:nonNegativeInteger
* Corresponds to a non-negative unbounded integer (>0). Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:Type xsd__nonNegativeInteger
declares a 64-bit unsigned integer which is encoded as a non-negative unbounded *xsd:nonNegativeInteger
*:
Another possibility is to use strings to represent unbounded integers and do the translation in code.
xsd:nonPositiveInteger
* Corresponds to a non-positive unbounded integer (<=0). Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:Type xsd__nonPositiveInteger
declares a 64-bit integer which is encoded as a *xsd:nonPositiveInteger
*:
Another possibility is to use strings to represent unbounded integers and do the translation in code.
xsd:normalizedString
* Represents normalized character strings. Normalized character strings do not contain the carriage return (#xD), line feed (#xA) nor tab (#x9) characters. It is recommended to use strings to store *xsd:normalizeString
* XSD Schema types. The type declaration is:Type xsd__normalizedString
declares a string type which is encoded as
It is solely the responsibility of the application to make sure the strings do not contain carriage return (#xD), line feed (#xA) and tab (#x9) characters.
xsd:positiveInteger
* Corresponds to a positive unbounded integer (>=0). Since C++ does not support unbounded integers as a standard feature, the recommended type declaration is:Type xsd__positiveInteger
declares a 64-bit unsigned integer which is encoded as a *xsd:positiveInteger
*:
Another possibility is to use strings to represent unbounded integers and do the translation in code.
xsd:short
* Corresponds to a 16-bit integer in the range -32768 to 32767. The type declaration is:Type xsd__short
declares a short 16-bit integer which is encoded as
xsd:string
* Represents character strings. The type declaration is:Type xsd__string
declares a string type which is encoded as
The type declaration for wide character strings is:
Both type of strings can be used at the same time, but requires one typedef name to be changed by appending an underscore which is invisible in XML. For example:
xsd:time
* Represents a time. The lexical representation for time is the left truncated lexical representation for dateTime: hh:mm:ss.sss with optional following time zone indicator. It is recommended to use strings (char*
) to store *xsd:time
* XSD Schema types. The type declaration is:xsd:token
* Represents tokenized strings. Tokens are strings that do not contain the line feed (#xA) nor tab (#x9) characters, that have no leading or trailing spaces (#x20) and that have no internal sequences of two or more spaces. It is recommended to use strings to store *xsd:token
* XSD Schema types. The type declaration is:Type xsd__token
declares a string type which is encoded as
It is solely the responsibility of the application to make sure the strings do not contain the line feed (#xA) nor tab (#x9) characters, that have no leading or trailing spaces (#x20) and that have no internal sequences of two or more spaces.
xsd:unsignedByte
* Corresponds to an 8-bit unsigned integer in the range 0 to 255. The type declaration is:Type xsd__unsignedByte
declares a unsigned 8-bit integer which is encoded as
xsd:unsignedInt
* Corresponds to a 32-bit unsigned integer in the range 0 to 4294967295. If the C++ compiler supports 32-bit int
types, the type declaration can use the int
type:Otherwise, the C++ compiler supports 16-bit int
types and the type declaration should use the long
type:
Type xsd__unsignedInt
declares an unsigned 32-bit integer which is encoded as
xsd:unsignedLong
* Corresponds to a 64-bit unsigned integer in the range 0 to 18446744073709551615. The type declaration is:Or in Visual C++:
Type xsd__unsignedLong
declares an unsigned 64-bit integer which is encoded as
xsd:unsignedShort
* Corresponds to a 16-bit unsigned integer in the range 0 to 65535. The type declaration is:Type xsd__unsginedShort
declares an unsigned short 16-bit integer which is encoded as
Other XSD Schema types such as *gYearMonth
*, *gYear
*, *gMonthDay
*, *gDay
*, *xsd:gMonth
*, *QName
*, *NOTATION
*, etc., can be encoded similarly using a typedef
declaration.
Trailing underscores (see Section C/C++ Identifier Name to XML Tag Name Mapping ) can be used in the type name in a typedef
to enable the declaration of multiple storage formats for a single XML Schema type. For example, one part of a C/C++ application's data structure may use plain strings while another part may use wide character strings. To enable this simultaneous use, declare:
Now, the xsd__string
and xsd__string_
types will both be encoded and decoded as XML string types and the use of trailing underscores allows multiple declarations for a single XML Schema type.
SOAP 1.1 supports polymorphic types, because XSD Schema types form a hierarchy. The root of the hierarchy is called *xsd:anyType
* (*xsd:ur-type
* in the older 1999 schema). So, for example, an array of *xsd:anyType
* in SOAP may actually contain any mix of element types that are the derived types of the root type. The use of polymorphic types is indicated by the WSDL and schema descriptions of a Web service and can therefore be predicted/expected for each particular case.
On the one hand, the typedef
construct provides a convenient way to associate C/C++ types with XML Schema types and makes it easy to incorporate these types in a (legacy) C/C++ application. However, on the other hand the typedef
declarations cannot be used to support polymorphic XML Schema types. Most SOAP clients and services do not use polymorphic types. In case they do, the primitive polymorphic types can be declared as a hierarchy of C++ class
es that can be used simultaneously with the typedef
declarations.
The general form of a primitive type declaration that is derived from a super type is:
where Type is a primitive C type. The __item
field MUST be the first field in this wrapper class.
For example, the XML Schema type hierarchy can be copied to C++ with the following declarations:
Note the use of the trailing underscores for the class
names to distinguish the typedef
type names from the class
names. Only the most frequently used built-in schema types are shown. It is also allowed to include the xsd:base64Binary
and xsd:hexBinary
types in the hierarchy:
See Sections Base64Binary XML Schema Type Encoding and hexBinary XML Schema Type Encoding .
Methods are allowed to be added to the classes above, such as constructors and getter/setter methods, see Section Getter and Setter Methods .
Wrapper structs are supported as well, similar to wrapper classes. But they cannot be used to implement polymorphism. Rather, the wrapper structs facilitate the use of XML attributes with a primitive typed object, see XML Attributes .
If more than one char
pointer points to the same string, the string is encoded as a multi-reference value. Consider for example
The s
and t
variables are assigned the same string, and when serialized, t
refers to the content of s
:
The example assumed that s
and t
are encoded as independent elements.
Note: the use of typedef
to declare a string type such as xsd__string
will not affect the multi-reference string encoding. However, strings declared with different typedef
s will never be considered multi-reference even when they point to the same string. For example
The variables s
and t
point to the same string, but since they are considered different types their content will not be shared in the SOAP payload through a multi-referenced string.
The implementation of string decoding in gSOAP allows for mixed content decoding. If the SOAP payload contains a complex data type in place of a string, the complex data type is decoded in the string as plain XML text.
For example, suppose the getInfo
service operation returns some detailed information. The service operation is declared as:
The proxy of the service is used by a client to request a piece of information and the service responds with:
As a result of the mixed content decoding, the detail
string contains "*`<picture>Mona Lisa by <i>Leonardo da Vinci</i></picture>`*".
gSOAP supports C++ strings std::string
and std::wstring
wide character strings. For example:
std::string
and C strings (char*
) in the header file when using SOAP 1.1 encoding. The problem is that multi-referenced strings in SOAP encoded messages cannot be assigned simultaneously to a std::string
and a char*
string.The double
encoding format is by default set to "`%.18G`" (see a manual on printf
text formatting in C), i.e. at most 18 digits of precision to limit a loss in accuracy. The float
encoding format is by default "`%.9G`", i.e. at most 9 digits of precision.
The encoding format of a double type can be set by assigning a format string to soap.double_format
, where soap
is a variable that contains the current runtime context. For example:
which causes all doubles to be encoded in scientific notation. Likewise, the encoding format of a float type can be set by assigning a format string to the static soap_float_format
string variable. For example:
which causes all floats to be encoded with four digits precision.
A special case for C format string patterns is introduced in gSOAP 2.8.18. A C format string that is used as a pattern for a typedef float or double in the gSOAP header file is used to format the output of the floating point value in XML. For example:
This will output the float with 5 digits total and 2 digits after the decimal point.
When *xs:totalDigits
* and *xs:fractionDigits
* are given in a XSD file, then also a C format string is produced to output floating point values with the proper precision and scale. For example:
The gSOAP runtime stdsoap2.cpp
and header file stdsoap2.h
support the marshalling of IEEE INF, -INF, and NaN representations. Under certain circumstances this may break if the hardware and/or C/C++ compiler does not support these representations. To remove the representations, remove the inclusion of the <math.h>
header file from the stdsoap2.h
file. You can control the representations as well, which are defined by the macros:
Enumerations are generally useful for the declaration of named integer-valued constants, also called enumeration constants.
The gSOAP soapcpp2
tool encodes the constants of enumeration-typed variables in symbolic form using the names of the constants when possible to comply to SOAP's enumeration encoding style. Consider for example the following enumeration of weekdays:
The enumeration-constant Mon
, for example, is encoded as
The value of the *xsi:type
* attribute is the enumeration-type identifier's name. If the element is independent as in the example above, the element name is the enumeration-type identifier's name.
The encoding of complex types such as enumerations requires a reference to an XML Schema through the use of a namespace prefix. The namespace prefix can be specified as part of the enumeration-type identifier's name, with the usual namespace prefix conventions for identifiers. This can be used to explicitly specify the encoding style. For example:
The enumeration-constant Sat
, for example, is encoded as:
The corresponding XML Schema for this enumeration data type would be:
If the value of an enumeration-typed variable has no corresponding named constant, the value is encoded as a signed integer literal. For example, the following declaration of a workday
enumeration type lacks named constants for Saturday and Sunday:
If the constant 5
(Saturday) or 6
(Sunday) is assigned to a variable of the workday
enumeration type, the variable will be encoded with the integer literals *5
* and *6
*, respectively. For example:
Since this is legal in C++ and SOAP allows enumeration constants to be integer literals, this method ensures that non-symbolic enumeration constants are correctly communicated to another party if the other party accepts literal enumeration constants (as with the gSOAP soapcpp2
tool).
Both symbolic and literal enumeration constants can be decoded.
To enforce the literal enumeration constant encoding and to get the literal constants in the WSDL file, use the following trick:
The difference with an enumeration type without a list of values and the enumeration type above is that the enumeration constants will appear in the WSDL service description.
The gSOAP soapcpp2
compiler supports the initialization of enumeration constants, as in:
The symbolic names *LESS
*, *EQUAL
*, and *GREATER
* will appear in the SOAP payload for the encoding of the ns1__relation
enumeration values -1
, 0
, and 1
, respectively.
A well-known deficiency of C and C++ enumeration types is the lack of support for the reuse of symbolic names by multiple enumerations. That is, the names of all the symbolic constants defined by an enumeration cannot be reused by another enumeration. To force encoding of the same symbolic name by different enumerations, the identifier of the symbolic name can end in an underscore (_
) or any number of underscores to distinguish it from other symbolic names in C++. This guarantees that the SOAP encoding will use the same name, while the symbolic names can be distinguished in C++. Effectively, the underscores are removed from a symbolic name prior to encoding.
Consider for example:
which will result in the encoding of the constants of enum ns1__weekday
without the underscore, for example as *Mon
*.
As an alternative to the trailing underscores that can get quite long for commonly used symbolic enum names, you can use the following convention with double underscores to add the enum name to the enum constants:
where the type name of the enumeration prefixedname
is a prefixed name, such as:
This ensures that the XML schema enumeration values are still simply *Mon
*, *Tue
*, *Wed
*, *Thu
*, *Fri
*, *Sat
*, and *Sun
*.
will not properly encode the weekday
enumeration when you assume that workdays are part of weekdays, because it lacks the named constants for workday
in its enumeration list. All enumerations must be self-contained and cannot use enum constants of other enumerations.
When developing a C Web service application, the C++ bool
type should not be used since it is not usually supported by the C compiler. Instead, an enumeration type should be used to serialize true/false values as *xsd:boolean
* Schema type enumeration values. The *xsd:boolean
* XML Schema type is defined as:
The value false_
, for example, is encoded as:
Peculiar of the SOAP boolean type encoding is that it only defines the values *0
* and *1
*, while the built-in XML Schema boolean type also defines the *false
* and *true
* symbolic constants as valid values. The following example declaration of an enumeration type lacks named constants altogether to force encoding of the enumeration values as literal constants:
The value 0
, for example, is encoded with an integer literal:
A bitmask is an enumeration of flags such as declared with C#'s [Flags] enum
annotation. gSOAP supports bitmask encoding and decoding for interoperability. However, bitmask types are not standardized with SOAP RPC.
A special syntactic convention is used in the header file input to the gSOAP soapcpp2
compiler to indicate the use of bitmasks with an asterisk:
The gSOAP soapcpp2
compiler will encode the enumeration constants as flags, i.e. as a series of powers of 2 starting with 1. The enumeration constants can be or-ed to form a bitvector (bitmask) which is encoded and decoded as a list of symbolic values in SOAP. For example:
Note that the use of the enum
does not require the asterisk, only the definition. The gSOAP soapcpp2
compiler generates the enumeration:
A service operation implementation in a Web service can return:
A struct
data type is encoded as an XML Schema complexType such that the struct
name is the XML Schema type name and the fields of the struct
are the type's accessors. This encoding is identical to the class
instance encoding without inheritance and method declarations, see Section Class Instance Serialization for further details. However, the encoding and decoding of struct
s is more efficient compared to class
instances due to the lack of inheritance and the requirement by the serialization routines to check inheritance properties at run time.
Certain type of fields of a struct
can be (de)serialized as XML attributes using the @
type qualifier. See Section XML Attributes for more details.
See Section C/C++ Identifier Name to XML Tag Name Mapping for more details on the struct/class member field serialization and the resulting element and attribute qualified forms.
A class
instance is serialized as an XML Schema complexType (SOAP-encoded compound data type) such that the class
name forms the data type's element name and schema type and the data member fields are the data type's accessors. Only the data member fields are encoded in the SOAP payload. Class methods are not encoded.
The general form of a class
declaration is:
where
namespace_prefix__
is the optional namespace prefix of the compound data type (see identifier translation rules C/C++ Identifier Name to XML Tag Name Mapping )class_name1
is the element name of the compound data type (see identifier translation rules C/C++ Identifier Name to XML Tag Name Mapping ).class_name2
is an optional base class.field
is a field declaration (data member). A field MAY be declared static
and const
and MAY be initialized.method
is a method declaration. A method MAY be declared virtual
, but abstract methods are not allowed. The method parameter declarations are REQUIRED to have parameter identifier names.public
acces permission can be serialized.A class name is REQUIRED to be unique and cannot have the same name as a struct
, enum
, or service operation name specified in the header file input to the gSOAP soapcpp2
compiler. The reason is that service operation requests are encoded similarly to class instances in SOAP and they are in principle undistinguishable (the method parameters are encoded just as the fields of a class
).
Only single inheritance is supported by the gSOAP soapcpp2
compiler. Multiple inheritance is not supported, because of the limitations of the SOAP protocol.
If a constructor method is present, there MUST also be a constructor declaration with empty parameter list.
Classes should be declared "volatile" if you don't want gSOAP to add serialization methods to these classes, see Section Serialization "{as is" with Volatile Data Types} for more details.
Class templates are not supported by the gSOAP soapcpp2
compiler, but you can use STL containers, see Section STL Containers . You can also define your own containers similar to STL containers.
Certain type of fields of a struct
can be (de)serialized as XML attributes using the @
type qualifier. See Section XML Attributes for more details.
See Section C/C++ Identifier Name to XML Tag Name Mapping for more details on the struct/class member field serialization and the resulting element and attribute qualified forms.
Arrays may be embedded within a class (and struct) using a pointer field and size information, see Section Encoding XML Generics Containing Dynamic Arrays . This defines what is sometimes referred to in SOAP as "generics".
Void pointers may be used in a class (or struct), but you have to add a type field so the gSOAP runtime can determine the type of object pointed to, see Section Void Pointers .
A class
instance is encoded as:
where the *field-name
* accessors have element-name representations of the class fields and the *basefield-name
* accessors have element-name representations of the base class fields. (The optional parts resulting from the specification are shown enclosed in [optional: ].)
The decoding of a class instance allows any ordering of the accessors in the SOAP payload. However, if a base class field name is identical to a derived class field name because the field is overloaded, the base class field name MUST precede the derived class field name in the SOAP payload for decoding. gSOAP guarantees this, but interoperability with other SOAP implementations is cannot be guaranteed.
The following example declares a base class ns__Object
and a derived class ns__Shape
:
The implementation of the methods of class ns__Shape
must not be part of the header file and need to be defined elsewhere.
An instance of class ns__Shape
with name Triangle, 3 sides, and color Green is encoded as:
The namespace URI of the namespace prefix *ns
* must be defined by a namespace mapping table, see Section Namespace Mapping Table .
A data member field of a class declared as static const
is initialized with a constant value at compile time. This field is encoded in the serialization process, but is not decoded in the deserialization process. For example:
An instance of class ns__Triangle
is encoded in SOAP as:
Decoding will ignore the sides
field's value.
static const
fields, due to C++ compiler compatibility differences. This feature may be provided the future.A class
declaration in the header file input to the gSOAP soapcpp2
compiler MAY include method declarations. The method implementations MUST NOT be part of the header file but are required to be defined in another C++ source that is externally linked with the application. This convention is also used for the constructors and destructors of the class
.
Dynamic binding is supported, so a method MAY be declared virtual
.
Setter and getter methods are invoked at run time upon serialization and deserialization of class instances, respectively. The use of setter and getter methods adds more flexibility to the serialization and deserialization process.
A setter method is called in the serialization phase from the virtual soap_serialization
method generated by the gSOAP soapcpp2
compiler. You can use setter methods to process a class instance just before it is serialized. A setter method can be used to convert application data, such as translating transient application data into serializable data, for example. You can also use setter methods to retrieve dynamic content and use it to update a class instance right before serialization. Remember setters as "set to serialize" operations.
Getter methods are invoked after deserialization of the instance. You can use them to adjust the contents of class instances after all their members have been deserialized. Getters can be used to convert deserialized members into transient members and even invoke methods to process the deserialized data on the fly.
Getter and setter methods have the following signature:
The active soap struct will be passed to the get
and set
methods. The methods should return SOAP_OK
when successful. A setter method should prepare the contents of the class instance for serialization. A getter method should process the instance after deserialization.
Here is an example of a base64 binary class:
Suppose that the type and options members of the attachment should be set when the class is about to be serialized. This can be accomplished with the set
method from the information provided by the __ptr
to the data and the soap struct passed to the set
method (you can pass data via the void *soap.user
field).
The get
method is invoked after the base64 data has been processed. You can use it for post-processing purposes.
Here is another example. It defines a primitive update
type. The class is a wrapper for the time_t
type, see Section How to use C++ Wrapper Classes to Specify Polymorphic Primitive Types . Therefore, elements of this type contain *xsd:dateType
* data.
The setter method assigns the current time:
Therefore, serialization results in the inclusion of a time stamp in XML.
get
method is invoked only when the XML element with its data was completely parsed. The method is not invoked when the element is an *xsi:nil
* element or has an *href
* attribute.soap_out
method of a class calls the setter (when provided). However, the soap_out
method is declared const
while the setter should be allowed to modify the contents of the class instance. Therefore, the gSOAP-generated code recasts the instance and the const
is removed when invoking the setter.Getter methods enable streaming XML operations. A getter method is invoked when the object is deserialized and the rest of the SOAP/XML message has not been processed yet. For example, you can add a getter method to the SOAP Header class to implement header processing logic that is activated as soon as the SOAP Header is received. An example code is shown below:
The Authentication
SOAP Header field is instantiated and decoded. After decoding, the getter method is invoked, which can be used to check the id
before the rest of the SOAP message is processed.
Interoperability between client and service applications developed with gSOAP is established even when clients and/or services use derived classes instead of the base classes used in the declaration of the service operation parameters. A client application MAY use pointers to instances of derived classes for the input parameters of a service operation. If the service was compiled with a declaration and implementation of the derived class, the service operation base class input parameters are demarshalled and a derived class instance is created instead of a base class instance. If the service did not include a declaration of the derived class, the derived class fields are ignored and a base class instance is created. Therefore, interoperability is guaranteed even when the client sends an instance of a derived classes and when a service returns an instance of a derived class.
The following example declares Base and Derived classes and a service operation that takes a pointer to a Base class instance and returns a Base class instance:
This header file specification is processed by the gSOAP soapcpp2
compiler to produce the stub and skeleton routines which are used to implement a client and service. The pointer of the service operation is also allowed to point to Derived class instances and these instances will be marshalled as Derived class instances and send to a service, which is in accord to the usual semantics of parameter passing in C++ with dynamic binding.
The Base and Derived class method implementations are:
Below is an example CLIENT
application that creates a Derived class instance that is passed as the input parameter of the service operation:
The following example SERVER1
application copies a class instance (Base or Derived class) from the input to the output parameter:
The following messages are produced by the CLIENT
and SERVER1
applications:
CLIENT: created a Derived class instance SERVER1: created a Derived class instance SERVER1: print(): Derived class instance X 3 CLIENT: created a Derived class instance CLIENT: print(): Derived class instance X 3
Which indicates that the derived class kept its identity when it passed through SERVER1
. Note that instances are created both by the CLIENT
and SERVER1
by the demarshalling process.
Now suppose a service application is developed that only accepts Base class instances. The header file is:
This header file specification is processed by the gSOAP soapcpp2
tool to produce skeleton routine which is used to implement a service (so the client will still use the derived classes).
The method implementation of the Base class are:
And the SERVER2
application is that uses the Base class is:
Here are the messages produced by the CLIENT
and SERVER2
applications:
CLIENT: created a Derived class instance SERVER2: created a Base class instance SERVER2: print(): Base class instance X CLIENT: created a Base class instance CLIENT: print(): Base class instance X
In this example, the object was passed as a Derived class instance to SERVER2
. Since SERVER2
only implements the Base class, this object is converted to a Base class instance and send back to CLIENT
.
The SOAP RPC/LIT and SOAP DOC/LIT encoding styles support XML attributes in SOAP messages while SOAP RPC with "Section 5" encoding does not support XML attributes other than the SOAP and XSD specific attributes. SOAP RPC Section 5 encoding has advantages for cross-language interoperability and data encodings such as graph serialization. However, RPC/LIT and DOC/LIT enables direct exchange of XML documents, which may include encoded application data structures. Language interoperability is compromised, because no mapping between XML and the typical language data types is defined. The meaning of the RPC/LIT and DOC/LIT XML content is Schema driven rather than application/language driven.
gSOAP supports XML attribute (de)serialization of members in structs and classes. Attributes are primitive XSD types, such as strings, enumerations, boolean, and numeric types. To declare an XML attribute in a struct/class, the qualifier @
is used with the type of the attribute. The type must be primitive type (including enumerations and strings), which can be declared with or without a typedef
to associate a XSD type with the C/C+ type. For example
The @
qualifier indicates XML attribute encoding for the ns__type
, ns__flag
, and ns__state
fields. Note that the namespace prefix ns
is used to distinguish these attributes from any other attributes such as *xsi:type
* (*ns:type
* is not to be confused with *xsi:type
*).
Default values can be associated with any field that has a primitive type in a struct/class, as is illustrated in this example. The default values are used when the receiving message does not contain the corresponding values.
String attributes are optional. Other type of attributes should be declared as pointers to make them optional:
Because a service operation request and response is essentially a struct, XML attributes can also be associated with method requests and responses. For example
Attributes can also be attached to the dynamic arrays, binary types, and wrapper classes/structs of primitive types. Wrapper classes are described in Section How to use C++ Wrapper Classes to Specify Polymorphic Primitive Types . For example
and
The attribute declarations MUST follow the __item
, __ptr
, and __size
fields which define the characteristics of wrapper structs/classes and dynamic arrays.
gSOAP ensures the proper decoding of XSD QNames. An element or attribute with type QName (Qualified Name) contains a namespace prefix and a local name. You can declare a QName type as a typedef char *xsd__QName
. Values of type QName are internally handled as regular strings. gSOAP takes care of the proper namespace prefix mappings when deserializing QName values. For example
When the elt
and att
fields are serialized, their string contents are just transmitted (which means that the application is responsible to ensure proper formatting of the QName strings prior to transmission). When the fields are deserialized however, gSOAP takes care mapping the qualifiers to the appropriate namespace prefixes. Suppose that the inbound value for the elt
is *x:def
*, where the namespace name associated with the prefix *x
* matches the namespace name of the prefix *ns
* (as defined in the namespace mapping table). Then, the value is automatically converted into *ns:def
*. If the namespace name is not in the table, then *x:def
* is converted to "{</tt>URI":def} where *`"{<tt>*URI"} is the namespace URI bound to *
x`* in the message received. This enables an application to retrieve the namespace information, whether it is in the namespace mapping table or not.
Note: QName
is a pre-defined typedef type and used by gSOAP to (de)serialize SOAP Fault codes which are QName elements.
A union
is only serialized if the union
is used within a struct
or class
declaration that includes a int __union
field that acts as a discriminant or selector for the union
fields. The selector stores run-time usage information about the union
fields. That is, the selector is used to enumerate the union
fields such that the gSOAP engine is able to select the correct union
field to serialize.
A union
within a struct
or class
with a selector field represents *xs:choice
* within a Schema complexType component. For example:
The union ns__PO_or_Invoice
is expanded as a *xs:choice
*:
Therefore, the name of the union
and field can be freely chosen. However, the union
name should be qualified (as shown in the example) to ensure instances of XML Schemas with *elementFormDefault="qualified"
* are correctly serialized (po
and invoice
are *ns:
* qualified).
The int __union
field selector's values are determined by the soapcpp2
compiler. Each union
field name has a selector value formed by:
These selector values enumerate the union
fields starting with 1. The value 0 (or any negative value) can be assigned to omit the serialization of the union
, but only if explicitly allowed by validation rules, which requires *minOccurs="0"
* for the *xs:choice
* as follows:
This way we can treat the union
as an optional data item by setting __union=0
.
Since 2.7.16 it is also possible to use a '$
' as a special marker to annotate a selector field that must be of type int
and the field name is no longer relevant:
The following example shows how the struct ns__composite
instance is initialized for serialization using the above declaration:
Note that failing to set the selector to a valid union
field can lead to a crash of the gSOAP serializer because it will attempt to serialize an invalid union
field.
For deserialization of union
types, the selector will be set to one of the union
field selector values, as determined by the XML payload. The selector will be set to 0 or -1 when no union member was deserialized, where a negative value indicates that a member was required by validation rules. Strict validation enabled with SOAP_XML_STRICT
results in a validation fault.
When more than one union
is used in a struct
or class
, the __union
selectors must be renamed to avoid name clashes by using suffixes as in:
The serialization of a pointer to a data type amounts to the serialization of the data type in SOAP and the SOAP encoded representation of a pointer to the data type is indistinguishable from the encoded representation of the data type pointed to.
A data structure pointed to by more than one pointer is serialized as SOAP multi-reference data. This means that the data will be serialized only once and identified with a unique *id
* attribute. The encoding of the pointers to the shared data is done through the use of *href
* or *ref
* attributes to refer to the multi-reference data. See Section Run Time Flags on options to control the serialization of multi-reference data. To turn multi-ref off, use SOAP_XML_TREE
to process plain tree-based XML. To completely eliminate multi-ref (de)serialization use the WITH_NOIDREF
compile-time flag with all source code (including stdsoap2.c
and stdsoap2.cpp
) to permanently disable id-href processing. Cyclic C/C++ data structures are encoded with multi-reference SOAP encoding. Consider for example the following a linked list data structure:
Suppose a cyclic linked list is created. The first node contains the value "abc" and points to a node with value "def" which in turn points to the first node. This is encoded as:
In case multi-referenced data is received that "does not fit in a pointer-based structure", the data is copied. For example, the following two struct
s are similar, except that the first uses pointer-based fields while the other uses non-pointer-based fields:
Since both a
and b
fields of P
point to the same integer, the encoding of P
is multi-reference:
Now, the decoding of the content in the R
data structure that does not use pointers to integers results in a copy of each multi-reference integer. Note that the two struct
s resemble the same XML data type because the trailing underscore will be ignored in XML encoding and decoding.
A NULL
pointer is not serialized, unless the pointer itself is pointed to by another pointer (but see Section Run Time Flags to control the serialization of NULLs). For example:
Suppose pointer q
points to pointer p
and suppose p=NULL
. In that case the p
pointer is serialized as
and the serialization of q
refers to *href="#123"
*. Note that SOAP 1.1 does not support pointer to pointer types (!), so this encoding is specific to gSOAP. The pointer to pointer encoding is rarely used in codes anyway. More common is a pointer to a data type such as a struct
with pointer fields.
xsi:nil="true"
* attribute but the corresponding C++ data is not a pointer or reference, the deserializer will terminate with a SOAP_NULL
fault when the SOAP_XML_STRICT
flag is set. The types section of a WSDL description contains information on the "nilability" of data.In general, void pointers (void*
) cannot be (de)serialized because the type of data referred to is untyped. To enable the (de)serialization of the void pointers that are members of structs or classes, you can insert a int __type
field right before the void pointer field. The int __type
field contains run time information on the type of the data pointed to by void*
member in a struct/class to enable the (de)serialization of this data. The int __type
field is set to a SOAP_TYPE_X
value, where X
is the name of a type. gSOAP generates the SOAP_TYPE_X
definitions in soapH.h
and uses them internally to uniquely identify the type of each object. The type naming conventions outlined in Section Serializing C/C++ Data to XML are used to determine the type name for X.
Here is an example to illustrate the (de)serialization of a void*
field in a struct:
The __type
integer can be set to 0 at run time to omit the serialization of the void pointer field.
The following example illustrates the initialization of myStruct
with a void pointer to an int:
The serialized output of S
contains the integer.
The deserializer for myStruct
will automatically set the __type
field and void pointer to the deserialized data, provided that the XML content for p
carries the *xsi:type
* attribute from which gSOAP can determine the type.
void*
field, the void*
pointer MUST directly point to the string value rather than indirectly as with all other types. For example:This is the case for all string-based types, including types defined with typedef char*
.
You may use an arbitrary suffix with the __type
fields to handle multiple void pointers in structs/classes. For example
Because service method parameters are stored within structs, you can use __type
and void*
parameters to pass polymorphic arguments without having to define a C++ class hierarchy (Section Polymorphism, Derived Classes, and Dynamic Binding ). For example:
This method has a polymorphic input parameter data
and a polymorphic output parameter return_
. The __type
parameters can be one of SOAP_TYPE_xsd__string
, SOAP_TYPE_xsd__int
, SOAP_TYPE_xsd__float
, SOAP_TYPE_ns__status
, or SOAP_TYPE_ns__widget
. The WSDL produced by the gSOAP soapcpp2
compiler declares the polymorphic parameters of type *xsd:anyType
* which is "too
loose" and doesn't allow the gSOAP importer to handle the WSDL accurately. Future gSOAP releases might replace *xsd:anyType
* with a *choice
* schema type that limits the choice of types to the types declared in the header file.
Fixed size arrays are encoded as per SOAP 1.1 one-dimensional array types. Multi-dimensional fixed size arrays are encoded by gSOAP as nested one-dimensional arrays in SOAP. Encoding of fixed size arrays supports partially transmitted and sparse array SOAP formats.
The decoding of (multi-dimensional) fixed-size arrays supports the SOAP multi-dimensional array format as well as partially transmitted and sparse array formats.
An example:
This specifies a fixed-size array part of the struct Example
. The encoding of array a
is:
As the name suggests, dynamic arrays are much more flexible than fixed-size arrays and dynamic arrays are better adaptable to the SOAP encoding and decoding rules for arrays. In addition, a typical C application allocates a dynamic array using malloc
, assigns the location to a pointer variable, and deallocates the array later with free
. A typical C++ application allocates a dynamic array using new
, assigns the location to a pointer variable, and deallocates the array later with delete
. Such dynamic allocations are flexible, but pose a problem for the serialization of data: how does the array serializer know the length of the array to be serialized given only a pointer to the sequence of elements? The application stores the size information somewhere. This information is crucial for the array serializer and has to be made explicitly known to the array serializer by packaging the pointer and array size information within a struct
or class
.
SOAP encoded arrays use the *SOAP-ENC:Array
* type and the *SOAP-ENC:arrayType
* attribute to define the array dimensionality and size. As a security measure to avoid denial of service attacks based on sending a huge array size value requiring the allocation of large chunks of memory, the total number of array elements set by the *SOAP-ENC:arrayType
* attribute cannot exceed SOAP_MAXARRAYSIZE
, which is set to 100,000 by default. This constant is defined in stdsoap2.h
. This constant only affects multi-dimensional arrays and the dimensionality of the receiving array will be lost when the number of elements exceeds 100,000. One-dimensional arrays will be populated in sequential order as expected.
A special form of struct
or class
is used to define one-dimensional dynamic SOAP-encoded arrays. Each array has a pointer variable and a field that records the number of elements the pointer points to in memory.
The general form of the struct
declaration that contains a one-dimensional dynamic SOAP-encoded array is:
where Type
MUST be a type associated with an XML Schema or MUST be a primitive type. If these conditions are not met, a vector-like XML (de)serialization is used (see Section Encoding XML Generics Containing Dynamic Arrays ). A primitive type can be used with or without a typedef
. If the array elements are structs or classes, then the struct
/class
type names should have a namespace prefix for schema association, or they should be other (nested) dynamic arrays.
An alternative to a struct
is to use a class
with optional methods that MUST appear after the __ptr
and __size
fields:
To encode the data type as an array, the name of the struct
or class
SHOULD NOT have a namespace prefix, otherwise the data type will be encoded and decoded as a generic vector, see Section Encoding XML Generics Containing Dynamic Arrays .
The deserializer of a dynamic array can decode partially transmitted and/or SOAP sparse arrays, and even multi-dimensional arrays which will be collapsed into a one-dimensional array with row-major ordering.
__offset
field of a dynamic array is ignored.The following example header file specifies the XMethods Service Listing service getAllSOAPServices
service operation and an array of SOAPService
data structures:
An example client application:
The declaration of a dynamic array as described in Dynamic Arrays MAY include an int __offset
field. When set to an integer value, the serializer of the dynamic array will use this field as the start index of the array and the SOAP array offset attribute will be used in the SOAP payload. Note that array offsets is a SOAP 1.1 specific feature which is not supported in SOAP 1.2.
For example, the following header file declares a mathematical Vector
class, which is a dynamic array of floating point values with an index that starts at 1:
The implementations of the Vector
methods are:
An example program fragment that serializes a vector of 3 elements:
The output is a partially transmitted array:
Note that the size of the encoded array is necessarily set to 4 and that the encoding omits the non-existent element at index 0.
The decoding of a dynamic array with an __offset
field is more efficient than decoding a dynamic array without an __offset
field, because the __offset
field will be assigned the value of the *SOAP-ENC:offset
* attribute instead of padding the initial part of the array with default values.
One-dimensional dynamic arrays MAY be nested. For example, using class Vector
declared in the previous section, class Matrix
is declared:
The Matrix type is essentially an array of pointers to arrays which make up the rows of a matrix. The encoding of the two-dimensional dynamic array in SOAP will be in nested form.
The general form of the struct
declaration for K-dimensional (K>1) dynamic arrays is:
where Type
MUST be a type associated with an XML Schema, which means that it must be a typedef
ed type in case of a primitive type, or a struct
/class
name with a namespace prefix for schema association, or another dynamic array. If these conditions are not met, a generic vector XML (de)serialization is used (see Section Encoding XML Generics Containing Dynamic Arrays ).
An alternative is to use a class
with optional methods:
In the above, K is a constant denoting the number of dimensions of the multi-dimensional array.
To encode the data type as an array, the name of the struct
or class
SHOULD NOT have a namespace prefix, otherwise the data type will be encoded and decoded as a generic vector, see Section Encoding XML Generics Containing Dynamic Arrays .
The deserializer of a dynamic array can decode partially transmitted multi-dimensional arrays.
For example, the following declaration specifies a matrix class:
In contrast to the matrix class of Section Nested One-Dimensional Dynamic SOAP Arrays that defined a matrix as an array of pointers to matrix rows, this class has one pointer to a matrix stored in row-major order. The size of the matrix is determined by the __size
field: __size[0]
holds the number of rows and __size[1]
holds the number of columns of the matrix. Likewise, __offset[0]
is the row offset and __offset[1]
is the columns offset.
The XML "generics" concept discussed in the SOAP encoding protocols extends the concept of a SOAP struct by allowing repetitions of elements within the struct. This is just a form of a repetition of XML elements without the SOAP-encoded array requirements. While SOAP-encoded arrays are more expressive (offset information to encode sparse arrays for example), simple repetitions of values are used more frequently.
A simple generic reperition is an array-like data structure with a repetition of an element. To achieve this, declare a dynamic array as a struct
or class
with a name that is qualified with a namespace prefix. SOAP arrays are declared without prefix.
For example, we define a Map structure that contains a sequence of pairs of key-val:
Since 2.7.16 it is also possible to use a '$
' as a special marker to annotate a size field that must be of type int
or size_t
and the field name is no longer relevant:
This declares a dynamic array pointed to by pair
and size __size
. The array will be serialized and deserialized as a sequence of pairs:
Deserialization is less efficient compared to a SOAP-encoded array, because the size of the sequence is not part of the SOAP encoding. Internal buffering is used by the deserializer to collect the elements. When the end of the list is reached, the buffered elements are copied to a newly allocated space on the heap for the dynamic array.
Multiple arrays can be used in a struct/class to support the concept of "generics". Each array results in a repetition of elements in the struct/class. This is achieved with a int __size
(or $int
) field in the struct/class where the next field (i.e. below the __size
field) is a pointer type. The pointer type is assumed to point to an array of values at run time. The __size
field holds the number of values at run time. Multiple arrays can be embedded in a struct/class with __size
fields that have a distinct names. To make the __size
fields distinct, you can end them with a unique name suffix such as __sizeOfstrings
, for example.
The general convention for embedding arrays is:
where name1
and name2
are identifiers used as a suffix to distinguish the __size
field. These names can be arbitrary and are not visible in XML.
In 2.7.16 and higher this is simplified with a '$
' marker:
For example, the following struct has two embedded arrays:
The XML serialization of an example ns__Contact
is:
gSOAP supports the STL containers std::deque
, std::list
, std::set
, and std::vector
.
STL containers can only be used within classes to declare members that contain multiple values. This is somewhat similar to the embedding of arrays in structs in C as explained in Section Encoding XML Generics Containing Dynamic Arrays , but the STL container approach is more flexible.
You need to import stldeque.h
, stllist.h
, stlset.h
, or stlvector.h
to enable std::deque
, std::list
, std::set
, and std::vector
(de)serialization. Here is an example:
The use of pointer members is not required but advised. The reason is that interoperability with other SOAP toolkits may lead to copying of ns__myClass
instances at run time when (de)serializing multi-referenced data. When a copy is made, certain parts of the containers will be shared between the copies which could lead to disaster when the classes with their containers are deallocated. Another way to avoid this is to declare class ns__myClass
within other data types via a pointer. (Interoperability between gSOAP clients and services does not lead to copying.)
The XML Schema that corresponds to the ns__myClass
type is
You can specify the minOccurs and maxOccurs values as explained in Section Customizing the WSDL and Namespace Mapping Table File Contents With gSOAP Directives .
You can also implement your own containers similar to STL containers. The containers must be class templates and should define a forward iterator type, and provide the following methods:
void clear()
empty the container;iterator begin()
return iterator to beginning;const_iterator begin() const
return const iterator to beginning;iterator end()
return iterator to end;const_iterator end() const
return const iterator to end;size_t size()
return size;iterator insert(iterator pos, const_reference val)
insert element.The iterator
should be a forward iterator with a dereference operator to access the container's elements, it must be comparable (equal/unequal), and be pre-incrementable (++it
). The const iterator is used by gSOAP to send a sequence of XML element values. The insert
method is used to populate a container with Container::iterator i = container.insert(container.end(), val)
.
Here is in example user-defined container template class:
To enable the container, we add the following two lines to our gSOAP header file:
The container class should not be defined in the gSOAP header file. It must be defined in a separate header file (e.g. "simpleVector.h"). The template <class T> class simpleVector
declaration ensures that gSOAP will recognize simpleVector
as a container class.
insert
container methods to store elements one by one. However, element content that is "forwarded" with *href
* attributes will be appended to the container. Forwarding can take place with multi-referenced data that is referred to from the main part of the SOAP 1.1 XML message to the independent elements that carry *id
*s. Therefore, your application should not rely on the preservation of the order of elements in a container.Polymorphic arrays (arrays of polymorphic element types) can be encoded when declared as an array of pointers to class instances. For example:
The pointers in the array can point to the ns__Object
base class or ns__Data
derived class instances which will be serialized and deserialized accordingly in SOAP. That is, the array elements are polymorphic.
Since we can't use dynamic binding to support polymorphism in C, another mechanism is available based on the serialization of void pointers, that is, dynamic serialization of data referenced by void pointers, see Section Void Pointers .
The __ptr
field in a struct
or class
declaration of a dynamic array may have an optional suffix part that describes the name of the tags of the SOAP array XML elements. The suffix is part of the field name:
The suffix describes the tag name to be used for all array elements. The usual identifier to XML translations apply, see Section C/C++ Identifier Name to XML Tag Name Mapping . The default XML element tag name for array elements is *item
* (which corresponds to the use of field name __ptritem
).
Consider for example:
The array is serialized as:
SOAP 1.1 and 1.2 do not require the use of a specific tag name for array elements. gSOAP will deserialize a SOAP array while ignoring the tag names. Certain XML Schemas used in doc/literal encoding may require the declaration of array element tag names.
The *base64Binary
* XML Schema type is a special form of dynamic array declared with a pointer (__ptr
) to an unsigned char
array.
For example using a struct
:
Or with a class
:
When compiled by the gSOAP soapcpp2
tool, this header file specification will generate *base64Binary
* serializers and deserializers.
The *SOAP_ENC:base64
* encoding is another type for base 64 binary encoding specified by the SOAP data type schema and some SOAP applications may use this form (as indicated by their WSDL descriptions). It is declared by:
Or with a class
:
When compiled by the gSOAP soapcpp2
tool, this header file specification will generate *SOAP-ENC:base64
* serializers and deserializers.
The advantage of using a class
is that methods can be used to initialize and manipulate the __ptr
and __size
fields. The user can add methods to this class to do this. For example:
Here are example method implementations:
The following example in C/C++ reads from a raw image file and encodes the image in SOAP using the *base64Binary
* type:
where filesize
is a function that returns the size of a file given a file descriptor.
Reading the *xsd:base64Binary
* encoded image.
The struct
or class
name soap_enc__base64
should be used for *SOAP-ENC:base64
* schema type instead of xsd__base64Binary
.
The *hexBinary
* XML Schema type is a special form of dynamic array declared with the name xsd__hexBinary
and a pointer (__ptr
) to an unsigned char
array, similar to the base64Binary type described in the previous section. The only difference with the base64Binary type is the hexadecimal content instead of base64 content. Both types are declared identically, with the exception that the word "`hex`" occurs in the struct/class name.
For example, using a struct
:
Or using a class
:
or if a binary type such as xsd__base64Binary
is defined, then we can simply use a typedef
to introduce the hex variant:
When compiled by the gSOAP soapcpp2
tool, this header file specification will generate *hexBinary
* serializers and deserializers.
gSOAP supports document/literal encoding by default. Just as with SOAP RPC encoding, literal encoding requires the XML Schema of the message data to be provided e.g. in WSDL in order for the gSOAP soapcpp2
compiler to generate the (de)serialization routines.
The //gsoap service encoding
, //gsoap service method-encoding
, and //gsoap service method-response-encoding
directives explicitly enable SOAP encoded or literal encoded messages. For example, to enable RPC encoding style for the entire service, use:
To enable encoding for particular service methods, use:
To enable encoding for particular service methods responses when the method request is literal, use:
Instead of the encoded
value, you can use literal
, or a specific encoding style value.
Consider the following example that uses the directive to make the literal encoding explicit. The LocalTimeByZipCode
service operation of the LocalTime service provides the local time given a zip code and uses literal encoding (with MS .NET). The following header file declares the method:
Note that none of the data types need to be namespace qualified using namespace prefixes.
In this case, the method name requires to be associated with a schema through a namespace prefix, e.g. ns
is used in this example. See Section Customizing the WSDL and Namespace Mapping Table File Contents With gSOAP Directives for more details on gSOAP directives. With these directives, the gSOAP soapcpp2
compiler generates client and server sources with the specified settings.
The example client program is:
To illustrate the manual doc/literal setting, the following client program sets the required properties before the call:
The SOAP request is:
To declare a literal XML "type" to hold XML documents in regular strings, use:
To declare a literal XML "type" to hold XML documents in wide character strings, use:
Note: only one of the two storage formats can be used. The differences between the use of regular strings versus wide character strings for XML documents are:
Here is a C++ example of a service operation specification in which the parameters of the service operation uses literal XML encoding to pass an XML document to a service and back:
and in C:
The ns__Document
is essentially a struct
that forms the root of the XML document. The use of the underscore in the ns__Document
response part of the message avoids the name clash between the struct
s. Assuming that the namespace mapping table contains the binding of ns
to *http://my.org/
* and the binding of m
to *http://my.org/mydoc.xsd
*, the XML message is:
When using literal encoding of method parameters and response as shown in the example above, the literal XML encoding style MUST be specified by setting soap.encodingStyle
. For example, to specify no constraints on the encoding style (which is typical) use NULL:
As a result, the *SOAP-ENV:encodingStyle
* attribute will not appear in the SOAP payload.
For interoperability with Apache SOAP, use
When the response parameter is an XML type, it will store the entire XML response content but without the enveloping response element.
The XML type can be used as part of any data structure to enable the rendering and parsing of custom XML documents. For example:
A predeclared standard SOAP Fault data structure is generated by the gSOAP soapcpp2
tool for exchanging exception messages. The built-in struct SOAP_ENV__Fault
data structure is defined as:
The first four fields in SOAP_ENV__Fault
are SOAP 1.1 specific. The last five fields are SOAP 1.2 specific. You can redefine these structures in the header file. For example, you can use a class
for the SOAP_ENV__Fault
and add methods for convenience.
The data structure content can be changed to the need of an application, but this is generally not necessary because the application-specific SOAP Fault details can be serialized via the __type
and fault
fields in the SOAP_ENV__Detail
field, see Section Void Pointers on the serialization of data refered to by __type
and fault
.
The __type
field allows application data to be serialized as part of the SOAP Fault. The application data SHOULD be defined as XML elements, which requires you to declare the type names with a leading underscore to ensure that the types are compatible with XML elements and not just simpleTypes and complexTypes.
When the skeleton of a service operation returns an error (see Section Error Codes ), then soap.fault
contains the SOAP Fault data at the receiving side (client).
Server-side faults are raised with soap_sender_fault
or soap_receiver_fault
. The soap_sender_fault
call should be used to inform that the sender is at fault and the sender (client) should not resend the request. The soap_receiver_fault
call should be used to indicate a temporary server-side problem, so a sender (client) can resend the request later. For example:
In the example, the SOAP Fault details were empty (NULL). You may pass an XML fragment, which will be literally included in the SOAP Fault message. For WS-I Basic Profile compliance, you must pass an XML string with one or more namespace qualified elements, such as:
When a service operation must raise an exception with application SOAP Fault details, it does so by assigning the soap.fault
field of the current reference to the runtime context with appropriate data associated with the exception and by returning the error SOAP_FAULT
. For example:
When soap_receiver_fault
allocates a fault struct, this data is removed with the soap_end
call (or soap_dealloc
). Note that the soap_receiver_fault
function is called to allocate the fault struct and set the fault string and detail fields, i.e. soap_receiver_fault(soap, "Stack dump", NULL)
. The advantage is that this is independent of SOAP 1.1 and SOAP 1.2. However, setting the custom detail fields requires inspecting the SOAP version used, using the soap->version
attribute which is 1 for SOAP 1.1 and 2 for SOAP 1.2.
Each service operation implementation in a service application can return a SOAP Fault upon an exception by returning an error code, see Section Example for details and an example. In addition, a SOAP Fault can be returned by a service application through calling the soap_send_fault
function. This is useful in case the initialization of the application fails, as illustrated in the example below:
A predeclared standard SOAP Header data structure is generated by the gSOAP soapcpp2
tool for exchanging SOAP messages with SOAP Headers. This predeclared data structure is:
which declares and empty header (some C and C++ compilers don't accept empty structs, use compile flag -DWITH_NOEMPTYSTRUCT
to avoid these errors).
To adapt the data structure to a specific need for SOAP Header processing, a new struct SOAP_ENV__Header
can be added to the header file input to the gSOAP compiler. A class
for the SOAP Header data structure can be used instead of a struct
.
For example, the following header can be used for transaction control:
with client-side code:
The SOAP Web service response can include a SOAP Header with a transaction number that the client is supposed to use for the next service operation invocation to the service. Therefore, the next request includes a transaction number:
This is just an example and the transaction control is not a feature of SOAP but can be added on by the application layer to implement stateful transactions between clients and services. At the client side, the soap.actor
attribute can be set to indicate the recipient of the header (the SOAP *SOAP-ENV:actor
* attribute).
A Web service can read and set the SOAP Header as follows:
See Section Customizing the WSDL and Namespace Mapping Table File Contents With gSOAP Directives on how to generate WSDL with the proper method-to-header-part bindings.
The *SOAP-ENV:mustUnderstand
* attribute indicates the requirement that the recipient of the SOAP Header (who must correspond to the *SOAP-ENV:actor
* attribute when present or when the attribute has the value *SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next"
*) MUST handle the Header part that carries the attribute. gSOAP handles this automatically on the background. However, an application still needs to inspect the header part's value and handle it appropriately. If a service operation in a Web service is not able to do this, it should return SOAP_MUSTUNDERSTAND
to indicate this failure.
The syntax for the header file input to the gSOAP soapcpp2
compiler is extended with a special storage qualifier mustUnderstand
. This qualifier can be used in the SOAP Header declaration to indicate which parts should carry a SOAP-ENV:mustUnderstand="1"
attribute. For example:
When both fields are set and soap.actor="http://some/actor"
then the message contains:
The gSOAP toolkit supports MIME attachments as per SOAP with Attachments (SwA) specification (http://www.w3.org/TR/SOAP-attachments). In the following discussion, MIME attachment data is assumed to be resident in memory for sending operations and MIME attachments received will be stored in memory. MTOM and DIME attachments on the other hand can be streamed and therefore MTOM/DIME attachment data does not need to be stored in memory, see Section DIME Attachments and MTOM Attachments .
Transmitting multipart/related MIME attachments with a SOAP/XML message is accomplished with two functions, soap_set_mime
and soap_set_mime_attachment
. The first function is for initialization purposes and the latter function is used to specify meta data and content data for each attachment.
The following functions should be used to set up a collection of multipart/related MIME attachments for transmission with a SOAP/XML message.
void soap_set_mime(struct soap *soap, const char *boundary, const char *start)
This function must be called first to initialize MIME attachment send operations (receives are automatic). The function specifies a MIME boundary and start content ID used for the SOAP message body. When boundary
is NULL, an appropriate MIME boundary will be choosen (important: boundaries cannot occur in the SOAP/XML message and cannot occur in any of the MIME attachments content). When a specific boundary value is provided, gSOAP will NOT verify that the boundary is valid. When start
is NULL, the start ID of the SOAP message is <*SOAP-ENV:Envelope
*>.int soap_set_mime_attachment(struct soap *soap, char *ptr, size_t size, enum soap_mime_encoding encoding, const char *type, const char *id, const char *location, const char *description)
This function adds a new attachment to the list of attachments, where ptr
and size
refer to the block of memory that holds the attachment data. The encoding
parameter specifies the content encoding of this block, where the value of encoding
is one of SOAP_MIME_7BIT
, SOAP_MIME_8BIT
, SOAP_MIME_BINARY
, SOAP_MIME_QUOTED_PRINTABLE
, SOAP_MIME_BASE64
, SOAP_MIME_IETF_TOKEN
, or SOAP_MIME_X_TOKEN
. These constants reflect the content encoding defined in RFC2045 and you MUST adhere to the content encoding rules defined by RFC2045. When in doubt, use SOAP_MIME_BINARY
, since this encoding type covers any content. The mandatory type
string parameter is the MIME type of the data. The id
string parameter is the content ID of the MIME attachment. The optional location
string parameter is the content location of the attachment. The optional description
string parameter holds a textual description of the attachment (it may not contain any control characters). All parameter values are copied, except ptr
which must point to a valid location of the attachment data during the transfer. The value SOAP_OK
is returned when the attachment was added. Otherwise a gSOAP error code is returned.void soap_clr_mime(struct soap *soap)
Disables MIME attachments, e.g. to avoid MIME attachments to be part of a SOAP Fault response message.When providing a MIME boundary with soap_set_mime
, you have to make sure the boundary cannot match any SOAP/XML message content. Or you can simply pass NULL and let gSOAP select a safe boundary for you.
The internal list of attachments is destroyed with soap_end
, you should call this function sometime after the message exchange was completed (the content of the block of memory referred to by the ptr
parameter is unaffected).
The following example shows how a multipart/related HTTP message with three MIME attachments is set up and transmitted to a server. The first attachment contains the SOAP message. The second and third attachments contain image data. In this example we let the SOAP message body refer to the attachments using *href
* attributes. The struct claim__form
data type includes a definition of a href
attribute for this purpose.
Note: the above example assumes that the boundary MIME_boundary
does not match any part of the SOAP/XML message.
The claim__form
struct is declared in the gSOAP header file as:
This data type defines the parameter data of the operation. The claim forms in the SOAP/XML message consist of *href
*s to the claim forms attached. The produced message is similar to the last example shown in the SOAP with Attachments specification (http://www.w3.org/TR/SOAP-attachments). Note that the use of *href
* or other attributes for referring to the MIME attachments is optional according to the SwA standard.
To associate MIME attachments with the request and response of a service operation in the generated WSDL, please see Section Generating MultipartRelated MIME Attachment Bindings in WSDL .
The server-side code to transmit MIME attachments back to a client is similar:
It is also possible to attach data to a SOAP fault message.
soap_set_mime
that is unique when using DIME in MIME.MIME attachments are automatically parsed and stored in memory. After receiving a set of MIME attachments, either at the client-side or the server-side, the list of MIME attachments can be traversed to extract meta data and the attachment content. The first attachment in the collection of MIME attachments always contains meta data about the SOAP message itself (because the SOAP message was processed the attachment does not contain any useful data).
To traverse the list of MIME attachments in C, you use a loop similar to:
C++ programmers can use an iterator instead, as in:
Note: keep in mind that the first attachment is associated with the SOAP message and you may want to ignore it if the message has the attachment data already embedded through its hrefs.
A call to soap_end
removes all of the received MIME data. To preserve an attachment in memory, use soap_unlink
on the ptr
field of the soap_multipart
struct. Recall that the soap_unlink
function is commonly used to prevent deallocation of deserialized data.
The gSOAP toolkit supports DIME attachments as per DIME specification, see http://msdn.microsoft.com/library/en-us/dnglobspec/html/draft-nielsen-dime-02.txt
Applications developed with gSOAP can transmit binary DIME attachments with or without streaming messages. Without streaming, all data is stored and retrieved in memory, which can be prohibitive when transmitting large files on small devices. The maximum DIME attachment size is limited to 8 MB by default as set with SOAP_MAXDIMESIZE
in stdsoap2.h
. This limit can be changed as needed by recompiling stdsoap2.c
and stdsoap2.cpp
with -DSOAP_MAXDIMESIZE=100000000
for example. In contrast, with DIME streaming, data handlers are used to pass the data to and from a resource, such as a file or device. With DIME output streaming, raw binary data is send from a data source in chunks on the fly without buffering the entire content to save memory. With DIME input streaming, raw binary data will be passed to data handlers (callbacks).
The following functions can be used to explicitly set up a collection of DIME attachments for transmission with a SOAP/XML message body. The attachments can be streamed, as described in Section Streaming DIME . Without streaming, each attachment must refer to a block of data in memory.
void soap_set_dime(struct soap *soap)
This function must be called first to initialize DIME attachment send operations (receives are automatic).int soap_set_dime_attachment(struct soap *soap, char *ptr, size_t size, const char *type, const char *id, unsigned short optype, const char *option)
This function adds a new attachment to the list of attachments, where ptr
and size
refer to the block of memory that holds the attachment data (except when DIME streaming callback handlers are used as described in Section Streaming DIME . The type
string parameter is the MIME type of the data. The id
string parameter is the content ID of the DIME attachment. The option
string parameter holds optional text (gSOAP supports DIME options, but it can send only one) and optype
is a user-defined option type (as per DIME option specification format). All parameter values are copied, except ptr
. The value SOAP_OK
is returned when the attachment was added. Otherwise a gSOAP error code is returned.void soap_clr_dime(struct soap *soap)
Disables DIME attachments, unless the serialized SOAP message contains attachments for transmission.These functions allow DIME attachments to be added without requiring message body references. This is also referred to as the open DIME attachment style. The closed attachment style requires all DIME attachments to be referenced from the SOAP message body with *href
* (or similar) references. For the closed style, gSOAP supports an automatic binary data serialization method, see Section Serializing Binary Data in DIME .
DIME attachments are automatically parsed and stored in memory (or passed to the streaming handlers, when applicable). After receiving a set of DIME attachments, either at the client-side or the server-side, the list of DIME attachments can be traversed to extract meta data and the attachment content.
To traverse the list of DIME attachments in C, you use a loop similar to:
C++ programmers can use an iterator instead, as in:
The options
field is available as well. The options
content is formatted according to the DIME specification: the first two bytes are reserved for the option type, the next two bytes store the size of the option data, followed by the (binary) option data.
A call to soap_end
removes all of the received DIME data. To preserve an attachment in memory, use soap_unlink
on the ptr
field of the soap_multipart
struct. Recall that the soap_unlink
function is commonly used to prevent deallocation of deserialized data.
Binary data stored in extended *xsd:base64Binary
* and *xsd:hexBinary
* types can be serialized and deserialized as DIME attachments. These attachments will be transmitted prior to the sequence of secondary DIME attachments defined by the user with soap_set_dime_attachment
as explained in the previous section. The serialization process is automated for SOAP encoded messages and DIME attachments will be send even when soap_set_dime
or soap_set_dime_attachment
are not used. For non-SOAP-encoded messages such as document/literal messages you must still call soap_set_dime
to enable sending messages with attachments.
The *xsd:base64Binary
* XSD type is defined in gSOAP as a struct or class by
To enable serialization of the data in DIME, we extend this type with three additional fields:
The three additional fields consist of an id
field for attachment referencing (typically a content id (CID) or UUID), a type
field to specify the MIME type of the binary data, and an options
field to piggy-back additional information with a DIME attachment. The order of the declaration of the fields is significant. In addition, no other fields or methods may be declared before any of these fields in the struct/class, but additional fields and methods may appear after the field declarations. An extended xsd__hexBinary
declaration is similar.
The id
and type
fields contain text. The set the DIME-specific options field, you can use the soap_dime_option
function:
returns a string with this encoding. For example
When either the id
or type
field values are non-NULL at run time, the data will be serialized as a DIME attachment. The SOAP/XML message refers to the attachments using *href
* attributes. This generally works will with SOAP RPC, because *href
* attributes are permitted. However, with document/literal style the referencing mechanism must be explicitly defined in the schema of the binary type. The gSOAP declaration of an extended binary type is
C++ programmers can use inheritance instead of textual extension required in C, as in
This defines an extension of *xsd:base64Binary
*, such that the data can be serialized as DIME attachments using *href
* attributes for referencing. When a different attribute name is in fact used, it must be explicitly defined:
The example above uses the *location
* attribute defined in the content reference schema, as defined in one of the vendor's specific WSDL extensions for DIME (http://www.gotdotnet.com/team/xml_wsspecs/dime/WSDL-Extension-for-DIME.htm).
When receiving DIME attachments, the DIME meta data and binary data content is stored in binary data types only when the XML parts of the message uses *href
* attributes to refer to these attachments. The gSOAP toolkit may support automatic (de)serialization with other user-defined (or WSDL-defined) attributes in future releases.
Messages may contain binary data that references external resources not provided as attachments. In that case, the __ptr
field is NULL and the id
field refers to the external data source.
The dime_id_format
attribute of the current gSOAP run-time context can be set to the default format of DIME id fields. The format string MUST contain a d
format specifier (or any other int
-based format specifier). The value of this specifier is a non-negative integer, with zero being the value of the DIME attachment id for the SOAP message. For example,
As a result, all attachments with a NULL id
field will use a gSOAP-generated id value based on the format string.
Streaming DIME is achieved with callback functions to fetch and store data during transmission. Three function callbacks for streaming DIME output and three callbacks for streaming DIME input are available.
void *(*soap.fdimereadopen)(struct soap *soap, void *handle, const char *id, const char *type, const char *options)
Called by the gSOAP run-time DIME attachment sender to start reading from a (binary) data source for outbound transmission. The content will be read from the application's data source in chunks using the fdimeread
callback and streamed into the SOAP/XML/DIME output stream. The handle
contains the value of the __ptr
field of an attachment struct/class, which could be a pointer to specific information such as a file descriptor or a pointer to a string to be passed to this callback. Both __ptr
and __size
fields should have been set by the application prior to the serialization of the content. The id
, type
, and options
arguments are the DIME id, type, and options, respectively. The callback should return handle
, or another pointer value which will be passed as a handle to fdimeread
and fdimereadclose
. The callback should return NULL and set soap->error
when an error occurred. The callback should return NULL (and not set soap->error
) when this particular DIME attachment is not to be streamed.size_t (*soap.fdimeread)(struct soap *soap, void *handle, char *buf, size_t len)
Called by the gSOAP run-time DIME attachment sender to read more data from a (binary) data source for streaming into the output stream. The handle
contains the value returned by the fdimereadopen
callback. The buf
argument is the buffer of length len
into which a chunk of data should be stored. The actual amount of data stored in the buffer may be less than len
and this amount should be returned by the application. A return value of 0 indicates an error (the callback may set soap->errnum
to errno). The __size
field of the attachment struct/class should have been set by the application prior to the serialization of the content. The value of __size
indicates the total size of the content to be transmitted. When the __size
is zero then DIME chunked transfers can be used under certain circumstances to stream content without prior determination of attachment size, see Section Streaming Chunked DIME below.void (*soap.fdimereadclose)(struct soap *soap, void *handle)
Called by the gSOAP run-time DIME attachment sender at the end of the streaming process to close the data source. The handle
contains the value returned by the fdimereadopen
callback. The fdimewriteclose
callback is called after successfully transmitting the data or when an error occurred.void *(*soap.fdimewriteopen)(struct soap *soap, const char *id, const char *type, const char *options)
Called by the gSOAP run-time DIME attachment receiver to start writing an inbound DIME attachment to an application's data store. The content is streamed into an application data store through multiple fdimewrite
calls from the gSOAP attachment receiver. The id
, type
, and options
arguments are the DIME id, type, and options respectively. The callback should return a handle which is passed to the fdimewrite
and fdimewriteclose
callbacks. The __ptr
field of the attachment struct/class is set to the value of this handle. The __size
field is set to the total size of the attachment after receiving the entire content. The size is unknown in advance because DIME attachments may be chunked.int (*soap.fdimewrite)(struct soap *soap, void *handle, const char *buf, size_t len)
Called by the gSOAP run-time DIME attachment receiver to write part of an inbound DIME attachment to an application's data store. The handle
contains the value returned by the fdimewriteopen
callback. The buf
argument contains the data of length len
. The callback should return a gSOAP error code (e.g. SOAP_OK
when no error occurred).void (*soap.fdimewriteclose)(struct soap *soap, void *handle)
Called by the gSOAP run-time DIME attachment receiver at the end of the streaming process to close the data store. The fdimewriteclose
callback is called after successfully receiving the data or when an error occurred. The handle
contains the value returned by the fdimewriteopen
callback.In addition, a void*user
field in the struct soap
data structure is available to pass user-defined data to the callbacks. This way, you can set soap.user
to point to application data that the callbacks need such as a file name for example.
The following example illustrates the client-side initialization of an image attachment struct to stream a file into a DIME attachment:
The following example illustrates the streaming of a DIME attachment into a file by a client:
Note that compression can be used with DIME to compress the entire message. However, compression requires buffering to determine the HTTP content length header, which cancels the benefits of streaming DIME. To avoid this, you should use chunked HTTP (with the output-mode SOAP_IO_CHUNK
flag) with compression and streaming DIME. At the server side, when you set SOAP_IO_CHUNK
before calling soap_serve
, gSOAP will automatically revert to buffering (SOAP_IO_STORE
flag is set). You can check this flag with (soap->omode & SOAP_IO) == SOAP_IO_CHUNK
to see if the client accepts chunking. More information about streaming chunked DIME can be found in Section Streaming Chunked DIME .
options
field is a DIME-specific data structure, consisting of a 4 byte header containing the option type info (hi byte, lo byte), option string length (hi byte, lo byte), followed by a non-'\0' terminated string. The gSOAP DIME handler recognizes one option at most.gSOAP automatically handles inbound chunked DIME attachments (streaming or non-streaming). To transmit outbound DIME attachments, the attachment sizes MUST be determined in advance to calculate HTTP message length required to stream DIME over HTTP. However, gSOAP also supports the transmission of outbound chunked DIME attachments without prior determination of DIME attachment sizes when certain conditions are met. These conditions require either non-HTTP transport (use the output-mode SOAP_ENC_PLAIN
flag), or chunked HTTP transport (use the output-mode SOAP_IO_CHUNK
flag). You can also use the SOAP_IO_STORE
flag (which is also used automatically with compression to determine the HTTP content length header) but that cancels the benefits of streaming DIME.
To stream chunked DIME, set the __size
field of an attachment to zero and enable HTTP chunking. The DIME fdimeread
callback then fetches data in chunks and it is important to fill the entire buffer unless the end of the data has been reached and the last chunk is to be send. That is, fdimeread
should return the value of the last len
parameter and fill the entire buffer buf
for all chunks except the last.
The wsdl2h
WSDL parser recognizes DIME attachments and produces an annotated header file. Both open and closed layouts are supported for transmitting DIME attachments. For closed formats, all DIME attachments must be referenced from the SOAP message, e.g. using hrefs with SOAP encoding and using the application-specific reference attribute included in the base64Binary
struct/class for doc/lit.
The gSOAP compiler soapcpp2
does not produce a WSDL with DIME extensions. DIME is an older binary format that has no WSDL protocol support, unlike MIME and MTOM.
MTOM (Message Transmission Optimization Mechanism) is a relatively new format for transmitting attachments with SOAP messages (see http://www.w3.org/TR/soap12-mtom). MTOM is a W3C working draft as of this writing. MTOM attachments are essentially MIME attachments with standardized mechanisms for cross referencing attachments from the SOAP body, which is absent in (plain) MIME attachments and optional with DIME attachments.
Unlike the name suggests, the speed by which attached data is transmitted is not increased compared to MIME, DIME, or even XML encoded base64 data (at least the performance differences in gSOAP will be small). The advantage of the format is the standardized attachment reference mechanism, which should improve interoperability.
The MTOM specification mandates SOAP 1.2 and the use of the XOP namespace. The XOP Include element *xop:Include
* is used to reference attachment(s) from the SOAP message body.
Because references from within the SOAP message body to attachments are mandatory with MTOM, the implementation of the serialization and deserialization of MTOM MIME attachments in gSOAP uses the extended binary type comparable to DIME support in gSOAP. This binary type is predefined in the import/xop.h
file:
The additional id
, type
, and option
fields enable MTOM attachments for the data pointed to by __ptr
of size __size
. The process for sending and receiving MTOM XOP attachments is fully automated. The id
field references the attachment (typically a content id CID or UUID). When set to NULL, gSOAP assigns a unique CID. The type
field specifies the required MIME type of the binary data, and the optional options
field can be used to piggy-back descriptive text with an attachment. The order of the declaration of the fields is significant.
You can explicitly import the xop.h
in your header file to use the MTOM attachments in your service, for example:
As you can see, there is really no difference between the specification of MTOM and DIME attachments in a gSOAP header file. Except that you MUST use SOAP 1.2 and the xop__Include
element.
When an instance of x__myDataType
is serialized and either or both the id
and type
fields are non-NULL, the data is transmitted as MTOM MIME attachment if the SOAP_ENC_MTOM
flag is set in the gSOAP's soap struct context:
Without this flag, the attachments will be transmitted in DIME format (Section DIME Attachments ). If your current clients and services are based on non-streaming DIME attachments using the SOAP body reference mechanism (thus, without using the soap_set_dime_attachment
function) or plain base64 binary XML data elements, it is very easy to adopt MTOM by renaming the binary types to xop__Include
and using the SOAP_ENC_MTOM
flag with the SOAP 1.2 namespace.
To generate multipartRelated bindings in the WSDL file, use the
directive (see also Section Customizing the WSDL and Namespace Mapping Table File Contents With gSOAP Directives . The directive can be repeated for each attachment you want to associate with a method's request and response messages.
For example:
The //gsoap x service method-mime-type
directive indicates that this operation accepts *text/xml
* MIME attachments. See the SOAP-with-Attachment specification for the MIME types to use (for example, **/*
* is a wildcard). If the operation has more than one attachment, just repeat this directive for each attachment you want to bind to the operation.
To bind attachments only to the request message of an operation, use //gsoap x service method-input-mime-type
. Similarly, to bind attachments only to the response message of an operation, use
The wsdl2h
WSDL parser recognizes MIME attachments and produces an annotated header file. However, the ordering of MIME parts in the multipartRelated elements is not reflected in the header file. Application developers should adhere the standards and ensure that multipart/related attachments are transmitted in compliance with the WSDL operation declarations.
A receiver must be informed to recognize MTOM attachments by setting the SOAP_ENC_MTOM
flag of the gSOAP context. Otherwise, the regular MIME attachment mechanism (SwA) will be used to store attachments.
When using wsdl2h
to build clients and/or services, you should use the typemap.dat
file included in the distribution package. The typemap.dat
file defines the XOP namespace and XML MIME namespaces as imported namespaces:
The wsdl2h
tool uses the typemap.dat
file (see also option -t) to convert WSDL into a gSOAP header file. In this case we don't want the wsdl2h
tool to read the XOP schema and translate it, since we have a pre-defined _xop__Include
element to handle XOP for MTOM. This _xop__Include
element is defined in xop.h
. Therefore, the bindings shown above will not translate the XOP and XML MIME schemas to code, but generates #import
statements instead:
The #import
statements are only added for those namespaces that are actually used by the service.
Let's take a look at an example. The wsdl2h
importer generates a header file with #import "xop.h"
from a WSDL that references XOP, for example:
Suppose the WSDL defines an operation:
After generating the stubs/proxies with the soapcpp2
compiler, we can invoke the stub at the client side with:
Note that the xop__Include.type
field must be set to transmit MTOM attachments, otherwise plain base64 XML will be used.
At the server side, we show an example of an operation handler that just copies the input data to output:
The server must use the SOAP_ENC_MTOM
flag to initialize the soap struct to receive and send MTOM attachments.
Streaming MTOM/MIME is achieved with callback functions to fetch and store data during transmission. Three function callbacks for streaming MTOM/MIME output and three callbacks for streaming MTOM/MIME input are available.
void *(*soap.fmimereadopen)(struct soap *soap, void *handle, const char *id, const char *type, const char *description)
Called by the gSOAP run-time MTOM/MIME attachment sender to start reading from a (binary) data source for outbound transmission. The content will be read from the application's data source in chunks using the fmimeread
callback and streamed into the SOAP/XML/MTOM/MIME output stream. The handle
contains the value of the __ptr
field of an attachment struct/class, which could be a pointer to specific information such as a file descriptor or a pointer to a string to be passed to this callback. Both __ptr
and __size
fields should have been set by the application prior to the serialization of the content. The id
, type
, and description
arguments are the MTOM/MIME id, type, and description, respectively. The callback should return handle
, or another pointer value which will be passed as a handle to fmimeread
and fmimereadclose
. The callback should return NULL and set soap->error
when an error occurred. The callback should return NULL (and not set soap->error
) when this particular MTOM/MIME attachment is not to be streamed.size_t (*soap.fmimeread)(struct soap *soap, void *handle, char *buf, size_t len)
Called by the gSOAP run-time MTOM/MIME attachment sender to read more data from a (binary) data source for streaming into the output stream. The handle
contains the value returned by the fmimereadopen
callback. The buf
argument is the buffer of length len
into which a chunk of data should be stored. The actual amount of data stored in the buffer may be less than len
and this amount should be returned by the application. A return value of 0 indicates an error (the callback may set soap->errnum
to errno). The __size
field of the attachment struct/class should have been set by the application prior to the serialization of the content. The value of __size
indicates the total size of the content to be transmitted. When the __size
is zero then MTOM/MIME chunked transfers can be used under certain circumstances to stream content without prior determination of attachment size, see Section Streaming Chunked MTOM/MIME below.void (*soap.fmimereadclose)(struct soap *soap, void *handle)
Called by the gSOAP run-time MTOM/MIME attachment sender at the end of the streaming process to close the data source. The handle
contains the value returned by the fmimereadopen
callback. The fmimewriteclose
callback is called after successfully transmitting the data or when an error occurred.void *(*soap.fmimewriteopen)(struct soap *soap, void *handle, const char *id, const char *type, const char *description, enum soap_mime_encoding encoding)
Called by the gSOAP run-time MTOM/MIME attachment receiver to start writing an inbound MTOM/MIME attachment to an application's data store. The content is streamed into an application data store through multiple fmimewrite
calls from the gSOAP attachment receiver. The handle
argument is normally NULL, unless soap_get_mime_attachment
is used that passes the handle to the callback, see Section Redirecting Inbound MTOM/MIME Streams Based on SOAP Body Content . The id
, type
, and description
arguments are the MTOM/MIME id, type, and description respectively. The encoding
enumeration value indicates the MIME content transfer encoding, which is one of SOAP_MIME_NONE
, SOAP_MIME_7BIT
, SOAP_MIME_8BIT
, SOAP_MIME_BINARY
, SOAP_MIME_QUOTED_PRINTABLE
, SOAP_MIME_BASE64
, SOAP_MIME_IETF_TOKEN
, SOAP_MIME_X_TOKEN
. Content decoding may have to be considered by the application based on this value. The callback should return a non-NULL handle which is passed to the fmimewrite
and fmimewriteclose
callbacks. The __ptr
field of the attachment struct/class is set to the value of this handle. The __size
field is set to the total size of the attachment after receiving the entire content. The size is unknown in advance because MTOM/MIME attachments may be chunked.int (*soap.fmimewrite)(struct soap *soap, void *handle, const char *buf, size_t len)
Called by the gSOAP run-time MTOM/MIME attachment receiver to write part of an inbound MTOM/MIME attachment to an application's data store. The handle
contains the value returned by the fmimewriteopen
callback. The buf
argument contains the data of length len
. The callback should return a gSOAP error code (e.g. SOAP_OK
when no error occurred).void (*soap.fmimewriteclose)(struct soap *soap, void *handle)
Called by the gSOAP run-time MTOM/MIME attachment receiver at the end of the streaming process to close the data store. The fmimewriteclose
callback is called after successfully receiving the data or when an error occurred. The handle
contains the value returned by the fmimewriteopen
callback.In addition, a void *user
field in the struct soap
data structure is available to pass user-defined data to the callbacks. This way, you can set soap.user
to point to application data that the callbacks need such as a file name for example.
The following example illustrates the client-side initialization of an image attachment struct to stream a file into a MTOM attachment without HTTP chunking (HTTP streaming chunked MTOM transfer is presented in Section Streaming Chunked MTOM/MIME ):
The following example illustrates the streaming of a MTOM/MIME attachment into a file by a client:
Note that compression can be used with MTOM/MIME to compress the entire message. However, compression requires buffering to determine the HTTP content length header, which cancels the benefits of streaming MTOM/MIME. To avoid this, you should use chunked HTTP (with the output-mode SOAP_IO_CHUNK
flag) with compression and streaming MTOM/MIME. At the server side, when you set SOAP_IO_CHUNK
before calling soap_serve
, gSOAP will automatically revert to buffering (SOAP_IO_STORE
flag is set). You can check this flag with (soap->omode & SOAP_IO) == SOAP_IO_CHUNK
to see if the client accepts chunking. More information about streaming chunked MTOM/MIME can be found in Section Streaming Chunked MTOM/MIME .
Note that the example above for mime_read
uses a handle that points to the open file FILE*
. The simple example above is not recommended when the platform imposes a limit on the number of open file descriptors. You can use the handle to pass along more information than just the file descriptor. So for example, when the number of open file descriptors is limited on your platform, you should let the handle point to a structure with file-related information. The C++ example below illustrates this:
To read the MTOM data for transmission:
When it is preferable or required to redirect inbound MTOM/MIME attachment streams based on SOAP message body content, where for example the names of the resources are listed in the SOAP message body, an alternative mechanism must be used. This mechanism can be used both at the client and server side.
Because the routing of the streams is accomplished with explicit function calls, this method should only be used when required and should not be considered optional. That is, when you enable this method, you MUST check for pending MTOM/MIME attachments and handle them appropriately. This is true even when you don't expect MTOM/MIME attachments in the payload, because the peer may trick you by sending attachments anyway and you should be prepared to accept or reject them.
The explicit MTOM/MIME streaming mechanism consists of three API functions:
void soap_post_check_mime_attachments(struct soap *soap)
Enables post-message body inbound streaming MTOM/MIME attachments. The presence of attachments must be explicitly checked using the function below.int soap_check_mime_attachments(struct soap *soap)
Should be called after a client-side call (e.g. soap_call_ns__method
) to check the presence of attachments. Returns 1 (true) when attachments are present. If present, each attachment MUST be processed with the function below.struct soap_multipart *soap_get_mime_attachment(struct soap *soap, void *handle)
Parses an attachment and invokes the MIME callbacks (when set). The handle
parameter is passed to fmimewriteopen
. The handle may contain any data that is extracted from the SOAP message body to guide the redirection of the stream in the callbacks. The return value is a struct with a char *ptr
field that contains the handle value returned by the fmimewriteopen
callback, and char *id
, char *type
, and char *description
fields with the optional MIME id, type, and description info.Example client-side code in C:
The server-side service operations are implemented as usual, but with additional checks for MTOM/MIME attachments:
gSOAP automatically handles inbound chunked MTOM/MIME attachments (streaming or non-streaming). To transmit outbound MTOM/MIME attachments, the attachment sizes MUST be determined in advance to calculate HTTP message length required to stream MTOM/MIME over HTTP. However, gSOAP also supports the transmission of outbound chunked MTOM/MIME attachments without prior determination of MTOM/MIME attachment sizes when certain conditions are met. These conditions require either non-HTTP transport (use the output-mode SOAP_ENC_PLAIN
flag), or chunked HTTP transport (use the output-mode SOAP_IO_CHUNK
flag). You can also use the SOAP_IO_STORE
flag (which is also used automatically with compression to determine the HTTP content length header) but that cancels the benefits of streaming MTOM/MIME.
To stream chunked MTOM/MIME, set the __size
field of an attachment to zero and enable HTTP chunking. The MTOM/MIME fmimeread
callback then fetches data in chunks of any size between 1 and the value of the len
argument. The fmimeread
callback should return 0 upon reaching the end of the data stream.
The gSOAP XML parser applies basic rules to validate content. Constraints are not automatically verified unless explicitly set using flags. This helps to avoid interoperability problems with toolkits that do not strictly enforce validation rules. In addition, we cannot always use strict validation for SOAP RPC encoded messages, since SOAP RPC encoding adopts a very loose serialization format.
Validation constraints are enabled with the SOAP_XML_STRICT
input mode flag set, e.g. with soap_set_imode(soap, SOAP_XML_STRICT)
or soap_new(SOAP_XML_STRICT)
, see Section Run Time Flags for the complete list of flags.
Default values can be defined for optional elements and attributes, which means that the default value will be used when the element or attribute value is not present in the parsed XML. See also Section How to Specify Default Values for Omitted Data and examples in subsequent subsections below.
Default values must be primitive types, integer, float, string, etc. Default values can be specified for struct and class members, as shown in the example below:
Upon deserialization of absent data, these members will be set accordingly. When classes are instantiated with soap_new_ClassName
the instance will be initialized with default values.
To force the validation of minOccurs and maxOccurs contraints the SOAP_XML_STRICT
input mode flag must be set. The minOccurs and maxOccurs constraints are specified for fields of a struct and members of a class in a header file using the following syntax:
The minOccurs and maxOccurs values must be integer literals. Also a default initialization value can be provided. When minOccurs is not specified it is assumed to be one (1) for non-pointer fieldname
members that are elements and zero (0) for fieldname
members that are pointers or are attributes (i.e. have a @
qualifier).
The nillable property can by specified by adding a nullptr before the minOccurs (version 2.8.24 or greater).
A fixed initialization value can be specified with ==
(version 2.8.48 or greater).
For example
Similar to the minOccurs and maxOccurs annotations defined in the previous section, attributes in a struct or class can be annotated with occurrence constraints to make them optional (0), required (1), or prohibited (0:0). Default values can be assigned to optional attributes.
For example
Remember to set the SOAP_XML_STRICT
input mode flag to enable the validation of attribute occurrence constraints.
A schema simpleType is defined with a typedef
by taking a base primitive to defined a derived simpleType. For example:
This defines the following schema type in time.xsd
:
A complexType with simpleContent is defined with a wrapper struct/class:
This defines the following schema type in time.xsd
:
Data value length constraints of simpleTypes and complexTypes with simpleContent are defined as follows.
Remember to set the SOAP_XML_STRICT
input mode flag to enable the validation of value length constraints.
Use compiler flag WITH_REPLACE_ILLEGAL_UTF8
to force strict UTF-8 text conversions, which replaces invalid UTF-8 with U+FFFD. Compile stdsoap2.c
and stdsoap2.cpp
with this flag.
Similar to data length constraints for string-based data, integer and floatig point value range constraints on numeric simpleTypes and complexTypes with simpleContent are declared with low : high
, where low
and high
are optional.
As of gSOAP 2.8.26, floating point value ranges and integer ranges can be exclusive by adding < on either side of the ':
' range operator:
Range | Validitation check |
---|---|
1 | 1 <= x |
1 : | 1 <= x |
: 10 | x <= 10 |
1 : 10 | 1 <= x <= 10 |
1 < : < 10 | 1 < x < 10 |
1 < 10 | 1 < x < 10 |
1 : < 10 | 1 <= x < 10 |
: < 10 | x < 10 |
< 10 | x < 10 |
1 < : | 1 < x |
1 < | 1 < x |
1 < : 10 | 1 < x <= 10 |
Example:
Remember to set the SOAP_XML_STRICT
input mode flag to enable the validation of value range constraints.
Use compiler flag WITH_REPLACE_ILLEGAL_UTF8
to force strict UTF-8 text conversions, which replaces invalid UTF-8 with U+FFFD. Compile stdsoap2.c
and stdsoap2.cpp
with this flag.
Patterns can be defined for simpleType content. However, patterns are currently not enforced in the validation process though possibly in future releases.
To associate a pattern with a simpleType, you can define a simpleType with a typedef
and a pattern string:
This defines the following schema type in time.xsd
:
The pattern string MUST contain a valid regular expression.
A special case for C format string patterns is introduced in gSOAP 2.8.18. When *xs:totalDigits
* and *xs:fractionDigits
* are given in a XSD file, then a C format string is produced to output floating point values with the proper precision and scale. For example:
produces:
The format string is used to format the output the floating point value in XML.
Struct, class, and union members represent elements and attributes that are automatically qualified or unqualified depending on the schema element and attribute default forms specified. The gSOAP engine always validates the prefixes of elements and attributes. When a namespace mismatch occurs, the element or attribute is not consumed which can lead to a validation error (unless the complexType is extensible or when SOAP_XML_STRICT
is turned off).
See Section C/C++ Identifier Name to XML Tag Name Mapping for details on the the struct/class/union member identifier translation rules. Consider for example:
Here, the ns__record
struct is serialized with qualified element name
and unqualified attribute type
:
The "colon notation" for struct/class/union member field names is used to override element and attribute qualified or unqualified forms. To override the form for individual members that represent elements and attributes, use a namespace prefix and colon with the member name:
where name
is unqualified and type
is qualified:
The colon notation is a syntactic notation used only in the gSOAP header file syntax, it is not translated to the C/C++ output.
The colon notation does not avoid name clashes between members. For example:
results in a redefinition error, since both members have the same name. To avoid name clashes, use a underscore suffix:
Not that the namespace prefix convention can be used instead:
which avoids the name clash. However, the resulting schema is different since the last example generates a global name
element definition that is referenced by the local element.
More specifically, the difference between the namespace prefix convention with double underscores and colon notation is that the namespace prefix convention generates schema element/attribute references to elements/attributes at the top level, while the colon notation only affects the local element/attribute namespace qualification by form overriding. This is best illustrated by an example:
which generates the following x.xsd
schema:
and the y.xsd
schema defines contains:
UDP is a simple, unreliable datagram protocol: UDP sockets are connectionless. UDP address formats are identical to those used by TCP. In particular UDP provides a port identifier in addition to the normal Internet address format. The UDP port space is separate from the TCP port space (i.e. a UDP port may not be "connected" to a TCP port). In addition broadcast packets may be sent (assuming the underlying network supports this) by using a reserved "broadcast address"; this address is network interface dependent.
Client-side messages with SOAP-over-UDP endpoint URLs (soap.udp://...
) will be automatically transmitted as datagrams. Server-side applications should set the SOAP_IO_UDP
mode flag to accept UDP requests, e.g. using soap_init1
or soap_set_mode
.
The maximum message length for datagram packets is restricted by the buffer size SOAP_BUFLEN
, which is 65536 by default, unless compiled with WITH_LEAN
to support small-scale embedded systems. For UDP transport SOAP_BUFLEN
must not exceed the maximum UDP packet size 65536 (the size of datagram messages is constrained by the UDP packet size 2^16=65536 as per UDP standard). You can use gzip compression to reduce the message size, but note that compressed SOAP-over-UDP is a gSOAP-specific feature because it is not part of the SOAP-over-UDP specification.
The SOAP-over-UDP specification relies on WS-Addressing. The wsa.h
file in the import
directory defines the WS-Addressing elements for client and server applications.
The gSOAP implementation conforms to the SOAP-over-UDP requirements:
The following additional features are also available, but are not supported by the SOAP-over-UDP specification:
-DWITH_GZIP
).-DWITH_IPV6
)A SOAP-over-UDP application MUST use WS-Addressing to control message delivery as per SOAP-over-UDP specification.
The wsa.h
file in the import
directory defines the WS-Addressing elements. To include the WS-Addressing elements in the SOAP Header for messaging, a struct SOAP_ENV__Header
structure must be defined in your header file with the appropriate WS-Addressing elements. For example:
We also included a //gsoap wsa schema import
directive in the wsa.h
file to enable the generation of WSDL specifications that import (instead of includes) the WS-Addressing elements. Note that the //gsoapopt w
directive must not be present in your header file to enable WSDL generation.
One-way SOAP-over-UDP messages (see Section Asynchronous One-Way Message Passing ) should be declared to include the wsa:MessageID
, wsa:To
, and wsa:Action
elements in the SOAP Header of the request message as follows:
Request-response SOAP-over-UDP messages should be declared to include the wsa:MessageID
, wsa:To
, wsa:Action
, and wsa:ReplyTo
elements in the SOAP Header of the request message, and the the wsa:MessageID
, wsa:To
, wsa:Action
, and wsa:RelatesTo
elements in the SOAP Header of the response message:
For the content requirements of these elements, please consult the SOAP-over-UDP specification and/or read the next sections explaining SOAP-over-UDP unicast, multicast, one-way, and request-response client and server applications.
This example assumes that the gSOAP header file includes the SOAP Header with WS-Addressing elements and the ns__sendString
function discussed in Section udp.h
This example is similar to the one-way unicast example discussed above, but uses a broadcast address and the SO_BROADCAST
socket option:
Please refer to the socket options for IPPROTO_IP IP_MULTICAST_IF
to specify the default interface for multicast datagrams to be sent from. This is a struct in_addr
(in_addr_t
for sin6_scope_id
) interface value. Otherwise, the default interface set by the system administrator will be used (if any).
Please refer to the socket options for IPPROTO_IP IP_MULTICAST_TTL
to limit the lifetime of the packet. Multicast datagrams are sent with a default value of 1, to prevent them to be forwarded beyond the local network. This parameter can be set between 1 to 255.
This example assumes that the gSOAP header file includes the SOAP Header with WS-Addressing elements and the ns__echoString
function discussed in Section udp.h
This example is similar to the request-response unicast example discussed above, but uses a broadcast address and the SO_BROADCAST
socket option. Because we expect to receive multiple responses, we also need to use separate request-response messages to send one request and consume multiple responses. In this example we defined a bcastString
request and a bcastStringResponse
response message, which are essentially declared as one-way messages in the header file:
To obtain response one-way operations, use the wsdl2h -b
option.
The client code includes a loop to receive response messages until a timeout occurs:
Note that a server for the bcastString
does not need to use two-one way messages. Thus, multicast request-response message pattern can be declared and implemented as request-response operations at the server side.
The following example code illustrates a SOAP-over-UDP server for one-way sendString
and request-response echoString
messages. This example assumes that the gSOAP header file includes the SOAP Header with WS-Addressing elements and the ns__echoString
function discussed in Section udp.h .
The server binds to a host and port and accepts messages in a tight sequential loop. Because UDP does not have the equivalent of an accept the messages cannot be dispatched to threads, the soap_serve
waits for a message and immediately accepts it. You can use a receive timeout to make soap_serve
non-blocking.
To obtain response one-way operations from a WSDL, use the wsdl2h -b
option. This produces additional one-way operations to support asynchronous handling of response messages in the same way requests are handled.
For UDP multicast support, follow the suggestions in Section SOAP-over-UDP Server and change the initialization parts of the code to enable UDP multicast port binding by to telliing the kernel which multicast groups you are interested in:
gSOAP uses regular strings by default. Regular strings cannot be used to hold UCS characters outside of the character range [1,255]. gSOAP can handle wide-character content in two ways. First, applications can utilize wide-character strings (wchar_t*
) instead of regular strings to store wide-character content. For example, the *xsd:string
* string schema type can be declared as a wide-character string and used subsequently:
Second, regular strings can be used to hold wide-character content in UTF-8 format. This is accomplished with the SOAP_C_UTFSTRING
flag (for both input/output mode), see Section Run Time Flags . With this flag set, gSOAP will deserialize XML into regular strings in UTF-8 format. An application is responsible for filling regular strings with UTF-8 content to ensure that strings can be correctly serialized XML. Third, the SOAP_C_MBSTRING
flag (for both input/output mode) can be used to activate multibyte character support. Multibyte support depends on the locale settings for dealing with extended natural language encodings.
Both regular strings and wide-character strings can be used together within an application. For example, the following header file declaration introduces two string schema types:
The input
string parameter is a wide-character string and the output
string parameter is a regular string. The regular string has UCS character content in the range [1,255] unless the SOAP_C_UTFSTRING
flag is set. With this flag, the string has UTF-8 encoded content.
Please consult the UTF-8 specification for details on the UTF-8 format. Note that the ASCII character set [1-127] is a subset of UTF-8. Therefore, with the SOAP_C_UTFSTRING
flag set, strings may hold ASCII character data and UTF-8 extensions.
A header file can be augmented with directives for the gSOAP soapcpp2
tool to automatically generate customized WSDL and namespace mapping tables contents. The WSDL and namespace mapping table files do not need to be modified by hand (Sections How to Generate WSDL Service Descriptions and Namespace Mapping Table ). In addition, the sample SOAP/XML request and response files generated by the compiler are valid provided that XML Schema namespace information is added to the header file with directives so that the gSOAP soapcpp2
compiler can produce example SOAP/XML messages that are correctly namespace qualified. These compiler directive are specified as //
-comments. (Note: blanks can be used anywhere in the directive, except between //
and gsoap
.)
Three directives are currently supported that can be used to specify details associated with namespace prefixes used by the service operation names in the header file. To specify the name of a Web Service in the header file, use:
where namespace-prefix is a namespace prefix used by identifiers in the header file and service-name is the name of a Web Service (only required to create new Web Services). The name may be followed by text up to the end of the line which is incorporated into the WSDL service documentation. Alternatively, the service documentation can be provided with the directive below.
To specify the name of the WSDL definitions in the header file, use:
where namespace-prefix is a namespace prefix used by identifiers in the header file and definitions-name is the name of the WSDL definitions. By default, the WSDL definitions name is the same as the service name.
To specify the documentation of a Web Service in the header file, use:
or shorthand:
where namespace-prefix is a namespace prefix used by identifiers in the header file and text is the documentation text up to the end of the line. The text is incorporated into the WSDL service documentation.
To specify the portType of a Web Service in the header file, use:
or just
or using WSDL 2.0 terms
where namespace-prefix is a namespace prefix used by identifiers in the header file and portType-name is the portType name of the WSDL service portType.
To specify the port name of a Web Service in the header file, use:
where namespace-prefix is a namespace prefix used by identifiers in the header file and port-name is the name of the WSDL service port element. By default, the port name is the same as the service name.
To specify the binding name of a Web Service in the header file, use:
where namespace-prefix is a namespace prefix used by identifiers in the header file and binding-name is the binding name of the WSDL service binding element. By default, the binding name is the same as the service name.
To specify the binding's transport protocol of a Web Service in the header file, use:
where namespace-prefix is a namespace prefix used by identifiers in the header file and transport-URL is the URL of the transport protocol such as http://schemas.xmlsoap.org/soap/http
for HTTP. HTTP transport is assumed by default.
To specify the location (or port endpoint) of a Web Service in the header file, use:
or alternatively
or
where URL is the location of the Web Service (only required to create new Web Services). The URL specifies the path to the service executable (so URL/service-executable is the actual location of the executable when declared).
To specify the name of the executable of a Web Service in the header file, use:
where executable-name is the name of the executable of the Web Service.
When doc/literal encoding is required for the entire service, the service encoding can be specified in the header file as follows:
or when the *SOAP-ENV:encodingStyle
* attribute is different from the SOAP 1.1/1.2 encoding style:
To specify the namespace URI of a Web Service in the header file, use:
where namespace-URI is the URI associated with the namespace prefix.
In addition, the schema namespace URI can be specified in the header file:
where namespace-URI is the schema URI associated with the namespace prefix. If present, it defines the schema-part of the generated WSDL file and the URI in the namespace mapping table. This declaration is useful when the service declares its own data types that need to be associated with a namespace. Furthermore, the header file for client applications do not need the full service details and the specification of the schema namespaces for namespace prefixes suffices. In addition, a second namespace can be defined that is only used to match the namespaces of inbound XML:
If the first namespace does not match the inbound parsed XML, then the second will be tried. This pattern may contain *
multichar wildcards and -
single chard wildcards. This allows two or more namespace versions to be handled by the same namespace prefix.
The directive above specifies a new schema and the gSOAP soapcpp2
compiler generates a schema files (.xsd) file for the schema. An existing schema namespace URI can be imported with:
where namespace-URI is the schema URI associated with the namespace prefix. gSOAP does not produce XML Schema files for imported schemas and imports the schema namespaces in the generated WSDL file.
A schema namespace URI can be imported from a location with:
The elementFormDefault and attributeFormDefault qualification of a schema can be defined with:
or:
A shortcut to define the default qualification of elements and attributes of a schema:
or:
To include *xsi:type
* attributes in the runtime XML element output for specific schemas, use:
Note that soapcpp2 -t
enables *xsi:type
* for all elements in the runtime XML output.
To document a data type, use:
or shorthand:
where type-name is the unqualified name of the data type and text is a line of text terminated by a newline. Do not use any XML reserved characters in text such as <
and >
. Use well-formed XML and XHTML markup instead. For example:
To document a data type's fields and members, use:
or shorthand
where type-name is the unqualified name of the data type, field is a field, member, or enum name, and text is a line of text terminated by a newline. Do not use any XML reserved characters in text such as <
and >
. Use well-formed XML and XHTML markup instead. For example:
The documentation form above can also be used to document SOAP/XML message parts in the generated WSDL. For the type-name use the function name. For the field names, you can use the function name and/or the function argument names.
To document a method, use:
or shorthand:
where method-name is the unqualified name of the method and text is a line of text terminated by a newline. Do not use any XML reserved characters in text such as < and >. Use well-formed XML and XHTML markup instead. For example:
To specify the SOAP Action for a SOAP method, use:
where method-name is the unqualified name of the method and action is a string without spaces and blanks (the string can be quoted when preferred). For example:
Or, alternatively for the input action (part of the request):
To specify the HTTP "location" of REST methods to a perform POST/GET/PUT action, use:
where method-name is the unqualified name of the method and action is a string without spaces and blanks (the string can be quoted when preferred). This directive requires that the protocol:
directive for this method is set to HTTP, POST, GET, or PUT.
A response action and fault action are defined by:
To override the SOAP or REST protocol of an operation (SOAP by default), use:
where protocol is one of
*protocol* | result |
---|---|
SOAP | default SOAP transport (supports 1.1 and 1.2) |
SOAP1.1 | SOAP 1.1 only |
SOAP1.2 | SOAP 1.2 only |
SOAP-GET | one-way SOAP with HTTP GET |
SOAP1.1-GET | one-way SOAP 1.1 with HTTP GET |
SOAP1.2-GET | one-way SOAP 1.2 with HTTP GET |
HTTP | REST HTTP (POST or one-way PUT) |
POST | REST HTTP POST |
GET | one-way REST HTTP GET |
PUT | one-way REST HTTP PUT |
DELETE | REST HTTP DELETE |
When document style is preferred for a particular service method, use:
When SOAP RPC encoding is required for a particular service method, use:
When literal encoding is required for a particular service method, use:
or when the *SOAP-ENV:encodingStyle
* attribute is different from the SOAP 1.1/1.2 encoding style, use:
When SOAP RPC encoding is required for a particular service method response when the request message is literal, use:
When literal encoding is required for a particular service method response when the request message is encoded, use:
or when the *SOAP-ENV:encodingStyle
* attribute is different from the SOAP 1.1/1.2 encoding style, use:
Note that the method-response-encoding
is set to the value of method-encoding
by default.
When header processing is required, each method declared in the WSDL should provide a binding to the parts of the header that may appear as part of a method request message. Such a binding is given by:
For example:
Suppose method ns__login
uses both header parts (at most), then this is declared as:
Suppose method ns__search
uses only the first header part, then this is declared as:
Note that the method name and header part names MUST be namespace qualified. The headers MUST be present in all operations that declared the header parts.
To specify the header parts for the method input (method request message), use:
Similarly, to specify the header parts for the method output (method response message), use:
The declarations only affect the WSDL. For example:
The headers MUST be present in all operations that declared the header parts.
When SOAP Faults include custom fault information, the SOAP Fault detail element contains one or more elements with this information. Assuming that the SOAP Fault detail structure contains one or more members that correspond to the optional detail elements, a method may be associated with one or more of these members by using:
The declarations only affect the WSDL. For example:
The fault members MUST be present in the SOAP_ENV__Detail
structure, for example, f__invalid
must be a pointer-based member of the SOAP_ENV__Detail
struct, see Section SOAP Fault Processing . Alternatively to adding members to the structure, use the __type
field that can be set to point to an object that is serialized as the SOAP Fault detail element.
To specify MIME attachments for the method input and output (method request and response messages), use:
You can repeat this directive for all multipartRelated MIME attachments you want to associate with the method.
To specify MIME attachments for the method input (method request message), use:
Similarly, to specify MIME attachments for the method output (method response message), use:
You can repeat these directives for all multipartRelated MIME attachments you want to associate with the method.
The use of directives is best illustrated with an example. The example uses a hypothetical stock quote service and exchange rate service, actual services such as these are available for free on the web.
The quotex.h
example is a new Web Service created by combining two existing Web Services: a Stock Quote service and a Currency Exchange service.
Namespace prefix ns3
is used for the new quotex
Web Service with namespace URI urn:quotex
, service name quotex
, and endpoint port http://www.mydomain.com/quotex.cgi
.
Since the new Web Service invokes the ns1__getQuote
and ns2__getRate
service operations, the service namespaces and other details such as style and encoding of these methods are given by directives. After invoking the gSOAP soapcpp2
tool on the quotex.h
header file:
> soapcpp2 quotex.h
the WSDL of the new quotex
Web Service is saved as quotex.wsdl
. Since the service name, endpoint port, and namespace URI were provided in the header file, the generated WSDL file can be published together with the compiled Web Service installed as a CGI application.
The namespace mapping table for the quotex.cpp
Web Service implementation is saved as quotex.nsmap
. This file can be directly included in quotex.cpp
instead of specified by hand in the source of quotex.cpp
:
The automatic generation and inclusion of the namespace mapping table requires compiler directives for all namespace prefixes to associate each namespace prefix with a namespace URI. Otherwise, namespace URIs have to be manually added to the table (they appear as http://tempuri.org
).
There are situations when certain data types have to be ignored by gSOAP for the compilation of (de)marshalling routines. For example, in certain cases only a few members of a class or struct need not be (de)serialized, or the base class of a derived class should not be (de)serialized. Certain built-in classes such as ostream
cannot be (de)serialized. Data parts that should be kept invisible to gSOAP are called "transient". Transient data types and transient struct/class members are declared with the \extern
keyword or are declared within [
and ]
blocks in the header file. The \extern
keyword has a special meaning to the gSOAP soapcpp2
compiler and won't affect the generated codes. The special [
and ]
block construct can be used with data type declarations and within struct
and class
declarations. The use of \extern
or [ ]
achieve the same effect, but [ ]
may be more convenient to encapsulate transient types in a larger part of the header file. The use of \extern
with typedef
is reserved for the declaration of user-defined external (de)serializers for data types, see Section How to Declare User-Defined Serializers and Deserializers .
First example:
Second example:
Third example:
In this example, class ns__myClass
has three transient fields: b
, s
, and n
which will not be (de)serialized in SOAP. Field n
is transient because the type is declared within a transient block. Pointers, references, and arrays of transient types are transient. The single class method is encapsulated within [
and ]
to prevent gSOAP from creating (de)serializers for the char[1024]
type. gSOAP will generate (de)serializers for all types that are not declared within a [
and ]
transient block.
Volatile-declared data types in gSOAP are assumed to be part of an existing non-modifiable software package, such as a built-in library. It would not make sense to redefine the data types in a gSOAP header file. In certain cases it could also be problematic to have classes augmented with serializer methods. When you need to (de)serialize such data types "as is", you must declare them in a gSOAP header file and use the volatile
qualifier.
Consider for example struct tm
, declared in time.h
. The structure may actually vary between platforms, but the tm structure includes at least the following fields:
Note that we qualified the structure volatile
in the gSOAP header file to inform the gSOAP soapcpp2
compiler that it should not attempt to redeclare it. We can now readily serialize and deserialize the tm structure. The following program fragment serializes the local time stored in a tm structure to stdout:
It is also possible to serialize the tm fields as XML attributes using the @
qualifier, see Section XML Attributes .
If you must produce a schema file, say time.xsd
, that defines an XML schema and namespace for the tm struct, you can add a typedef
declaration to the header file:
We used the typedef
name time__struct_tm
rather than time__tm
, because a schema name clash will occur with the latter since taking off the time
prefix will result in the same name being used.
Classes should be declared volatile to prevent modification of these classes by ithe gSOAP soapcpp2
source code output. Note that gSOAP adds serialization methods to classes to support polymorphism. However, this is a problem when you can't modify class declarations because they are part of a non-modifiable software package. The solution is to declare these classes volatile
, similar to the tm structure example illustrated above. You can also use a typedef
to associate a schema with a class.
Users can declare their own (de)serializers for specific data types instead of relying on the gSOAP-generated (de)serializers. To declare a external (de)serializer, declare a type with extern typedef
. gSOAP will not generate the (de)serializers for the type name that is declared. For example:
The user is required to supply the following routines for each extern typedef
'ed name T:
The function prototypes can be found in soapH.h
.
For example, the (de)serialization of MyData
can be done with the following code:
More information on custom (de)serialization is available in the package in the gsoap/custom directory where you can also find several examples of custom serializers. Note that (de)serializer code requires the use of the low-level gSOAP API that may differ in older gSOAP releases. If in doubt, we recommend to follow the material and examples in gsoap/custom.
gSOAP serializes data in XML with *xsi:type
* attributes when the types are declared with namespace prefixes to indicate the schema type of the data contained in the elements. SOAP 1.1 and 1.2 requires *xsi:type
* attributes in the presence of polymorphic data or when the type of the data cannot be deduced from the SOAP payload. The namespace prefixes are associated with the type names of typedef
s (Section How to Represent Primitive C/C++ Types as XSD Types ) for primitive data types, struct
/class
names, and enum
names.
To prevent the output of these *xsi:type
* attributes in the XML serialization, you can simply use type declarations that do not include these namespace prefixes. That is, don't use the typedef
s for primitive types and use unqualified type names with struct
s, class
es, and enum
s.
However, there are two issues. Firstly, if you want to use a primitive schema type that has no C/C++ counterpart, you must declare it as a typedef
name with a leading underscore, as in:
This will produce the necessary *xsd:date
* information in the WSDL output by the gSOAP soapcpp2
compiler. But the XML serialization of this type at run time won't include the *xsi:type
* attribute. Secondly, to include the proper schema definitions in the WSDL produced by the gSOAP soapcpp2
compiler, you should use qualified struct
, class
, and enum
names with a leading underscore, as in:
This ensures that myStruct
is associated with a schema, and therefore included in the appropriate schema in the generated WSDL. The leading underscore prevents the XML serialization of *xsi:type
* attributes for this type in the SOAP/XML payload.
gSOAP provides five callback functions for customized I/O and HTTP handling:
SOAP_SOCKET (*soap.fopen)(struct soap *soap, const char *endpoint, const char *host, int port)
Called from a client proxy to open a connection to a Web Service located at endpoint
. Input parameters host
and port
are micro-parsed from endpoint
. Should return a valid file descriptor, or SOAP_INVALID_SOCKET
and soap->error
set to an error code. Built-in gSOAP function: tcp_connect
int (*soap.fclose)(struct soap *soap)
Called by client proxy multiple times, to close a socket connection before a new socket connection is established and at the end of communications when the SOAP_IO_KEEPALIVE
flag is not set and soap.keep_alive != 0
(indicating that the other party supports keep alive). Should return SOAP_OK
, or a gSOAP error code. Built-in gSOAP function: tcp_disconnect
int (*soap.fget)(struct soap *soap)
Called by the main server loop upon an HTTP GET request. The SOAP_GET_METHOD
error is returned by default. This callback can be used to respond to HTTP GET methods with content, see Section HTTP GET Support . Should return SOAP_OK
, or a gSOAP error code. Built-in gSOAP function: http_get
int (*soap.fput)(struct soap *soap)
Called by the main server loop upon an HTTP PUT request. The SOAP_PUT_METHOD
error is returned by default. This callback can be used to respond to HTTP PUT. Should return SOAP_OK
, or a gSOAP error code. Built-in gSOAP function: http_put
int (*soap.fdel)(struct soap *soap)
Called by the main server loop upon an HTTP DELETE request. The SOAP_DELETE_METHOD
error is returned by default. This callback can be used to respond to HTTP DELETE methods. Should return SOAP_OK
, or a gSOAP error code. Built-in gSOAP function: http_del
int (*soap.fhead)(struct soap *soap)
Called by the main server loop upon an HTTP HEAD request. The SOAP_HEAD_METHOD
error is returned by default. This callback can be used to respond to HTTP HEAD methods. Should return SOAP_OK
, or a gSOAP error code. Built-in gSOAP function: http_get
int (*soap.fform)(struct soap *soap)
Called by the main server loop when a user-defined fparsehdr
callback returned SOAP_FORM
to signal that the HTTP body must be processed by this form handler callback. The HTTP POST form data MUST be read, otherwise keep-alive messages will end up out of sync. Should return SOAP_OK
or a gSOAP error code. Built-in gSOAP function: none.int (*soap.fpost)(struct soap *soap, const char *endpoint, const char *host, int port, const char *path, const char *action, size_t count)
Called from a client proxy to generate the HTTP header to connect to endpoint
. Input parameters host
, port
, and path
are micro-parsed from endpoint
, action
is the SOAP action, and count
is the length of the SOAP message or 0 when SOAP_ENC_PLAIN
is set or when SOAP_IO_LENGTH
is reset. Use function soap_send(struct soap *soap, char *s)
to write the header contents. Should return SOAP_OK
, or a gSOAP error code. Built-in gSOAP function: http_post
.int (*soap.fposthdr)(struct soap *soap, const char *key, const char *val)
Called by http_post
and http_response
(through the callbacks). Emits HTTP key
: val
header entries. Should return SOAP_OK
, or a gSOAP error code. Built-in gSOAP function: http_post_header
.int (*soap.fresponse)(struct soap *soap, int soap_error_code, size_t count)
Called from a service to generate the response HTTP header. Input parameter soap_error_code
is a gSOAP error code (see Section Error Codes and count
is the length of the SOAP message or 0 when SOAP_ENC_PLAIN
is set or when SOAP_IO_LENGTH
is reset. Use function soap_send(struct soap *soap, char *s)
to write the header contents. Should return SOAP_OK
, or a gSOAP error code Built-in gSOAP function: http_response
int (*soap.fparse)(struct soap *soap)
Called by client proxy and service to parse an HTTP header (if present). When user-defined, this routine must at least skip the header. Use function int soap_getline(struct soap *soap, char *buf, int len)
to read HTTP header lines into a buffer buf
of length len
(returns empty line at end of HTTP header). Should return SOAP_OK
, or a gSOAP error code. Built-in gSOAP function: http_parse
int (*soap.fparsehdr)(struct soap *soap, const char *key, const char *val)
Called by http_parse
(through the fparse
callback). Handles HTTP key
: val
header entries to set gSOAP's internals. Should return SOAP_OK
, SOAP_STOP
(see fstop
) or a gSOAP error code. Built-in gSOAP function: http_parse_header
int (*soap.fsend)(struct soap *soap, const char *s, size_t n)
Called for all send operations to emit contents of s
of length n
. Should return SOAP_OK
, or a gSOAP error code. Built-in gSOAP function: fsend
size_t (*soap.frecv)(struct soap *soap, char *s, size_t n)
Called for all receive operations to fill buffer s
of maximum length n
. Should return the number of bytes read or 0 in case of an error, e.g. EOF. Built-in gSOAP function: frecv
int (*soap.fignore)(struct soap *soap, const char *tag)
Called when an unknown XML element was encountered on the input. The tag
parameter is the offending XML element tag name. Should return SOAP_OK
, or a gSOAP error code such as SOAP_TAG_MISMATCH
to stop processing. Can be used to override *mustUnderstand="true"
* attributes on unrecognized SOAP Header elements that raise faults. Check soap->mustUnderstand != 0
in fignore
function for presence of mustUnderstand attribute. Built-in gSOAP function: none.int (*soap.fconnect)(struct soap *soap, const char *endpoint, const char *host, int port)
When non-NULL, this callback is called for all client-to-server connect operations instead of the built-in socket connect code. Therefore, it can be used to override the built-in connection establishment. Parameter endpoint
contains the server endpoint URL, host
the domain name or IP, and port
the port number. Should return SOAP_OK
, or a gSOAP error code. Built-in gSOAP function: noneSOAP_SOCKET (*soap.faccept)(struct soap *soap, SOAP_SOCKERT s, struct sockaddr *a, int *n)
Called by soap_accept
. This is a wrapper routine for accept
. Given master socket s
should return a valid socket descriptor or SOAP_INVALID_SOCKET
and set soap->error
to an error code. Built-in gSOAP function: tcp_accept
int (*soap.fresolve)(struct soap *soap, const char *addr, struct in_addr *inaddr)
Called by soap_bind
if a host name is given and soap_connect
to resolve a domain name addr
. Should set in_addr *a
and return SOAP_OK
or return SOAP_ERR
upon failure. Built-in gSOAP function: tcp_gethost
int (*soap.fpoll)(struct soap *soap)
Used by clients to check if the server is still responsive. Built-in gSOAP function: soap_poll
int (*soap.fserveloop)(struct soap *soap)
Called after successful invocation of a server operation in the server loop, immediately after sending the response to a client. Can be used to clean up resources (e.g. using soap_end()
) while serving a long sequence of keep-alive connections. Should return SOAP_OK
, or set soap->error
to a gSOAP error code and return soap->error
. Built-in gSOAP function: none.void (*soap.fmalloc)(struct soap *soap, size_t n)
Use to override memory allocation for deserialized C data. Memory allocated via this callback will not be automatically released by the gSOAP engine. The application must release this data by keeping track of the allocations. Note: it is not safe to traverse deserialized data structures and free each node, since data might be shared (SOAP multiref) and some allocated data such as the HTTP SOAPAction might no be part of the structure. Built-in gSOAP function: none.int (*soap.fheader)(struct soap *soap)
Called immediately after parsing a SOAP Header. The SOAP Header struct referenced by soap->header
can be inspected and verified. The function should return SOAP_OK
or a fault. Built-in gSOAP function: none.int (*soap.fsvalidate)(struct soap *soap, const char *pattern, const char *string)
Called to validate a non-NULL string against a non-NULL pattern. Patterns use XML schema regex syntax. Allows user-defined pattern validation. Should return SOAP_OK
when the string matches the pattern or SOAP_TYPE
when the string does not match. Built-in gSOAP function: none.int (*soap.fwvalidate)(struct soap *soap, const char *pattern, const wchar_t *string)
Called to validate a non-NULL wide string against a non-NULL pattern. Patterns use XML schema regex syntax. Allows user-defined pattern validation. Should return SOAP_OK
when the string matches the pattern or SOAP_TYPE
when the string does not match. Built-in gSOAP function: none.void (*soap.fseterror)(struct soap *soap, const char **code, const char **string)
Called to set the SOAP Fault code
and string
values based on the value of soap->error
. Allows user-defined messages to be associated with gSOAP error codes to override gSOAP's built-in error messages. Built-in gSOAP function: none.In addition, a void *user
field in the struct soap
data structure is available to pass user-defined data to the callbacks.
The following example uses I/O function callbacks for customized serialization of data into a fixed-size buffer and deserialization back into a datastructure:
A fixed-size buffer to store the outbound message sent is not flexible to handle large content. To store the message in an expanding buffer, use for example:
The soap_done
function can be called to reset the callback to the default internal gSOAP I/O and HTTP handlers.
The following example illustrates customized I/O and (HTTP) header handling. The SOAP request is saved to a file. The client proxy then reads the file contents as the service response. To perform this trick, the service response has exactly the same structure as the request. This is declared by the struct ns__test
output parameter part of the service operation declaration. This struct resembles the service request (see the generated soapStub.h
file created from the header file).
The header file is:
The client program is:
SOAP 1.1 and 1.2 specify that XML elements may be ignored when present in a SOAP payload on the receiving side. gSOAP ignores XML elements that are unknown, unless the XML attribute *mustUnderstand="true"
* is present in the XML element. It may be undesirable for elements to be ignored when the outcome of the omission is uncertain. The soap.fignore
callback can be set to a function that returns SOAP_OK
in case the element can be safely ignored, or SOAP_MUSTUNDERSTAND
to throw an exception, or to perform some application-specific action. For example, to throw an exception as soon as an unknown element is encountered on the input, use:
To selectively throw an exception when *mustUnderstand="true"
* SOAP Header element is encountered or when an unknown element is encountered except for element *ns:xyz
*, use:
Function soap_match_tag
compares two tags. The third parameter may be a pattern where *
is a wildcard and -
is a single character wildcard. So for example soap_match_tag(tag, "ns:*")
will match any element in namespace ns
or when no namespace prefix is present in the XML message.
The callback can also be used to keep track of unknown elements in an internal data structure such as a list:
gSOAP uses HTTP 1.1 by default. You can revert to HTTP 1.0 as follows:
This sets the HTTP version and reconfigures the engine to revert to HTTP 1.0. Note that you cannot use HTTP chunking with HTTP 1.0.
The client-side handling of HTTP 307 code "Temporary Redirect" and any of the redirect codes 301, 302, and 303 are not automated in gSOAP. Client application developers may want to consider adding a few lines of code to support redirects. It was decided not to automatically support redirects for the following reasons:
To implement client-side 307 redirect, add the following lines of code:
To implement your own HTTP (HTTPS) GET request responses, you need to set the soap.fget
callback. The callback is required to produce a response to the request in textual form, such as a Web page or a SOAP/XML response. This method does not work with CGI.
The following example produces a Web page upon a HTTP GET request (e.g. from a browser):
The example below produces a WSDL file upon a HTTP GET with path ?wsdl
:
Using one-way SOAP/XML message, you can also return a SOAP/XML response:
where ns1__mySendMethodResponse
is a one-way message declared in a gSOAP header file as:
The generated soapClient.cpp
includes the sending-side stub function.
gSOAP supports keep-alive socket connections. To activate keep-alive support, set the SOAP_IO_KEEPALIVE
flag for both input and output modes, see Section Run Time Flags . For example
When a client or a service communicates with another client or service that supports keep alive, the attribute soap.keep_alive
will be set to 1, otherwise it is reset to 0 (indicating that the other party will close the connection). The connection maybe terminated on either end before the communication completed, for example when the server keep-alive connection has timed out. This generates a "Broken Pipe" signal on Unix/Linux platforms. This signal can be caught with a signal handler:
where, for example:
Alternatively, broken pipes can be kept silent by setting:
This setting will not generate a sigpipe but read/write operations return SOAP_EOF
instead. Note that Win32 systems do not support signals and lack the MSG_NOSIGNAL
flag. The sigpipe handling and flags are not very portable.
A connection will be kept open only if the request contains an HTTP 1.0 header with "*`Connection: Keep-Alive`*" or an HTTP 1.1 header that does not contain "*`Connection: close`*". This means that a gSOAP client method call should use "*`http://`*" in the endpoint URL of the request to the stand-alone service to ensure HTTP headers are used.
If the client does not close the connection, the server will wait forever when no recv_timeout
is specified. In addition, other clients will be denied service as long as a client keeps the connection to the server open. To prevent this from happening, the service should be multi-threaded such that each thread handles the client connection:
To prevent a malicious client from keeping a thread waiting forever by keeping the connection open, timeouts are set in the process_request
routine as shown. See Section Timeout Management for Non-Blocking Operations for more details on timeout settings.
A gSOAP client call will automatically attempt to re-establish a connection to a server when the server has terminated the connection for any reason. This way, a sequence of calls can be made to the server while keeping the connection open. Client stubs will poll the server to check if the connection is still open. When the connection was terminated by the server, the client will automatically reconnect.
A client should reset SOAP_IO_KEEPALIVE
just before the last call to a server to close the connection after this last call. This will close the socket after the call and also informs the server to gracefully close the connection.
The client-side can also set the TCP keep-alive socket properties, using the soap.tcp_keep_alive
flag (set to 1 to enable), soap.tcp_keep_idle
to set the TCP_KEEPIDLE value, soap.tcp_keep_intvl
to set the TCP_KEEPINTVL value, and soap.tcp_keep_cnt
to set the TCP_KEEPCNT value.
If a client is in the middle of soap call that might take a long time and the server goes away/down the caller does not get any feedback until the soap.recv_timeout
is reached. Enabling TCP keep alive on systems that support it allows for a faster connection teardown detection for applications that need it.
gSOAP supports HTTP chunked transfer encoding. Un-chunking of inbound messages takes place automatically. Outbound messages are never chunked, except when the SOAP_IO_CHUNK
flag is set for the output mode. Most Web services, however, will not accept chunked inbound messages.
The entire outbound message can be stored to determine the HTTP content length rather than the two-phase encoding used by gSOAP which requires a separate pass over the data to determine the length of the outbound message. Setting the flag SOAP_IO_STORE
for the output mode will buffer the entire message. This can speed up the transmission of messages, depending on the content, but may require significant storage space to hold the verbose XML message.
Zlib compressed transfers require buffering. The SOAP_IO_STORE
flag is set when the SOAP_ENC_ZLIB
flag is set to send compressed messages. The use of chunking significantly reduces memory usage and may speed up the transmission of compressed SOAP/XML messages. This is accomplished by setting the SOAP_IO_CHUNK
flag with SOAP_ENC_ZLIB
for the output mode.
HTTP authentication (basic) is enabled at the client-side by setting the soap.userid
and soap.passwd
strings to a username and password, respectively. A server may request user authentication and denies access (HTTP 401 error) when the client tries to connect without HTTP authentication (or with the wrong authentication information).
Here is an example client code fragment to set the HTTP authentication username and password:
A client SOAP request will have the following HTTP header:
A client MUST set the soap.userid
and soap.passwd
strings for each call that requires client authentication. The strings are reset after each successful or unsuccessful call.
When present, the value of the WWW-Authenticate
HTTP header with the authentication realm can be obtained from the soap.authrealm
string. This is useful for clients to respond intelligently to authentication requests.
A stand-alone gSOAP Web Service can enforce HTTP authentication upon clients, by checking the soap.userid
and soap.passwd
strings. These strings are set when a client request contains HTTP authentication headers. The strings SHOULD be checked in each service method (that requires authentication to execute).
Here is an example service method implementation that enforced client authentication:
When the authentication fails, the service response with a SOAP Fault message and a HTTP error code "401 Unauthorized". The HTTP error codes are described in Section Error Codes .
HTTP NTLM authentication is enabled at the client-side by installing libntlm
from http://www.nongnu.org/libntlm and compiling all project source codes with -DWITH_NTLM
.
In your application code set the soap.userid
, soap.passwd
, and soap.authrealm
strings to a username, password, and the authentication domain respectively. A server may request NTLM authentication and denies access (HTTP 401 authentication required or HTTP 407 HTTP proxy authentication required) when the client tries to connect without HTTP authentication (or with the wrong authentication information).
Here is an example client code fragment to set the NTLM authentication username and password:
The following NTLM handshake between the client C and server S is performed:
where stages 1 and 2 indicates a client attempting to connect without authorization information, which is the first method call in the code above. Stage 3 to 6 happen with the proper client authentication set with soap.userid
, soap.passwd
, and soap.authrealm
provided. NTLM authenticates connections, not requests. When the connection is kept alive, subsequent messages can be exchanged without re-authentication.
To avoid the overhead of the first rejected call, use:
When the authentication fails (stage 1 and 2), the service response with a SOAP Fault message and a HTTP error code "401 Unauthorized". The HTTP error codes are described in Section Error Codes .
On windows, an alternative is to use the WinInet module, which has built-in NTLM support. The WinInet for gSOAP module is available in the mod_gsoap
directory of the gSOAP package. Instructions for WinInet use are included there.
For HTTP 407 Proxy Authentication Required, set the proxy userid and passwd:
To avoid the overhead of the first rejected call, use:
HTTP proxy authentication (basic) is enabled at the client-side by setting the soap.proxy_userid
and soap.proxy_passwd
strings to a username and password, respectively. For example, a proxy server may request user authentication. Otherwise, access is denied by the proxy (HTTP 407 error). Example client code fragment to set proxy server, username, and password:
A client SOAP request will have the following HTTP header:
When X-Forwarded-For headers are returned by the proxy, the header can be accessed in the soap.proxy_from
string.
The CONNECT method is used for HTTP proxy authentication:
In some cases, it may be necessary to use the Host HTTP header with the CONNECT protocol:
If so, compile the gSOAP code with -DWITH_CONNECT_HOST
to include the Host HTTP header with the CONNECT protocol.
Here are some tips you can use to speed up gSOAP. gSOAP's default settings are choosen to maximize portability and compatibility. The settings can be tweaked to optimize the performance as follows:
SOAP_BUFLEN
by changing the SOAP_BUFLEN
macro in stdsoap2.h
. Use buffer size 2^18=262144 for example.SOAP_XML_TREE
flag to disable id-ref multi-ref object (de)serialization. This boosts performance significantly and works with SOAP document/literal style (i.e. no id-ref graph serialization as required with SOAP encoding style).stdsoap2.c
and stdsoap2.cpp
and all other source codes with -DWITH_NOIDREF
to improve performance even better by permanently disabling id-ref multi-ref object (de)serialization.The XML parser is configured to restrict the XML nesting depth level to SOAP_MAXLEVEL
and restricts the repeated occurrence of elements that are deserialized into arrays and containers by SOAP_MAXOCCURS
. These macros can be changed, but you can also change the following context attributes at runtime, e.g. to enhance the safety for specific service and/or client operations:
soap.maxlevel
is an unsigned int
to restrict the XML nesting depth level, where the default value is SOAP_MAXLEVEL=10000
.soap.maxoccurs
is a size_t
to restrict the number of repeated occurrences of elements that are deserialized into arrays and structs, where the default value is SOAP_MAXOCCURS=100000
.soap.maxlength
is a positive long
length that restricts the length of strings deserialized from XML. A zero or negative value is unrestricted length. When restricted, the XML schema validation maxLength takes precedence over this length restriction. So setting a smaller value will not interfere with the XML validation rules. The default value is SOAP_MAXLENGTH=0
. Note that string length is expressed in number of characters, not bytes. So UTF-8 encodings are not truncated.XML schema validation constraints are enforced with the SOAP_XML_STRICT
context flag. The schema maxLength validation constraint overrules the soap.maxlength
guard. The schema maxOccurs validation constraint DOES NOT overrule the soap.maxoccurs
guard, so arrays and containers are always restricted in length by this guard.
Socket connect, accept, send, and receive timeout values can be set to manage socket communication timeouts. The soap.connect_timeout
, soap.accept_timeout
, soap.send_timeout
, soap.recv_timeout
and soap.transfer_timeout
context attributes of the current gSOAP runtime context soap
can be set to the appropriate user-defined socket send, receive, and accept timeout values. A positive value measures the timeout in seconds. A negative timeout value measures the timeout in microseconds (10^-6 sec).
The soap.connect_timeout
specifies the timeout for soap_call_ns__method
calls.
The soap.accept_timeout
specifies the timeout for soap_accept(&soap)
calls.
The soap.send_timeout
and soap.recv_timeout
specify the timeout for non-blocking socket I/O operations. This is the maximum delay on the socket operation permitted.
The soap.transfer_timeout
is new since 2.8.48 and limits the time a message send and a message receive operation can take. This value should be used in combination with soap.send_timeout
and soap.recv_timeout
for accurate timeout control.
Example:
This will result in a timeout if no data can be send in 10 seconds and no data is received within 10 seconds after initiating a send or receive operation over the socket. A value of zero disables timeout, for example:
When a timeout occurs in the send or receive operations, a SOAP_EOF
exception will be raised ("end of file or no input"). Negative timeout values measure timeouts in microseconds, for example:
The macros improve readability.
connect()
. Therefore, setting soap.connect_timeout
for non-blocking soap_call_ns__method
calls may not work under Linux.SOAP_MAXEINTR
in stdsoap2.h
, which is 10 by default. Each EINTR may increase the blocking time by up to one second, up to SOAP_MAXEINTR
seconds total.gSOAP's socket communications can be controlled with socket options and flags. The gSOAP run-time context struct soap
flags are: int soap.socket_flags
to control socket send() and recv() calls, int soap.connect_flags
to set client connection socket options, int soap.bind_flags
to set server-side port bind socket options, int soap.accept_flags
to set server-side request message accept socket options. See the manual pages of send
and recv
for soap.socket_flags
values and see the manual pages of setsockopt
for soap.connect_flags
, soap.bind_flags
, and soap.accept_flags
(SOL_SOCKET) values. These SO_
socket option flags (see setsockopt
manual pages) can be bit-wise or-ed to set multiple socket options at once. The client-side flag soap.connect_flags=SO_LINGER
is supported with values l_onoff=1
and l_linger=soap.linger_time
. The soap.linger_time
determines the wait time (the time resolution is system dependent, though according to some experts only zero and nonzero values matter). The linger option can be used to manage the number of connections that remain in TIME_WAIT state at the server side.
For example, to disable sigpipe signals on Unix/Linux platforms use: soap.socket_flags = MSG_NOSIGNAL
and/or soap.connect_flags = SO_NOSIGPIPE
(i.e. client-side connect) depending on your platform.
Use soap.bind_flags=SO_REUSEADDR
to enable server-side port reuse and local port sharing (but be aware of the possible security implications such as port hijacking).
Note that multiple socket options can be explicitly set with setsockopt
as follows:
To override the host and port of the client connecting to a server, set soap.override_host
and soap.override_port
:
When a Web Service is installed as CGI, it uses standard I/O that is encrypted/decrypted by the Web server that runs the CGI application. HTTPS/SSL support must be configured for the Web server (not CGI-based Web Service application itself).
To enable SSL for stand-alone gSOAP servers, first install OpenSSL and use option -DWITH_OPENSSL
to compile the sources with your C or C++ compiler (or use -DWITH_GNUTLS
if you prefer GNUTLS), for example:
> c++ -DWITH_OPENSSL -o myprog myprog.cpp stdsoap2.cpp soapC.cpp soapServer.cpp -lssl -lcrypto
With GNUTLS:
> c++ -DWITH_GNUTLS -o myprog myprog.cpp stdsoap2.cpp soapC.cpp soapServer.cpp -lgnutls -lgcrypt -lgpg-error
SSL support for stand-alone gSOAP Web services is enabled by calling soap_ssl_accept
to perform the SSL/TLS handshake after soap_accept
. In addition, a key file, a CA file (or path to certificates), DH file (if RSA is not used), and password need to be supplied. Instructions on how to do this can be found in the OpenSSL documentation http://www.openssl.org
. See also Section SSL Certificates and Key Files .
Let's take a look at an example SSL secure multi-threaded stand-alone SOAP Web Service:
The soap_ssl_server_context
function initializes the server-side SSL context. The server.pem
key file is the server's private key concatenated with its certificate. The cacert.pem
is used to authenticate clients and contains the client certificates. Alternatively a directory name can be specified. This directory is assumed to contain the certificates. The dh512.pem
file specifies that DH will be used for key agreement instead of RSA. A numeric value greater than 512 can be provided instead as a string constant (e.g. "512"
) to allow the engine to generate the DH parameters on the fly (this can take a while) rather than retrieving them from a file. The randfile entry can be used to seed the PRNG. The last entry enable server-side session caching. A unique server name is required.
The GNUTLS mutex lock setup is automatically peformed in the gSOAP engine, but only when POSIX threads are detected and available.
OpenSSL requires mutex locks to be explicitly setup in your code for multithreaded applications, for which we need to call CRYPTO_thread_setup()
and CRYPTO_thread_cleanup()
. These routines can be found in openssl/crypto/threads/th-lock.c
and are also used in the SSL example codes samples/ssl
. These routines are required to setup locks for multi-threaded applications that use SSL.
We give a Windows and POSIX threads implementation of these here:
For Unix and Linux, make sure you have signal handlers set in your service and/or client applications to catch broken connections (SIGPIPE
):
where, for example:
By default, clients are not required to authenticate. To support client authentication use the following:
This requires each client to authenticate with its certificate.
Since release version 2.8.20 SSL v3 is disabled. To enable SSL v3 together with TLS v1.0, v1.1, and v1.2 use SOAP_SSLv3_TLSv1
in soap_ssl_server_context
. To use TLS v1.1 only use SOAP_TLSv1_1
. To use TLS v1.2 only use SOAP_TLSv1_2
. To use SSL v3 only use SOAP_SSLv3
.
The cacert
file and capath
are optional. Either one can be specified when clients must run on non-trusted systems (capath
is not used with GNUTLS). We want to avoid storing trusted certificates in the default location on the file system when that is not secure. Therefore, a flat cacert.pem
file or directory can be specified to store trusted certificates.
The gSOAP distribution includes a cacerts.pem
file with the certificates of all certificate authorities such as Verisign. You can use this file to verify the authentication of servers that provide certificates issued by these CAs.
The cacert.pem
, client.pem
, and server.pem
files in the gSOAP distribution are examples of self-signed certificates. The client.pem
and server.pem
contain the client/server private key concatenated with the certificate. The keyfiles (client.pem
and server.pem
) are created by concatenating the private key PEM with the certificate PEM. The keyfile SHOULD NEVER be shared with any party. With OpenSSL, you can encrypt the keyfiles with a password to offer some protection and the password is used in the client/server code to read the keyfile. GNUTLS does not support this feature and cannot encrypt or decrypt a keyfile.
WITH_OPENSSL
macro MUST be consistently defined to compile the sources, such as stdsoap2.cpp
, soapC.cpp
, soapClient.cpp
, soapServer.cpp
, and all application sources that include stdsoap2.h
or soapH.h
. If the macros are not consistently used, the application will crash due to a mismatches in the declaration and access of the gSOAP context.To utilize HTTPS/SSL, you need to install the OpenSSL library on your platform or GNUTLS for a light-weight SSL/TLS library. After installation, compile all the sources of your application with option -DWITH_OPENSSL
(or -DWITH_GNUTLS
when using GNUTLS). For example on Linux:
> c++ -DWITH_OPENSSL myclient.cpp stdsoap.cpp soapC.cpp soapClient.cpp -lssl -lcrypto
or Unix:
> c++ -DWITH_OPENSSL myclient.cpp stdsoap.cpp soapC.cpp soapClient.cpp -lxnet -lsocket -lnsl -lssl -lcrypto
or you can add the following line to soapdefs.h
:
and compile with option -DWITH_SOAPDEFS_H
to include soapdefs.h
in your project. Alternatively, compile with GNUTLS:
> c++ -DWITH_GNUTLS myclient.cpp stdsoap.cpp soapC.cpp soapClient.cpp -lgnutls -lgcrypt -lgpg-error
A client program simply uses the prefix https:
instead of http:
in the endpoint URL of a service operation call to a Web Service to use encrypted transfers (if the service supports HTTPS). You need to specify the client-side key file and password of the keyfile:
By default, server authentication is enabled and the cacerts.pem
or capath
(not used with GNUTLS) must be set so that the CA certificates of the server(s) are accessible at run time. The cacerts.pem
file included in the package contains the certificates of common CAs. This file must be supplied with the client, if server authentication is required. Althernatively, you can use the plugin/cacerts.h
and plugin/cacerts.c
code to embed CA certificates in your client code.
Other client-side SSL options are SOAP_SSL_SKIP_HOST_CHECK
to skip the host name verification check and SOAP_SSL_ALLOW_EXPIRED_CERTIFICATE
to allow connecting to a host with an expired certificate. For example,
For systems based on Microsoft windows, the WinInet module can be used instead, see mod_gsoap/gsoap_win/wininet
.
Since release version 2.8.20 SSL v3 is disabled. To enable SSL v3 together with TLS v1.0, v1.1, and v1.2 use SOAP_SSLv3_TLSv1
in soap_ssl_server_context
. To use TLS v1.1 only use SOAP_TLSv1_1
. To use TLS v1.2 only use SOAP_TLSv1_2
. To use SSL v3 only use SOAP_SSLv3
.
To disable server authentication for testing purposes, use the following:
This also assumes that the server does not require clients to authenticate (the keyfile is absent).
Make sure you have signal handlers set in your application to catch broken connections (SIGPIPE
):
where, for example:
WITH_OPENSSL
macro MUST be consistently defined to compile the sources, such as stdsoap2.cpp
, soapC.cpp
, soapClient.cpp
, soapServer.cpp
, and all application sources that include stdsoap2.h
or soapH.h
. If the macros are not consistently used, the application will crash due to a mismatches in the declaration and access of the gSOAP context. soap_copy
from an originating struct initialized with soap_ssl_client_context
. In addition, the thread initialization code discussed in Section Secure Web Services with HTTPS/SSL~ MUST be used to properly setup OpenSSL in a multi-threaded client application.The fsslauth
callback function controls OpenSSL/GNUTLS authentication initialization:
int (*soap.fsslauth)(struct soap *soap)
Initialize the authentication information for clients and services, such as the certificate chain, password, read the key and/or DH file, generate an RSA key, and initialization of the RNG. Should return a gSOAP error code or SOAP_OK
. Built-in gSOAP function: ssl_auth_init
The fsslverify
callback function controls OpenSSL peer certificate verification, via internally invoking SSL_CTX_set_verify
:
int (*soap.fssverify)(int ok, X509_STORE_CTX *store
Used to control the certificate verification behaviour when the SOAP_SSL_REQUIRE_CLIENT_AUTHENTICATION
or SOAP_SSL_REQUIRE_SERVER_AUTHENTICATION
flags are specified with soap_ssl_client_context
and soap_ssl_server_context
. It receives two arguments: ok
indicates, whether the verification of the certificate in question was passed (ok=1
) or not (]blk{ok=0}). If the callback returns 0, the verification process is immediately stopped with "verification failed" state. A verification failure alert is sent to the peer and the TLS/SSL handshake is terminated. If the callback returns 1, the verification process is continued. Built-in gSOAP function: ssl_verify_callback
and ssl_verify_callback_allow_expired_certificate
. These functions are used when fsslverify
is initially set to NULL
and were not reassigned before soap_ssl_client_context
or soap_ssl_server_context
are called.The gSOAP distribution includes a cacerts.pem
file with the certificates of all certificate authorities (such as Verisign). You can use this file to verify the authentication of servers that provide certificates issued by these CAs. Just set the cafile
parameter to the location of this file on your file system. Therefore, when you obtain a certifice signed by a trusted CA such as Verisign, you can simply use the cacerts.pem
file to develop client applications that can verify the authenticity of your server.
Althernatively, you can use the plugin/cacerts.h
and plugin/cacerts.c
code to embed CA certificates in your client code.
For systems based on Microsoft windows, the WinInet module can be used instead, see the README.txt
located in the package under mod_gsoap/gsoap_win/wininet
.
The other .pem
files in the gSOAP distribution are examples of self-signed certificates for testing purposes (cacert.pem
, client.pem
, server.pem
). The client.pem
and server.pem
contain the private key and certificate of the client or server, respectively. The keyfiles (client.pem
and server.pem
) are created by concatenating the private key PEM with the certificate PEM. The keyfile SHOULD NEVER be shared with any party. With OpenSSL, you can encrypt the keyfiles with a password to offer some protection and the password is used in the client/server code to read the keyfile. GNUTLS does not support this feature and cannot encrypt or decrypt a keyfile.
You can also create your own self-signed certificates. There is more than one way to generate the necessary files for clients and servers. See http://www.openssl.org
for information on OpenSSL and http://sial.org/howto/openssl/ca/
on how to setup and manage a local CA and http://sial.org/howto/openssl/self-signed/
on how to setup self-signed test certificates.
It is possible to convert IIS-generated certificates to PEM format with the openssl library and openssl command-line tool:
This converts the CRT-formatted mycert.cer to PEM-formatted mycert.pem.
Here is the simplest way to setup self-signed certificates. First you need to create a private Certificate Authority (CA). The CA is used in SSL to verify the authenticity of a given certificate. The CA acts as a trusted third party who has authenticated the user of the signed certificate as being who they say. The certificate is signed by the CA, and if the client trusts the CA, it will trust your certificate. For use within your organization, a private CA will probably serve your needs. However, if you intend use your certificates for a public service, you should probably obtain a certificate from a known CA (e.g. VeriSign). In addition to identification, your certificate is also used for encryption.
Creating certificates should be done through a CA to obtain signed certificates. But you can create your own certificates for testing purposes as follows.
/usr/local/ssl
by default and /System/Library/OpenSSL
on Mac OS X)You now have a self-signed CA root certificate cacert.pem and a server.pem (or client.pem) certificate in PEM format. The cacert.pem certificate is used in the cafile
parameter of the soap_ssl_client_context
(or soap_ssl_server_context
) at the client (or server) side to verify the authenticity of the peer. You can also provide a capath parameter to these trusted certificates. The server.pem (or client.pem) must be provided with the soap_ssl_server_context
at the server side (or soap_ssl_client_context
at the client side) together with the password you entered when generating the certificate using cert.sh to access the file. These certificates must be present to grant authentication requests by peers. In addition, the server.pem (and client.pem) include the host name of the machine on which the application runs (e.g. localhost), so you need to generate new certificates when migrating a server (or client).
Finally, you need to generate Diffie-Helmann (DH) parameters for the server if you wish to use DH instead of RSA. There are two options:
dhfile
parameter to the numeric DH prime length in bits required (for example "1024") to let the engine generate DH parameters at initialization. This can be time consuming.dhfile
parameter of soap_ssl_server_context
. The file should be generated beforehand. To do so with the OpenSSL command line tool, use: > openssl dhparam -outform PEM -out dh.pem 512
File dh512.pem
is the output file and 512 is the number of bits used.
You can specify a hardware engine to enable hardware support for cryptographic acceleration. This can be done once in a server or client with the following statements:
The following table lists the names of the hardware and software engines:
engine | result |
---|---|
openssl | The default software engine for cryptographic operations |
openbsd_dev_crypto | OpenBSD supports kernel level cryptography |
cswift | CryptoSwift acceleration hardware |
chil | nCipher CHIL acceleration hardware |
atalla | Compaq Atalla acceleration hardware |
nuron | Nuron acceleration hardware |
ubsec | Broadcom uBSec acceleration hardware |
aep | Aep acceleration hardware |
sureware | SureWare acceleration hardware |
Set the full path to libssl.lib and libcrypto.lib under the MSVC++ "Projects" menu, then choose "Link": "Object/Modules". Please make sure libssl32.dll
and libeay32.dll
can be loaded by gSOAP applications, thus they must be installed properly on the target machine.
If you're using compilation settings such as /MTd
then link to the correct libeay32MTd.lib
and ssleay32MTd.lib
libraries.
Alternatively, you can use the WinInet interface available in the mod_gsoap
directory of the gSOAP package. API instructions are included in the source.
To enable deflate and gzip compression with Zlib, install Zlib from http://www.zlib.org if not already installed on your system. Compile stdsoap2.cpp
(or stdsoap2.c
) and all your sources that include stdsoap2.h
or soapH.h
with compiler option -DWITH_GZIP
and link your code with the Zlib library, e.g. -lz
on Unix/Linux platforms.
The gzip compression is orthogonal to all transport encodings such as HTTP, SSL, DIME, and can be used with other transport layers. You can even save and load compressed XML data to/from files.
gSOAP supports two compression formats: deflate and gzip. The gzip format is used by default. The gzip format has several benefits over deflate. Firstly, gSOAP can automatically detect gzip compressed inbound messages, even without HTTP headers, by checking for the presence of a gzip header in the message content. Secondly, gzip includes a CRC32 checksum to ensure messages have been correctly received. Thirdly, gzip compressed content can be decompressed with other compression software, so you can decompress XML data saved by gSOAP in gzip format.
Gzip compression is enabled by compiling the sources with -DWITH_GZIP
. To transmit gzip compressed SOAP/XML data, set the output mode flags to SOAP_ENC_ZLIB
. For example:
This will send a compressed SOAP/XML request to a service, provided that Zlib is installed and linked with the application and the -DWITH_GZIP
option was used to compile the sources. Receiving compressed SOAP/XML over HTTP either in gzip or deflate formats is automatic. The SOAP_ENC_ZLIB
flag does not have to be set at the server side to accept compressed messages. Reading and receiving gzip compressed SOAP/XML without HTTP headers (e.g. with other transport protocols) is also automatic.
To control the level of compression for outbound messages, you can set the soap.z_level
to a value between 1 and 9, where 1 is the best speed and 9 is the best compression (default is 6). For example
To verify and monitor compression rates, you can use the values soap.z_ratio_in
and soap.z_ratio_out
. These two float values lie between 0.0 and 1.0 and express the ratio of the compressed message length over uncompressed message length.
Note: lower ratios mean higher compression rates.
Compressed transfers require buffering the entire output message to determine HTTP message length. This means that the SOAP_IO_STORE
flag is automatically set when the SOAP_ENC_ZLIB
flag is set to send compressed messages. The use of HTTP chunking significantly reduces memory usage and may speed up the transmission of compressed SOAP/XML messages. This is accomplished by setting the SOAP_IO_CHUNK
flag with SOAP_ENC_ZLIB
for the output mode. However, some Web servers do not accept HTTP chunked request messages (even when they return HTTP chunked messages!). Stand-alone gSOAP services always accept chunked request messages.
To restrict the compression to the deflate format only, compile the sources with -DWITH_ZLIB
. This limits compression and decompression to the deflate format. Only plain and deflated messages can be exchanged, gzip is not supported with this option. Receiving gzip compressed content is automatic, even in the absence of HTTP headers. Receiving deflate compressed content is not automatic in the absence of HTTP headers and requires the flag SOAP_ENC_ZLIB
to be set for the input mode to decompress deflated data.
WITH_GZIP
and WITH_ZLIB
macros MUST be consistently defined to compile the sources, such as stdsoap2.cpp
, soapC.cpp
, soapClient.cpp
, soapServer.cpp
, and all application sources that include stdsoap2.h
or soapH.h
. If the macros are not consistently used, the application will crash due to a mismatches in the declaration and access of the gSOAP context.Client-side cookie support is optional. To enable cookie support, compile all sources with option -DWITH_COOKIES
, for example:
> c++ -DWITH_COOKIES -o myclient stdsoap2.cpp soapC.cpp soapClient.cpp
or add the following line to stdsoap.h
:
Client-side cookie support is fully automatic. So just (re)compile stdsoap2.cpp
with -DWITH_COOKIES
to enable cookie-based session control in your client.
A database of cookies is kept and returned to the appropriate servers. Cookies are not automatically saved to a file by a client. An example cookie file manager is included as an extras in the distribution. You should explicitly remove all cookies before terminating a gSOAP context by calling soap_free_cookies(soap)
or by calling soap_done(soap)
.
To avoid "cookie storms" caused by malicious servers that return an unreasonable amount of cookies, gSOAP clients/servers are restricted to a database size that the user can limit (32 cookies by default), for example:
The cookie database is a linked list pointed to by soap.cookies
where each node is declared as:
Since the cookie database is linked to a soap
struct, each thread has a local cookie database in a multi-threaded implementation.
Server-side cookie support is optional. To enable cookie support, compile all sources with option -DWITH_COOKIES
, for example:
> c++ -DWITH_COOKIES -o myserver ...
gSOAP provides the following cookie API for server-side cookie session control:
struct soap_cookie *soap_set_cookie(struct soap *soap, const char *name, const char *value, const char *domain, const char *path);
Add a cookie to the database with name name
and value value
. domain
and path
may be NULL to use the current domain and path given by soap_cookie_domain
and soap_cookie_path
. If successful, returns pointer to a cookie node in the linked list, or NULL otherwise.struct soap_cookie *soap_cookie(struct soap *soap, const char *name, const char *domain, const char *path);
Find a cookie in the database with name name
and value value
. domain
and path
may be NULL to use the current domain and path given by soap_cookie_domain
and soap_cookie_path
. If successful, returns pointer to a cookie node in the linked list, or NULL otherwise.char *soap_cookie_value(struct soap *soap, const char *name, const char *domain, const char *path);
Get value of a cookie in the database with name name
. domain
and path
may be NULL to use the current domain and path given by soap_cookie_domain
and soap_cookie_path
. If successful, returns the string pointer to the value, or NULL otherwise.long soap_cookie_expire(struct soap *soap, const char *name, const char *domain, const char *path);
Get expiration value of the cookie in the database with name name
(in seconds). domain
and path
may be NULL to use the current domain and path given by soap_cookie_domain
and soap_cookie_path
. Returns the expiration value, or -1 if cookie does not exist.int soap_set_cookie_expire(struct soap *soap, const char *name, long expire, const char *domain, const char *path);
Set expiration value expire
of the cookie in the database with name name
(in seconds). domain
and path
may be NULL to use the current domain and path given by soap_cookie_domain
and soap_cookie_path
. If successful, returns SOAP_OK
, or SOAP_EOF
otherwise.int soap_set_cookie_session(struct soap *soap, const char *name, const char *domain, const char *path);
Set cookie in the database with name name
to be a session cookie. This means that the cookie will be returned to the client. (Only cookies that are modified are returned to the client). domain
and path
may be NULL to use the current domain and path given by soap_cookie_domain
and soap_cookie_path
. If successful, returns SOAP_OK
, or SOAP_EOF
otherwise.int soap_clr_cookie_session(struct soap *soap, const char *name, const char *domain, const char *path);
Clear cookie in the database with name name
to be a session cookie. domain
and path
may be NULL to use the current domain and path given by soap_cookie_domain
and soap_cookie_path
. If successful, returns SOAP_OK
, or SOAP_EOF
otherwise.void soap_clr_cookie(struct soap *soap, const char *name, const char *domain, const char *path);
Remove cookie from the database with name name
. domain
and path
may be NULL to use the current domain and path given by soap_cookie_domain
and soap_cookie_path
.int soap_getenv_cookies(struct soap *soap);
Initializes cookie database by reading the HTTP_COOKIE
environment variable. This provides a means for a CGI application to read cookies send by a client. If successful, returns SOAP_OK
, or SOAP_EOF
otherwise.void soap_free_cookies(struct soap *soap);
Release cookie database.The following global variables are used to define the current domain and path:
const char *cookie_domain
MUST be set to the domain (host) of the serviceconst char *cookie_path
MAY be set to the default path to the serviceint cookie_max
maximum cookie database size (default=32)The cookie_path
value is used to filter cookies intended for this service according to the path prefix rules outlined in RFC2109.
The following example server adopts cookies for session control:
When a client needs to connect to a Web Service through a proxy server, set the soap.proxy_host
string and soap.proxy_port
integer attributes of the current soap
runtime context to the proxy's host name and port, respectively. For example:
The context attributes soap.proxy_host
and soap.proxy_port
keep their values through a sequence of service operation calls, so they only need to be set once.
When X-Forwarded-For headers are returned by the proxy, the header can be accessed in the soap.proxy_from
string.
To bind the client to a port before connect, set the soap.client_port
to a non-negative port number:
This port number is used only once and reset to -1 (disabled). Set it again for the next call.
To set a client interface address for the connection that is an IP address of the client:
This client interface address string is used only once and reset to NULL (disabled). Set it again for the next call. This feature is not available when compiling the code on windows.
To enable FastCGI support, install FastCGI and compile all sources (do not use libgsoap
but compile stdsoap2.c
) and your application sources with option -DWITH_FASTCGI
or add
to stdsoap2.h
and recompile the project code.
libgsoap
libraries as these are not suitable for FastCGI. Compile stdsoap2.c
(or stdsoap2.cpp
) instead.To compile gSOAP applications intended for small memory devices, you may want to remove all non-essential features that consume precious code and data space. To do this, compile the gSOAP sources with -DWITH_LEAN
(i.e. #define WITH_LEAN
) to remove many non-essential features. The features that will be disabled are:
WITH_GZIP
)soap.socket_flag, soap.connect_flag, soap.bind_flag, soap.accept_flag
)time_t
serializationhexBinary
XML attribute serialization (remap hexBinary
to strings by adding a remap entry to typemap.dat)Use -DWITH_LEANER
to make the executable even smaller by removing DIME and MIME attachment handling, LONG64
(64 bit) serialization, wchar_t*
serialization, and support for XML DOM operations. Note that DIME/MIME attachments are not essential to achieve SOAP/XML interoperability. DIME attachments are a convenient way to exchange non-text-based (i.e. binary) content, but are not required for basic SOAP/XML interoperability. Attachment requirements are predictable. That is, applications won't suddenly decide to use DIME or MIME instead of XML to exchange content.
It is safe to try to compile your application with -DWITH_LEAN
, provided that your application does not rely on I/O timeouts. When no linkage error occurs in the compilation process, it is safe to assume that your application will run just fine.
The stdsoap2.c
and stdsoap2.cpp
gSOAP runtime libraries should be linked with a BSD socket library in the project build, e.g. winsock2 for Win32. To eliminate the need to link a socket library, you can compile stdsoap2.c
(for C) and stdsoap2.cpp
(for C++) with the -DWITH_NOIO
macro set (i.e. #define WITH_NOIO
). This eliminates the dependency on the BSD socket API, IO streams, FILE
type, and errno
.
As a consequence, you MUST define callbacks to replace the missing socket stack. To do so, add to your code the following definitions:
These functions are supposed to provide a (minimal) transport stack. See Section Function Callbacks for Customized I/O and HTTP Handling for more details on the use of these callbacks. All callback function pointers should be non-NULL, except fpoll
.
You cannot use soap_print_fault
and soap_print_fault_location
to print error diagnostics. Instead, the value of soap.error
, which contains the gSOAP error code, can be used to determine the cause of a fault.
The wsdl2h
tool can be used to import multiple WSDLs and schemas at once. The service definitions are combined in one header file to be parsed by soapcpp2
. It is important to assign namespace prefixes to namespace URIs using the typemap.dat
file. Otherwise, wsdl2h
will assign namespace prefixes ns1
, ns2
, and so on to the service operations and schema types. Thus, any change to a WSDL or schema may result in a new prefix assignment. For more details, please see Section Customizing Data Bindings With The typemap.dat File .
Another approach to combine multiple client and service applications into one executable is by using C++ namespaces to structurally separate the definitions or by creating C libraries for the client/server objects as explained in subsequent sections. This is automated with wsdl2h
option -q
.
Both approaches are demonstrated by example in the gSOAP distribution, the samples/link
(C only) and samples/link++
(C++ with C++ namespaces) examples.
You can use a C++ code namespace of your choice in your header file to build a client or server in that code namespace. In this way, you can create multiple clients and servers that can be combined and linked together without conflicts, which is explained in more detail in the next section (which also shows an example combining two client libraries defined in two C++ code namespaces).
Use wsdl2h
option -q
*name* to generate definitions in the C++ name namespace. This option can also be used in combination with C++ proxy and server object generation, using soapcpp2
options -i
(or -j
) and -p
.
At most one namespace can be defined for the entire gSOAP header file. The code namespace MUST completely encapsulate the entire contents of the header file:
When compiling this header file with the gSOAP soapcpp2
compiler, all type definitions, the (de)serializers for these types, and the stub/skeleton codes will be placed in this namespace. The XML namespace mapping table (saved in a .nsmap
file) will not be placed in the code namespace to allow it to be linked as a global object. You can use option -n
to create local XML namespace tables, see Section soapcpp2 Options (but remember that you explicitly need to initialize the soap.namespaces
to point to a table at run time). The generated files are prefixed with the code namespace name instead of the usual soap
file name prefix to enable multiple client/server codes to be build in the same project directory (a code namespace automatically sets the -p
compiler option, see Section soapcpp2 Options for options).
Because the SOAP Header and Fault serialization codes will also be placed in the namespace, they cannot be called from the stdsoap2.cpp
run time library code and are therefore rendered unusable. Therefore, these serializers are not compiled at all (enforced with #define WITH_NOGLOBAL
). To add SOAP Header and Fault serializers, you MUST compile them separately as follows. First, create a new header file env.h
with the SOAP Header and Fault definitions. You can leave this header file empty if you want to use the default SOAP Header and Fault. Then compile this header file with:
> soapcpp2 -penv env.h
The generated envC.cpp
file holds the SOAP Header and Fault serializers and you can link this file with your client or server application.
The gSOAP soapcpp2
compiler produces soapClientLib.cpp
and soapServerLib.cpp
codes that are specifically intended for building static or dynamic client/server libraries. These codes export the stubs and skeletons, but keep all marshaling code (i.e. parameter serializers and deserializers) local (i.e. as static functions) to avoid link symbol conflicts when combining multiple clients and/or servers into one executable. Note that it is far simpler to use the wsdl2h
tool on multiple WSDL files to generate a header file that combines all service definitions. However, the approach presented in this section is useful when creating (dynamic) libraries for client and server objects, such as DLLs as described in Section How to Create DLLs .
Do not link soapClientLib.cpp
or soapServerLib.cpp
together with soapC.cpp
, soapClient.cpp
, and soapServer.cpp
. The library versions already include all of the necessary definitions.
To build multiple libraries in the same project directory, you can define a C++ code namespace in your header file (see Section How to Build a Client or Server in a C++ Code Namespace ) or you can use soapcpp2
with option -p
to rename the generated soapClientLib.cpp
and soapServerLib.cpp
(and associated) files. The -p
option specifies the file name prefix to replace the soap
prefix. The libraries don't have to be C++ codes. You can use option -c
to generate C code. A clean separation of libraries can also be achieved with C++ code namespaces, see Section How to Build a Client or Server in a C++ Code Namespace .
The library codes do not define SOAP Header and Fault serializers. You MUST add SOAP Header and Fault serializers to your application, which are compiled separately as follows. First, create a new header file env.h
with the SOAP Header and Fault definitions. You can leave this header file empty if you want to use the default SOAP Header and Fault. Then compile this header file with:
> soapcpp2 -penv env.h
The generated envC.cpp
file holds the SOAP Header and Fault serializers and you can create a (dynamic) library for it to link this code with your client or server application.
You MUST compile the stdsoap2.cpp
library using -DWITH_NONAMESPACES
:
> c++ -DWITH_NONAMESPACES -c stdsoap2.cpp
This omits the reference to the global namespaces table, which is nowhere to be defined since we will use XML namespaces for each client/service separately. Therefore, you MUST explicitly set the namespaces value of the gSOAP context in your code every time after initialization of the soap struct with the soap_set_namespaces(struct soap*, const struct Namespace*)
function.
For example, suppose we have two clients defined in header files client1.h
and client2.h
. We first generate the envH.h
file for the SOAP Header and Fault definitions:
> soapcpp2 -c -penv env.h
Then we generate the code for client1 and client2:
> soapcpp2 -c -n -pmyClient1 client1.h > soapcpp2 -c -n -pmyClient2 client2.h
This generates myClient1ClientLib.c
and myClient2ClientLib.c
(among many other files). These two files should be compiled and linked with your application. The source code of your application should include the generated envH.h
, myClient1H.h
, myClient2.h
files and myClient1.nsmap
, myClient2.nsmap
files:
It is important to use soapcpp2
option -n
, see Section soapcpp2 Options , to rename the namespace tables so we can include them all without running into redefinitions.
Note: Link conflicts may still occur in the unlikely situation that identical service operation names are defined in two or more client stubs or server skeletons when these methods share the same XML namespace prefix. You may have to use C++ code namespaces to avoid these link conflicts or rename the namespace prefixes used by the service operation defined in the header files.
As an example we will build a Delayed Stock Quote client library and a Currency Exchange Rate client library.
First, we create an empty header file env.h
(which may contain optional SOAP Header and Fault definitions), and compile it as follows:
> soapcpp2 -penv env.h > c++ -c envC.cpp
We also compile stdsoap2.cpp
without namespaces:
> c++ -c -DWITH_NONAMESPACES stdsoap2.cpp
Note: when you forget to use -DWITH_NONAMESPACES
you will get an unresolved link error for the global namespaces
table. You can define a dummy table to avoid having to recompile stdsoap2.cpp
.
Second, we create the Delayed Stock Quote header file specification, which may be obtained using the WSDL importer. If you want to use C++ namespaces then you need to manually add the namespace
declaration to the generated header file:
We then compile it as a library and we use option -n
to rename the namespace table to avoid link conflicts later:
> soapcpp2 -n quote.h > c++ -c quoteClientLib.cpp
If you don't want to use a C++ code namespace, you should compile quote.h
"as is" with soapcpp2 option -pquote
:
> soapcpp2 -n -pquote quote.h > c++ -c quoteClientLib.cpp
Third, we create the Currency Exchange Rate header file specification:
Similar to the Quote example above, we compile it as a library and we use option -n
to rename the namespace table to avoid link conflicts:
> soapcpp2 -n rate.h
Fourth, we consider linking the libraries to the main program. The main program can import the quoteServiceProxy.h
and rateServiceProxy.h
files to obtain client proxies to invoke the services. The proxy implementations are defined in quoteClient.cpp
. The -n
option also affects the generation of the C++ proxy codes to ensure that the gSOAP context is properly initialized with the appropriate namespace table (so you don't have to initialize explicitly – this feature is only available with C++ proxy and server object classes).
Compile and link this application with stdsoap2.o
, envC.o
, quoteServerProxy.o
, and rateServerProxy.o
.
To compile and link a server object is very similar. For example, assume that we need to implement a calculator service and we want to create a library for it.
We compile this with:
> soapcpp2 -n calc.h
The effect of the -n
option is that it creates local namespace tables, and a modified calcServiceObject.h
server class definitions that properly initialize the gSOAP run time with the table.
In fact, the calc::Service
class is derived from the struct soap
. So the context is available as this
, which can be passed to all gSOAP functions that require a soap struct context.
The example above serves requests over stdin/out. Use the bind and accept calls to create a stand-alone server to service inbound requests over sockets, see How to Create a Stand-Alone Server .
This is the same example as above, but the clients are build with pure C.
We create a env.h
that contains the joint SOAP Header and SOAP Fault definitions. You may have to copy-paste these from the other header files. Then, compile it as follows:
> soapcpp2 -c -penv env.h > cc -c envC.c
We also compile stdsoap2.c
without namespaces:
> cc -c -DWITH_NONAMESPACES stdsoap2.c
Second, we create the Delayed Stock Quote header file specification, which may be obtained using the WSDL importer.
We compile it as a library and we use options -n
and -p
to rename the namespace table to avoid link conflicts:
> soapcpp2 -c -n -pquote quote.h > cc -c quoteClientLib.c
Third, we create the Currency Exchange Rate header file specification:
We compile it as a library and we use options -n
and -p
to rename the namespace table to avoid link conflicts:
> soapcpp2 -c -n -prate rate.h > cc -c rateClientLib.c
The main program is:
Compile and link this application with stdsoap2.o
, envC.o
, quoteClientLib.o
, and rateClientLib.o
.
To compile and link a server library is very similar. Assuming that the server is named "`calc`" (as specified with options -n
and -p
), the application needs to include the calcStub.h
file, link the calcServerLib.o
file, and call calc_serve(&soap)
function at run time.
We build a C application for multiple services served on one port.
We create a env.h
that contains the joint SOAP Header and SOAP Fault definitions. You may have to copy-paste these from the other header files. Then, compile it as follows:
> soapcpp2 -c -penv env.h > cc -c envC.c
We also compile stdsoap2.c
without namespaces:
> cc -c -DWITH_NONAMESPACES stdsoap2.c
Say we have a service definition in quote.h
. We compile it as a library and we use options -n
and -p
to rename the namespace table to avoid link conflicts:
> soapcpp2 -c -n -pquote quote.h > cc -c quoteClientLib.c
We do the same for a service definition in rate.h
:
> soapcpp2 -c -n -prate rate.h > cc -c rateClientLib.c
To serve both the quote and rate services on the same port, we chain the service dispatchers as follows:
This chaining can be arbitrarily deep. When the previous request fails with a SOAP_NO_METHOD
then next request dispatcher can be tried.
The server should also define the service operations:
First, create a new header file env.h
with the SOAP Header and Fault definitions. You can leave this header file empty if you want to use the default SOAP Header and Fault. Then compile this header file with:
> soapcpp2 -penv env.h
The generated envC.cpp
file holds the SOAP Header and Fault serializers, which need to be part of the base library functions. The SOAP Header and Fault structures are generated by wsdl2h and are also located in the .h
files for plugins such as wsse.h
. You should copy these Header and Fault structures into env.h
to ensure processing by these plugins succeeds.
The next step is to create stdsoap2.dll
which consists of the file stdsoap2.cpp
and envC.cpp
and optionally the plugins you want to use such as wsseapi.c
(rename to .cpp to avoid warnings). This DLL contains all common functions needed for all other clients and servers based on gSOAP. Compile envC.cpp
and stdsoap2.cpp
into stdsoap2.dll
using the compiler option -DWITH_NONAMESPACES
and the MSVC Pre-Processor definitions SOAP_FMAC1=__declspec(dllexport)
, SOAP_FMAC3=__declspec(dllexport)
, and the SOAP_STD_EXPORTS
macro set as shown below from the MSVC command prompt:
C: cl /c /I. /EHsc /DWITH_NONAMESPACES /DSOAP_FMAC1=__declspec(dllexport) /DSOAP_FMAC3=__declspec(dllexport) /DSOAP_STD_EXPORTS envC.cpp stdsoap2.cpp C: link /LIBPATH ws2_32.lib /OUT:mygsoap.dll /DLL envC.obj stdsoap2.obj
Note: as of gSOAP 2.8.30 and later, the DLL export macros shown here are all set with one pre-processor definition SOAP_STD_EXPORTS
.
Alternatively, you can compile with -DWITH_SOAPDEFS_H
and put the macro definitions in soapdefs.h
. This exports all functions which are preceded by the macro SOAP_FMAC1
in the soapcpp2.cpp
source file and macro SOAP_FMAC3
in the envC.cpp
source file.
Finally, note that the gSOAP distribution package contains a number of .c files. Mixing .c with .cpp files is not recommended with Visual Studio and will lead to runtime errors when building DLLs. Therefore, always rename .c files to .cpp files when creating DLLs.
Compile the soapClientLib.cpp
and soapServerLib.cpp
sources as DLLs by using the MSVC Pre-Processor definitions SOAP_FMAC5=__declspec(dllexport)
and SOAP_CMAC=__declspec(dllexport)
, and by using the C++ compiler option -DWITH_NONAMESPACES
. All of these macros are set as a shorthand with one pre-processor definition SOAP_STD_EXPORTS
(requires gSOAP 2.8.30 or later).
This DLL links to stdsoap2.dll
.
To create multiple DLLs in the same project directory, you SHOULD use option -p
to rename the generated soapClientLib.cpp
and soapServerLib.cpp
(and associated) files. The -p
option specifies the file name prefix to replace the soap
prefix. A clean separation of libraries can also be achieved with C++ namespaces, see Section How to Build a Client or Server in a C++ Code Namespace .
Unless you use the client proxy and server object classes (soapXProxy.h
and soapXObject.h
where X
is the name of the service), all client and server applications MUST explicitly set the namespaces value of the gSOAP context:
where the namespaces[]
table should be defined in the client/server source. These tables are generated in the .nsmap
files. You can rename the tables using option -n
, see Section soapcpp2 Options .
The gSOAP plug-in feature enables a convenient extension mechanism of gSOAP capabilities. When the plug-in registers with gSOAP, it has full access to the run-time settings and the gSOAP function callbacks. Upon registry, the plug-in's local data is associated with the gSOAP run-time. By overriding gSOAP's function callbacks with the plug-in's function callbacks, the plug-in can extend gSOAP's capabilities. The local plug-in data can be accessed through a lookup function, usually invoked within a callback function to access the plug-in data. The registry and lookup functions are:
Other functions that deal with plug-ins are:
The soap_copy
function returns a new dynamically allocated gSOAP context that is a copy of another, such that no data is shared between the copy and the original context. The soap_copy
function invokes the plug-in copy callbacks to copy the plug-ins' local data. The soap_copy
function returns a gSOAP error code or SOAP_OK
. The soap_done
function de-registers all plugin-ins, so this function should be called to cleanly terminate a gSOAP run-time context.
An example will be used to illustrate these functions. This example overrides the send and receive callbacks to copy all messages that are sent and received to the terminal (stderr).
First, we write a header file plugin.h
to define the local plug-in data structure(s) and we define a global name to identify the plug-in:
Then, we write the plugin registry function and the callbacks:
The fdelete
callback of struct soap_plugin
MUST be set to register the plugin. It is the responsibility of the plug-in to handle registry (init), copy, and deletion of the plug-in data and callbacks.
A plugin is copied with the soap_copy()
call. This function copies a soap struct and the chain of plugins. It is up to the plugin implementation to share the plugin data or not:
fcopy()
callback is set by the plugin initialization, this callback will be called to allow the plugin to copy its local data upon a soap_copy()
call. When soap_done()
is called on the soap struct copy, the fdelete()
callback is called for deallocation and cleanup of the local data.fcopy()
callback is not set, then the plugin data will be shared (i.e. the data pointer points to the same address). The fdelete()
callback will not be called upon a soap_done()
on a copy of the soap struct. The fdelete()
callback will be called for the original soap struct with which the plugin was registered.The example plug-in should be used as follows:
Note: soap_register_plugin(...)
is an alias for soap_register_plugin_arg(..., NULL)
. That is, it passes NULL as an argument to plug-in's registry callback.
A number of example plug-ins are included in the gSOAP package's plugin
directory. Some of these plug-ins are discussed.
The message logging and access statistics plug-in can be used to selectively log inbound and outbound messages to a file or stream. It also keeps access statistics to log the total number of bytes sent and received.
To use the plug-in, compile and link your application with logging.c
located in the plugin
directory of the package. To enable the plug-in in your code, register the plug-in and set the streams as follows:
If you use soap_copy
to copy the soap struct with the plug-in, the plug-in's data will be shared by the copy. Therefore, the statistics are not 100% guaranteed to be accurate for multi-threaded services since race conditions on the counters may occur. Mutex is not used to update the counters to avoid introducing expensive synchronization points. If 100% server-side accuracy is required, add mutex at the points indicated in the logging.c
code.
The soapcpp2 tool generates the following functions for client-side REST operations:
soap_PUT_
*Name*(struct soap *soap, const char *URL,
*Type** data)
REST PUT XML of type Type to the endoint at the specified URL.
soap_POST_send_
*Name*(struct soap *soap, const char *URL,
*Type** data)
REST POST send XML of type Type to the endoint at the specified URL, which MUST be followed by a REST POST receive (see below) to receive response data.
soap_GET_
*Name*(struct soap *soap, const char *URL,
*Type** data)
REST GET XML of type Type from the endoint at the specified URL.
soap_POST_recv_
*Name*(struct soap *soap,
*Type** data)
REST POST receive XML of type Type after REST POST send.
The REST POST operation is a two-step process, first a POST send of the data followed by a POST receive of the response data.
Server-side use of RESTful HTTP GET operations is supported with the HTTP GET plug-in plugin/httpget.c
.
The HTTP GET plug-in allows your server to handle RESTful HTTP GET requests and at the same time still serve SOAP-based POST requests. The plug-in provides support to client applications to issue HTTP GET operations to a server.
Note that HTTP GET requests can also be handled at the server side with the fget
callback, see Section Function Callbacks for Customized I/O and HTTP Handling . However, the HTTP GET plug-in also keeps statistics on the number of successful POST and GET exchanges and failed operations (HTTP faults, SOAP Faults, etc.). It also keeps hit histograms accumulated for up to a year of runtime.
To use the plug-in, compile and link your application with httpget.c
located in the plugin
directory of the package. To enable the plug-in in your code, register the plug-in with your HTTP GET handler function as follows:
An HTTP GET handler can simply produce some HTML content, or any other type of content by sending data:
If you use soap_copy
to copy the soap struct with the plug-in, the plug-in's data will be shared by the copy. Therefore, the statistics are not 100% guaranteed to be accurate for multi-threaded services since race conditions on the counters may occur. Mutex is not used to update the counters to avoid introducing expensive synchronization points. If 100% server-side accuracy is required, add mutex at the points indicated in the httpget.c
code.
The client-side use of HTTP GET is provided by the soap_get_connect
operation. To receive a SOAP/XML (response) message *ns:methodResponse
*, use:
To receive any HTTP Body data into a buffer, use:
Server-side use of RESTful HTTP POST, PUT, and DELETE operations are supported with the HTTP POST plug-in plugin/httppost.c
.
The HTTP POST plug-in allows your server to handle RESTful HTTP POST requests and at the same time still serve SOAP-based POST requests. The plug-in also provides support for client applications to issue HTTP POST operations to a server.
To simplify the server-side handling of POST requests, handlers can be associated with media types:
Note that *
can be used as a wildcard and some media types may have optional parameters (after ;
). The handlers are functions that will be invoked when a POSTed request message matching media type is send to the server.
An example image handler that checks the specific image type:
The HTTP POST plug-in provides a soap_http_body
operation as illustrated above to copy the HTTP Body content into a buffer.
The above example returns HTTP OK. If content is supposed to be returned, then use:
For client applications to use HTTP POST, use the soap_post_connect
operation:
Similarly, soap_put_connect
and soap_delete_connect
commands are provided for PUT and DELETE handling.
The HTTP MD5 plug-in works in the background to automatically verify the content of messages using MD5 checksums. With the plug-in, messages can be transferred over (trusted but) unreliable connections. The plug-in can be used on the client side and server side.
To use the plug-in, compile and link your application with httpmd5.c
and md5evp.c
located in the plugin
directory of the package. The md5evp.c
implementation uses the EVP interface to compute MD5 checksums with OpenSSL (compiled with -DWITH_OPENSSL
).
To enable the plug-in in your code, register the plug-in as follows:
Once registered, MD5 checksums are produced for all outbound messages. Inbound messages with MD5 checksums in the HTTP header are automatically verified.
The plug-in requires you to set the SOAP_IO_STORE
flag when sending SOAP with attachments:
Unfortunately, this eliminates streaming.
The HTTP digest authentication plug-in enables a more secure authentication scheme compared to basic authentication. HTTP basic authentication sends unencrypted userids and passwords over the net, while digest authentication does not exchange passwords but exchanges checksums of passwords (and other data such as nonces to avoid replay attacks). For more details, please see RFC 2617.
The HTTP digest authentication can be used next to the built-in basic authentication, or basic authentication can be rejected to tighten security. The server must have a database with userid's and passwords (in plain text form). The client, when challenged by the server, checks the authentication realm provided by the server and sets the userid and passwords for digest authentication. The client application can temporarily store the userid and password for a sequence of message exchanges with the server, which is faster than repeated authorization challenges and authentication responses.
At the client side, the plug-in is registered and service invocations are checked for authorization challenges (HTTP error code 401). When the server challenges the client, the client should set the userid and password and retry the invocation. The client can determine the userid and password based on the authentication realm part of the server's challenge. The authentication information can be temporarily saved for multiple invocations.
Client-side example:
This code supports both basic and digest authentication.
The server can challenge a client using HTTP code 401. With the plug-in, HTTP digest authentication challenges are send. Without the plug-in, basic authentication challenges are send.
Each server method can implement authentication as desired and may enforce digest authentication or may also accept basic authentication responses. To verify digest authentication responses, the server should compute and compare the checksums using the plug-in's http_da_verify_post
function for HTTP POST requests (and http_da_verify_get
for HTTP GET requests with the HTTP GET plugin) as follows:
For more details, including how to configure HTTP Digest authentication for proxies, please see the doc/httpda/html/index.html
documentation in the gSOAP package.
The WSA WS-Addressing plug-in and the source code are extensively documented in the doc/wsa
directory of the gSOAP package. Please refer to the documentation included in the package.
The plug-in code is located in the plugin
directory containing wsaapi.h
and wsaapi.c
(C and C++).
To enable WS-Addressing 2005 (and support for 8/2004), the service definitions header file for soapcpp2
should include the following imports:
This imports the SOAP header elements required by WS-Addressing.
For more details, please see the doc/wsa/html/index.html
documentation in the gSOAP package.
The WSRM WS-ReliableMessaging plug-in and the source code are extensively documented in the doc/wsrm
directory of the gSOAP package. Please refer to the documentation included in the package.
The plug-in code is located in the plugin
directory containing wsrmapi.h
and wsrmapi.c
(C and C++).
Also needed are: threads.h
and threads.c
for multithreading and locking support.
To enable WS-ReliableMessaging, the service definitions header file for soapcpp2
should include the following imports:
This imports the SOAP header elements required by WS-ReliableMessaging.
For more details, please see the doc/wsrm/html/index.html
documentation in the gSOAP package.
The WSSE WS-Security plug-in and the source code are extensively documented in the doc/wsse
directory of the gSOAP package. Please refer to the documentation included in the package for details.
The plug-in code is located in the plugin
directory containing wsseapi.h
and wsseapi.c
(C and C++).
Also needed are: smdevp.h
and smdevp.c
for streaming XML signature and message digest engine, mecevp.h
and mecevp.c
for streaming XML encryption engine, threads.h
and threads.c
for multithreading and locking support.
To enable WS-Secrutiy, the service definitions header file for soapcpp2
should include the following imports:
This imports the SOAP header elements required by WS-Security.
For more details, please see the doc/wsse/html/index.html
documentation in the gSOAP package.
The WS-Discovery implementation is documented in the doc/wsdd
directory of the gSOAP package. Please refer to the documentation included in the package for details.
Basically, to add WS-Discovery support the following event handlers must be defined and linked:
These event handlers will be invoked when inbound WS-Discovery messages arrive using:
which will listen for timeout
seconds to inbound WS-Discovery messages on a port and dispatches them to the event handlers. A negative timeout
is measured in ns.
Copyright (c) 2017, Robert van Engelen, Genivia Inc. All rights reserved.