복붙노트

[SPRING] Spring Rest Template 사용으로 인해 EOFException이 발생 함

SPRING

Spring Rest Template 사용으로 인해 EOFException이 발생 함

Android에서 Spring RESTtemplate을 사용할 때 java.io.EOFException을 수신합니다.

stacktrace 원인은 다음과 같이 읽습니다.

Caused by: java.io.EOFException
at libcore.io.Streams.readAsciiLine(Streams.java:203)
at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:560)
at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:813)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:274)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:486)
at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:49)
at org.springframework.http.client.SimpleClientHttpResponse.getStatusCode(SimpleClientHttpResponse.java:55)
at org.springframework.http.client.BufferingClientHttpResponseWrapper.getStatusCode(BufferingClientHttpResponseWrapper.java:47)
at com.company.util.LoggingClientHttpRequestInterceptor.intercept(LoggingClientHttpRequestInterceptor.java:33)
at org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:81)
at com.company.api.interceptor.AuthTokenInterceptor.intercept(AuthTokenInterceptor.java:51)
at org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:81)
at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:67)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:46)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:63)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:475)
... 14 more

또 다른 비슷한 스택 추적 :

org.springframework.web.client.ResourceAccessException: I/O error: null; nested exception is java.io.EOFException
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:490)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:438)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:414)
at com.company.api.ApiClient_.logLoginAttempt(ApiClient_.java:299)
at com.company.security.CompanyAuthenticationService$2.onCreateCall(CompanyAuthenticationService.java:206)
at com.company.api.SafeApiCall.doInBackground(SafeApiCall.java:49)
at com.company.api.SafeApiCall.doInBackground(SafeApiCall.java:22)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
Caused by: java.io.EOFException
at libcore.io.Streams.readAsciiLine(Streams.java:203)
at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:560)
at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:813)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:274)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:486)
at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:49)
at org.springframework.http.client.SimpleClientHttpResponse.getStatusCode(SimpleClientHttpResponse.java:55)
at org.springframework.http.client.BufferingClientHttpResponseWrapper.getStatusCode(BufferingClientHttpResponseWrapper.java:47)
at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:46)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:476)
... 13 more

이것은 내 Xoom 태블릿에 설치된 Android 4.1.2에서 모두 발생합니다.

문제가 나타나고 사라집니다. 긴 요청으로도 트리거되지 않습니다. 서버 부분은 로컬 네트워크 내의 시스템에서 실행 중입니다. 컬을 통해 API 호출을 실행하려고하면 정상적으로 작동합니다.

인증 토큰 인터셉터 :

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] data, ClientHttpRequestExecution execution) throws IOException {
    HttpHeaders headers = request.getHeaders();
    if (!StringUtils.isEmpty(mAuthToken)) {
        headers.add((mIsOAuth ? "Authorization" : "authToken"), (mIsOAuth ? "Bearer " : "") + mAuthToken);
    }
    return execution.execute(request, data);
}

로깅 ClientHttpRequestInterceptor :

/** {@inheritDoc} */
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
    Log.d(TAG, "To     : " + httpRequest.getURI());
    Log.d(TAG, "Method : " + httpRequest.getMethod().name());
    Log.d(TAG, "Data   : " + new String(bytes));

    for (Object key : httpRequest.getHeaders().keySet()) {
        Log.d(TAG, "Header <" + key + ">: " + httpRequest.getHeaders().get(key));
    }

    final ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes);

    if (response != null) {
        Log.d(TAG, "Response: " + response.getStatusCode());
        if (response.getBody() != null) {
            Log.d(TAG, "Response: " + convertStreamToString(response.getBody()));
        }
    } else {
        Log.d(TAG, "Response: " + response);
    }

    return response;
}

나머지 템플릿은 다음과 같이 구성됩니다.

final RestTemplate template = new RestTemplate(false);
template.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
template.setRequestFactory(new BufferingClientHttpRequestFactory(template.getRequestFactory()));
ApiUtils.addAuthTokenHeaderToRestTemplate(template, mAuthToken, false);
ApiUtils.addRequestLoggingToRestTemplate(template);

문제가 된 API 호출은 Android 주석 기반 인터페이스에 설명되어 있습니다.

@Post("/user/memberships")
@Accept(MediaType.APPLICATION_JSON)
CompanyApiResponse saveGroupMembership(UserGroupMembership membership) throws RestClientException;

