Click here to Skip to main content
13,141,354 members (88,614 online)
Click here to Skip to main content
Add your own
alternative version

Stats

149.4K views
62 bookmarked
Posted 6 Jan 2006

HTTP compression in the .NET Framework 1.1

, 6 Jan 2006
Rate this:
Please Sign up or sign in to vote.
An article on HTTP compression in the .NET Framework 1.1.

Introduction

free hit countersThis article will show how to request for HTTP compression and handle a compressed response from a web server (or any appliance connected to the server) without changing the client application.

The factory pattern of creating WebRequest instances

The WebRequest supplies two methods to create instances:

The WebRequest class uses instances of classes that implement the IWebRequestCreate interface and are registered in the webRequestModules section in the configuration files. The Create method (called by both CreateDefault and Create) returns an initialized instance of a WebRequest descendent class capable of performing a standard request/response transaction for the protocol without needing any protocol-specific fields modified.

Because we can control the WebRequest factory using the configuration files, we can change the behavior of already built applications (our applications, or even the .NET Framework) to request and handle HTTP compression without changing them.

The WebRequest descendent instances will be responsible to create the WebResponse descendent instance that will handle the response from the web server.

The code

As shown before, to add HTTP compression to our applications, we just have to build three classes:

  • CompressibleHttpRequestCreator - implementing the IWebRequestCreate interface.
  • CompressibleHttpWebRequest - derived from WebRequest.
  • CompressibleHttpWebResponse - derived from WebResponse.

    In order for the applications that use HttpWebRequest and HttpWebResponse to work without any changes, CompressibleHttpWebRequest has to derive from HttpWebRequest, and CompressibleHttpWebResponse has to derive from HttpWebResponse.

    Fortunately, HttpWebRequest and HttpWebResponse are not sealed (NotInheritable in VB.NET). Unfortunately, these classes don't have any public constructor, and the only protected constructor each one has is the one required by the implementation of the ISerializable interface. Due to this drawback, reflection must be used.

    CompressibleHttpRequestCreator

    This is the class that will be implementing the IWebRequestCreate interface and is used to create HttpWebRequest descendant instances.

    As mentioned before, the only way to create an instance of a HttpWebRequest derived class, is by deserialization. To accomplish this, an instance of HttpWebRequest will be created, serialized, and deserialized into an instance of CompressibleHttpWebRequest.

    public class CompressibleHttpRequestCreator : IWebRequestCreate
    {
        WebRequest IWebRequestCreate.Create(Uri uri)
        {
            ISerializable httpWebRequest = Activator.CreateInstance(
                typeof(HttpWebRequest),
                BindingFlags.CreateInstance | BindingFlags.Public | 
                BindingFlags.NonPublic | BindingFlags.Instance,
                null,
                new object[] { uri },
                null) as ISerializable;
    
            if (httpWebRequest == null)
            {
                return null;
            }
    
            SerializationInfo serializationInfo = new SerializationInfo(
                typeof(CompressibleHttpWebRequest), new FormatterConverter());
            StreamingContext streamingContext = 
                new StreamingContext(StreamingContextStates.All);
            httpWebRequest.GetObjectData(serializationInfo, streamingContext);
    
            CompressibleHttpWebRequest webRequest = new 
                CompressibleHttpWebRequest(serializationInfo, streamingContext);
            webRequest.Method = serializationInfo.GetString("_OriginVerb");
    
            return webRequest;
        }
    }

    CompressibleHttpWebRequest

    This class will handle the setup of the HTTP request and the creation of the HttpWebResponse derived instance to handle the response.

    As with CompressibleHttpWebRequest, when creating the HttpWebResponse, an instance of HttpWebResponse will be created, serialized, and deserialized into an instance of CompressibleHttpWebResponse.

    To specify what types of compression will be accepted in the response, the AcceptEncodings will be used. Its value will, also, be used to set the accept-encoding HTTP header in the request (in the BeginGetResponse). The possible values for AcceptEncodings are:

    • Identity - The default (identity) encoding; the use of no transformation whatsoever.
    • GZip - An encoding format produced by the file compression program "gzip" (GNU zip) as described in RFC 1952 [25]. This format is a Lempel-Ziv coding (LZ77) with a 32 bit CRC.
    • Deflate - The "zlib" format defined in RFC 1950 [31] in combination with the "deflate" compression mechanism described in RFC 1951 [29].
    [Serializable]
    public class CompressibleHttpWebRequest : HttpWebRequest
    {
        private static FieldInfo m_UsesProxySemanticsFieldInfo = 
            typeof(HttpWebResponse).GetField("m_UsesProxySemantics", 
            BindingFlags.NonPublic | BindingFlags.Instance);
        private static FieldInfo m_ConnectStreamFieldInfo = 
            typeof(HttpWebResponse).GetField("m_ConnectStream", 
            BindingFlags.NonPublic | BindingFlags.Instance);
        private AcceptEncodings acceptEncodings = AcceptEncodings.GZip | 
            AcceptEncodings.Deflate | AcceptEncodings.Identity;
    
        public AcceptEncodings AcceptEncodings
        {
            get
            {
                return this.acceptEncodings;
            }
            set
            {
                this.acceptEncodings = value;
            }
        }
    
        protected internal CompressibleHttpWebRequest(SerializationInfo 
                  serializationInfo, StreamingContext 
                  streamingContext) : base(serializationInfo, 
                                           streamingContext)
        {
        }
    
        public override IAsyncResult 
            BeginGetResponse(AsyncCallback callback, object state)
        {
            if (this.acceptEncodings != AcceptEncodings.Identity)
            {
                this.Headers.Add("Accept-Encoding",
                    (this.acceptEncodings & 
                       ~AcceptEncodings.Identity).ToString().ToLower());
            }
            return base.BeginGetResponse(callback, state);
        }
    
        public override WebResponse EndGetResponse(IAsyncResult asyncResult)
        {
            ISerializable httpWebResponse = 
               base.EndGetResponse(asyncResult) as ISerializable;
            if (httpWebResponse == null)
            {
                return null;
            }
    
            SerializationInfo serializationInfo = new 
                SerializationInfo(typeof(CompressibleHttpWebResponse),
                new FormatterConverter());
            StreamingContext streamingContext = new 
                StreamingContext(StreamingContextStates.All);
            httpWebResponse.GetObjectData(serializationInfo, streamingContext);
            CompressibleHttpWebResponse webResponse = new 
                CompressibleHttpWebResponse(serializationInfo,
                streamingContext);
            m_UsesProxySemanticsFieldInfo.SetValue(webResponse, 
                m_UsesProxySemanticsFieldInfo.GetValue(httpWebResponse));
            m_ConnectStreamFieldInfo.SetValue(webResponse, 
                m_ConnectStreamFieldInfo.GetValue(httpWebResponse));
    
            return webResponse;
        }
    }

    CompressibleHttpWebResponse

    This is the easiest one. All that's needed is to handle the response stream accordingly to the content-encoding HTTP response header. Unfortunately, the .NET Framework 1.1 base classes don't provide any class for handling Deflate and GZip streams. For that, the SharpZipLib will be used.

    [Serializable]
    public class CompressibleHttpWebResponse : HttpWebResponse
    {
        public CompressibleHttpWebResponse(SerializationInfo 
            serializationInfo, StreamingContext streamingContext)
            : base(serializationInfo, streamingContext)
        {
        }
    
        public override Stream GetResponseStream()
        {
            Stream stream = base.GetResponseStream();
    
            if (stream == null)
            {
                return Stream.Null;
            }
    
            if (string.Compare(ContentEncoding, "gzip", true) == 0)
            {
                return new GZipInputStream(stream);
            }
            else if (string.Compare(ContentEncoding, "deflate", true) == 0)
            {
                return new InflaterInputStream(stream);
            }
            else
            {
                return stream;
            }
        }
    }

    Configuration

    Now, to add HTTP compression support to any application, all that's needed is to add the corresponding entry to the webRequestModules section in the configuration file.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <system.net>
            <webRequestModules>
                  <add prefix="http" 
    
                    type="PaJoCoMo.Net.CompressibleHttpRequestCreator, PaJoCoMo" />
              </webRequestModules>
        </system.net>
    </configuration>

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Paulo Morgado
Software Developer (Senior) Paulo Morgado
Portugal Portugal

