Запрос сертификата клиента HTTPS зависает, когда Сервер обрабатывает большой запрос PUT / POST - PullRequest
0 голосов
/ 04 июня 2019

Я пытаюсь установить двустороннее взаимное подтверждение связи между клиентом и сервером, где сервер использует TLS.API от сервера принимает файл через почтовый запрос, а клиент настроен с хранилищем ключей и может отправлять файлы размером менее 50 КБ.API начал работать с ошибкой Borken Pipe при отправке размером более 50 КБ.Я нашел ссылку здесь , которая говорит о подобной проблеме.

Как предложено в статье, я изменил код, чтобы отправить запрос 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, и я не имею представления о его реализации.Он работает для файлов размером менее 50 КБ, поэтому моя конфигурация безопасности хорошая.

...