Handle NormalizePoisonException in MSMQ Integration Binding - c#

Good evening,
I have an MSMQ queue that is pushing messages to a WCF service using msmqIntegrationBinding.
The receiveErrorHandling property is set to the default of "Fault".
Occasionally, MSMQ has a hissy fit trying to deserialise a message:
System.ServiceModel.ProtocolException: An error was encountered while deserializing the message. The message cannot be received.
System.Runtime.Serialization.SerializationException: An error occurred while deserializing an MSMQ message's XML body. The message cannot be received. Ensure that the service contract is decorated with appropriate [ServiceKnownType] attributes or the TargetSerializationTypes property is set on the MsmqIntegrationBindingElement.
at System.ServiceModel.Channels.MsmqDecodeHelper.XmlDeserializeForIntegration(MsmqIntegrationChannelListener listener, Stream stream, Int64 lookupId)
at System.ServiceModel.Channels.MsmqDecodeHelper.DeserializeForIntegration(MsmqIntegrationChannelListener listener, Stream bodyStream, MsmqIntegrationMessageProperty property, Int64 lookupId)
The message never reaches the method in the service:
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void ProcessMessage(MsmqMessage<MyMessage> msg)
{...
The service has this attribute:
[ServiceKnownType(typeof(MyMessage))]
There is a dead letter queue set in the binding:
<msmqIntegrationBinding>
<binding name="MyBinding" serializationFormat="Xml" exactlyOnce="false" deadLetterQueue="Custom" customDeadLetterQueue="msmq.formatname:DIRECT=OS:.\private$\services/deadLetterQueue" useMsmqTracing="true" receiveErrorHandling="Fault">
The message is not processed by the WCF service and is dumped straight into the Journal queue. It is not left in the Messaging queue or moved to the dead letter queue.
I have tried implementing an IErrorHandler as detailed here but it doesn't reach it.
When receiving a message from MSMQ in the traditional way...
MessageQueue msMq = new MessageQueue(_queueName);
msMq.Formatter = new XmlMessageFormatter(new Type[] { typeof(MyMessage) });
Message m = msMq.Receive();
It works if I set the formatter as above. But when despite serializationFormat="Xml" being set in the binding, it still fails to deserialise.
I've missed something for sure. I've Googled everywhere. Any help is greatly appreciated.
Thanks.

After some intense Googling I came across two issues:
1) If you want transactions in MSMQ to work, make sure that whoever setup the queue made it a transactional queue when they created it. (Sigh..)
2) In the IErrorHandler example, I wasn't concentrating with my copying and pasting (Less coffee, more sleep) and put the ApplyDispatchBehavior logic into Validate by mistake, where no ChannelDispatchers exist yet (Extra sigh...).
foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
channelDispatcher.ErrorHandlers.Add(errorHandler);
}
What a kerfluffle!

Related

Microsoft service bus - Receive messages from Bus with OnMessage() method

My application has to receive a message every time new message is posted. So I'm using OnMessage() method as mentioned in Microsoft documentation.
When new messages are posted the OnMessage() method does not seem to be working. To resolve this, I've placed the code into a separate task with infinite loop. This seems totally wrong.
public void ReceiveMessageFromSubscription(string topicName, string subscriptioName)
{
Task.Factory.StartNew(() =>
{
while (true)
{
SubscriptionClient Client = SubscriptionClient.CreateFromConnectionString(connectionString, topicName, subscriptionName);
Client.OnMessage((message) =>
{
try
{
var message = brokerMessage.GetBody<MessageDto>();
newMessage.AnnounceNewMessage(message);
message.Complete();
}
catch (Exception ex)
{
message.Abandon();
}
});
}
});
}
Whenever there is a message in Subscription the OnMessage() method has to be called. Can anyone please help me with this.
OnMessage API is an asynchronous process that receives messages in an event-driven message pump. It doesn't stop receiving until you either dispose the client or the code that is running it is terminated. The code above is wrong. You should not instantiate a subscription client in a tight loop and register your callback each time. What you should be doing is creating your client, registering a callback with a desired concurrency, and hold on to that client until you no longer need to receive messages.
Remember, it's a message pump that has to run all the time. Official documentation is a bit dry, perhaps this post will help.
In addition to that, I would strongly recommend not to use the legacy client WindowsAzure.ServiceBus which you're using. Instead, prefer the new Microsoft.Azure.ServiceBus client.