You may also be interested in...

Comments and Discussions

 
GeneralRegard Http Compression Pin
attalurisubbu9-Oct-08 2:56
memberattalurisubbu9-Oct-08 2:56 
Questionhey dude, I need some help with this as it didnt work for me Pin
skilledguru13-Aug-08 14:38
memberskilledguru13-Aug-08 14:38 
AnswerRe: hey dude, I need some help with this as it didnt work for me Pin
Paulo Morgado13-Aug-08 22:10
memberPaulo Morgado13-Aug-08 22:10 
QuestionRe: hey dude, I need some help with this as it didnt work for me Pin
skilledguru14-Aug-08 6:11
memberskilledguru14-Aug-08 6:11 
AnswerRe: hey dude, I need some help with this as it didnt work for me Pin
Paulo Morgado17-Aug-08 11:45
memberPaulo Morgado17-Aug-08 11:45 
GeneralAwsome Pin
Eyal Itskovits19-May-08 5:13
memberEyal Itskovits19-May-08 5:13 
GeneralRe: Awsome Pin
Paulo Morgado19-May-08 8:49
memberPaulo Morgado19-May-08 8:49 
GeneralHmmm ... doesnt work :-( Pin
Tech023-Jul-07 1:37
memberTech023-Jul-07 1:37 
Hi Paulo,

Thank you for the great article, but ... hm, there is something wrong Frown | :( I've just recode it on VB and add the stuffs in the webconfig, but the compression doesnt work. Also put a breakpoint in CompressibleHttpRequestCreator's Create function and it never gets into it. Any ideas ? Here is my VB interpretation.

CompressibleHttpRequestCreator.vb :

Imports System.Net<br />
Imports System.Runtime.Serialization<br />
Imports System.Reflection<br />
Imports System<br />
<br />
Namespace Tech0HTMLCompression.Net<br />
<br />
    Public Class CompressibleHttpRequestCreator<br />
        Implements IWebRequestCreate<br />
<br />
        Public Sub New()<br />
<br />
        End Sub<br />
<br />
#Region "IWebRequestCreate Members"<br />
 br />
        ' <summary><br />
        ' Creates a <see cref="T:PaJoCoMo.Net.CompressibleHttpWebRequest"/><br />
        ' instance.<br />
        ' </summary><br />
        ' <param name="uri">The uniform resource identifier (URI) of the Web resource.</param><br />
        ' <returns><br />
        ' A <see cref="T:Tech0HTMLCompression.Net.CompressibleHttpWebRequest"/><br />
        ' instance.<br />
        ' </returns><br />
        Public Function Create(ByVal uri As Uri) As WebRequest Implements IWebRequestCreate.Create<br />
            Dim httpWebRequest As ISerializable = Activator.CreateInstance(GetType(HttpWebRequest), BindingFlags.CreateInstance Or BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance, Nothing, New Object() {uri}, Nothing)<br />
<br />
            If httpWebRequest Is Nothing Then<br />
                Return Nothing<br />
            End If<br />
<br />
            Dim serializationInfo As SerializationInfo = New SerializationInfo(GetType(CompressibleHttpWebRequest), New FormatterConverter)<br />
            Dim streamingContext As StreamingContext = New StreamingContext(StreamingContextStates.All)<br />
            httpWebRequest.GetObjectData(serializationInfo, streamingContext)<br />
<br />
            Dim webRequest As CompressibleHttpWebRequest = New CompressibleHttpWebRequest(serializationInfo, streamingContext)<br />
            webRequest.Method = serializationInfo.GetString("_OriginVerb")<br />
<br />
            Return webRequest<br />
        End Function<br />
<br />
#End Region<br />
<br />
    End Class<br />
<br />
End Namespace<br />


CompressibleHttpWebRequest.vb:

<br />
Imports System.Net<br />
Imports System.Runtime.Serialization<br />
Imports System.Reflection<br />
Imports System<br />
<br />
Namespace Tech0HTMLCompression.Net<br />
<br />
    <Serializable()> _<br />
    Public Class CompressibleHttpWebRequest<br />
        Inherits HttpWebRequest<br />
<br />
#Region "Private instance fields"<br />
<br />
        Private Shared m_UsesProxySemanticsFieldInfo As FieldInfo = GetType(HttpWebResponse).GetField("m_UsesProxySemantics", BindingFlags.NonPublic Or BindingFlags.Instance)<br />
        Private Shared m_ConnectStreamFieldInfo As FieldInfo = GetType(HttpWebResponse).GetField("m_ConnectStream", BindingFlags.NonPublic Or BindingFlags.Instance)<br />
<br />
#End Region<br />
<br />
#Region "Private instance fields"<br />
<br />
        Private m_acceptEncodings As AcceptEncodings = AcceptEncodings.GZip Or AcceptEncodings.Deflate Or AcceptEncodings.Identity<br />
<br />
#End Region<br />
<br />
#Region "Protected internal instance constructors"<br />
<br />
<br />
        Public Sub New(ByVal serializationInfo As SerializationInfo, ByVal streamingContext As StreamingContext)<br />
            MyBase.New(serializationInfo, streamingContext)<br />
        End Sub<br />
<br />
<br />
<br />
#End Region<br />
<br />
        Public Property AcceptEncodings() As AcceptEncodings<br />
            Get<br />
                Return Me.AcceptEncodings<br />
            End Get<br />
            Set(ByVal Value As AcceptEncodings)<br />
                Me.AcceptEncodings = Value<br />
            End Set<br />
        End Property<br />
<br />
#Region "System.Net.HttpWebRequest members"<br />
<br />
<br />
        Public Overrides Function BeginGetResponse(ByVal callback As AsyncCallback, ByVal state As Object) As IAsyncResult<br />
<br />
            If Me.AcceptEncodings <> AcceptEncodings.Identity Then<br />
                Me.Headers.Add("Accept-Encoding", (Me.AcceptEncodings And AcceptEncodings.Identity).ToString().ToLower())<br />
            End If<br />
<br />
            Return MyBase.BeginGetResponse(callback, state)<br />
        End Function<br />
<br />
        Public Overrides Function EndGetResponse(ByVal asyncResult As IAsyncResult) As WebResponse<br />
<br />
            Dim httpWebResponse As ISerializable = CType(MyBase.EndGetResponse(asyncResult), ISerializable)<br />
<br />
            If httpWebResponse Is Nothing Then<br />
                Return Nothing<br />
            End If<br />
<br />
            Dim serializationInfo As SerializationInfo = New SerializationInfo(GetType(CompressibleHttpWebResponse), New FormatterConverter)<br />
            Dim streamingContext As StreamingContext = New StreamingContext(StreamingContextStates.All)<br />
            httpWebResponse.GetObjectData(serializationInfo, streamingContext)<br />
<br />
            Dim webResponse As CompressibleHttpWebResponse = New CompressibleHttpWebResponse(serializationInfo, streamingContext)<br />
<br />
            m_UsesProxySemanticsFieldInfo.SetValue(webResponse, m_UsesProxySemanticsFieldInfo.GetValue(httpWebResponse))<br />
<br />
            m_ConnectStreamFieldInfo.SetValue(webResponse, m_ConnectStreamFieldInfo.GetValue(httpWebResponse))<br />
<br />
            Return webResponse<br />
        End Function<br />
<br />
#End Region<br />
<br />
    End Class<br />
<br />
End Namespace<br />


CompressibleHttpWebResponse.vb:

Imports System.Net<br />
Imports System.Runtime.Serialization<br />
Imports System.IO<br />
Imports System<br />
Imports ICSharpCode.SharpZipLib.GZip<br />
Imports ICSharpCode.SharpZipLib.Zip.Compression.Streams<br />
<br />
Namespace Tech0HTMLCompression.Net<br />
<br />
    <Serializable()> _<br />
    Public Class CompressibleHttpWebResponse<br />
        Inherits HttpWebResponse<br />
<br />
        Sub New(ByVal serializationInfo As SerializationInfo, ByVal streamingContext As StreamingContext)<br />
<br />
            MyBase.New(serializationInfo, streamingContext)<br />
<br />
        End Sub<br />
<br />
        Public Overrides Function GetResponseStream() As Stream<br />
<br />
            Dim stream As Stream = MyBase.GetResponseStream()<br />
<br />
            If stream Is Nothing Then<br />
                Return stream.Null<br />
            End If<br />
<br />
            If String.Compare(ContentEncoding, "gzip", True) = 0 Then<br />
                Return New GZipInputStream(stream)<br />
            ElseIf String.Compare(ContentEncoding, "deflate", True) = 0 Then<br />
                Return New InflaterInputStream(stream)<br />
            Else<br />
                Return stream<br />
            End If<br />
<br />
        End Function<br />
<br />
<br />
    End Class<br />
<br />
End Namespace<br />


WebConfig:

<br />
<system.net><br />
			<webRequestModules><br />
				<add prefix="http" type="Tech0HTMLCompression.Net.CompressibleHttpRequestCreator, Tech0HTMLCompression" /><br />
			</webRequestModules><br />
		</system.net><br />


Nothing special, just VB version of your code. I have no idea whats the problem. Frown | :-( . I dont use it in webservice, and dont have problems with the Accept-Encoding header. OMG | :OMG:


Thanks, in advance. Wink | ;)
GeneralRe: Hmmm ... doesnt work :-( Pin
Paulo Morgado23-Jul-07 12:12
memberPaulo Morgado23-Jul-07 12:12 
GeneralRe: Hmmm ... doesnt work :-( [modified] Pin
Tech023-Jul-07 21:10
memberTech023-Jul-07 21:10 
GeneralRe: Hmmm ... doesnt work :-( Pin
Paulo Morgado24-Jul-07 0:50
memberPaulo Morgado24-Jul-07 0:50 
GeneralRe: Hmmm ... doesnt work :-( Pin
Tech024-Jul-07 3:18
memberTech024-Jul-07 3:18 
GeneralRe: Hmmm ... doesnt work :-( Pin
Paulo Morgado24-Jul-07 4:37
memberPaulo Morgado24-Jul-07 4:37 
GeneralRe: Hmmm ... doesnt work :-( Pin
Tech024-Jul-07 5:04
memberTech024-Jul-07 5:04 
GeneralRe: Hmmm ... doesnt work :-( Pin
Paulo Morgado24-Jul-07 5:07
memberPaulo Morgado24-Jul-07 5:07 
GeneralRe: Hmmm ... doesnt work :-( Pin
Tech024-Jul-07 20:57
memberTech024-Jul-07 20:57 
GeneralRe: Hmmm ... doesnt work :-( Pin
Paulo Morgado25-Jul-07 1:44
memberPaulo Morgado25-Jul-07 1:44 
GeneralRe: Hmmm ... doesnt work :-( Pin
FredDalgleish21-Oct-07 14:43
memberFredDalgleish21-Oct-07 14:43 
GeneralIt doesn't work in No touch deploy Pin
nory29-Mar-07 16:24
membernory29-Mar-07 16:24 
GeneralRe: It doesn't work in No touch deploy Pin
Paulo Morgado29-Mar-07 22:32
memberPaulo Morgado29-Mar-07 22:32 
GeneralRe: It doesn't work in No touch deploy Pin
nory30-Mar-07 0:18
membernory30-Mar-07 0:18 
GeneralRe: It doesn't work in No touch deploy Pin
Paulo Morgado1-Apr-07 8:24
memberPaulo Morgado1-Apr-07 8:24 
GeneralRe: It doesn't work in No touch deploy Pin
nory1-Apr-07 16:12
membernory1-Apr-07 16:12 
GeneralNot attaching headers Pin
kryzchek13-Sep-06 11:35
memberkryzchek13-Sep-06 11:35 
GeneralRe: Not attaching headers Pin
Paulo Morgado13-Sep-06 11:41
memberPaulo Morgado13-Sep-06 11:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.170915.1 | Last Updated 6 Jan 2006
Article Copyright 2006 by Paulo Morgado
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid