我正在尝试在服务器使用TLS的客户端和服务器之间建立双向双向握手。来自服务器的API通过发布请求接受文件,并且客户端配置了密钥库,并且能够发送小于50kb的文件。发送大于50kb的API时,由于Borken Pipe
错误而中断。我找到了一个链接here,该链接讨论了类似的问题。
如文章所述,我修改了代码,以在发送带有文件的原始请求之前发送get请求以建立安全连接。问题仍然存在,并给出相同的错误。
客户端设置:
@Bean
@DependsOn(value = {"customRestTemplateCustomizer"})
public RestTemplateBuilder restTemplateBuilder() {
try {
return new RestTemplateBuilder()
.requestFactory(this::clientHttpRequestFactory)
.rootUri(properties.getBaseUrl())
.errorHandler(new RestTemplateErrorHandler());
} catch (Exception e) {
log.info("Exception thrown while setting ssl context : {} ", e.getMessage());
throw e;
}
}
@Bean
ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
httpComponentsClientHttpRequestFactory.setBufferRequestBody(false);
return httpComponentsClientHttpRequestFactory;
}
@Bean
HttpClient httpClient() {
SSLContext sslContext = sslContext();
SSLConnectionSocketFactory sslSocketFactory = socketFactory(sslContext);
return HttpClients.custom()
.setSSLSocketFactory(sslSocketFactory)
.build();
}
@Bean
@SneakyThrows
public SSLContext sslContext(){
return new SSLContextBuilder()
.loadKeyMaterial(ResourceUtils.getFile(keyStore), keyStorePassword.toCharArray(), keyStorePassword.toCharArray())
.build();
}
@Bean
public SSLConnectionSocketFactory socketFactory(SSLContext sslContext) {
return new SSLConnectionSocketFactory(sslContext);
}
static {
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> hostname.equals(url));
}
@Bean
public CustomRestTemplateCustomizer customRestTemplateCustomizer() {
return new CustomRestTemplateCustomizer();
}
获取呼叫以建立连接呼叫:
//get call before making actual call
String response = restTemplate.getForObject("www.getapi.com", String.class);
//实际通话
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.setConnection("keep-alive");
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("SigningId", signingId);
body.add("Payload", new StreamedFile(inputStream, fileName));
body.add("Payload Hash", hashValue);
body.add("Callback URL", callbackURL);
ResponseEntity<Response> responseEntity = restTemplate.postForEntity(
outgoingURL,
new HttpEntity<>(body, headers),
Response.class);
例外:
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "www.postapi.com": null; nested exception is org.apache.http.client.ClientProtocolException] with root cause
java.net.SocketException: Broken pipe (Write failed)
at java.net.SocketOutputStream.socketWrite0(Native Method) ~[na:1.8.0_202]
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111) ~[na:1.8.0_202]
at java.net.SocketOutputStream.write(SocketOutputStream.java:155) ~[na:1.8.0_202]
at sun.security.ssl.OutputRecord.writeBuffer(OutputRecord.java:431) ~[na:1.8.0_202]
at sun.security.ssl.OutputRecord.write(OutputRecord.java:417) ~[na:1.8.0_202]
at sun.security.ssl.SSLSocketImpl.writeRecordInternal(SSLSocketImpl.java:879) ~[na:1.8.0_202]
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:850) ~[na:1.8.0_202]
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:123) ~[na:1.8.0_202]
at org.apache.http.impl.io.SessionOutputBufferImpl.streamWrite(SessionOutputBufferImpl.java:124) ~[httpcore-4.4.11.jar:4.4.11]
at org.apache.http.impl.io.SessionOutputBufferImpl.write(SessionOutputBufferImpl.java:160) ~[httpcore-4.4.11.jar:4.4.11]
at org.apache.http.impl.io.ContentLengthOutputStream.write(ContentLengthOutputStream.java:113) ~[httpcore-4.4.11.jar:4.4.11]
at org.apache.http.impl.io.ContentLengthOutputStream.write(ContentLengthOutputStream.java:120) ~[httpcore-4.4.11.jar:4.4.11]
at org.springframework.util.StreamUtils.copy(StreamUtils.java:106) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.lambda$execute$1(InterceptingClientHttpRequest.java:102) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.http.client.HttpComponentsStreamingClientHttpRequest$StreamingHttpEntity.writeTo(HttpComponentsStreamingClientHttpRequest.java:152) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.apache.http.impl.execchain.RequestEntityProxy.writeTo(RequestEntityProxy.java:123) ~[httpclient-4.5.1.jar:4.5.1]
at org.apache.http.impl.DefaultBHttpClientConnection.sendRequestEntity(DefaultBHttpClientConnection.java:156) ~[httpcore-4.4.11.jar:4.4.11]
at org.apache.http.impl.conn.CPoolProxy.sendRequestEntity(CPoolProxy.java:162) ~[httpclient-4.5.1.jar:4.5.1]
我以为httpclient正在关闭每个请求的连接,所以我已经添加了
headers.setConnection("keep-alive");
但是没有用。我尝试搜索有关httpconnecton关闭的信息,但很多人说,默认情况下,它重新使用与会话相同的连接。
不幸的是,服务器代码是用python编写的,我对它的实现一无所知。它适用于小于50kb的文件,因此我的安全配置很好。