I will present a general solution to the two-way compression of WebServices" issue. The issue can be critical in a complicated SOA solution where a lot of data is passed not only from the server to the client but also from the client to the server. The solution is a part of a Data Service component I currently work on.
On one hand, the application server can compress the HTTP data sent to the client "out-of-the-box", just enable "HTTP Compression". While such solution seems attractive, it does not provide two-way compression. It's nice to have a server's response compressed, however uploading huge amount of data is still a problem.
On the other hand, a generic solution already exists and consists in writing a custom SOAP extension. The solution by Saurabh Nandu dates 2002 is described here. There are however two issues with that approach. A small issue is that you have to apply a custom attribute to both server and client code. While applying a custom attribute to a server method is not an issue, the client proxy class is regenerated each time you update the reference which means that you have to remember to manually edit the proxy class after you update the reference. Unfortunately, there's also a big issue. We've observed that the solution causes random OutOfMemory exceptions in a production server environment! The randomness was a complete disaster for our proprietary software!
Towards the solution
Let's start with a basic code we'll enhance during this tutorial. Open Visual Studio, create a new solution with two projects: a console applications and an ASP.NET Web Service application.
Implement a method on the WebService.
Note that the method has an input parameter and just returns it's value to the client. We are going to pass really long strings here and we'll gonna see how much data is actually passed to and from the server.
In a console application, add a web service reference and following code:
Note that I manually set the Url of the WebService and what I actually did was to add a dot after the "localhost". This is to be able to use a HTTP sniffer, Fiddler.
Run the application and inspect the HTTP session in Fiddler. Please take a look at "Content-Length" headers for both the request and the response, the content length of the request is 520318 bytes, and the content length of the response is 520352 bytes.
One way compression (compression of the response)
The OutOfMemory exceptions caused by the solution based of a custom SOAP Extensions made us to search for another approach and lead us to a common and well known "HttpCompressionModule on a server - HttpWebResponseDecompressed on a client proxy". This solution solves only the half of the compression issue - the data sent from the server to the client is compressed.
Start with a HttpCompressionModule: add a HttpCompressionModule.cs file into the WebService project:
Now, make the module active by adding
to your web.Config.
Now go back to the client console application and add two methods to the partial proxy class. Assuming that the proxy's class name is MyService, add MyService.cs:
Note that what this does is to make sure that the correct header is sent to the server and that the response is actually decompressed on the client side. The HttpWebResponseDecompressed is a common class but it's not in the Base Class Library so here it is:
Now run the solution and inspect it with Fiddler:
Note that the content length of the server's response is not only 5771 bytes! (Note also that the 100:1 compression ratio is rather unusual and is caused by repetitive data I send to the server from the console application!)
Two-way solution (compression of requests and responses)
We are now ready to enhance the partial solution and enable a two-way compression (and this is my contribution to the issue).
First, go to the HttpCompressionModule and add a line:
Then, go back to the client application, and modify the partial class definition:
and add the HttpWebRequestCompressed class:
And that's it! Note that the HttpWebRequestCompressed uses HttpWebResponseDecompressed when getting the response back. This is why we do not need to override the GetWebResponse in the proxy class.
Now inspect the application with Fiddler:
Note that this time the content length of the request is 6130 bytes (and the request is unreadable in Fiddler since it's compressed) and the response's length is still 5771 bytes.
Please download the source code for this tutorial here. If you find any bugs or have any other related comments, please feel free to comment this post.