Java-клиент + HTTP-сервер Apache + сжатие/распаковка GZIP

Я хочу сжимать HTTP-трафик через HTTP-заголовок, кодирующий содержимое gzip, как в запросах, так и в ответах. Я могу получать сжатые данные с HTTP-сервера и распаковывать их, но если я попытаюсь сжать данные в формате gzip и отправить эти данные на HTTP-сервер, я получу 400 неверных запросов, в журнале Apache я увижу:

AH01387: Zlib: неверный заголовок

У меня есть следующий код:

public class HttpClientTester extends StandaloneApplication {
    public static byte[] deflate(byte[] input) {
        Deflater deflater = new Deflater();
        deflater.setLevel(9);
        deflater.setInput(input);
        deflater.finish();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        while (!deflater.finished()) {
            int compressedSize = deflater.deflate(buffer);
            outputStream.write(buffer, 0, compressedSize);
        }
        return outputStream.toByteArray();
    }

    public static byte[] compress(byte[] input) throws IOException {
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        GZIPOutputStream zipStream = new GZIPOutputStream(byteStream);
        zipStream.write(input);
        zipStream.flush();
        zipStream.finish();
        zipStream.close();
        return byteStream.toByteArray();
    }

    public static String deflateReverse(byte[] input) throws DataFormatException, UnsupportedEncodingException {
        Inflater inflater = new Inflater();
        inflater.setInput(input);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        while (!inflater.finished()) {
            int decompressedSize = inflater.inflate(buffer);
            outputStream.write(buffer, 0, decompressedSize);
        }
        return new String(outputStream.toByteArray(), "UTF-8");
    }

    public static String decompress(byte[] input) throws IOException {
        GZIPInputStream zippedInputStream = new GZIPInputStream(new ByteArrayInputStream(input));
        return Tools.readStream(zippedInputStream, StandardCharsets.UTF_8);
    }

    @Override
    public Void execute(StandaloneApplicationContext arg0) throws Exception {
        Bundle sslProperties = new Bundle();
        sslProperties.set(SSLProperties.SSL_VERSION, "TLSv1.2");
        sslProperties.set(SSLProperties.SSL_TRUST_ALL_CERTS, true);
        HTTPSHandler httpsHandler = new HTTPSHandler(null, arg0.getLoggingContext().getLoggingId(), sslProperties);
        HttpClientV2 clientV2 = new HttpClientV2(null, arg0.getLoggingContext().getLoggingId(), arg0.getConfigurationContext().getParametersReader());
        clientV2.setSSLHandler(httpsHandler);
        HttpRequest httpRequest = HttpFactory.createHttpRequest(HttpConstants.HTTP_CONTENT_TYPE_TEXT_HTML);
        httpRequest.setTransactionId(arg0.getLoggingContext().getLoggingId());
        httpRequest.setHeaderField("Accept-encoding", "gzip");
        httpRequest.setHeaderField("Content-encoding", "gzip");
        httpRequest.setURL("https://myserver/myapp");
        httpRequest.setMethod(HttpMethod.POST);
        byte[] unzippedInputBody = "<MessageEnvelop><Type>ERROR</Type><Anomaly>ERROR WHILE PARSING XML REQUEST</Anomaly></MessageEnvelop>".getBytes("UTF-8");
        byte[] zippedInputBody = compress(unzippedInputBody);
        httpRequest.setBody(zippedInputBody);
        httpRequest.setHeaderField("content-length", "" + zippedInputBody.length);
        HttpResponse httpResponse = clientV2.doCommunication(httpRequest);
        byte[] stream = httpResponse.getBodyStream();
        String data = decompress(stream);
        //@formatter:off
        pause(
            "Data length.........: " + stream.length + "\r\n"+ 
            "Real data...........: " + data + "\r\n"+
            "Response B64 data...: " + new Base64().encode(stream) + "\r\n"+
            "Request B64 data....: " + new Base64().encode(zippedInputBody));
        //@formatter:on
        return null;
    }

    public static void main(String[] args) {
        StandaloneApplicationContext.run(new HttpClientTester());
    }
}

Я установил HTTP-сервер Apache со следующими настройками в своем VirtualHost:

SetInputFilter DEFLATE
SetOutputFilter DEFLATE

Цель состоит в том, чтобы сжать весь трафик (запрос и ответ). Теперь я могу распаковывать данные, полученные HTTP-сервером Apache. В настоящее время несжатые данные представляют собой следующую строку:

<MessageEnvelop><Type>ERROR</Type><Anomaly>ERROR WHILE PARSING XML REQUEST</Anomaly></MessageEnvelop>

Данные, сжатые GZIP (B64), в настоящее время используются как для запроса, так и для ответа. Обратите внимание, что данные отправляются в двоичном формате, формат base64 используется для сравнения моего заархивированного потока и полученного:

H4sIAAAAAAAAALPxTS0uTkxPdc0rS83JL7CzCaksSLVzDQryD7LRB7NtHPPycxNzKiGCCuEenj6uCgGOQcGefu4KEb4+CkGugaGuwSE2+jCFNvpohgIA/IH0c2UAAAA=

Текущий XML-код получен, когда веб-приложение получило недопустимый XML-запрос. Без сжатия GZIP по HTTP-запросу я могу получить его в сжатом виде и распаковать в своем Java-клиенте. Если я также попытаюсь сжать свой запрос, я получу 400 неверных запросов от HTTP-сервера без какой-либо возможности достичь моего веб-приложения.

Прикреплены обменявшиеся сообщения, экспортированные журналами Java-клиента:

Сообщения

Разве вы не можете делегировать сжатие gzip самому клиенту вместо того, чтобы пытаться справиться с этим самостоятельно?

Mark Rotteveel 26.06.2024 09:29

Используемый клиент - клиент ручной работы. Невозможно использовать внешние библиотеки в текущей ситуации.

Francesco Rizzi 26.06.2024 09:42
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Все вышесказанное верно. Проблема была в используемом объекте HttpClient. Он обрабатывал данные gzip как строки, а не как байтовые массивы. После исправления управления телом HTTP-запросов никаких проблем больше не возникало.

Спасибо.

Другие вопросы по теме