Timeout exception - c# ( wcf )

While I'm executing the server method asynchronously, getting this Timeout exception continuously.
"Additional information:
This request operation sent to http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous
did not receive a reply within the configured timeout (00:01:00).
The time allotted to this operation may have been a portion of a longer timeout.
This may be because the service is still processing the operation or
because the service was unable to send a reply message.
Please consider increasing the operation timeout
(by casting the channel/proxy to IContextChannel and setting the OperationTimeout property)
and ensure that the service is able to connect to the client."
Could someone mention how to increasing the operation timeout
by casting the channel/proxy to IContextChannel and setting the OperationTimeout property ?
This is my existing binding (with client) code.
DuplexChannelFactory<IPortal> datafactory;
NetTcpBinding tcpBinding = new NetTcpBinding();
String sURL = "net.tcp://localhost:8002/MyPortal";
tcpBinding.MaxReceivedMessageSize = System.Int32.MaxValue;
tcpBinding.ReaderQuotas.MaxArrayLength = System.Int32.MaxValue;
datafactory = new DuplexChannelFactory<IPortal>(this,tcpBinding, sURL);
Portal = datafactory.CreateChannel();
If you follow the link in the error (schemas.microsoft.com etc etc) it serves up:
Theresource you are looking for has been removed, had its name
changed, or is temporarily unavailable.
Why are you looking up MS? It sounds like you've got some config data wrong somewhere. If you search your source for that url, what do you find? Does it look sensible?

MSMQ: Does MSMQ guarantees sequential message passing?

After load testing, I found some packets are not sequential.
it's a basic WCF service and client is continuously sending the request.
It is possible to have guaranteed (in-order, exactly-once) delivery using netMsmqBinding.
The first thing you need to do is to create your actual MSMQ message queue transactional.
Secondly, you must tell WCF to enlist in the transaction like so:
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void Handle(Something msg)
{
....
}
Lastly, you need to specify guaranteed behavior of the service by using the exactlyOnce binding paramter:
<netMsmqBinding>
<binding name="netMsmqBinding_IMyServiceInterface"
exactlyOnce="true">
...
</binding>
</netMsmqBinding>
ExactlyOnce tells WCF that we require transactions, that each message will be delivered exactly once and in the order they were sent.

Can I throw a message fault back to a WCF client from a routing service

My company has long used ASPX and windows services through a routing program to manage the connections and allow our datacenters to control where the clients make connections.
Just recently we started using MVC and WCF. Yes in 2015 we are just moving to these things. Anyway they figured out about a month before release they need to route the WCF traffic because of where it sits outside the firewall or in the DMZ.
We will be putting the routing service inside the network and opening a port on a firewall that way all services can communicate protected by the firewall on the routing service.
The only reason I have given all these details is because this sets up the expectations for how our WCF service is configured. We are using certificates for security and message encryption as we must send our messages encrypted. We are also adding a custom header that our router will use to route the messages.
What we have working is our router receives the message, inspects the header, finds if we have an endpoint, forwards the message, gets the response and sends it back to the WCF service. Currently that is all working beautifully. We can even receive the messagefault from the endpoint and send it back to the client.
What I am struggling with is how the router can send a messageFault back to the client when the fault originates in the router. But because my router doesn't know how to encrypt the message this fault is being sent back unencrypted. The faults beings things like "endpoint unreachable" or "no endpoint found". The client receives these faults with the error:
An unsecured or incorrectly secured fault was received from the other party.
See the inner FaultException for the fault code and detail.
With the inner exception being what I defined in my code below.
Dim fe As FaultException = New FaultException(status)
Dim fault As MessageFault = fe.CreateMessageFault()
Dim msg = System.ServiceModel.Channels.Message.CreateMessage(getMessageVersion, fault, getAction)
getMessageVersion will return Soap11 or Soap12 depending on the client
getAction is returning the action from the client request in the header.
Is there a way to configure the clients to accept these faults even though they aren't encrypted? We would like to catch them as Fault Exceptions not as Exceptions to keep our Fault Exception logic together for general Faults.
Any help or insight would be appreciated. Our newer programs are using C# our older ones are still written in VB, so throw whatever .Net code at me you are comfortable with and I'll use it.
Here are the Fault class:
[Serializable]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "yourNamespace")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "yourNamespace")]
[System.Runtime.Serialization.DataContractAttribute(Namespace = "yourNamespace")]
public class CustomFault : ISerializable
{
public string Message { get; set; }
}
Here is the interface and operating method referencing the Fault:
[ServiceContract(Namespace = "yourNamespace")]
public interface IService
{
[FaultContractAttribute(
typeof(CustomFault),
Action = "",
Name = "Fault",
Namespace = "yourNamespace")]
[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults = true)]
[OperationContract]
Response Operation(Request request);
}
And finally how I throw the Fault:
throw new FaultException<CustomFault>
(
new GuiaMedicoFault("Custom Error description"),
new FaultReason("Error description")
);
Maybe some piece of this code can help you or give any ideas.
Using Ricardo's information I was able to narrow down what I was doing. The actual answer to this is entirely up to the Endpoint configuration. It is dependent on your binding.
We didn't have to change any thing in the router, but we had to change the client it looks like this.
serviceClient.Endpoint.Binding = getWSHttpBinding();
Then it should be
private CustomBinding getWSHttpBinding()
{
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.NegotiateServiceCredential = false;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
binding.MaxReceivedMessageSize = (1024 * 1024 * 10);
BindingElementCollection elements = binding.CreateBindingElements();
elements.Find<SecurityBindingElement>().EnableUnsecuredResponse = true;
return new CustomBinding(elements);
}
this link has further information if you see this problem. https://support.microsoft.com/en-us/kb/971493
We will probably just handle this as an exception rather than a fault.

