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.
="1.0"="utf-8"
<configuration>
<system.net>
<webRequestModules>
<add prefix="http"
type="PaJoCoMo.Net.CompressibleHttpRequestCreator, PaJoCoMo" />
</webRequestModules>
</system.net>
</configuration>