请注意:此问题也已发布到Apache HttpClient官方用户邮件列表中。
问题
基于Apache HttpComponents构建的基于Java的服务器应如何组装并发送带有附件(SWA)响应的大型SOAP?
问题详情
Apache HttpClient Java API不支持分块多部分HttpEntity实例。当SWA响应中的附件很大(> 2 MiB)时,向模拟服务器发出SOAP请求的客户端(特别是基于Apache的Java客户端正在抛出org.apache.http.NoHttpResponseExceptions)失败。传输小附件时不会出现任何例外情况。 HTTP Content-Length标头是正确的。
用例
能够传输任意内容长度的SWA响应的多线程SOAP服务器
平台
Java 8 64b
必需的图书馆
HttpCore v4.4.6 + HttpMime v4.5.3
错误复制
示例服务器来源
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));
}
}
}
粘贴到源的链接