Java-передача ответа SWA в виде HttpEntity с большим вложением завершается неудачно (не может быть чанком multipart)

Обратите внимание: эта проблема также была опубликована в официальном списке рассылки Apache HttpClient Users.

Вопрос

Как серверы на основе Java, построенные на компонентах Apache HttpComponents, должны собирать и отправлять большие ответы SOAP с вложениями (SWA)?

Детали вопроса

API-интерфейс Apache HttpClient Java не поддерживает разбиение на части составных экземпляров HttpEntity. Клиенты, отправляющие SOAP-запросы на фиктивный сервер, не работают (в частности, Java-клиенты на основе Apache генерируют org.apache.http.NoHttpResponseExceptions), когда вложение в ответе SWA велико (>2 МБ). Небольшие вложения передаются без каких-либо исключений. HTTP Content-Length заголовок правильный.

Случай использования

Многопоточный сервер SOAP, способный передавать ответы SWA произвольной длины контента

Платформа

Java 8 64b

Обязательные библиотеки

HttpCore v4.4.6 + HttpMime v4.5.3

Ошибка репликации

  1. Создайте исходный код примера с библиотеками HttpCore v4.4.6 и HttpMime v4.5.3 (HttpMime является частью проекта HttpClient).
  2. Запустите программу с достаточно большим (>2 МБ) двоичным файлом random.png.gz.
  3. Отправьте серверу произвольный запрос HTTP POST через какой-либо сторонний клиент. Так как это Java-клиент на основе Apache с точным ведением журнала, рекомендуется SoapUI. Примечание: сбой при получении большого сгенерированного сервером многочастного результата.

Пример исходного сервера

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.http.ExceptionLogger;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import static org.apache.http.HttpStatus.SC_OK;
import org.apache.http.MethodNotSupportedException;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.nio.bootstrap.HttpServer;
import org.apache.http.impl.nio.bootstrap.ServerBootstrap;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.nio.protocol.BasicAsyncRequestConsumer;
import org.apache.http.nio.protocol.BasicAsyncResponseProducer;
import org.apache.http.nio.protocol.HttpAsyncExchange;
import org.apache.http.nio.protocol.HttpAsyncRequestConsumer;
import org.apache.http.nio.protocol.HttpAsyncRequestHandler;
import org.apache.http.protocol.HttpContext;

public class HttpComponentsConciseTest {

    /* 
     * The random.png.gz file must be large (> 2 MiB)
     */
    private static final String LARGE_COMPRESSED_FILE_NAME = "random.png.gz";
    private static final File LARGE_TEST_GZIP_FILE = new File(LARGE_COMPRESSED_FILE_NAME);
    private static final String SOAP_RESPONSE_MESSAGE_XML
            = "<soapenv:Envelope "
            + "xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
            + "   <soapenv:Header/>\n"
            + "   <soapenv:Body>\n"
            + "      <AResponse>\n"
            + "         <ResponseFile>" + LARGE_COMPRESSED_FILE_NAME + "</ResponseFile>\n"
            + "      </AResponse>\n"
            + "   </soapenv:Body>\n"
            + "</soapenv:Envelope>";

    private static final int PORT_NUMBER = 8080;

    public static void main(String[] args) {
        startHttpServer();
    }

    private static void startHttpServer() {
        IOReactorConfig config = IOReactorConfig.custom()
                .setIoThreadCount(1)
                .setSoTimeout(15000)
                .setTcpNoDelay(true)
                .build();

        final HttpServer server = ServerBootstrap.bootstrap()
                .setListenerPort(PORT_NUMBER)
                .setServerInfo("Test/1.1")
                .setIOReactorConfig(config)
                .setExceptionLogger(ExceptionLogger.STD_ERR)
                .registerHandler("*", new PrimaryRequestHandler())
                .create();

        Runtime.getRuntime().addShutdownHook(new Thread(() -> server.shutdown(5,
                TimeUnit.MILLISECONDS)));

        Thread serverOwner = new Thread(() -> {

            try {
                server.start();
            } catch (IOException ex) {
                Logger.getLogger(HttpComponentsConciseTest.class.getName()).log(Level.SEVERE, null,
                        ex);
            }
        }, "ServerOwner");
        serverOwner.start();
    }

    private static class PrimaryRequestHandler implements HttpAsyncRequestHandler<HttpRequest> {

        @Override
        public HttpAsyncRequestConsumer<HttpRequest> processRequest(
                final HttpRequest request,
                final HttpContext context) {
            return new BasicAsyncRequestConsumer();
        }

        @Override
        public void handle(
                final HttpRequest request,
                final HttpAsyncExchange httpexchange,
                final HttpContext context) throws HttpException, IOException {
            HttpResponse response = httpexchange.getResponse();

            String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);
            if (!method.equals("POST")) {
                throw new MethodNotSupportedException(method + " method not supported");
            }

            StringBody soapResponseStringBody = new StringBody(SOAP_RESPONSE_MESSAGE_XML,
                    ContentType.APPLICATION_XML);
            FileBody soapAttachment = new FileBody(LARGE_TEST_GZIP_FILE);
            HttpEntity responseEntity = MultipartEntityBuilder.create()
                    .addPart("SOAP Envelope", soapResponseStringBody)
                    .addPart(LARGE_COMPRESSED_FILE_NAME, soapAttachment)
                    .build();
            response.setStatusCode(SC_OK);
            response.setEntity(responseEntity);

            httpexchange.submitResponse(new BasicAsyncResponseProducer(response));
        }
    }
}

Вставить ссылку на источник

https://pastebin.com/2Hzdhpt3

0 ответов

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