To use the wsa plugin:
An example wsa client/server application can be found in samples/wsa.
To use WS-Addressing, the gSOAP header file for a service should declare a SOAP Header with the required WS-Addressing information headers. The header file is automatically generated by wsdl2h for a set of WSDLs. The gSOAP header file is further processed by soapcpp2 to generate the binding code.
The SOAP Header structure contains the WS-Addressing information headers extracted from the WSDL and the WS-Addressing specification. For example:
#import "soap12.h" #import "wsa.h" // or wsa3.h (2003/03), wsa4.h (2004/03), wsa5.h (2005/03) struct SOAP_ENV__Header { _wsa__MessageID wsa__MessageID 0; _wsa__RelatesTo *wsa__RelatesTo 0; _wsa__From *wsa__From 0; mustUnderstand _wsa__ReplyTo *wsa__ReplyTo 0; mustUnderstand _wsa__FaultTo *wsa__FaultTo 0; mustUnderstand _wsa__To wsa__To 0; mustUnderstand _wsa__Action wsa__Action 0; };
The SOAP Header struct is automatically generated by wsdl2h from a WSDL. But it must be manually defined when creating new services from gSOAP header files. In either case, the gSOAP header file is processed with soapcpp2 to generate the client-side and/or server-side binding code.
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" struct SOAP_ENV__Header { ... }; //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 currenct 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); soap_wsa_request(soap, RequestMessageID, ToAddress, RequestAction); soap_wsa_add_From(soap, FromAddress); // optional: add a 'From' address 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); soap_wsa_request(soap, RequestMessageID, ToAddress, RequestAction); soap_wsa_add_From(soap, FromAddress); // optional: add a 'From' address soap_wsa_add_ReplyTo(soap, ReplyToAddress); 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 // error: 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); soap_wsa_request(soap, RequestMessageID, ToAddress, RequestAction); soap_wsa_add_From(soap, FromAddress); // optional: add a 'From' address soap_wsa_add_FaultTo(soap, FaultToAddress); 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" struct SOAP_ENV__Header { ... }; //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.