服务器处理大型PUT / POST请求时,HTTPS客户端证书请求冻结

时间:2019-06-04 20:26:53

标签: java ssl httpclient resttemplate tls1.2

我正在尝试在服务器使用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的文件,因此我的安全配置很好。

0 个答案:

没有答案