To use the wsa plugin:
An example wsa client/server application can be found in samples/wsa.
The header file with a wsa import is automatically generated by wsdl2h for a set of WSDLs that use WS-Addressing. The gSOAP header file is further processed by soapcpp2 to generate the binding code. The wsdl2h-generated header file might include the following imports:
#import "soap12.h" #import "wsa.h" // or wsa3.h (2003/03), wsa4.h (2004/03), wsa5.h (2005/03)
Note that the wsa.h header file located in the import directory of the gSOAP package declares the WS-Addressing information header elements and types. The soap12.h header file enables SOAP 1.2 messaging.
For developers: the WS-Addressing header blocks in wsa.h were generated from the WS-Addressing schema with the wsdl2h tool and WS/WS-typemap.dat as follows:
> wsdl2h -cegy -o wsa.h -t WS/WS-typemap.dat WS/WS-Addressing.xsd
#import "wsa.h" //gsoap ns service method-header-part: example wsa__MessageID //gsoap ns service method-header-part: example wsa__RelatesTo //gsoap ns service method-header-part: example wsa__From //gsoap ns service method-header-part: example wsa__ReplyTo //gsoap ns service method-header-part: example wsa__FaultTo //gsoap ns service method-header-part: example wsa__To //gsoap ns service method-header-part: example wsa__Action //gsoap ns service method-action: example urn:example/examplePort/example int ns__example(char *in, struct ns__exampleResponse *out);
In the client-side code, the WS-Addressing information headers are set with soap_wsa_request() by passing an optional message UUID string, a mandatory destination address URI string, and a mandatory request action URI string. The wsa plugin should be registered with the current soap struct context. An optional source address information header can be added with soap_wsa_add_From() (must be invoked after the soap_wsa_request call).
For example:
soap_register_plugin(soap, soap_wsa); if (soap_wsa_request(soap, RequestMessageID, ToAddress, RequestAction)) || soap_wsa_add_From(soap, FromAddress)) // optional: add a 'From' address ... // error: out of memory if (soap_call_ns__example(soap, ToAddress, NULL, ...)) soap_print_fault(soap, stderr); // an error occurred else // process the response
For example:
soap_register_plugin(soap, soap_wsa); if (soap_wsa_request(soap, RequestMessageID, ToAddress, RequestAction) || soap_wsa_add_From(soap, FromAddress) // optional: add a 'From' address || soap_wsa_add_ReplyTo(soap, ReplyToAddress)) ... // error: out of memory if (soap_call_ns__example(soap, ToAddress, NULL, ...)) { if (soap->error == 202) // HTTP ACCEPTED printf("Request was accepted and results were forwarded\n"); else soap_print_fault(soap, stderr); // an error occurred } else // unexpected OK: for some reason the response was not relayed
Note: the response message will be relayed when the From address is absent or different than the ReplyTo address
For example:
soap_register_plugin(soap, soap_wsa); if (soap_wsa_request(soap, RequestMessageID, ToAddress, RequestAction) || soap_wsa_add_From(soap, FromAddress) // optional: add a 'From' address || soap_wsa_add_FaultTo(soap, FaultToAddress)) ... // error: out of memory if (soap_call_ns__example(soap, ToAddress, NULL, ...)) { if (soap->error == 202) // HTTP ACCEPTED printf("A fault occurred and the fault details were forwarded\n"); else soap_print_fault(soap, stderr); // a connection error occurred } else // process response
Note that the call can still return a fault, such as a connection error when the service is not responding. In addition to the fault relay, the responses can be relayed with soap_wsa_add_ReplyTo().
if (soap_call_ns__example(soap, ToAddress, NULL, ...)) { if (soap->error == 202) // HTTP ACCEPTED printf("A fault occurred and the fault details were forwarded\n"); else soap_print_fault(soap, stderr); // a connection error occurred } else // process response
wsa__FaultSubcodeValues fault; if (soap_wsa_check_fault(soap, &fault)) { switch (fault) { case wsa__InvalidMessageInformationHeader: ... case wsa__MessageInformationHeaderRequired: ... case wsa__DestinationUreachable: ... case wsa__ActionNotSupported: ... case wsa__EndpointUnavailable: ... } }
wsa5__FaultCodesType fault; char *info; if (soap_wsa_check_fault(soap, &fault, &info)) { switch (fault) { case wsa5__InvalidAddressingHeader: if (info) printf("The invalid addressing header element is %s\n", info); ... } }
soap_register_plugin(soap, soap_wsa);
Once the plugin is registered, the soap_bind(), soap_accept(), and soap_serve() functions can be called to process requests.
Important: to dispatch service operations based on the WS-Addressing wsa:Action information header, use soapcpp2 option -a. The generates a new dispatcher (in soapServer.c) based on the action value.
A service operation implementation should use soap_wsa_check() to verify the validity of the WS-Addressing information headers in the SOAP request message. To allow response message to be automatically relayed based on the ReplyTo information header, the service operation should return soap_wsa_reply() with an optional message UUID string and a mandatory response action string.
For example:
int ns__example(struct soap *soap, char *in, struct ns__exampleResponse *out) { if (soap_wsa_check(soap)) return soap->error; // ... service logic return soap_wsa_reply(soap, ResponseMessageID, ResponseAction); }
To return a SOAP fault that is automatically relayed to a fault service based on the FaultTo information header, the soap_wsa_sender_fault(), soap_wsa_receiver_fault(), soap_wsa_sender_fault_subcode(), and soap_wsa_receiver_fault_subcode() functions should be used instead of the soap_sender_fault(), soap_receiver_fault(), soap_sender_fault_subcode(), and soap_receiver_fault_subcode(), respectively.
For example:
int ns__example(struct soap *soap, char *in, struct ns__exampleResponse *out) { if (soap_wsa_check(soap)) return soap->error; // ... service logic // ... an error occurred, need to return fault possibly to fault service: return soap_wsa_sender_fault(soap, "Exception in service operation", NULL); // ... normal execution continues return soap_wsa_reply(soap, ResponseMessageID, ResponseAction); }
For example, suppose a service operation returns an exampleResponse message. We declare the one-way exampleResponse operation as follows:
#import "wsa.h" //gsoap ns service method-header-part: exampleResult wsa__MessageID //gsoap ns service method-header-part: exampleResult wsa__RelatesTo //gsoap ns service method-header-part: exampleResult wsa__From //gsoap ns service method-header-part: exampleResult wsa__ReplyTo //gsoap ns service method-header-part: exampleResult wsa__FaultTo //gsoap ns service method-header-part: exampleResult wsa__To //gsoap ns service method-header-part: exampleResult wsa__Action //gsoap ns service method-action: exampleResult urn:example/examplePort/exampleResponse int ns__exampleResponse(char *out, void);
Note that the action information is important, because it is used by the service dispatcher (assuming soapcpp2 option -a is used).
The implementation in the server code uses soap_wsa_check() to check the presense and validity of the WS-Addressing information header in the message. The soap_send_empty_response() function should be used to return an acknowledgment HTTP header with HTTP 202 ACCEPTED to the sender:
int ns__exampleResponse(struct soap *soap, char *out) { if (soap_wsa_check(soap)) return soap_send_empty_response(soap, 500); // HTTP 500 Internal Server Error // ... service logic return soap_send_empty_response(soap, SOAP_OK); // HTTP 202 ACCEPTED }
Basically, we use a trick to generate the SOAP-ENV:Fault struct via a one-way service operation. This allows us both to implement a one-way service operation that accepts faults and to automatically generate the fault struct for fault data storage and manipulation.
The fault operation in the header file should be declared as follows (for the 2004/08 standard):
//gsoap SOAP_ENV service method-action: Fault http://schemas.xmlsoap.org/ws/2004/08/addressing/fault int SOAP_ENV__Fault ( _QName faultcode, // SOAP 1.1 char *faultstring, // SOAP 1.1 char *faultactor, // SOAP 1.1 struct SOAP_ENV__Detail *detail, // SOAP 1.1 struct SOAP_ENV__Code *SOAP_ENV__Code, // SOAP 1.2 struct SOAP_ENV__Reason *SOAP_ENV__Reason, // SOAP 1.2 char *SOAP_ENV__Node, // SOAP 1.2 char *SOAP_ENV__Role, // SOAP 1.2 struct SOAP_ENV__Detail *SOAP_ENV__Detail, // SOAP 1.2 void );
Because each service operation has a struct to hold its input parameters, we automatically generate the (original) SOAP_ENV__Fault struct on the fly!
Note: it is important to associate the wsa fault action with this operation as shown above.
The implementation of the service operation in the server code is:
int SOAP_ENV__Fault(struct soap *soap, char *faultcode, char *faultstring, char *faultactor, struct SOAP_ENV__Detail *detail, struct SOAP_ENV__Code *SOAP_ENV__Code, struct SOAP_ENV__Reason *SOAP_ENV__Reason, char *SOAP_ENV__Node, char *SOAP_ENV__Role, struct SOAP_ENV__Detail *SOAP_ENV__Detail) { ... = faultcode; // SOAP 1.1 fault code string (QName) ... = faultstring; // SOAP 1.1 fault string ... = faultactor; // SOAP 1.1 fault actor string ... = detail; // SOAP 1.1 fault detail struct ... = SOAP_ENV__Code; // SOAP 1.2 fault code struct ... = SOAP_ENV__Reason; // SOAP 1.2 reason struct ... = SOAP_ENV__Node; // SOAP 1.2 node string ... = SOAP_ENV__Role; // SOAP 1.2 role string ... = SOAP_ENV__Detail; // SOAP 1.2 detail struct return SOAP_OK; }
Note that SOAP 1.1 or SOAP 1.2 parameters are set based on the 1.1/1.2 messaging requirements.