HRESULT CALLBACK Receive5( __in HRESULT hr, __in WS_CALLBACK_MODEL callbackModel, __in void* state, __inout WS_ASYNC_OPERATION* next, __in_opt const WS_ASYNC_CONTEXT* asyncContext, __in_opt WS_ERROR* error) { UNREFERENCED_PARAMETER(callbackModel); RECEIVE_STATE* receiveState = (RECEIVE_STATE*) state; if (FAILED(hr)) { return hr; } if (hr == WS_S_END) { next->function = NULL; // Close the channel return WsCloseChannel(receiveState->channel, asyncContext, error); } // Get action value WS_XML_STRING receivedAction; hr = WsGetHeader( receiveState->message, WS_ACTION_HEADER, WS_XML_STRING_TYPE, WS_READ_REQUIRED_VALUE, NULL, &receivedAction, sizeof(receivedAction), error); if (FAILED(hr)) { return hr; } // Make sure action is what we expect if (WsXmlStringEquals(&receivedAction, PurchaseOrder_wsdl.messages.PurchaseOrder.action, error) != S_OK) { return WS_E_ENDPOINT_ACTION_NOT_SUPPORTED; } // Get the reader for the body hr = WsGetMessageProperty(receiveState->message, WS_MESSAGE_PROPERTY_BODY_READER, &receiveState->reader, sizeof(receiveState->reader), error); if (FAILED(hr)) { return hr; } next->function = Receive6; return S_OK; }
// The server version of ProcessMessage. This is the entry point for the application-specific code. HRESULT CFileRepServer::ProcessMessage( __in CRequest* request, __in const WS_XML_STRING* receivedAction) { PrintVerbose(L"Entering CFileRepServer::ProcessMessage"); HRESULT hr = S_OK; FileRequest* fileRequest = NULL; WS_MESSAGE* requestMessage = request->GetRequestMessage(); WS_CHANNEL* channel = request->GetChannel(); WS_ERROR* error = request->GetError(); // Make sure action is what we expect if (WsXmlStringEquals(receivedAction, &fileRequestAction, error) != S_OK) { PrintInfo(L"Illegal action"); hr = WS_E_ENDPOINT_ACTION_NOT_SUPPORTED; } else { // Read file request WS_HEAP* heap; IfFailedExit(WsGetMessageProperty(requestMessage, WS_MESSAGE_PROPERTY_HEAP, &heap, sizeof(heap), error)); IfFailedExit(WsReadBody(requestMessage, &fileRequestElement, WS_READ_REQUIRED_POINTER, heap, &fileRequest, sizeof(fileRequest), error)); IfFailedExit(WsReadMessageEnd(channel, requestMessage, NULL, error)); IfFailedExit(ReadAndSendFile(request, fileRequest->fileName, fileRequest->filePosition, error)); } EXIT // We do not print error messages here. That is handled in the caller. PrintVerbose(L"Leaving CFileRepServer::ProcessMessage"); return hr; }
// Main entry point int __cdecl wmain() { HRESULT hr = S_OK; WS_ERROR* error = NULL; WS_METADATA* metadata = NULL; WS_CHANNEL* channel = NULL; WS_XML_READER* reader = NULL; WS_HEAP* heap = NULL; // Declare constraints on what policy is acceptable // Require HTTP WS_CHANNEL_BINDING channelBinding = WS_HTTP_CHANNEL_BINDING; // Set up channel property contraints that override the default constraints WS_CHANNEL_PROPERTY_CONSTRAINT channelPropertyConstraints[3]; // Allow text encodings WS_ENCODING allowedEncodings[] = { WS_ENCODING_XML_UTF8, WS_ENCODING_XML_UTF16LE, WS_ENCODING_XML_UTF16BE }; channelPropertyConstraints[0].id = WS_CHANNEL_PROPERTY_ENCODING; channelPropertyConstraints[0].allowedValues = allowedEncodings; channelPropertyConstraints[0].allowedValuesSize = sizeof(allowedEncodings); // Allow addressing 1.0 WS_ADDRESSING_VERSION allowedAddressingVersions[] = { WS_ADDRESSING_VERSION_1_0, }; channelPropertyConstraints[1].id = WS_CHANNEL_PROPERTY_ADDRESSING_VERSION; channelPropertyConstraints[1].allowedValues = allowedAddressingVersions; channelPropertyConstraints[1].allowedValuesSize = sizeof(allowedAddressingVersions); // Allow SOAP 1.1 or SOAP 1.2 WS_ENVELOPE_VERSION allowedEnvelopeVersions[] = { WS_ENVELOPE_VERSION_SOAP_1_1, WS_ENVELOPE_VERSION_SOAP_1_2, }; channelPropertyConstraints[2].id = WS_CHANNEL_PROPERTY_ENVELOPE_VERSION; channelPropertyConstraints[2].allowedValues = allowedEnvelopeVersions; channelPropertyConstraints[2].allowedValuesSize = sizeof(allowedEnvelopeVersions); // Set up security property contraints that override the default constraints WS_SECURITY_PROPERTY_CONSTRAINT securityPropertyConstraints[1]; // Allow with/without a timestamp WS_SECURITY_TIMESTAMP_USAGE allowedTimestampValues[] = { WS_SECURITY_TIMESTAMP_USAGE_NEVER, WS_SECURITY_TIMESTAMP_USAGE_ALWAYS, }; securityPropertyConstraints[0].id = WS_SECURITY_PROPERTY_TIMESTAMP_USAGE; securityPropertyConstraints[0].allowedValues = allowedTimestampValues; securityPropertyConstraints[0].allowedValuesSize = sizeof(allowedTimestampValues); // Set up the ssl security binding constraint structure WS_SSL_TRANSPORT_SECURITY_BINDING_CONSTRAINT sslSecurityBindingConstraint = { }; sslSecurityBindingConstraint.bindingConstraint.type = WS_SSL_TRANSPORT_SECURITY_BINDING_CONSTRAINT_TYPE; // Set up the X.509 security binding constraint structure WS_CERT_MESSAGE_SECURITY_BINDING_CONSTRAINT certSecurityBindingConstraint = { }; certSecurityBindingConstraint.bindingConstraint.type = WS_CERT_MESSAGE_SECURITY_BINDING_CONSTRAINT_TYPE; certSecurityBindingConstraint.bindingUsage = WS_SUPPORTING_MESSAGE_SECURITY_USAGE; // Set up the set of security binding constraints WS_SECURITY_BINDING_CONSTRAINT* securityBindingConstraints[] = { &sslSecurityBindingConstraint.bindingConstraint, &certSecurityBindingConstraint.bindingConstraint }; // Set up the security constraint structure WS_SECURITY_CONSTRAINTS securityConstraints = { }; securityConstraints.securityPropertyConstraints = securityPropertyConstraints; securityConstraints.securityPropertyConstraintCount = WsCountOf(securityPropertyConstraints); securityConstraints.securityBindingConstraints = securityBindingConstraints; securityConstraints.securityBindingConstraintCount = WsCountOf(securityBindingConstraints); // Set up the policy constraint structure WS_POLICY_CONSTRAINTS policyConstraints = { }; policyConstraints.channelBinding = channelBinding; policyConstraints.channelPropertyConstraints = channelPropertyConstraints; policyConstraints.channelPropertyConstraintCount = WsCountOf(channelPropertyConstraints); policyConstraints.securityConstraints = &securityConstraints; // Set up port type to match static const WS_XML_STRING desiredPortTypeName = WS_XML_STRING_VALUE("IPingService"); static const WS_XML_STRING desiredPortTypeNs = WS_XML_STRING_VALUE("http://example.com"); // Create an error object for storing rich error information hr = WsCreateError( NULL, 0, &error); if (FAILED(hr)) { goto Exit; } // Create object that will hold metadata documents hr = WsCreateMetadata(NULL, 0, &metadata, error); if (FAILED(hr)) { goto Exit; } // Create an XML reader hr = WsCreateReader( NULL, 0, &reader, error); if (FAILED(hr)) { goto Exit; } // Set the input of the reader to the policy text WS_XML_READER_BUFFER_INPUT bufferInput; ZeroMemory(&bufferInput, sizeof(bufferInput)); bufferInput.input.inputType = WS_XML_READER_INPUT_TYPE_BUFFER; bufferInput.encodedData = wsdlXml.bytes; bufferInput.encodedDataSize = wsdlXml.length; WS_XML_READER_TEXT_ENCODING textEncoding; ZeroMemory(&textEncoding, sizeof(textEncoding)); textEncoding.encoding.encodingType = WS_XML_READER_ENCODING_TYPE_TEXT; textEncoding.charSet = WS_CHARSET_AUTO; hr = WsSetInput(reader, &textEncoding.encoding, &bufferInput.input, NULL, 0, error); if (FAILED(hr)) { goto Exit; } // Read the metadata into the metadata object. hr = WsReadMetadata(metadata, reader, &wsdlUrl, error); if (FAILED(hr)) { goto Exit; } // After adding a document to the metadata object, it can be queried // to determine the address of any documents which have been referenced // but have not yet been added. WS_ENDPOINT_ADDRESS* missingAddress; hr = WsGetMissingMetadataDocumentAddress(metadata, &missingAddress, error); if (FAILED(hr)) { goto Exit; } if (missingAddress != NULL) { // We only support one document in this example hr = E_FAIL; goto Exit; } // Get the endpoints from the metadata object WS_METADATA_ENDPOINTS endpoints; hr = WsGetMetadataEndpoints(metadata, &endpoints, error); if (FAILED(hr)) { goto Exit; } BOOL foundEndpoint = FALSE; WS_METADATA_ENDPOINT* endpoint = NULL; // Search for port types for (ULONG i = 0; i < endpoints.endpointCount; i++) { // Get the endpoint from the array of endpoints endpoint = &endpoints.endpoints[i]; // See if the port type name matches hr = WsXmlStringEquals(endpoint->portTypeName, &desiredPortTypeName, error); if (FAILED(hr)) { goto Exit; } if (hr == S_FALSE) { continue; } // See if the port type namespace matches hr = WsXmlStringEquals(endpoint->portTypeNs, &desiredPortTypeNs, error); if (FAILED(hr)) { goto Exit; } if (hr == S_FALSE) { continue; } foundEndpoint = TRUE; break; } if (!foundEndpoint) { // No matching port types hr = E_FAIL; goto Exit; } // Get the policy for the endpoint WS_POLICY* policy; policy = endpoint->endpointPolicy; // Get the number of policy alternatives available in the policy object ULONG alternativeCount; hr = WsGetPolicyAlternativeCount( policy, &alternativeCount, error); if (FAILED(hr)) { goto Exit; } // Create a heap used to allocate fields of initialized values hr = WsCreateHeap(/* maxSize */ 16*1024, /* trimSize */ 2*1024, NULL, 0, &heap, error); if (FAILED(hr)) { goto Exit; } BOOL matchFound = FALSE; // For each alternative in the policy object for (ULONG alternativeIndex = 0; alternativeIndex < alternativeCount; alternativeIndex++) { // This example uses FALSE for the matchRequired parameter to WsMatchPolicyAlternative // which means that the function will return S_FALSE if there is not a match. // If diagnosing why a policy can not be matched, it may be useful to instead set // matchRequired to TRUE meaning an error will be returned (and the error object // will contain information about why the policy did not match). BOOL matchRequired = FALSE; // Try to match policy given the constraints hr = WsMatchPolicyAlternative( policy, alternativeIndex, &policyConstraints, matchRequired, heap, error); if (FAILED(hr)) { goto Exit; } if (hr == S_OK) { // The policy met the constraints matchFound = TRUE; break; } } if (!matchFound) { // None of the policy alternatives matched hr = E_FAIL; goto Exit; } // Initialize channel properties based on the values found in the policy WS_CHANNEL_PROPERTY channelProperties[4]; channelProperties[0] = channelPropertyConstraints[0].out.channelProperty; channelProperties[1] = channelPropertyConstraints[1].out.channelProperty; channelProperties[2] = channelPropertyConstraints[2].out.channelProperty; // Initialize additional channel properties that specify local behavior // that is not part of policy. WS_TRANSFER_MODE transferMode = WS_BUFFERED_TRANSFER_MODE; channelProperties[3].id = WS_CHANNEL_PROPERTY_TRANSFER_MODE; channelProperties[3].value = &transferMode; channelProperties[3].valueSize = sizeof(transferMode); // Initialize security properties based on values extracted from policy WS_SECURITY_PROPERTY securityProperties[1]; securityProperties[0] = securityPropertyConstraints[0].out.securityProperty; // Set up SSL security binding WS_SSL_TRANSPORT_SECURITY_BINDING sslSecurityBinding; ZeroMemory(&sslSecurityBinding, sizeof(sslSecurityBinding)); sslSecurityBinding.binding.bindingType = WS_SSL_TRANSPORT_SECURITY_BINDING_TYPE; if (sslSecurityBindingConstraint.out.clientCertCredentialRequired) { // Server wants a client cert, but this example does not have one hr = E_FAIL; goto Exit; } else { sslSecurityBinding.localCertCredential = NULL; } // The runtime does not support X.509 binding hence use a WS_XML_TOKEN_MESSAGE_SECURITY_BINDING to create a channel WS_XML_TOKEN_MESSAGE_SECURITY_BINDING xmlTokenSecurityBinding; ZeroMemory(&xmlTokenSecurityBinding, sizeof(xmlTokenSecurityBinding)); xmlTokenSecurityBinding.binding.bindingType = WS_XML_TOKEN_MESSAGE_SECURITY_BINDING_TYPE; xmlTokenSecurityBinding.bindingUsage = certSecurityBindingConstraint.bindingUsage; // To obtain a security token to specify for the xmlToken field, use the following steps: // - Find the appropriate certificate and create a X.509 token // - Use WsCreateXmlSecurityToken and to create a security token with the keys from the above X.509 token xmlTokenSecurityBinding.xmlToken = NULL; // Set up security bindings WS_SECURITY_BINDING* securityBindings[2]; securityBindings[0] = &sslSecurityBinding.binding; securityBindings[1] = &xmlTokenSecurityBinding.binding; // Set up security description WS_SECURITY_DESCRIPTION securityDescription; securityDescription.securityBindings = securityBindings; securityDescription.securityBindingCount = WsCountOf(securityBindings); securityDescription.properties = securityProperties; securityDescription.propertyCount = WsCountOf(securityProperties); // Create a channel or proxy to the service using the accumulated binding information: // - channelBinding // - channelProperties // - securityDecription Exit: if (FAILED(hr)) { // Print out the error PrintError(hr, error); } if (metadata != NULL) { WsFreeMetadata(metadata); } if (channel != NULL) { WsFreeChannel(channel); } if (reader != NULL) { WsFreeReader(reader); } if (heap != NULL) { WsFreeHeap(heap); } if (error != NULL) { WsFreeError(error); } fflush(stdout); return SUCCEEDED(hr) ? 0 : -1; }
// Main entry point int __cdecl wmain() { HRESULT hr = S_OK; WS_ERROR* error = NULL; WS_CHANNEL* channel = NULL; WS_LISTENER* listener = NULL; WS_HEAP* heap = NULL; WS_MESSAGE* message = NULL; // Create an error object for storing rich error information hr = WsCreateError( NULL, 0, &error); if (FAILED(hr)) { goto Exit; } // Create a listener hr = WsCreateListener( WS_CHANNEL_TYPE_DUPLEX_SESSION, WS_NAMEDPIPE_CHANNEL_BINDING, NULL, 0, NULL, &listener, error); if (FAILED(hr)) { goto Exit; } // Create a channel suitable for the listener hr = WsCreateChannelForListener( listener, NULL, 0, &channel, error); if (FAILED(hr)) { goto Exit; } // Open listener using named pipe duplex session WS_STRING uri; uri.chars = L"net.pipe://localhost/example"; uri.length = (ULONG)::wcslen(uri.chars); hr = WsOpenListener( listener, &uri, NULL, error); if (FAILED(hr)) { goto Exit; } // Accept a channel from the client hr = WsAcceptChannel(listener, channel, NULL, error); if (FAILED(hr)) { goto Exit; } hr = WsCreateMessageForChannel( channel, NULL, 0, &message, error); if (FAILED(hr)) { goto Exit; } // Create a heap to store deserialized data hr = WsCreateHeap( /*maxSize*/ 2048, /*trimSize*/ 512, NULL, 0, &heap, error); if (FAILED(hr)) { goto Exit; } // Receive messages for (;;) { // Receive the message start (headers) hr = WsReadMessageStart( channel, message, NULL, error); if (FAILED(hr)) { goto Exit; } if (hr == WS_S_END) { // No more messages break; } // Get action value WS_XML_STRING receivedAction; hr = WsGetHeader( message, WS_ACTION_HEADER, WS_XML_STRING_TYPE, WS_READ_REQUIRED_VALUE, NULL, &receivedAction, sizeof(receivedAction), error); if (FAILED(hr)) { goto Exit; } // Make sure action is what we expect hr = WsXmlStringEquals( &receivedAction, PurchaseOrder_wsdl.messages.PurchaseOrder.action, error); if (hr != S_OK) { hr = WS_E_ENDPOINT_ACTION_NOT_SUPPORTED; goto Exit; } // Get the reader for the body WS_XML_READER* reader; hr = WsGetMessageProperty( message, WS_MESSAGE_PROPERTY_BODY_READER, &reader, sizeof(reader), error); if (FAILED(hr)) { goto Exit; } // Stream in the body data for (;;) { // Read purchase order into heap, if there are any more. _PurchaseOrderType* purchaseOrder; hr = WsReadElement( reader, &PurchaseOrder_wsdl.globalElements.PurchaseOrderType, WS_READ_OPTIONAL_POINTER, heap, &purchaseOrder, sizeof(purchaseOrder), error); if (FAILED(hr)) { goto Exit; } // NULL indicates no more purchase orders if (purchaseOrder == NULL) { break; } // Print out purchase order contents wprintf(L"%ld, %s\n", purchaseOrder->quantity, purchaseOrder->productName); // Free purchase order hr = WsResetHeap( heap, error); if (FAILED(hr)) { goto Exit; } } // Read the end of the message hr = WsReadMessageEnd( channel, message, NULL, error); if (FAILED(hr)) { goto Exit; } hr = WsResetMessage( message, error); if (FAILED(hr)) { goto Exit; } } Exit: if (FAILED(hr)) { // Print out the error PrintError(hr, error); } if (channel != NULL) { // Close the channel WsCloseChannel(channel, NULL, error); } if (listener != NULL) { // Close the listener if it was opened WsCloseListener(listener, NULL, error); } if (channel != NULL) { WsFreeChannel(channel); } if (listener != NULL) { WsFreeListener(listener); } if (message != NULL) { WsFreeMessage(message); } if (heap != NULL) { WsFreeHeap(heap); } if (error != NULL) { WsFreeError(error); } fflush(stdout); return SUCCEEDED(hr) ? 0 : -1; }
// Main entry point int __cdecl wmain() { HRESULT hr = S_OK; WS_ERROR* error = NULL; WS_CHANNEL* channel = NULL; WS_LISTENER* listener = NULL; WS_HEAP* heap = NULL; WS_MESSAGE* requestMessage = NULL; WS_MESSAGE* replyMessage = NULL; static const WS_STRING uri = WS_STRING_VALUE(L"http://+:80/example"); // Create an error object for storing rich error information hr = WsCreateError( NULL, 0, &error); if (FAILED(hr)) { goto Exit; } // Create a listener hr = WsCreateListener( WS_CHANNEL_TYPE_REPLY, WS_HTTP_CHANNEL_BINDING, NULL, 0, NULL, &listener, error); if (FAILED(hr)) { goto Exit; } // Open listener hr = WsOpenListener( listener, &uri, NULL, error); if (FAILED(hr)) { goto Exit; } // Set up a property indicating streamined input and output WS_TRANSFER_MODE transferMode = WS_STREAMED_TRANSFER_MODE; WS_CHANNEL_PROPERTY transferModeProperty; transferModeProperty.id = WS_CHANNEL_PROPERTY_TRANSFER_MODE; transferModeProperty.value = &transferMode; transferModeProperty.valueSize = sizeof(transferMode); // Create a channel suitable for accepting from the listener hr = WsCreateChannelForListener( listener, &transferModeProperty, 1, &channel, error); if (FAILED(hr)) { goto Exit; } hr = WsCreateMessageForChannel( channel, NULL, 0, &requestMessage, error); if (FAILED(hr)) { goto Exit; } hr = WsCreateMessageForChannel( channel, NULL, 0, &replyMessage, error); if (FAILED(hr)) { goto Exit; } // Create a heap to store deserialized data hr = WsCreateHeap( /*maxSize*/ 2048, /*trimSize*/ 512, NULL, 0, &heap, error); if (FAILED(hr)) { goto Exit; } // Receive messages and send replies for (int i = 0; i < 10; i++) { // Accept a channel from the client hr = WsAcceptChannel(listener, channel, NULL, error); if (FAILED(hr)) { goto Exit; } // Receive the message start (headers) hr = WsReadMessageStart( channel, requestMessage, NULL, error); if (FAILED(hr)) { goto Exit; } // Get action value WS_XML_STRING receivedAction; hr = WsGetHeader( requestMessage, WS_ACTION_HEADER, WS_XML_STRING_TYPE, WS_READ_REQUIRED_VALUE, NULL, &receivedAction, sizeof(receivedAction), error); if (FAILED(hr)) { goto Exit; } // Make sure action is what we expect hr = WsXmlStringEquals( &receivedAction, PurchaseOrder_wsdl.messages.PurchaseOrder.action, error); if (hr != S_OK) { hr = WS_E_ENDPOINT_ACTION_NOT_SUPPORTED; goto Exit; } // Initialize the reply message based on the request hr = WsInitializeMessage( replyMessage, WS_REPLY_MESSAGE, requestMessage, error); if (FAILED(hr)) { goto Exit; } // Write the start of the reply message (headers) hr = WsWriteMessageStart( channel, replyMessage, NULL, error); if (FAILED(hr)) { goto Exit; } // Read the contents of the request body, and send response body for (;;) { // Make sure we have at least one purchase order buffered in the request message. // Each purchase order may be up to 1024 bytes in size. hr = WsFillBody( requestMessage, 1024, NULL, error); if (FAILED(hr)) { goto Exit; } // Deserialize purchase order into heap (if any more) _PurchaseOrderType* purchaseOrder; hr = WsReadBody( requestMessage, &PurchaseOrder_wsdl.globalElements.PurchaseOrderType, WS_READ_OPTIONAL_POINTER, heap, &purchaseOrder, sizeof(purchaseOrder), error); if (FAILED(hr)) { goto Exit; } // NULL indicates no more purchase orders if (purchaseOrder == NULL) { break; } // Print out purchase order contents wprintf(L"%ld, %s\n", purchaseOrder->quantity, purchaseOrder->productName); // Serialize a confirmation into the reply message _OrderConfirmationType orderConfirmation; orderConfirmation.expectedShipDate = L"1/1/2006"; orderConfirmation.orderID = 123; hr = WsWriteBody( replyMessage, &PurchaseOrder_wsdl.globalElements.OrderConfirmationType, WS_WRITE_REQUIRED_VALUE, &orderConfirmation, sizeof(orderConfirmation), error); if (FAILED(hr)) { goto Exit; } // Flush the confirmation data if at least 4096 bytes have been accumulated hr = WsFlushBody( replyMessage, 4096, NULL, error); if (FAILED(hr)) { goto Exit; } // Free purchase order hr = WsResetHeap( heap, error); if (FAILED(hr)) { goto Exit; } } // Read the end of the message hr = WsReadMessageEnd( channel, requestMessage, NULL, error); if (FAILED(hr)) { goto Exit; } // Write the end of the message hr = WsWriteMessageEnd( channel, replyMessage, NULL, error); if (FAILED(hr)) { goto Exit; } // Reset the message so it can be used again hr = WsResetMessage( requestMessage, error); if (FAILED(hr)) { goto Exit; } // Reset the message so it can be used again hr = WsResetMessage( replyMessage, error); if (FAILED(hr)) { goto Exit; } if (channel != NULL) { // Close the channel WsCloseChannel(channel, NULL, error); } // Reset the channel so it can be used again hr = WsResetChannel( channel, error); if (FAILED(hr)) { goto Exit; } } Exit: if (FAILED(hr)) { // Print out the error PrintError(hr, error); } if (channel != NULL) { // Close the channel WsCloseChannel(channel, NULL, error); } if (listener != NULL) { // Close the listener if it was opened WsCloseListener(listener, NULL, error); } if (channel != NULL) { WsFreeChannel(channel); } if (listener != NULL) { WsFreeListener(listener); } if (requestMessage != NULL) { WsFreeMessage(requestMessage); } if (replyMessage != NULL) { WsFreeMessage(replyMessage); } if (heap != NULL) { WsFreeHeap(heap); } if (error != NULL) { WsFreeError(error); } fflush(stdout); return SUCCEEDED(hr) ? 0 : -1; }
HRESULT CALLBACK ProcessMessage( __in const WS_OPERATION_CONTEXT* context, __in_opt const WS_ASYNC_CONTEXT* asyncContext, __in_opt WS_ERROR* error) { UNREFERENCED_PARAMETER(asyncContext); WS_CHANNEL* channel = NULL; HRESULT hr = NOERROR; WS_HEAP* heap = NULL; WS_MESSAGE* replyMessage = NULL; hr = WsGetOperationContextProperty( context, WS_OPERATION_CONTEXT_PROPERTY_HEAP, &heap, sizeof(heap), error); if (FAILED(hr)) { goto Exit; } WS_MESSAGE* requestMessage = NULL; hr = WsGetOperationContextProperty( context, WS_OPERATION_CONTEXT_PROPERTY_INPUT_MESSAGE, &requestMessage, sizeof(requestMessage), error); if (FAILED(hr)) { goto Exit; } hr = WsGetOperationContextProperty( context, WS_OPERATION_CONTEXT_PROPERTY_CHANNEL, &channel, sizeof(channel), error); if (FAILED(hr)) { goto Exit; } hr = WsCreateMessageForChannel( channel, NULL, 0, &replyMessage, error); if (FAILED(hr)) { goto Exit; } // Get action value WS_XML_STRING receivedAction; hr = WsGetHeader( requestMessage, WS_ACTION_HEADER, WS_XML_STRING_TYPE, WS_READ_REQUIRED_VALUE, NULL, &receivedAction, sizeof(receivedAction), error); if (FAILED(hr)) { goto Exit; } // Make sure action is what we expect if (WsXmlStringEquals( &receivedAction, PurchaseOrder_wsdl.messages.PurchaseOrder.action, error) != S_OK) { hr = WS_E_ENDPOINT_ACTION_NOT_SUPPORTED; goto Exit; } // Read purchase order _PurchaseOrderType purchaseOrder; hr = WsReadBody( requestMessage, &PurchaseOrder_wsdl.globalElements.PurchaseOrderType, WS_READ_REQUIRED_VALUE, heap, &purchaseOrder, sizeof(purchaseOrder), error); if (FAILED(hr)) { goto Exit; } // Read end of message hr = WsReadMessageEnd( channel, requestMessage, NULL, error); if (FAILED(hr)) { goto Exit; } // Print out purchase order contents wprintf(L"%ld, %s\n", purchaseOrder.quantity, purchaseOrder.productName); // Initialize order confirmation data _OrderConfirmationType orderConfirmation; orderConfirmation.expectedShipDate = L"1/1/2006"; orderConfirmation.orderID = 123; // Send a reply message hr = WsSendReplyMessage( channel, replyMessage, &PurchaseOrder_wsdl.messages.OrderConfirmation, WS_WRITE_REQUIRED_VALUE, &orderConfirmation, sizeof(orderConfirmation), requestMessage, NULL, error); if (FAILED(hr)) { goto Exit; } hr = WS_S_END; Exit: fflush(stdout); if (replyMessage != NULL) { WsFreeMessage( replyMessage); } return hr; }
// This thread is used to recieve reply messages DWORD WINAPI ReceiverThread( __in void* parameter) { HRESULT hr = S_OK; THREAD_INFO* threadInfo = (THREAD_INFO*)parameter; WS_CHANNEL* channel = threadInfo->channel; WS_MESSAGE* message = NULL; WS_ERROR* error = NULL; WS_HEAP* heap = NULL; // Create an error object for storing rich error information hr = WsCreateError( NULL, 0, &error); if (FAILED(hr)) { goto Exit; } // Create a heap to store deserialized data hr = WsCreateHeap( /*maxSize*/ 2048, /*trimSize*/ 512, NULL, 0, &heap, error); if (FAILED(hr)) { goto Exit; } hr = WsCreateMessageForChannel( channel, NULL, 0, &message, error); if (FAILED(hr)) { goto Exit; } // Go into a receive loop. The loop terminates when the // main thread aborts the channel (causing subsequent receives // to fail). for (;;) { // Receive start of reply message (headers) hr = WsReadMessageStart(channel, message, NULL, error); if (FAILED(hr)) { goto Exit; } // Get action value WS_XML_STRING receivedAction; hr = WsGetHeader( message, WS_ACTION_HEADER, WS_XML_STRING_TYPE, WS_READ_REQUIRED_VALUE, NULL, &receivedAction, sizeof(receivedAction), error); if (FAILED(hr)) { goto Exit; } // Make sure action is what we expect if (WsXmlStringEquals(&receivedAction, PurchaseOrder_wsdl.messages.OrderConfirmation.action, error) != S_OK) { hr = WS_E_ENDPOINT_ACTION_NOT_SUPPORTED; goto Exit; } // Read the order confirmation from the body _OrderConfirmationType* orderConfirmation; hr = WsReadBody(message, &PurchaseOrder_wsdl.globalElements.OrderConfirmationType, WS_READ_REQUIRED_POINTER, heap, &orderConfirmation, sizeof(orderConfirmation), error); if (FAILED(hr)) { goto Exit; } // Receive end of message hr = WsReadMessageEnd(channel, message, NULL, error); if (FAILED(hr)) { goto Exit; } // Print out confirmation contents wprintf(L"%s\n", orderConfirmation->expectedShipDate); // Reset message so it can used again hr = WsResetMessage(message, error); if (FAILED(hr)) { goto Exit; } // Reset heap hr = WsResetHeap(heap, error); if (FAILED(hr)) { goto Exit; } } Exit: // Print out the error. Ignore aborted errors, which are // caused by the client thread aborting the channel. if (FAILED(hr) && hr != WS_E_OPERATION_ABORTED) { PrintError(hr, error); } if (message != NULL) { WsFreeMessage(message); } if (error != NULL) { WsFreeError(error); } if (heap != NULL) { WsFreeHeap(heap); } return hr; }
// Messages arrive on this thread from the relay thread DWORD WINAPI ReceiverThread(void* parameter) { HRESULT hr; THREADINFO* threadInfo = (THREADINFO*) parameter; HANDLE receiverReadyEvent = threadInfo->event; WS_ERROR* error = NULL; WS_CHANNEL* receiveChannel = NULL; WS_MESSAGE* receiveMessage = NULL; WS_LISTENER* listener = NULL; WS_HEAP* heap = NULL; // Create an error object for storing rich error information hr = WsCreateError( NULL, 0, &error); if (FAILED(hr)) { goto Exit; } // Create a heap to store deserialized data hr = WsCreateHeap( /*maxSize*/ 2048, /*trimSize*/ 512, NULL, 0, &heap, error); if (FAILED(hr)) { goto Exit; } // Create a listener hr = WsCreateListener(WS_CHANNEL_TYPE_DUPLEX_SESSION, WS_TCP_CHANNEL_BINDING, NULL, 0, NULL, &listener, error); if (FAILED(hr)) { goto Exit; } // Open listener using TCP duplex session hr = WsOpenListener(listener, &serviceUrl, NULL, error); if (FAILED(hr)) { goto Exit; } // Create a channel hr = WsCreateChannelForListener(listener, NULL, 0, &receiveChannel, error); if (FAILED(hr)) { goto Exit; } // Notify that thread is ready threadInfo->hr = NOERROR; SetEvent(receiverReadyEvent); // Accept a channel from the client hr = WsAcceptChannel(listener, receiveChannel, NULL, error); if (FAILED(hr)) { goto Exit; } hr = WsCreateMessageForChannel( receiveChannel, NULL, 0, &receiveMessage, error); if (FAILED(hr)) { goto Exit; } // Receive all messages for (;;) { // Receive start of message (headers) hr = WsReadMessageStart(receiveChannel, receiveMessage, NULL, error); if (FAILED(hr)) { goto Exit; } if (hr == WS_S_END) { // No more messages on this channel break; } // Get action value WS_XML_STRING receivedAction; hr = WsGetHeader( receiveMessage, WS_ACTION_HEADER, WS_XML_STRING_TYPE, WS_READ_REQUIRED_VALUE, NULL, &receivedAction, sizeof(receivedAction), error); if (FAILED(hr)) { goto Exit; } // Make sure action is what we expect if (WsXmlStringEquals(&receivedAction, PurchaseOrder_wsdl.messages.PurchaseOrder.action, error) != S_OK) { hr = WS_E_ENDPOINT_ACTION_NOT_SUPPORTED; goto Exit; } // Read the body of the message as a purchase order _PurchaseOrderType* purchaseOrder; hr = WsReadBody(receiveMessage, &PurchaseOrder_wsdl.globalElements.PurchaseOrderType, WS_READ_REQUIRED_POINTER, heap, &purchaseOrder, sizeof(purchaseOrder), error); if (FAILED(hr)) { goto Exit; } // Print out purchase order wprintf(L"%ld, %s\n", purchaseOrder->quantity, purchaseOrder->productName); // Receive message end hr = WsReadMessageEnd(receiveChannel, receiveMessage, NULL, error); if (FAILED(hr)) { goto Exit; } // Reset message so we can use it again hr = WsResetMessage(receiveMessage, error); if (FAILED(hr)) { goto Exit; } // Reset heap hr = WsResetHeap(heap, error); if (FAILED(hr)) { goto Exit; } } Exit: if (FAILED(hr)) { // Print out the error PrintError(hr, error); } fflush( stdout); if (receiveChannel != NULL) { // Close the channel WsCloseChannel(receiveChannel, NULL, error); } if (receiveChannel != NULL) { WsFreeChannel(receiveChannel); } if (listener != NULL) { // Close the listener if it was opened WsCloseListener(listener, NULL, error); } if (listener != NULL) { WsFreeListener(listener); } if (receiveMessage != NULL) { WsFreeMessage(receiveMessage); } if (error != NULL) { WsFreeError(error); } if (heap != NULL) { WsFreeHeap(heap); } if (FAILED(hr)) { // Notify that thread is ready SetEvent(receiverReadyEvent); threadInfo->hr = hr; } return 1; }