GZip compression with WCF hosted on IIS7

2019-01-21 17:35发布

问题:

Everyone, as far as I'm concerned the question is ansered in EDIT 2. Although it's only a partial solution to the IIS side of the problem, it's what I was looking for.


So I'm going to add my query to the small ocean of questions on the subject.

I'm trying to enable GZip compression on large soap responses from a WCF service. So far, I've followed instructions here and in a variety of other places to enable dynamic compression on IIS. Here's my dynamicTypes section from the applicationHost.config:

<dynamicTypes>
    <add mimeType="text/*" enabled="true" />
    <add mimeType="message/*" enabled="true" />
    <add mimeType="application/x-javascript" enabled="true" />
    <add mimeType="application/atom+xml" enabled="true" />
    <add mimeType="application/xaml+xml" enabled="true" />
    <add mimeType="application/xop+xml" enabled="true" />
    <add mimeType="application/soap+xml" enabled="true" />
    <add mimeType="*/*" enabled="false" />
</dynamicTypes>

And also:

<urlCompression doDynamicCompression="true" doStaticCompression="true" />

Though I'm not so clear on why that's needed.

Threw some extra mime-types in there just in case. I've implemented IClientMessageInspector to add Accept-Encoding: gzip, deflate to my client's HttpRequests. Here's an example of a request-header taken from fiddler:

POST http://[omitted]/TestMtomService/TextService.svc HTTP/1.1
Content-Type: application/soap+xml; charset=utf-8
Accept-Encoding: gzip, deflate
Host: [omitted]
Content-Length: 542
Expect: 100-continue

Now, this doesn't work. There's simply no compression happening, no matter what the size of the message (tried up to 1.5Mb). I've looked at this post, but have not run into an exception as he describes, so I haven't tried the CodeProject implementation that he proposes. Also I've seen a lot of other implementations that are supposed to get this to work, but cannot make sense of them (e.g., msdn's GZip encoder). Why would I need to implement the encoder, or the code-project solution? Shouldn't IIS take care of the compression?

So what else do I need to do to get this to work?

Joni

EDIT: I thought the WCF bindings might be worth posting, though I'm not sure if they're relevant (these are from client):

<system.serviceModel>
<bindings>
    <wsHttpBinding>
    <binding name="WsTextBinding" closeTimeout="00:01:00" openTimeout="00:01:00"
      receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"
      transactionFlow="false" hostNameComparisonMode="StrongWildcard"
      maxBufferPoolSize="5000000" maxReceivedMessageSize="5000000"
      messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
      allowCookies="false">
      <readerQuotas maxDepth="32" maxStringContentLength="5000000"
        maxArrayLength="5000000" maxBytesPerRead="5000000" maxNameTableCharCount="5000000" />
      <reliableSession ordered="true" inactivityTimeout="00:10:00"
        enabled="false" />
      <security mode="None">
        <transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
        <message clientCredentialType="None" negotiateServiceCredential="false"
          algorithmSuite="Default" establishSecurityContext="false" />
      </security>
<client>
  <endpoint address="http://[omitted]/TestMtomService/TextService.svc"
   binding="wsHttpBinding" bindingConfiguration="WsTextBinding" behaviorConfiguration="GzipCompressionBehavior"
   contract="TestMtomModel.ICustomerService" name="WsTextEndpoint">
  </endpoint>
</client>
<behaviors>
  <endpointBehaviors>
    <behavior name="GzipCompressionBehavior">
      <gzipCompression />
    </behavior>
  </endpointBehaviors>
</behaviors>
<extensions>
  <behaviorExtensions>
    <add name="gzipCompression"
         type="TestMtomModel.Behavior.GzipCompressionBehaviorExtensionElement, TestMtomModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  </behaviorExtensions>
</extensions>
    </binding>
  </wsHttpBinding>
</bindings>

EDIT 2: Well for anyone else in this mysterious situation, I have a partial solution. I.e., I've gotten IIS7 to at least compress the soap messages from the service (though I now get an exception on the client, but for that there have been several solutions posted). The problem was that the DynamicCompressionModule was not installed on my server. "Installing" it actually, for me, meant simply adding this line to applicationHost.config's section:

<add name="DynamicCompressionModule" image="%windir%\System32\inetsrv\compdyn.dll" />

(Assuming the dll exists in that directory, which in my case it did.) And then adding the module via IIS7's Modules section for the website or server.

回答1:

