9

I have an asp.net .asmx webservice written to handle requests from a third party tool. The third party tool makes an http POST request to the webservice to get user information. I'm using IIS7

Running Fiddler with "Remove All Encodings" checked, I can see the webservice call and and everything functions properly. If I uncheck "Remove All Encodings", the webservice call fails with a 400 Bad Request. The difference I see is that the header "Content-Encoding: gzip" is being removed by Fiddler and the content is being decompressed.

So, when the Content-Encoding header is removed and the content is decompressed, my webservice functions perfectly. When the header is present and the content is compressed, the webservice fails.

How can I either:

  1. Configure my webservice to tell the client that it won't accept compressed requests (and hope that the third party tool respects that)
  2. Decompress the content early in the asp.net handling
  3. Modify my webservice to work with compressed data

Update: To be clear, I don't need to configure gzip encoding in the Response, I need to deal with a Request TO my webservice that is gzip encoded.

Update 2: The third-party tool is the Salesforce.com Outlook plugin. So, I don't have access to modify it and it is used by many other companies without trouble. It's got to be something I'm doing (or not doing)

Update 3: I found one post here that says that IIS does not support incoming POST requests with compressed data, it only supports compressed Responses. Can this still be true?

Geoff
  • 9,340
  • 7
  • 38
  • 48

5 Answers5

6

The simplest technique is to create an HttpModule that replaces the request filter. It is more reusable and avoids having a Global.asax. There is also no need to create a new decompress stream class as the GZipStream is ready for that. Here is the full code, that also removes the Content-Encoding: gzip that is not needed any more:

public class GZipRequestDecompressingModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.BeginRequest += (sender, e) =>
        {
            var request = (sender as HttpApplication).Request;

            string contentEncoding = request.Headers["Content-Encoding"];

            if (string.Equals(contentEncoding, "gzip",
                StringComparison.OrdinalIgnoreCase))
            {
                request.Filter = new GZipStream(request.Filter,
                    CompressionMode.Decompress);
                request.Headers.Remove("Content-Encoding");
            }
        };
    }
    public void Dispose()
    {
    }
}

To activate this module, add the following section into your web.config:

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
        <add name="AnyUniqueName"
            type="YourNamespace.GZipRequestDecompressingModule, YourAssembly"
            preCondition="integratedMode" />
    </modules>
</system.webServer>
Mart
  • 5,608
  • 3
  • 32
  • 45
  • Is it possible to only activate this module on certain paths, to avoid any potential side effects? Ie only enable it for ASMX files? – NickG May 21 '13 at 15:49
  • 1
    Certainly, use the [location element](http://msdn.microsoft.com/en-us/library/b6x6shw7(v=vs.100).aspx) in web.config where you specify a `path` attribute as needed. – Mart May 23 '13 at 09:13
  • BTW: instead of string.Equals(contentEncoding, "gzip", StringComparison.OrdinalIgnoreCase) you can do contentEncoding.ToLower() == "gzip" which is much quicker to type :) – NickG May 23 '13 at 11:12
  • @Mart I've implemented this on my end.... The problem I was having was the HttpContext.Current was not being populated when moving into the API endpoint.... After adding in this decompressor I now have a HttpContext.Current, however the request content is not being populated into the FromBody ... I don't think the Filter is being called – Jessie Lulham Nov 03 '22 at 20:10
2

Since the 3rd party service is just sending you a POST, I do not think that it is possible to tell them not to send in compressed.

You could try to override GetWebRequest and decompress it on the way in

public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol

{
protected override WebRequest GetWebRequest(Uri uri)
{
    base.GetWebRequest(uri);request.AutomaticDecompression = System.Net.DecompressionMethods.GZip;
    return request;
}
}
Shiraz Bhaiji
  • 64,065
  • 34
  • 143
  • 252
  • Thanks. Unfortunately, the request never makes it this far. Your answer led me to figure out the last call in the chain that succeeds is PreRequestHandlerExecute and that led me to trying to Filter the input as described in my answer. – Geoff Dec 13 '10 at 15:39
