gzip C#/.NET EnhancedSoapHttpClientProtocol.cs

The EnhancedSoapHttpClientProtocol.cs un-annotated code can be seen by clicking here.

The SoapHTTPClientProtocol class is the base class that implements the bulk of the work for client-side SOAP Proxies. When you add a web reference to GWS Web Service, VisualStudio.NET creates a custom proxy class for that web service, which inherits from SoapHTTPClientProtocol. So, there are components of the code to override, or add to, the behavior in the base class.  By encapsulating these changes in a class derived from SoapHTTPClientProtocol, it is easy to modify the generated proxy classes to get the new behavior. It is a matter of inserting text, which is an advantage because it needs to be done for each service used. This is described in steps 5 and 6.  Essentially,

public class XmlSelect : System.Web.Services.Protocols.SoapHttpClientProtocol

becomes

public class XmlSelect : Galileo.Web.Services.EnhancedSoapHttpClientProtocol

The assembly is called GalileoHttpUtil. Here is a summary of the enhancements it provides:

 

Reference the needed namespaces.

using System;

using System.Net;

using System.Text;

using System.Web.Services.Protocols;

using System.Web.Services;

using Galileo.Web;

 

 

Specify the namespace for this class.

namespace Galileo.Web.Services

 

{

 

/// <remarks/>

This attribute statement directs Visual Studio.NET to load this file in the code editor instead of the designer

[System.ComponentModel.DesignerCategoryAttribute("code")]

Declare the class.

public class EnhancedSoapHttpClientProtocol : System.Web.Services.Protocols.SoapHttpClientProtocol

 

{

Declare properties to control the new behaviors.

public bool Expect100Continue { get { return _expect100Continue; } set { _expect100Continue = value; } }

public bool KeepAlive { get { return _keepAlive; } set { _keepAlive = value; } }

public bool UseNagleAlgorithm { get { return _useNagleAlgorithm; } set { _useNagleAlgorithm = value; } }

public bool AcceptGZip { get { return _acceptGZip; } set { _acceptGZip = value; } }

public bool GZipRequest { get { return _gZipRequest;} set { _gZipRequest = value; } }

public Version ProtocolVersion { get { return _protocolVersion;} set { _protocolVersion = value; } }

 

 

Declare member variables.

private bool _expect100Continue;

private bool _keepAlive;

private bool _useNagleAlgorithm;

private bool _acceptGZip;

private bool _gZipRequest;

private Version _protocolVersion;

 

 

 

/// <remarks/>

This is the constructor for the class that initializes the member variables with their default values.

public EnhancedSoapHttpClientProtocol()

{

_expect100Continue = false;

_keepAlive = false;

_useNagleAlgorithm = false;

_gZipRequest = true;

_acceptGZip = true;

_protocolVersion = HttpVersion.Version11;

 

}

 

 

To modify the SoapHTTPClientProtocol class's behavior for the Soap request, we need to override the GetWebRequest method.

protected override WebRequest GetWebRequest(Uri uri)

 

{

First we override the Expect100Continue and the UseNagleAlgorithm behavior, which is controlled by the service point class. Expect100Continue is not valid for HTTP 1.0.

// The following disables 100-continue for web service requests,

// and must be done BEFORE the WebRequest object is created.

// expect100Continue is valid only for HTTP 1.1 and above

ServicePoint sp = ServicePointManager.FindServicePoint(uri);

sp.Expect100Continue = _expect100Continue && _protocolVersion >= HttpVersion.Version11;

sp.UseNagleAlgorithm = _useNagleAlgorithm;

 

 

 

// Declare locals.

Declare the return value.

System.Net.WebRequest request = null;

 

 

 

// Grab the instance of the request that will be used to service this call

If the request will be gzipped, then we need to create a special gzip capable version of the HttpWebRequest object.

if(_gZipRequest)

{

GZipHttpWebRequest gZipRequest = new GZipHttpWebRequest(uri);

Indicate that the request should be compressed.

gZipRequest.Headers["Content-Encoding"] = "gzip";

Override the KeepAlive behavior. KeepAlive is not valid for HTTP 1.0.

// keepAlive is valid only for HTTP 1.1 and above

gZipRequest.KeepAlive = _keepAlive && _protocolVersion >= HttpVersion.Version11;

Override the default ProtocolVersion.

gZipRequest.ProtocolVersion = _protocolVersion;

Set the return value.

request = gZipRequest;

 

}

If the request will not be gzipped, then we use the existing HttpWebRequest object.

else

{

HttpWebRequest httpRequest = (System.Net.HttpWebRequest)base.GetWebRequest(uri);

Override KeepAlive behavior. KeepAlive is not valid for HTTP 1.0

// keepAlive is valid only for HTTP 1.1 and above

httpRequest.KeepAlive = _keepAlive && _protocolVersion >= HttpVersion.Version11;

Override the default ProtocolVersion.

httpRequest.ProtocolVersion = _protocolVersion;

Set the return value.

request = httpRequest;

 

}

 

 

Override the PreAuthenticate behavior, which is to ignore this property.

// The SoapHttpClientProtocol does not respect the Preauthenticate property.

// The following code ensures that we preauthenticate when this property is set to true.

if(PreAuthenticate)

 

{

Make sure that we have credentials for HTTP Basic Authentication.

System.Net.NetworkCredential nc = this.Credentials.GetCredential(uri,"Basic");

if(nc != null)

 

{

Format the user name and password for the Authorization header...

string credentials = string.Format("{0}:{1}", nc.UserName, nc.Password);

...and Base64 encode it.

string encodedCredentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials));

Set the Authorization header with the Base64 encoded user name and password.

request.Headers["Authorization"] = string.Format("Basic {0}", encodedCredentials);

 

}

 

}

 

 

If a gzipped request is permitted, set the Accept-Encoded header to say so.

if(_acceptGZip)

{

request.Headers["Accept-Encoding"] = "gzip";

}

 

 

These are some comments on the KeepAlive behavior.

// The HttpWebRequest.KeepAlive property controls whether or not

// the value KeepAlive is sent in the the HTTP 1.1 Connection Header.

// KeepAlive controls whether the ServicePoint class will cache

// this socket connection and reuse it for subsequent requests.

// In some circumstances, connection pooling can provide a

// performance benefit. However, the ServicePoint's implementation of

// connection pooling can also result in errors when pooled

// connections are closed or reset by a server, proxy server, firewall,

// or other network device that is part of the communications path.

// For most applications, reliability will be more important than the

// performance gain achieved by connection pooling, and it is

// Galileo's recommendation to NOT use connection pooling when

// invoking web services.

 

// The SoapHttpClientProtocol class does not provide any way to

// override the default value (false) of HttpWebRequest.KeepAlive.

// This class adds a KeepAlive property and then propogates it to

// to the HttpWebRequest.KeepAlive property in the code above that creates

// the WebRequest instance above.

 

 

 

// Return the request object.

 

return request;

 

}

 

 

