Как включить сжатие GZIP для запросов POST (загрузки) к веб-службе SOAP в IIS 7?

Как включить сжатие данных POST, загружаемых в веб-службу .NET (SOAP, а не WCF)? Я думал, что достаточно просто включить динамическое сжатие в IIS, но после включения , он сжимает только ответ, а не запрос POST.

Я добавил его как ссылку на службу и не могу найти никаких настроек в сгенерированном SOAPClient для включения сжатия запросов.

Кажется, мне не хватает параметра конфигурации или кода на стороне клиента для сжатия запроса перед его отправкой на сервер? Или то, что я пытаюсь сделать (GZipping POST-данные), даже не поддерживается?

Дополнительная информация: я использую .NET 4.0 на клиенте и сервере.


person NickG    schedule 21.05.2013    source источник
comment
Пожалуйста, обратитесь к этому сообщению SO (stackoverflow.com/questions/4416177/)   -  person Rajesh    schedule 21.05.2013


Ответы (2)


Я писал об этом 4 года назад

http://netpl.blogspot.com/2009/07/aspnet-webservices-two-way-response-and.html

Странно, почему вы не нашли это в гугле. В любом случае, это должно сработать для вас, у нас это работает в производственной среде уже 4 года.

person Wiktor Zychla    schedule 23.05.2013
comment
На самом деле я уже использовал этот код, но в нем было несколько ошибок, которые я изо всех сил пытался исправить. Однако я потратил на это больше времени и исправил ошибки. В моей версии устранены следующие проблемы: Установить заголовок gzip Content-encoding при возникновении исключения на сервере. Второе исправление: не пытайтесь расшифровать поток запросов GZIP, если запрос не был сжат GZIP. Я разместил обновленную версию кода здесь: pastebin.com/Aak9FUiw - person NickG; 23.05.2013
comment
Большое спасибо за оригинальное решение. Я награжу тебя наградой :) - person NickG; 23.05.2013
comment
Третье исправление заключалось в том, чтобы подавить дублирование заголовка кодирования содержимого, поэтому он стал gzip, gzip. Не уверен, что это действительно необходимо, но это неправильно. - person NickG; 23.05.2013
comment
Спасибо, Ник, я надеюсь, что решение поможет вам так же, как и нам. - person Wiktor Zychla; 28.05.2013
comment
Мне действительно пришлось удалить этот код сейчас. Даже в исправленной версии есть пара проблем, которые я не знаю, как исправить. Например, если время ожидания веб-службы истекает, вы получаете сообщение об ошибке (которое скрывает реальную ошибку) объекта, не установленного на экземпляр объекта. - person NickG; 04.06.2013
comment
Я помню, как пробовал ловить в нескольких местах, чтобы сделать его более надежным, и он работает у нас в производственной среде под большой нагрузкой уже около 4 лет. - person Wiktor Zychla; 04.06.2013
comment
Я исправил одну проблему сейчас, но мне не нравится тот факт, что если время ожидания операции истекло, вы не получите сообщение об истечении времени ожидания операции, а вместо этого просто Object не установлен на экземпляр объекта. Это затрудняет определение того, что на самом деле пошло не так. Вы вносили какие-либо исправления для этого? Если да, то можете ли вы опубликовать пример? - person NickG; 04.06.2013
comment
К сожалению, я не помню никаких конкретных исправлений для возможного тайм-аута. - person Wiktor Zychla; 04.06.2013
comment
У вас когда-нибудь возникала проблема, когда люди, использующие глючные прокси-серверы, получали сообщение о неверном магическом числе в заголовке gzip? Похоже, код пытается безоговорочно распаковать поток, даже если это уже сделал прокси-сервер, что вызывает ошибку у некоторых пользователей. - person NickG; 12.05.2014
comment
@NickG: да, у нас были проблемы с несовместимыми прокси. Некоторые вопросы решить не удалось. - person Wiktor Zychla; 12.05.2014
comment
Я пытаюсь выяснить, можно ли это исправить, изучив первые несколько байтов потока, чтобы увидеть, содержит ли он магическое число gzip, и если да, добавить отсутствующий заголовок Content-encoding: gzip. Однако я изо всех сил пытаюсь воспроизвести проблему на своей машине, поэтому, не имея возможности воспроизвести ее, я сомневаюсь, что смогу ее исправить :( - person NickG; 12.05.2014
comment
offtopic - не уверен насчет ответов только по ссылкам - meta.stackexchange.com/questions/8231/ - person Mars Robertson; 19.07.2016

В конце концов, я использовал ответ Виктора Зыхлы, но наткнулся на пару ошибок (также упомянутых в комментариях к его статье). Для полноты картины я опубликую свою исправленную версию модуля HttpCompression здесь, но остальная часть кода (и рекомендации по реализации), которые вам нужны, находятся в статья Виктора.

ОБНОВЛЕНИЕ:

На самом деле мне пришлось прекратить использовать этот код, так как в нем есть ошибка, которую я не могу исправить (даже с моей улучшенной версией ниже). У многих людей за прокси-серверами возникает ошибка, в которой говорится, что «магическое число в заголовке gzip неверно», и я не могу понять, как это исправить. Я думаю, это связано с тем, что прокси-серверы распаковывают GZIP, а этот код не позволяет получать как сжатые, так и не сжатые ответы в его текущем виде.

using System;
using System.IO.Compression;
using System.Web;
using System.Web.Security;

/// <summary>
/// Implement two way HTTP compression for the SOAP API
/// Code taken from: http://netpl.blogspot.co.uk/2009/07/aspnet-webservices-two-way-    response-and.html
/// Fix: Set Content-encoding: gzip header when an Exception occurs on the server.
/// Fix: Do not attempt to decrypt GZIP request stream when request was not GZIPed.
/// </summary>
public class HttpCompressionModule : IHttpModule
{
    private bool isDisposed = false;

    ~HttpCompressionModule()
    {
            Dispose(false);
    }

    public void Init(HttpApplication context)
    {
            context.BeginRequest += new EventHandler(Context_BeginRequest);
            context.PreSendRequestHeaders += new EventHandler(context_PreSendRequestHeaders);
    }

    void context_PreSendRequestHeaders(object sender, EventArgs e)
    {
            // Fix headers having been lost if an exception occurred.
            HttpApplication app = sender as HttpApplication;
            HttpContext ctx = app.Context;
            if (app.Response.Filter is GZipStream) SetEncoding("gzip");
            else if (app.Response.Filter is DeflateStream) SetEncoding("deflate");

            // Fix double header
            if (ctx.Response.Headers["Content-encoding"] == "gzip,gzip")
                    ctx.Response.Headers.Set("Content-encoding", "gzip");
    }

    public void Context_BeginRequest(object sender, EventArgs e)
    {
            HttpApplication app = sender as HttpApplication;
            HttpContext ctx = app.Context;

            // Currently only enable for the Uploader API webservice
            if (!ctx.Request.Url.PathAndQuery.ToLower().Contains("uploaderapi.asmx"))
            {
                    return;
            }

            // Add request filter if request was GZIP encoded
            string requestEncoding = ctx.Request.Headers["Content-encoding"];
            if (requestEncoding != null && requestEncoding == "gzip")
            {
               app.Request.Filter =
                    new System.IO.Compression.GZipStream(app.Request.Filter, CompressionMode.Decompress);
            }

            // Add response compression filter if the client accepts compressed responses
            if (IsEncodingAccepted("gzip"))
            {
                    app.Response.Filter = new GZipStream(app.Response.Filter, CompressionMode.Compress);
                    SetEncoding("gzip");
            }
            else if (IsEncodingAccepted("deflate"))
            {
                    app.Response.Filter = new DeflateStream(app.Response.Filter, CompressionMode.Compress);
                    SetEncoding("deflate");
            }
    }

    private bool IsEncodingAccepted(string encoding)
    {
            return HttpContext.Current.Request.Headers["Accept-encoding"] != null &&
                    HttpContext.Current.Request.Headers["Accept-encoding"].Contains(encoding);
    }

    private void SetEncoding(string encoding)
    {
            HttpContext ctx = HttpContext.Current;
            string responseEncodings = ctx.Response.Headers.Get("Content-encoding");
            if (responseEncodings == null || !responseEncodings.Contains(encoding))
                    HttpContext.Current.Response.AppendHeader("Content-encoding", encoding);
    }

    public void Dispose()
    {
            Dispose(true);
    }

    private void Dispose(bool dispose)
    {
            isDisposed = dispose;
    }
}
person NickG    schedule 23.05.2013
comment
Ник, помни, что это не полный ответ. Также требуются HttpResponseDecompressed и HttpRequestCompressed. - person Wiktor Zychla; 24.05.2013
comment
@WiktorZychla Спасибо. Я обновил свой ответ, чтобы было ясно, что ваша статья содержит весь код/инструкции. Мой ответ заключается только в том, чтобы исправить те ошибки / проблемы совместимости, которые я обнаружил (и они будут излишними, если вы решите включить их в свою статью). - person NickG; 24.05.2013
comment
Ник, из-за вашего четкого объяснения я не собираюсь вносить какие-либо изменения в запись. Я предполагаю, что любой, кто попытается использовать его, найдет ваши комментарии и последует вашему обновленному подходу. Рад, что решение помогло, и спасибо за ваши исправления. - person Wiktor Zychla; 24.05.2013