Try adding 'application/soap+xml; charset=utf-8' as dynamic type in applicationHost. Adding this charset part helped me to enable compression for some JSON responses from my http handler.



回答2:

This is basically the same answer as @marko - with detailed steps. This is a frustrating topic every time I revisit it so I wanted to outline everything I need to do to get it working.

  • First things first you'll want to be using .NET 4 on IIS7. This is because it wasn't until .NET 4 that WCF was capable of automatically decompressing gzip streams. The details of this are descibed in 'Whats new in WCF 4' (some useful comments in feedback).

    We’ve made that easier when using HTTP by allowing the client to automatically negotiate using gzip or deflate compressed streams and then automatically decompress them.

  • Second, make sure you have enabled the dynamic compression module in IIS. You may need to go to 'Programs and Features' to install it. Be sure that you have it enabled for the web application in question - as it is not a global setting.

  • Now WPF uses the MIME type application/soap+xml; charset=utf-8 for HTTP transmission (at least with wsHttpBinding it does). By default this is NOT classed as a dynamic type so it needs to be added to the applicationHost.config file.

    Just edit this file ON THE SERVER: C:\Windows\System32\Inetsrv\Config\applicationHost.config

    In the <dynamicTypes> node add the following line (BEFORE THE / LINE) :

    <add mimeType="application/soap+xml; charset=utf-8" enabled="true" />
    

    More on the applicationHost.config file

  • Restart IIS

You should now have compressed data - which can be verified in Fiddler.



回答3:

I've been struggling with this too and couldn't get it to work with my .svc even though it was fine with .aspx files in the same app. It turns out that it only works with basicHttpBinding and not wsHttpBinding or a binary encoded customBinding. This is fine for me as the compression factor outwieghs the benefit of the binary encoder (which reduces message size by quite a lot but not the factor of 10 that the compression gives).



回答4:

Best bet is to evaluate the specific mime type you are having trouble with (e.g. Fiddler) and ensure that is incorporated in applicationHost.config. If compression is installed and configured correctly, failed request tracing will let you know that compression was not performed with a "NO_MATCHING_CONTENT_TYPE" disposition.



回答5:

The mime type is most likely Application/octet-stream



回答6:

Just for someone else who may look in the future. We upgraded to Windows 2012 server and IIS 8. The old config compression mimetypes which worked for IIS 7 did not work for IIS 8.

So, I tried a couple of suggestions here, but nothing worked. I ended up just adding this to httpcompression dynamictypes: multipart/* and then gzip compression was working again.

Hope this helps someone else.



回答7:

A lot of people are struggling with enabling IIS level compression for WCF Services. Mainly people are struggling with wsHttpBinding, where basicHttpBinding compresses out of the box if dynamic compression is enabled in IIS. I am not going to go into how to enable dynamic compression because it's covered in many different posts, just search for "iis compression application/soap+xml". As previously stated, if you've configured everything properly you should be able to see WCF responses compressed with Content-Encoding: gzip in response header. I suggest using Fiddler to track your request/response. This was my (any many others) experience with basicHttpBinding. The question then becomes why does it not work with wsHttpBinding? Undoubtedly you've already read that it's because the Content-Type is different between those two bindings. basicHttpBinding uses Content-Type: text/xml; charset=utf-8 where wsHttpBinding uses Content-Type: application/soap+xml; charset=utf-8. The former is covered by the default IIS applicationHost.config setting under dynamicTypes. The latter is not. You probably read that you need to add this additional mimeType and restart IIS to fix this. This works for some people, however it does not for many. Some declare that it's simply not compatible with wsHttpBinding. Here is the problem. There are potentially two applicationHost.config files:

C:\Windows\System32\inetsrv\config
C:\Windows\SysWOW64\inetsrv\Config

System32 is the 64bit one, and is the one used by your installation of IIS. This is also the one you've modified to add the additional mimeType to, or so you thought!

cannot manually edit applicationhost.config - MUST READ

As it turns out, if you use a 32bit application such as Notepad++, what you end up modifying is the file in the SysWOW64 folder. This is completely transparent to you. If you use regular Notepad application you'll notice that the mimeType you've added is actually not in the file that is located in the System32 folder, it was magically added to the SysWOW64 folder, even though you've never actually browsed to that folder to begin with. Hopefully this saves you many hours of grief.



回答8:

If anyone ever stumble upon this when hosting in Azure as WebApp, you can configure the applicationHost.config using an extension called "IIS Manager" using the azure portal under "Extensions". More info on the extension itself https://github.com/shibayan/IISManager