Compress a HttpWebRequest using gzip

2019-03-01 01:19发布

问题:

I am developing a .NET 4.0 Console Application to serve as a SOAP Web Service client which will send data (POST) through to a third-party. I have no control over the web service on the server-side. The third-party did provide WSDL's to use, and I was able to import them and use them with reasonable success. However, there is a requirement to compress the request message using gzip, and I could not for the life of me figure out how to do that using the proxy services.

This topic here on SO, led me to believe that without having control over both the client and server code, the request is unable to be compressed. As a result of this finding, I wrote the code in my application to manually create the SOAP XML in a XDocument object; then, populated the values from the WSDL proxy class objects which I had previously coded my client application to use.

The first requirement for this client, is to send the message compressed via gzip. After some research, I have seen answers as simple as just adding the HttpRequestHeader.AcceptEncoding, "gzip, deflate" to the request header. Unfortunately, doing that that didn't appear to work.

Currently, the certificate which I am retrieving is not the real certificate. I am trying to make the code as sound as I can before deploying to a test environment to do the actual service testing.

  1. Is there a way to compress the request via the proxy call (wsdl)?
  2. Is there something I am missing in order to properly compress the HttpWebRequest?
  3. Might there be something else going wrong which would result in the error message to be returned?
    I would expect a different message pertaining to authentication being invalid if the request itself was OK.
  4. Is there a way the compression can be done through the app.config?

The next set of requirements I'm a little confused on how to handle/what to do. Under the assumption that what I have set the ContentType of the request to, how (and what) do I add in order to add the content-transfer-encoding piece to the request? If the ContentType is incorrect, how should I add this information as well?

The content type for the SOAP Evenlope with MTOM encoded attachment must be "application/xop+xml" and the content-transfer-encoding must be 8-bit.

I've gone through a few iterations of the code below, however, I believe the relevant snippets are the code at its simplest form. Please let me know if there is other information that would be helpful.

Method to create the HttpWebRequest:

private static HttpWebRequest CreateWebRequest(SoapAction action)
{
    string url = GetUrlAddress(action);

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

    request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
    request.Headers.Add("SOAPAction", action.ToString());
    request.ContentType = "application/xop+xml";
    request.Accept = "text/xml";
    request.Method = "POST";

    request.ClientCertificates.Add(/* Retrieve X509Certificate Object*/);

    return request;
}

Code to Send Request:

using (Stream stream = request.GetRequestStream())
{
    soapXml.Save(stream);
}

Code to retrieve the Response:
This is how I am retrieving the error message that is occurring

try
{
    using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
    {
        using (StreamReader reader = new StreamReader(response, Encoding.Default))
        {
            File.AppendAllText(filePath, response.Headers.ToString());
            File.AppendAllText(filePath, reader.ReadToEnd());
        }
    }
}
catch (WebException ex)
{
    using (var stream = ex.Response.GetResponseStream())
    {
        using (var reader = new StreamReader(stream))
        {
            Console.WriteLine(reader.ReadToEnd());
        }
    }
}

Error Message being Received:

The request message must be sent using HTTP compression (RFC 1952 - GZIP).

回答1:

I think I was able to solve the compression error message by adding the following to the HttpWebRequest method:

request.Headers.Add(HttpRequestHeader.ContentEncoding, "gzip"); 

Updated Method to create the HttpWebRequest:

private static HttpWebRequest CreateWebRequest(SoapAction action)
{
    string url = GetUrlAddress(action);

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

    request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
    request.Headers.Add(HttpRequestHeader.ContentEncoding, "gzip"); 
    request.Headers.Add("SOAPAction", action.ToString());
    request.ContentType = "application/xop+xml";
    request.Accept = "text/xml";
    request.Method = "POST";

    request.ClientCertificates.Add(/* Retrieve X509Certificate Object*/);

    return request;
}


回答2:

Use the following in place of your current code to send the request.

using (Stream stream = request.GetRequestStream())
using (GZipStream gz = new GZipStream(stream, CompressionMode.Compress, false))
{
    soapXml.Save(gz);
}

Edit: Might need to use "new GZipStream(stream, CompressionMode.Compress, true)". Can't say for certain how that constructor parameter will affect the web request stream.