To use the wsrm plugin:
An example wsrm client/server application can be found in samples/wsrm.
A gSOAP service definitions header file with a "wsrm import" to support WS-ReliableMessaging is automatically generated by wsdl2h for a set of WSDLs that use WS-ReliableMessaging. The wsdl2h-generated header file should be further processed by soapcpp2 to generate the binding code. The wsrmapi.h and wsrmapi.c implement the WS-ReliableMessaging API described in this document.
A wsdl2h-generated service definitions header file might include the following imports, where the wsrm.h is mandatory to support WS-ReliableMessaging:
#import "soap12.h" #import "wsrm.h"
The wsrm.h header file is imported from import/wsrm.h by soapcpp2. The wsrm.h import can be manually added to enable WS-ReliableMessaging when needed. The gSOAP service definitions header file is processed with soapcpp2 to generate the client-side and/or server-side binding code.
Note that the wsrm.h and the WS-ReliableMessaging-dependent wsrx.h and wsa5.h header files are located in the import directory of the gSOAP package. These files define the WS-ReliableMessaging and WS-Addressing information header elements and types. The wsrx.h header file defines the WS-ReliableMessaging CreateSequence, CloseSequence, and TerminateSequence operations, as well as an one-way SequenceAcknowledgement operation to accept acknowledgements. The soap12.h header file enables SOAP 1.2 messaging.
For developers working on protocols: the WS-ReliableMessaging header blocks in wsrm.h were generated from the WS-ReliableMessaging schema with the wsdl2h tool and WS/WS-typemap.dat as follows:
> wsdl2h -cgyex -o wsrm.h -t WS/WS-typemap.dat WS/WS-ReliableMessaging.xsd
This step is not needed to use the wsrm plugin.
For example, the following gSOAP service definitions header file illustrates a typical import and service operation definition of operation 'example' in service namespace 'ns':
#import "wsrm.h" //gsoap ns service method-header-part: example wsa5__MessageID //gsoap ns service method-header-part: example wsa5__RelatesTo //gsoap ns service method-header-part: example wsa5__From //gsoap ns service method-header-part: example wsa5__ReplyTo //gsoap ns service method-header-part: example wsa5__FaultTo //gsoap ns service method-header-part: example wsa5__To //gsoap ns service method-header-part: example wsa5__Action //gsoap ns service method-header-part: example wsrm__Sequence //gsoap ns service method-header-part: example wsrm__AckRequested //gsoap ns service method-header-part: example wsrm__SequenceAcknowledgement //gsoap ns service method-action: example urn:example/examplePort/example //gsoap ns service method-response-action: example urn:example/examplePort/exampleResponse int ns__example(char *in, struct ns__exampleResponse { char *out; } *);
The wsa5 information headers are defined in wsa5.h and imported by wsrm.h (both are located in the 'import' directory). Here, all of the WS-Addressing and WS-ReliableMessaging information headers are bound to the ns__example operation request and response messages.
The method action directive is important for WS-Addressing, because WS-Addressing Action information headers must be included that are unique for each operation. The soapcpp2 option -a ensures that WS-Addressing Action header blocks (and HTTP Action headers) are processed at the receiving side, which means that the service dispatcher uses the Action together with the operation name to invoke the service operation at the destination. This also means that Action headers must be properly set by the client.
Note: the SOAP_ENV__Header struct can be declared in multiple files. The soapcpp2 tool gathers all members of the structs into the "final" SOAP_ENV__Header struct used by the gSOAP engine and your application. This is convenient when service-specific header blocks are combined with WS-ReliableMessaging and WS-Addressing header blocks or when WS-Security header blocks are added by the WSSE plugin.
WS-ReliableMessaging is useful to improve the reliability of one-way asynchronous messaging, for unreliable data gram messaging (SOAP-over-UDP), or to improve reliable delivery of responses relayed to other destinations, such as response messages that are relayed to destinations indicated by the WS-Addressing ReplyTo header. The protocol is also useful when multiple sources are sending messages that arrive in different order or must be flagged as an incomplete message collection when messages are missing as defined by the notion of a collection of related messages.
WS-ReliableMessaging is not essential to improve the reliability of request-response message exchanges between two parties over HTTP, since a successful delivery of a request message can be inferred from the fact that a response was received for the request sent.
WS-ReliableMessaging "protects" message sequences, i.e. a collection of related messages. A WS-ReliableMessaging message sequence is created after which the sequence of messages is sent. The sequence is closed and terminated by the client after the last message. Either the message sequence is complete or not, and the resulting action to discard the incomplete message sequence or not depends on the chosen behavior. Duplicate messages (e.g. resulting from resends) are always discarded.
To create a new sequence, a client (WS-RM source) requests from the server (WS-RM destination) a unique (new) message sequence identification. The server responds with the identification to be used as well as other details, such as the expiration time of the sequence and the behavior implemented when a sequence was received incomplete:
When the client terminates the sequence, it first sends a sequence close request and then a sequence termination request to the destination server. The sequence close informs the server how many messages should have been received. The client can still resend messages after the close, but no new messages are supposed to be send. After the optional resends, the client requests termination of the sequence. The termination will be successful depending on the behavior when messages went missing, as was listed above.
The ensure reliable delivery, the WS-ReliableMessaging protocol allows the client to resend messages. Message resends are desirable when messages are lost in transit. Since the client has limited information on delivery success (message delivery acknowledgments can get lost as well), the client may resend more messages than necessary. This could lead to message duplication. However, messages that were already received by the server are discarded.
The client may request message delivery acknowledgements from the server. The server sends message receipt acknowledgements for all the messages it has received in the sequence back to the client, usually by piggy-backing them with the response of a subsequent message exchange. When the client is informed about successful delivery it reduces the number of resends the client will attempt.
Messages in a sequence are uniquely identified by their enumeration number in the sequence. Messages may be transmitted out of order. A missing message number indicates a gap in the message sequence. Message receipt acknowledgements consist of ranges of message numbers. Acknowledgements are normally sent to the source to help identify which messages should be resend.
With the WS-Addressing protocol, message responses and fault messages can be relayed to other destinations. The ReplyTo and FaultTo WS-Addressing header blocks are used for this purpose. The WS-ReliableMessaging protocol allows message acknowledgements to be relayed. The WS-ReliableMessaging AcksTo header block is used for this purpose.
In all, there are four types of communicating peers that are visible to the source (the client):
Any or all of the above destination services except the first can be the source (client) itself, so message responses, faults, and acknowledgements can be relayed back to the client. To this end, it is important that the client sets the WS-Addressing From information header for each message to match the ReplyTo, FaultTo, and AcksTo address.
The practical aspects of message sequence creation, the message exchanges, the message relays, and sequence close/termination are presented for the client side first and then for each of the four types of destination servers.
struct soap *soap = soap_new(); // Note: can use C++ proxy instead of 'soap' soap_register_plugin(soap, soap_wsa); soap_register_plugin(soap, soap_wsrm); const char *destination = "..."; // WS-RM destination server address const char *acksto = "..."; // WS-RM AcksTo server, or NULL if none ULONG64 expires = 10000; // 10000 ms to expire (10 seconds) const char *id = NULL; // id = NULL: generate a temp sequence ID const char *opt_msg_id = NULL; // WS-Addressing message ID, or NULL soap_wsrm_sequence_handle seq; // a local handle to the sequence state // Step 1: create a sequence if (soap_wsrm_create_offer(soap, destination, acksto, id, expires, NoDiscard, opt_msg_id, &seq)) { if (seq) soap_wsrm_seq_free(soap, seq); ... // error creating sequence } // Step 2: exchange messages with WS-RM destination, request acks, receive acks, issue resends (see later) ... // Step 3: optionally close first before terminating if (soap_wsrm_close(soap, seq, NULL)) { soap_wsrm_seq_free(soap, seq); ... // error closing sequence } // Step 4: terminate if (soap_wsrm_terminate(soap, seq, NULL)) { soap_wsrm_seq_free(soap, seq); ... // error terminating sequence } // Step 5: cleanup soap_wsrm_seq_free(soap, seq);
As usual, the context can release all its temporary and deserialized data with the following pair of calls:
soap_destroy(soap); soap_end(soap);
This cleanup of memory resources may be performed at any time in the sequence of message exchange or afterwards when desired. The sequence state is maintained independent of these cleanup operations.
The sequence termination may fail when the delivery of a sequence of messages is incomplete or when the lifetime of the sequence expired (see SOAP_WSRM_MAX_SEC_TO_EXPIRE). The WS-RM destination determines the failure based on the final sequence state and the sequence behavior. The behavior is set to NoDiscard by default, which means that the sequence is not discared when transmission gaps appeared in the messages and the sequence is incomplete. The desired behavior can be specified with a sequence creation offer as explained in the next section.
ULONG64 expires = 60000; // 1 minute = 60,000 milliseconds const char *id = NULL; // id = NULL: generate a temp sequence ID const char *opt_msg_id = NULL; // WS-Addressing message ID if (soap_wsrm_create_offer(soap, destination, acksto, id, expires, DiscardEntireSequence, opt_msg_id, &seq)) { if (seq) soap_wsrm_seq_free(soap, seq); ... // error creating sequence }
This offers sequence parameters to the WS-RM destination, which may accept the offer and apply the requested lifetime (expiration time in ms) and sequence behavior.
The sequence behavior is either
For example, consider the 'example' operation defined previously and suppose we invoke the 'example' operation in a sequence (after creation and before closing):
const char *exampleRequestAction = "urn:example/examplePort/example"; const char *exampleRequestMessageID = NULL; // optional WS-Addressing ID struct ns__exampleResponse response; if (soap_wsrm_request(soap, seq, exampleRequestMessageID, exampleRequestAction)) ... // error: out of memory if (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response)) soap_print_fault(soap, stderr); // an error occurred else ... // process the response
The soap_wsrm_request takes the sequence handle and optional WS-Addressing message ID and mandatory WS-Addressing action string (this string must match the method-action defined in the gSOAP service definition header file). It produces a WS-RM header block with the message number incremented by one for the invocation. Messages are enumerated from one and included in the WS-RM header to allow the destination to determine which messages were received in the sequence (for acknowledgements) and to ignore duplicate messages.
The remote invocation soap_call_ns__example() uses soap_wsrm_to(seq) for the WS-RM destination address, which was set by soap_wsrm_create() or soap_wsrm_create_offer. Because the address may change due to a redirect, we encourage the use of soap_wsrm_to for the WS-RM destination address.
A C++ proxy object (generated by soapcpp2 option -i) that invokes a service operation should reset the destination address explicitly by setting the 'soap_endpoint' member string before each operation invocation.
if (soap_wsrm_request_acks(soap, seq, exampleRequestMessageID, exampleRequestAction)) ... // error: out of memory
This informs the WS-RM destination to return message delivery acknowledgements back to the sender (piggy-backed in the header of the response message), unless the AcksTo is set to target an acknowledgement service endpoint.
The soap_wsrm_close returns acknowledgements automatically, so requesting intermediate acknowledgements is not required to issue a final soap_wsrm_resend to resend all non-acknowledged messages, e.g. after soap_wsrm_close. See also below.
soap_wsrm_resend(soap, seq, 0, 0);
To resend a range of non-acknowledged messages, say between 3 and 7, use:
soap_wsrm_resend(soap, seq, 3, 7);
Or all messages after message number 3:
soap_wsrm_resend(soap, seq, 3, 0);
Resends should be used with care, since in the worst case when no acknowledgements have been received all messages up to the last will be resend (and ignored by the WS-RM destination when a message is received more than once).
Note that when an AcksTo destination service address was set with soap_wsrm_create or soap_wsrm_create_offer, then the acknowledgements will not be returned to the sender (client). In this case message resends are performed for all messages sent, since these have not been acknowledged.
It is permitted to issue resends between creation and termination of a sequence, including after a sequence close (as long as no new messages are sent after close). The sequence close provides acknowledgement information to limit the number of messages that need to be resend.
To find out if none, some, or all messages have been acknowledged, use:
ULONG64 nack = soap_wsrm_nack(seq); if (nack == 0) ... // all sent messages have been acknowledged else if (nack == soap_wsrm_num(seq)) ... // none of the sent messages have been acknowledged else ... // some sent messages have been acknowledged
The ReplyTo and FaultTo destination service endpoints can be specified for each message as follows:
const char *replyto = "..."; // endpoint of response processing service const char *faultto = "..."; // endpoint of fault processing service if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction) || soap_wsa_add_ReplyTo(soap, replyto) || soap_wsa_add_FaultTo(soap, faultto)) ... // error: out of memory if (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response)) { if (soap->error == 202) ... // request was accepted by destination (HTTP 202 Accept) else ... // other error, see below on how to detect acks }
To sent acknowledgements back to the client, set AcksTo to NULL:
ULONG64 expires = 60000; // 1 minute = 60,000 milliseconds const char *id = NULL; // id = NULL: generate a temp sequence ID const char *opt_msg_id = NULL; // WS-Addressing message ID const char *destination = "..."; // WS-RM destination server address const char *acksto = NULL; // sent acks to client soap_wsrm_sequence_handle seq; // a local handle to the sequence state if (soap_wsrm_create_offer(soap, destination, acksto, id, expires, NoDiscard, opt_msg_id, &seq)) { if (seq) soap_wsrm_seq_free(soap, seq); ... // error creating sequence } if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction)) ... // error: out of memory if (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response)) { if (soap->error == 202) ... // request was accepted by destination (HTTP 202 Accept) else if (soap->error == SOAP_NO_TAG) // empty <Body> ... // request was accepted by destination, acks are returned in empty Body else ... // other error }
Otherwise, setting the AcksTo will relay acknowledgements to the address specified.
An optional source address information header can be added with soap_wsa_add_From:
const char *from = "..."; // endpoint of the client (could be any URI) if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction) || soap_wsa_add_From(soap, from)) ... // error: out of memory
Adding a From address is optional and usually not required with WS-ReliableMessaging.
Besides network failues, a request-response message exchange can also appear to fail due to a non SOAP_OK returned, such as a benign HTTP 202 Accept To distinguish fatal send errors from errors returned by the peer, the soap_wsrm_check_retry function can be used as follows to only retry the message exchange (or send) when needed:
if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction)) ... // error: out of memory while (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response)) { if (soap->error == 202) { // request was accepted by destination (HTTP 202 Accept) break; } else if (soap->error == SOAP_NO_TAG) // empty <Body> { // request was accepted by destination, acks are returned break; } soap_print_fault(soap, stderr); if (soap_wsrm_check_retry(soap, seq)) break; // do not continue sleep(1); // wait a second to give network a chance to recover } if (soap->error == SOAP_OK) ... // response can be processed
Note that the soap_wsrm_request is only invoked once in the above to increment the message enumeration.
The loop retries transmissions a maximum of SOAP_WSRM_MAX_RETRIES iterations before giving up.
struct soap *soap = soap_new(); // Note: can use C++ proxy instead of 'soap' soap_register_plugin(soap, soap_wsa); soap_register_plugin(soap, soap_wsrm); struct ns__exampleResponse response; const char *exampleRequestAction = "urn:example/examplePort/example"; const char *destination = "..."; // WS-RM destination server address const char *acksto = "..."; // WS-RM AcksTo server, or NULL if none const char *replyto = "..."; // WS-A ReplyTo server to relay response const char *faultto = "..."; // WS-A FaultTo server to relay fauls ULONG64 expires = 60000; // 1 minute sequence lifetime soap_wsrm_sequence_handle seq; if (soap_wsrm_create_offer(soap, destination, acksto, NULL, expires, DiscardEntireSequence, NULL, &seq)) { if (seq) soap_wsrm_seq_free(soap, seq); ... // error creating sequence } if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction) || soap_wsa_add_ReplyTo(soap, replyto) || soap_wsa_add_FaultTo(soap, faultto)) ... // error: out of memory while (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response)) { if (soap->error == 202) { // request was accepted by destination (HTTP 202 Accept) break; } else if (soap->error == SOAP_NO_TAG) // empty <Body> { // request was accepted by destination, acks are returned break; } soap_print_fault(soap, stderr); if (soap_wsrm_check_retry(soap, seq)) break; // do not continue sleep(1); // wait a second to give network a chance to recover } if (soap->error == SOAP_OK) ... // response can be processed if (soap_wsrm_close(soap, seq, NULL)) { soap_wsrm_seq_free(soap, seq); ... // error closing sequence } soap_wsrm_resend(soap, seq, 0, 0); // resend non-acked messages if (soap_wsrm_terminate(soap, seq, NULL)) { soap_wsrm_seq_free(soap, seq); ... // error terminating sequence } soap_wsrm_seq_free(soap, seq); soap_destroy(soap); soap_end(soap); soap_free(soap);
Responses can be discarded by the WS-RM destination service as requested with the soap_wsa_add_NoReply call, which adds a ReplyTo 'noreply' WS-Addressing header block URI.
struct soap *soap = soap_new(); // Note: can use C++ proxy instead of 'soap' soap_register_plugin(soap, soap_wsa); soap_register_plugin(soap, soap_wsrm);
The following subsections detail the differences between the types of WS-RM destination services.
int ns__example(struct soap *soap, char *in, struct ns__exampleResponse *response) { const char *ResponseAction = "urn:example/examplePort/exampleResponse"; // fatal service operation-specific errors (before soap_wsrm_check) if (!database) // suppose we need a database, if there is none terminate return soap_wsrm_receiver_fault(soap, "No database!", NULL); // check for WS-RM/WSA and set WS-RM/WSA return headers if (soap_wsrm_check(soap)) return soap->error; // check for non-fatal service operation-specific errors if (!in || !*in) // sender did not put anything in the 'in' string: fault return soap_wsrm_sender_fault(soap, "No string content!", NULL); response->out = ... // return normally, relaying the response to the ReplyTo service when needed return soap_wsrm_reply(soap, NULL, ResponseAction); }
An error produced by soap_wsrm_sender_fault or soap_wsrm_receiver_fault before soap_wsrm_check is considered fatal, it will terminate the sequence and the sender (client) will not be able to continue the sequence transmissions. While the faults preduced after soap_wsrm_check allow the sequence to continue.
To set up a response-processing destination (accepting ReplyTo-relayed response messages), set up the server in the same way as the destination server. Service opertions should not use soap_wsrm_check and soap_wsrm_reply. Because response messages are sent (as if these were request messages), the service must define the appropriate one-way operations and gSOAP service definitions bindings.
For example, the one-way response message of the ns__example operation is defined as follows in the gSOAP service definitions header file:
//gsoap ns service method-header-part: exampleResponse wsa5__MessageID //gsoap ns service method-header-part: exampleResponse wsa5__RelatesTo //gsoap ns service method-header-part: exampleResponse wsa5__From //gsoap ns service method-header-part: exampleResponse wsa5__ReplyTo //gsoap ns service method-header-part: exampleResponse wsa5__FaultTo //gsoap ns service method-header-part: exampleResponse wsa5__To //gsoap ns service method-header-part: exampleResponse wsa5__Action //gsoap ns service method-header-part: exampleResponse wsrm__SequenceAcknowledgement //gsoap ns service method-action: exampleResponse urn:example/examplePort/exampleResponse int ns__exampleResponse(char *out, void);
Note that when these definitions are combined with the previous definition for ns__example, there is no need to define the ns__ExampleResponse struct any longer as this is implied by the ns__exampleResponse function content.
The server operation implementation is for example:
int ns__exampleResponse(struct soap *soap, char *out) { ... // process return SOAP_OK; }
Note that message acknowledgements can be piggy-backed with these response messages from the destination service when AcksTo is not set (at the destintation service). These acknowledgements are processed automatically to update the local sequence states when the wsrm plugin is registered.
//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 );
Given the above service definitions, the soapcpp2 tool generates a SOAP_ENV__Fault service dispatcher that will invoke the SOAP_ENV__Fault function that should be implemented at the server side:
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 ) { // SOAP 1.1 ... = faultcode; ... = faultstring; ... = faultactor; ... = detail; // SOAP 1.2 ... = SOAP_ENV__Code; ... = SOAP_ENV__Reason; ... = SOAP_ENV__Node; ... = SOAP_ENV__Role; ... = SOAP_ENV__Detail; return soap_send_empty_response(soap, SOAP_OK); // HTTP 202 Accepted }
Because the FaultTo service only uses WS-Addressing, there is no need to register or use the wsrm plugin.
#import "wsrm.h"
This is sufficient, since the wsrm plugin implements an acknowledgement processing capability with the built-in __wsrm__SequenceAcknowledgement server operation, that updates the local sequence state when acknowledgements are received.
How to use the sequence state of the AcksTo server and use the state updates is up to the application developer.
Note that the WS-RM destination service may also relay messages to other HTTPS services, thus the WS-RM destination acts as a receiver (server) and sender (client). Therefore, the WS-RM destination server's SSL context should be set to authenticate the other servers:
if (soap_ssl_server_context(soap, SOAP_SSL_DEFAULT, "server.pem", // keyfile (server) "password", // password to read the key file (server) "cacert.pem", // cacert file to store trusted certificates (client) NULL, // optional capath NULL, // DH file name or DH param key len bits, NULL: RSA NULL, // file with random data to seed randomness argv[1] // unique server identification for SSL session cache )) { soap_print_fault(soap, stderr); ... // handle error }
Here, the cacert.pem file contains certificates to authenticate the ReplyTo, FaultTo, and AcksTo services when HTTPS is used.
The client side sets up the SSL context with the soap_ssl_client_context as instructed in the documentation and by the examples. Multi-threaded HTTPS clients and servers must register mutex locks with OpenSSL
To use Basic Authentication at the client side, set the userid and passwd values:
soap->userid = "..."; // Basic Auth user id soap->passwd = "..."; // Basic Auth password if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction) ... // error: out of memory if (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response)) { if (soap->error == 401) ... // authentication failed for the userid/passwd pair else ... // other error }
At the server side add the authentication check to the service operation before soap_wsrm_check to terminate the sequence when an authentication failure occurs. For example:
int ns__example(struct soap *soap, char *in, struct ns__exampleResponse *response) { if (!soap->userid || !soap->passwd || strcmp(soap->userid, "...") || strcmp(soap->passwd, "...")) { soap->authrealm = "..."; // optional to set HTTP WWW-Authenticate: Basic realm="..." return 401; // HTTP 401 Unauthorized } if (soap_wsrm_check(soap)) return soap->error;
Here, we check only one userid-passwd pair though normally we could search for valid credentials in an authentication store.
Note: never use Basic Authentication over HTTP because the password is sent in the clear. You must use HTTPS to encrypt the HTTP authentication information and message content. HTTP Digest Auth is preferred for this reason, because Digest Auth verifies the digest of a userid-passwd rather than require the password to be exchanged or stored in cleartext.
Note that UDP datagram messages should not exceed 8K, which is usually a size that UDP datagrams can support. To reduce the message size, we recommend compression (-DWITH_GZIP compile flag to enable ZLIB and use libgsoapssl.a/libgsoapssl++.a for OpenSSL and ZLIB compression combined).
The code of an UDP-enabled server is identical to an HTTP/TCP server except that the soap_accept call is disabled and unnecessary.
When message responses are not returned to the client, the client may block indefinitely when it expects a response. Therefore we recommend the use of send and receive timeouts:
struct soap *soap = soap_new(); const char *destination = "soap.udp://..."; const char *acksto = NULL; soap->send_timeout = soap->recv_timeout = 1; // 1 second to timeout if (soap_wsrm_create_offer(soap, destination, acksto, NULL, expires, NoDiscard, NULL, &seq)) ... // an error occured if (soap_wsrm_request(soap, seq, NULL, exampleRequestAction)) ... // an error occured if (soap_call_ns__example(soap, ...)) { if (soap->error == SOAP_EOF && soap->errnum == 0) ... // a timeout occured else ... // an error occured }
Note that the WS-Addressing ReplyTo and the use of NoReply do not return response message from the server. However, acknowledgements will be returned when acknowledgements were requested (unless acknowledgements are relayed with AcksTo).
struct soap *soap = soap_new(); soap->send_timeout = soap->recv_timeout = 1; // 1 second to timeout const char *destination = "soap.udp://..."; const char *from = "..."; // some identifying URI const char *acksto = from; soap->send_timeout = soap->recv_timeout = 1; // 1 second to timeout if (soap_wsrm_create_offer(soap, destination, acksto, NULL, expires, NoDiscard, NULL, &seq)) ... // an error occured if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction) || soap_wsa_add_NoReply(soap) ... // an error occured if (soap_call_ns__example(soap, ...)) { if (soap->error == SOAP_EOF && soap->errnum == 0) ... // a timeout occured else if (soap->error == SOAP_NO_TAG) ... // ack was received and recorded else ... // an error occured }
In this case an acknowledgement will be returned and the timeout reflects a possible network packet loss.
Both plugins must be registered at the client and server side. These APIs are independent.
int wsrmService::CreateSequence( struct wsrm__CreateSequenceType *wsrm__CreateSequence, struct wsrm__CreateSequenceResponseType *wsrm__CreateSequenceResponse) { return __wsrm__CreateSequence(this, wsrm__CreateSequence, wsrm__CreateSequenceResponse); } int wsrmService::CloseSequence( struct wsrm__CloseSequenceType *wsrm__CloseSequence, struct wsrm__CloseSequenceResponseType *wsrm__CloseSequenceResponse) { return __wsrm__CloseSequence(this, wsrm__CloseSequence, wsrm__CloseSequenceResponse); } int wsrmService::TerminateSequence( struct wsrm__TerminateSequenceType *wsrm__TerminateSequence, struct wsrm__TerminateSequenceResponseType *wsrm__TerminateSequenceResponse) { return __wsrm__TerminateSequence(this, wsrm__TerminateSequence, wsrm__TerminateSequenceResponse); } int wsrmService::SequenceAcknowledgement() { return __wsrm__SequenceAcknowledgement(this); }
In addition, we need to dispatch the wsrm service operations when received. Suppose we have a myService server class generated by soapcpp2 -i, which is used to process requests with the serve() member function (also generated):
myService service; if (soap_invalid_socket(service.bind(NULL, port, 100))) ... // error if (soap_invalid_socket(service.accept())) ... // error if (service.serve() == SOAP_NO_METHOD) // no matching operation { wsrmService wsrm(service); // wsrm server soap_copy_stream(&wsrm, &service); // copy open connection wsrm.dispatch(); // process WS-RM request }