Wednesday, 10 November 2010

Consume a Soap with Attachments service from WCF

I'm putting this one out there now before I completely forget what I did.  One of our providers, in their *cough* infinite wisdom *cough* decided to deliver us some new functionality using Soap with Attachments.  On researching soap with attachments I found various sources that said the technology was dead - specified in 2004 and defunct as of 2006 in favour of MTOM.  It's now 2010 - They've delivered us something that's been officially dead for 4 years.  Sigh....
Now, MTOM is supposedly backward compatible with SWA (Soap with attachments) and WCF does support MTOM but it's impossible to guarantee that an unsupported technology that is supposedly supported won't break with changes to the WCF framework down the line.  That said, if anyone has got information about how to use an MTOM binding to send an attachment in which you can get the id of the attachment out then please let me know.
So, here's how to send SWA messages from WCF to a service not specified in .NET.
First off - go to codeplex and get the WCF Soap with attachments encoder (http://wcfswaencoder.codeplex.com/).
Now, include the project in your solution as you'll probably need to do some updates to the code to fix a bug or two.
In the project your using to call the SWA service from make sure you're using a WCF service reference rather than a web reference.
Add the following config entries to make sure you're using the appropriate encoder:


<configuration>
    <system.serviceModel>
      <!--
      WCF Extensions configuration section
    -->
      <extensions>
        <!--
        SOAP-With-Attachments custom encoder with custom mime parsing sample
        You need to perform this configuration manually after you have added a 
        Service Reference with Visual Studio or svcutil.exe from the .NET Framework SDK!
      -->
        <bindingElementExtensions>
          <add name="swaMessageEncoding"
               type="Microsoft.Austria.WcfHelpers.SoapWithAttachments.SwaMessageEncodingElement, Microsoft.Austria.WcfHelpers.SoapWithAttachments" />
        </bindingElementExtensions>
      </extensions>
      
        <bindings>
            <customBinding>
              <binding name="SwaBindingConfiguration">
                <swaMessageEncoding innerMessageEncoding="textMessageEncoding" />
                <httpTransport manualAddressing="false" maxBufferPoolSize="524288"
                        maxReceivedMessageSize="62914560" allowCookies="false" authenticationScheme="Anonymous"
                        bypassProxyOnLocal="false" decompressionEnabled="true" hostNameComparisonMode="StrongWildcard"
                        keepAliveEnabled="true" maxBufferSize="62914560" proxyAuthenticationScheme="Anonymous"
                        realm="" transferMode="Streamed" unsafeConnectionNtlmAuthentication="false"
                        useDefaultWebProxy="true">
                  <!--<extendedProtectionPolicy policyEnforcement="Never" />-->
                </httpTransport>
              </binding>
            </customBinding>
        </bindings>
        <client>
           <endpoint address="http://xyz.zyx.com"
                binding="customBinding" bindingConfiguration="SwaBindingConfiguration"
                contract="ServiceRef.Contract" name="MyEndpoint" />
        </client>
    </system.serviceModel>
</configuration>

Now, once you've set up the client using something like ServiceRef.Contract client = new ServiceRef.Contract("MyEndpoint"); you'll need to wrap all your operations in a using statement that gives the OperationContext like so:

using (OperationContextScope Scope = new OperationContextScope(client.InnerChannel))
            {
                ServiceRef.WSLoginParamsDTO loginDTO = new ServiceRef.WSLoginParamsDTO();
                ...
                sessionId = client.login(loginDTO);
            }

That applies to all operations against this client.

Now, you can add an attachment to the message by doing the following:

using (OperationContextScope Scope = new OperationContextScope(client.InnerChannel))
            {
                OperationContext.Current.OutgoingMessageProperties.Add
                    (
                        Microsoft.Austria.WcfHelpers.SoapWithAttachments.SwaEncoderConstants.AttachmentProperty,
                        AttachmentContents
                    );


                Console.WriteLine("Calling the service...");
                ServiceRef.WSCreateJobImageParamsDTO createJobImageParameters = new ServiceRef.WSCreateJobImageParamsDTO();
                ...
// This bit might not be required, depending on your web service
                createJobImageParameters.imageId = "UZE_26123_";
                ...
                bool success = client.addJobImage(createJobImageParameters);
}


Personally, I also had to make a change to the Microsoft helper library itself.  In SwaEncoder the WriteMessage function needed to be changed so the line:
 _SoapMimeContent.Content = Encoding.UTF8.GetBytes(message.GetBody<string>());
became
_SoapMimeContent.Content = Encoding.UTF8.GetBytes(message.ToString());

If you're still struggling, you should get yourself two tools that will let you debug the messages that are going over the wire.  The first is Soap-UI, which lets you add attachments to messages and test the way it should work (yes, it's a java tool but it's really useful):
Add the attachment by clicking the Attachments button at the button, add an attachment then specify the Content ID for it if you need to reference it in the request.
Once you've submitted a successful request you can use the Raw tab at the side of the request to see exactly what went over the wire.

That shows you what the SOAP should look like.  Now, to see what you're actually sending in your app use Wireshark, start tracing on your network interface and send the message.  You can then follow the TCP conversation by finding the IP address you're supposed to be communicating with, right clicking on the packet and clicking "Follow TCP stream":

Hope that helped!

Friday, 15 October 2010

App Pool stopping in IIS7 - error 0xc0000374, fatal communication error with the Windows Process Activation Service

It's my first blog post people!  But don't get too excited, I'm only starting this as somewhere to record all those really evil issues you come across when trouble shooting that wind up being a pig to google for.

So, I'll dive right in.  We had an issue with a web service falling over on a live server.  It wasn't happening in dev and we hadn't really seen it happen in test but come go live, there it was.  Inconsistent but still quite common, our Asp .NET 2.0 App Pool would go down with the following entries in the event log:

Error:
Faulting application w3wp.exe, version 7.0.6001.18000, time stamp 0x47919413, faulting module ntdll.dll, version 6.0.6001.18000, time stamp 0x4791a7a6, exception code 0xc0000374, fault offset 0x000b015d, process id 0x1e78, application start time 0x01cb6b79e518d927.
Warning:
A process serving application pool 'ASP.NET 2.0 App Pool' suffered a fatal communication error with the Windows Process Activation Service. The process id was 'xxxx'. The data field contains the error number.

It turns out that this error message indicates some form of heap corruption going on.

After much googling I found the following potential solution that suggested we disable the DynamicIPRestrictionModule.  Well, there isn't a dynamic IP restriction module in IIS anymore, but I went ahead and disabled the IPRestrictionModule anyway.
We did that by opening IIS config %system32%/inetsrv/config/applicationHost.config file and commenting out the following 2 lines:
<!-- <add name="IpRestrictionModule" image="%windir%\System32\inetsrv\iprestr.dll" /> -->
<!-- <add name="IpRestrictionModule" lockItem="true" /> -->
So far as I'm aware, this module is used to prevent access from specific IP addresses or domains, which isn't quite the same as the dynamic ip restriction module but we weren't using it and we figured it was better to try than not.  That said, I don't think this did anything to help, but it was worth a try while we were waiting for the crash to happen again.

The other thing we did was to install the Debug Diag tool on the server and watch for the crash.  This gave us the following error message in the logs to work from:
Script Error
Error Code - 0x80004005
Error Source [Unavailable]
Error Description [Could not obtain System ID for this thread]

This lead us to another potential cause (I can't find the source of this now) that suggested it was a piece of code that was disposing of an already disposed object.  We had just one destructor on the project, so we took that out and we're hoping it takes the problem away.  I'll update this post once I know which solution did what.

UPDATE:
It's now significantly after the fact and we haven't seen this again since so I believe the problem was in fact a destructor that didn't need to be there.  Check your libraries, check your dlls people!  Make sure there aren't any unnecessary destructors in there! (Sorry Tim, I don't know how to reply to you directly).