El uso de la plantilla Spring Rest causa EOFException


Estoy recibiendo java.io.EOFException's cuando se utiliza la plantilla Spring REST en Android.

La causa stacktrace se lee así:

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

Otra pista similar:

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

Todo esto está sucediendo en Android 4.1.2, instalado en mi tableta Xoom.

El problema aparece y desaparece. Tampoco se desencadena por peticiones largas. La parte del servidor se está ejecutando en una máquina dentro de la red local. Cuando intento ejecutar las llamadas a la API a través de curl, funciona simplemente fino.

AuthTokenInterceptor :

@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);
}

LoggingClientHttpRequestInterceptor :

/** {@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;
}

La Plantilla Rest está configurada así:

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);

La llamada a la API en cuestión que se bloqueó aquí se describe en la interfaz basada en anotaciones de Android:

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

Cosas que he intentado:

  • LoggingInterceptor eliminado
  • Llamado a todas las llamadas API por CURL
  • Eliminó el búfer de llamadas ClientHttpRequestFactory - Ayudó a poco, pero el error todavía ocurre.
  • Probado en Android 2.3 - el error no se puede reproducir

He estado leyendo varios mensajes de foros, la excepción EOF parece aparecer si las URL son incorrectas, lo que comprobé dos veces en este caso.

También hay que destacar que una vez que se produce la Excepción EOF, la llamada ni siquiera llega al lado del servidor.

¿Dónde sería un buen punto para continuar la búsqueda de una solución? ¿Es esto un Android 4.1 inconveniente?

Mientras depuraba este problema, también encontré https://jira.springsource.org/browse/ANDROID-102 lo que me impidió ver el verdadero error (EOF) antes.


Actualización: Acaba de encontrar http://code.google.com/p/google-http-java-client/issues/detail?id=116 - podría estar relacionado.

La solución también se describe en https://codereview.appspot.com/6225045 / - por lo que podría haber sido fusionado para 4.1.

Author: Raedwald, 2012-11-01

4 answers

Este también me mordió, corriendo Jelly Bean 4.2. Después de investigar, parece que está sucediendo debido a una combinación de Keep-Alive que se establece y utilizando el cliente HTTP estándar J2SE, que creo que es HttpURLConnection.

Hay 2 soluciones que puedo confirmar que son correctos.

1) Desactive Keep-Alive.
Para mí, la solución dada en la respuesta de Sebastian, Sistema.setProperty ("http.keepAlive", "false"); no funcionó. Tuve que use

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

Y enviar esas cabeceras en un HttpEntity en el RestTemplate.
Como se mencionó, esta solución podría tener un impacto en el rendimiento

2) Cambie el cliente HTTP.
En primavera para Android (probado en 1.0.1.Versión, pero también podría estar en versiones anteriores) el cliente HTTP predeterminado para una instancia RestTemplate está determinado por la versión de Android en el dispositivo. La API 9 o más reciente usa HttpURLConnection, la anterior usa HttpClient. Para establecer explícitamente el cliente en el antiguo, use

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

Puede encontrar más información aquí: http://static.springsource.org/spring-android/docs/1.0.1.RELEASE/reference/htmlsingle/#d4e34
No estoy seguro de qué impacto tendrá esto en el rendimiento, pero supongo que es más eficiente que una aplicación que no funciona.

De todos modos, espero que eso ayude a alguien. Acabo de perder una semana persiguiendo a este.

 59
Author: jaseelder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-20 22:47:44

Http://code.google.com/p/google-http-java-client/issues/detail?id=116 contiene una solución alternativa en el último comentario:

Esto está defenetly de alguna manera conectado con las conexiones keepAlive.

Cuando uso: Sistema.setProperty ("http.keepAlive", "false"); problemas desaparecer.

Pero desde mi punto de vista mantener vivas las conexiones se incrementan enormemente rendimiento por lo que es mejor no desactivarlos.

Yo también quiero que mantengas vivo debe estar desactivado para las versiones antiguas, pero mi dispositivo es Jelly Bean.

Una vez aplicado el error desapareció.
Parece que no está totalmente relacionado con Spring, sino con un problema de JB.

 11
Author: Sebastian Roth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-26 03:14:41

Recientemente me enfrenté a este problema y podré resolver este problema después de configurar los encabezados con el siguiente fragmento de código:

headers.set("Accept-Language", "en-US,en;q=0.8");
 0
Author: Bajarang Agarwal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-28 11:06:35
RestTemplate restTemplate = new RestTemplate();
            ((SimpleClientHttpRequestFactory)restTemplate.getRequestFactory()).setOutputStreaming(false);

restTemplate.postForObject......
 -1
Author: moser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-24 11:52:55