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
Ошибка репликации
- Создайте исходный код примера с библиотеками HttpCore v4.4.6 и HttpMime v4.5.3 (HttpMime является частью проекта HttpClient).
- Запустите программу с достаточно большим (>2 МБ) двоичным файлом random.png.gz.
- Отправьте серверу произвольный запрос 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));
}
}
}
Вставить ссылку на источник