내가 시도한 것들 :

여러 포럼 게시물을 읽었습니다. URL이 올바르지 않은 경우 EOF 예외가 표시되는 것 같습니다.이 경우 두 번 확인됩니다.

또한 EOF 예외가 발생하면 호출이 서버 측으로 전달되지 않습니다.

수정 프로그램 검색을 계속하는 것이 좋은 점은 어디입니까? Android 4.1에 불편을 드려도 될까요?

이 문제를 디버깅하는 동안 https://jira.springsource.org/browse/ANDROID-102에서 실제 오류 (EOF)를 보지 못하게되었습니다.

업데이트 : http://code.google.com/p/google-http-java-client/issues/detail?id=116에서 발견되었습니다. 관련성이 있습니다.

이 수정 사항은 https://codereview.appspot.com/6225045/에 요약되어 있으므로 4.1에 병합되었을 수도 있습니다.

해결법

  1. ==============================

    1.이 비트는 나뿐만 아니라, 실행 젤리 빈 4.2. 조사한 결과, Keep-Alive가 설정되고 표준 J2SE HTTP 클라이언트 (HttpURLConnection이라고 생각되는)를 사용하기 때문에 상황이 발생하고있는 것 같습니다.

    이 비트는 나뿐만 아니라, 실행 젤리 빈 4.2. 조사한 결과, Keep-Alive가 설정되고 표준 J2SE HTTP 클라이언트 (HttpURLConnection이라고 생각되는)를 사용하기 때문에 상황이 발생하고있는 것 같습니다.

    내가 옳은지 확인할 수있는 2 가지 해결책이 있습니다.

    1) Keep-Alive 스위치를 끕니다. 나를 위해, 솔루션은 세바스챤의 답변, System.setProperty ( "http.keepAlive", "false"); 작동하지 않았다. 나는 사용했다.

    HttpHeaders headers = new HttpHeaders();
    headers.set("Connection", "Close");
    

    RestTemplate의 HttpEntity에서 해당 헤더를 보냅니다. 언급했듯이이 솔루션은 성능에 영향을 미칠 수 있습니다.

    2) HTTP 클라이언트를 변경하십시오. 안드로이드 용 Spring (1.0.1.RELEASE에서 테스트되었지만 이전 릴리즈에서도 가능)에서는 RestTemplate 인스턴스의 기본 HTTP 클라이언트가 장치의 Android 버전에 따라 결정됩니다. API 9 이상은 HttpURLConnection을 사용하고, 이전 버전은 HTTPClient를 사용합니다. 명시 적으로 클라이언트를 이전 클라이언트로 설정하려면 다음을 사용하십시오.

    restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
    

    자세한 정보는 http://static.springsource.org/spring-android/docs/1.0.1.RELEASE/reference/htmlsingle/#d4e34에서 확인할 수 있습니다. 이것이 성능에 어떤 영향을 미칠지 모르겠지만 작동하지 않는 앱보다 성능이 더 좋습니다.

    어쨌든, 누군가를 돕는 희망. 나는 야생의 거위 털이 일주일 만에 이것을 쫓아 냈다.

  2. ==============================

    2.http://code.google.com/p/google-http-java-client/issues/detail?id=116에는 최신 의견의 해결 방법이 포함되어 있습니다.

    http://code.google.com/p/google-http-java-client/issues/detail?id=116에는 최신 의견의 해결 방법이 포함되어 있습니다.

    일단 적용되면 오류가 사라집니다. Spring과 완전히 관련이없는 것 같지만 JB 문제입니다.

  3. ==============================

    3.최근에 나는이 문제에 직면했으며 다음 코드로 헤더를 설정 한 후에이 문제를 해결할 수있을 것이다.

    최근에 나는이 문제에 직면했으며 다음 코드로 헤더를 설정 한 후에이 문제를 해결할 수있을 것이다.

    headers.set("Accept-Language", "en-US,en;q=0.8");
    
  4. ==============================

    4.

    RestTemplate restTemplate = new RestTemplate();
                ((SimpleClientHttpRequestFactory)restTemplate.getRequestFactory()).setOutputStreaming(false);
    
    restTemplate.postForObject......
    
  5. from https://stackoverflow.com/questions/13182519/spring-rest-template-usage-causes-eofexception by cc-by-sa and MIT license