This method is for synchronous calls. To modify the SoapHTTPClientProtocol class's behavior for the Soap request, we need to override the GetWebResponse method.

protected override WebResponse GetWebResponse(WebRequest request)

 

//Putting validation to check the header beforhand so that if user requested for gzip response but response header doesnt have
// content-encoding element then we dont need to process response based on the code block under _acceptGZip

This code is getting the http response based on the request

HttpWebResponse httpresponse = (HttpWebResponse) request.GetResponse();

 

 

This code checks the http header in the response to see if the content encoding is available. If yes, it does nothing. If not, then _ acceptGZip False and skips to the else block.

if(httpresponse.ContentEncoding != "gzip" && _acceptGZip != false)
         _acceptGZip = false;

 

{

If a gzip response is permitted, return an HttpWebResponse object capable of gzip decompression.

if(_acceptGZip)

{

HttpWebResponse httpResponse = (HttpWebResponse) request.GetResponse();

GZipHttpWebResponse gZipResponse = new GZipHttpWebResponse(httpResponse);

return gZipResponse;

}

Otherwise, use the existing HttpWebResponse object.

else

{

return base.GetWebResponse(request);

}

}

 

 

This method is for asynchronous calls. To modify the SoapHTTPClientProtocol class's behavior for the Soap request, we need to override the GetWebResponse method.

protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult ar)

 

//Putting validation to check the header beforhand so that if user requested for gzip response but response header doesnt have
// content-encoding element then we dont need to process response based on the code block under _acceptGZip

This code is getting the http response based on the request

HttpWebResponse httpresponse = (HttpWebResponse) request.GetResponse();

 

 

This code checks the http header in the response to see if the content encoding is available. If yes, it does nothing. If not ,then _ acceptGZip False and skips to the else block.

if(httpresponse.ContentEncoding != "gzip" && _acceptGZip != false)
        _acceptGZip = false;

 

{

 

 

If a gzip response is permitted, return an HttpWebResponse object capable of gzip decompression.

if(_acceptGZip)

{

HttpWebResponse httpResponse = (HttpWebResponse) request.EndGetResponse(ar);

GZipHttpWebResponse gZipResponse = new GZipHttpWebResponse(httpResponse);

return gZipResponse;

Otherwise, use the existing HttpWebResponse object.

}

else

{

return base.GetWebResponse(request, ar);

 

}

 

}

 

}

 

}