1

GZIP compression is a function of the server.

If you're using IIS6, consult this link.

If you're using IIS7, you could use ISAPI_Rewrite to disable gzip. See this link.

That said, because gzip is a function of IIS, you really shouldn't need to do anything "special" to get it to work with a web service (IIS should handle decompressing and compressing requests). Hopefully this info will get you further down the road to troubleshooting and resolving the issue.

Bob Black
  • 2,375
  • 2
  • 18
  • 26
  • Thanks, but so far this doesn't help. I can turn off compression in IIS7 by unchecking the boxes in the configuration settings, but again, this seems to only affect the Response from the server. I need to deal with a Request To the server being compressed. – Geoff Dec 07 '10 at 14:45
  • Here's out the gzip client/server conversation normally works: http://www.websiteoptimization.com/speed/tweak/compress/ – Bob Black Dec 07 '10 at 14:55
  • It may be that your third-party tool isn't playing nice. – Bob Black Dec 07 '10 at 14:56
  • updated the post, the thirdparty tool is the salesforce.com outlook plugin, so I think its got to be me since other companies are using it. – Geoff Dec 07 '10 at 15:40
1

I am not sure that IIS supports decompressing incoming requests, so this might have to be done further down the pipe.

Shiraz's answer has the potential of working and it would be the first thing I would try.

If that doesn't work you might consider switching your server .asmx service to WCF, which while a bit more difficult to setup it also gives more flexibility.

On the WCF side there are two things I can suggest. The first is quite easy to implement and is based on setting the WebRequest object used by WCF to automatically accept compression. You can find the details here. This one is the WCF equivalent to the solution proposed by Shiraz.

The second is more complicated, since it involves creating Custom Message Encoders, but if none of the above methods work, this should solve the problem. Creating a message compression encoder is described here. You might also want to check the answer in here which presents a sample config for the message encoder.

Please let me know if this helped or if you need more help.

Community
  • 1
  • 1
Florin Dumitrescu
  • 8,182
  • 4
  • 33
  • 29
1

I've found a partial answer here.

class DecompressStream : Stream
{
    ...

    public override int Read(byte[] buffer, int offset, int count)
    {
        GZipStream test = new GZipStream(_sink, CompressionMode.Decompress);

        int c = test.Read(buffer, offset, count);

        return c;
    }

    ...
}

I can then specify the filter on the request object like this:

void Application_BeginRequest(object sender, EventArgs e)
    {
        string contentEncoding = Request.Headers["Content-Encoding"];
        Stream prevCompressedStream = Request.Filter;

        if(contentEncoding == null || contentEncoding.Length == 0)
            return;

        contentEncoding = contentEncoding.ToLower();

        if(contentEncoding.Contains("gzip"))
        {
            Request.Filter = new DecompressStream(Request.Filter);
        }
    }

I say partial answer because even though I can now process the incoming request, the response is getting a "Content-Encoding: gzip" header even though the response is not encoded. I can verify in Fiddler that the content is not encoded.

If I do encode the response, the client for the webservice fails. It seems that even though it is sending "Accept-Encoding: gzip", it does not in fact accept gzip compressed response. I can verify in Fiddler that the response is compressed and Fiddler will decompress it successfully.

So, now I'm stuck trying to get a stray "Content-Encoding: gzip" header removed from the response. I've removed all references I can find to compression from the application, the web.config, and IIS.

Geoff
  • 9,340
  • 7
  • 38
  • 48
  • I haven't been working with ASP Web Services (.asmx) web services for quite a few years, but couldn't you access the response headers with HttpContext.Current.Response.Headers? – Florin Dumitrescu Dec 13 '10 at 16:16
  • @Florin, You are correct. I was able to remove the offending Header in the PostRequestHandlerExecute function AND removing the Accelpt-Encoding header from the incoming request in the BeginRequest function. Had to do both to make it work. – Geoff Dec 15 '10 at 12:05