Catch-22 prevents streamed TCP WCF service securable by WIF; ruining my Christmas, mental health

I have a requirement to secure a streamed WCF net.tcp service endpoint using WIF. It should authenticate incoming calls against our token server. The service is streamed because it is designed to transfer large amounts of data n stuff.
This appears to be impossible. And if I can't get around the catch, my Christmas will be ruined and I'll drink myself to death in a gutter while merry shoppers step over my slowly cooling body. Totes serious, you guys.
Why is this impossible? Here's the Catch-22.
On the client, I need to create a channel with the GenericXmlSecurityToken I get from our token server. No problemo.
// people around here hate the Framework Design Guidelines.
var token = Authentication.Current._Token;
var service = base.ChannelFactory.CreateChannelWithIssuedToken(token);
return service.Derp();
Did I say "no problemo"? Problemo. In fact, NullReferenceException style problemo.
"Bro, " I asked the Framework, "do you even null check?" The Framework was silent, so I disassembled and found that
((IChannel)(object)tChannel).
GetProperty<ChannelParameterCollection>().
Add(federatedClientCredentialsParameter);
was the source of the exception, and that the GetProperty call was returning null. So, WTF? Turns out that if I turn on Message security and set the client credential type to IssuedToken then this property now exists in the ClientFactory (protip: There is no "SetProperty" equivalent in IChannel, the bastard).
<binding name="OMGWTFLOL22" transferMode="Streamed" >
<security mode="Message">
<message clientCredentialType="IssuedToken"/>
</security>
</binding>
Sweet. No more NREs. However, now my client is faulted at birth (still love him, tho). Digging through WCF diagnostics (protip: make your worst enemies do this after crushing them and driving them before you but right before enjoying the lamentations of their women and children), I see it's because of a security mismatch between the server and client.
The requested upgrade is not supported by 'net.tcp://localhost:49627/MyService'. This could be due to mismatched bindings (for example security enabled on the client and not on the server).
Checking the host's diags (again: crush, drive, read logs, enjoy lamentations), I see this is true
Protocol Type application/ssl-tls was sent to a service that does not support that type of upgrade.
"Well, self," I says, "I'll just turn on Message security on the host!" And I do. If you want to know what it looks like, it's an exact copy of the client config. Look up.
Result: Kaboom.
The binding ('NetTcpBinding','http://tempuri.org/') supports streaming which cannot be configured together with message level security. Consider choosing a different transfer mode or choosing the transport level security.
So, my host cannot be both streamed and secured via tokens. Catch-22.
tl;dr: How can I secure a streamed net.tcp WCF endpoint using WIF???
WCF has gotchas in a few areas with streaming (I'm looking at you, MTOM1) due to a fundamental issue in how it fails to perform preauthentication the way most people would think that should work (it only affects subsequent requests for that channel, not the first request) Ok, so this is not exactly your issue but please follow along as I will get to yours at the end. Normally the HTTP challenge works like this:
client hits server anonymously
server says, sorry, 401, I need authentication
client hits server with authentication token
server accepts.
Now, if you ever try to enable MTOM streaming on an WCF endpoint on the server, it will not complain. But, when you configure it on the client proxy (as you should, they must match bindings) it will explode in a fiery death. The reason for this is that the above sequence of events that WCF is trying to prevent is this:
client streams 100MB file to server anonymously in a single POST
server says sorry, 401, I need authentication
client again streams 100MB file to server with an authentication header
server accepts.
Notice that you just sent 200MB to the server when you only needed to send 100MB. Well, this is the problem. The answer is to send the authentication on the first attempt but this is not possible in WCF without writing a custom behaviour. Anyway, I digress.
Your Problem
First up, let me tell you that what you're trying is impossible2. Now, in order for you to stop spinning your wheels, let me tell you why:
It strikes me that you are now wandering in a similar class of problem. If you enable message level security, the client must load the entire stream of data into memory before it can actually close out the message with the usual hash function and xml signature required by ws-security. If it has to read the entire stream to sign the single message (which is not really a message, but it's a single continuous stream) then you can see the problem here. WCF will have to stream it once "locally" to compute the message security, then stream it again to send it to the server. This is clearly a silly thing, so WCF does not permit message level security for streaming data.
So, the simple answer here is that you should send the token either as a parameter to the initial web service, or as a SOAP header and use a custom behaviour to validate it. You cannot use WS-Security to do this. Frankly, this is not just a WCF issue - I cannot see how it could practically work for any other stacks.
Solving the MTOM Problem
This is just for an example how I solved my MTOM streaming issue for basic authentication, so perhaps you could take the guts of this and implement something similar for your issue. The crux of it is that in order to enable your custom message inspector, you have to disable all notion of security on the client proxy (it remains enabled on the server,) apart from transport level (SSL):
this._contentService.Endpoint.Behaviors.Add(
new BasicAuthenticationBehavior(
username: this.Settings.HttpUser,
password: this.Settings.HttpPass));
var binding = (BasicHttpBinding)this._contentService.Endpoint.Binding;
binding.Security.Mode = BasicHttpSecurityMode.Transport; // SSL only
binding.Security.Transport.ClientCredentialType =
HttpClientCredentialType.None; // Do not provide
Note that I have turned off transport security here because I will be providing that myself using a message inspector and custom behaviour:
internal class BasicAuthenticationBehavior : IEndpointBehavior
{
private readonly string _username;
private readonly string _password;
public BasicAuthenticationBehavior(string username, string password)
{
this._username = username;
this._password = password;
}
public void AddBindingParameters(ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
var inspector = new BasicAuthenticationInspector(
this._username, this._password);
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
}
internal class BasicAuthenticationInspector : IClientMessageInspector
{
private readonly string _username;
private readonly string _password;
public BasicAuthenticationInspector(string username, string password)
{
this._username = username;
this._password = password;
}
public void AfterReceiveReply(ref Message reply,
object correlationState) { }
public object BeforeSendRequest(ref Message request,
IClientChannel channel)
{
// we add the headers manually rather than using credentials
// due to proxying issues, and with the 101-continue http verb
var authInfo = Convert.ToBase64String(
Encoding.Default.GetBytes(this._username + ":" + this._password));
var messageProperty = new HttpRequestMessageProperty();
messageProperty.Headers.Add("Authorization", "Basic " + authInfo);
request.Properties[HttpRequestMessageProperty.Name] = messageProperty;
return null;
}
}
So, this example is for anyone who is suffering with the MTOM issue, but also as a skeleton for you to implement something similar to authenticate your token generated by the primary WIF-secured token service.
Hope this helps.
(1) Large Data and Streaming
(2) Message Security in WCF (see "disadvantages.")

Categories