El uso de la plantilla Spring Rest causa EOFException

Estoy recibiendo java.io.EOFException cuando utilizo 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 

Otro stacktrace 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 ocurre en Android 4.1.2, instalado en mi tableta Xoom.

El problema aparece y desaparece. No es desencadenada por largas peticiones tampoco. La parte del servidor se ejecuta en una máquina dentro de la red local. Cuando intento ejecutar las llamadas de la API a través de curl , funciona muy bien.

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 se configura de la siguiente manera:

 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 de la API en cuestión que se estrelló 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 probado:

  • Removed LoggingInterceptor
  • Llamado a todas las llamadas de la API por CURL
  • Se ha eliminado la llamada BufferingClientHttpRequestFactory – Ha ayudado un poco, pero el error sigue ocurriendo.
  • Probado en Android 2.3 – el error no se puede reproducir

He estado leyendo varias entradas de foros, la excepción EOF parece que aparece si las URL son incorrectas, lo que he comprobado en este caso.

También hay que tener en cuenta, 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 inconveniente de Android 4.1?

Durante la depuración de este problema, también encontré https://jira.springsource.org/browse/ANDROID-102 que me impidió ver el error real (EOF) antes.


Actualización: Acabo de encontrar http://code.google.com/p/google-http-java-client/issues/detail?id=116 – puede estar relacionado.

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

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

Hay 2 soluciones que puedo confirmar que son correctas.

1) Apague Keep-Alive.
Para mí, la solución dada en la respuesta de Sebastian, System.setProperty ("http.keepAlive", "false"); No funcionó. Tuve que usar

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

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

2) Cambiar el cliente HTTP.
En primavera para Android (probado en 1.0.1.RELEASE, pero podría estar en versiones anteriores también), el cliente HTTP predeterminado para una instancia de RestTemplate está determinado por la versión de Android en el dispositivo. API 9 o más reciente utiliza HttpURLConnection, usos anteriores HTTPClient. Para establecer explícitamente el cliente en el antiguo, utilice

 restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory()); 

Más información se puede encontrar 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 efectivo que una aplicación que no funciona.

De todos modos, espero que ayude a alguien. Acabo de perder una semana salvaje-ganso-persiguiendo éste abajo.

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

Esto es defenetly de alguna manera conectado con conexiones keepAlive.

Cuando uso: System.setProperty ("http.keepAlive", "false"); Problemas desaparece.

Pero desde mi entendimiento, mantener las conexiones activas aumentan considerablemente el rendimiento, por lo que es mejor no desactivarlas.

Im también awere que mantener vivo debería ser deshabilitado para versiones antiguas, pero mi dispositivo es Jelly Bean.

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

Recientemente me enfrenté a este problema y será capaz de resolver este problema después de establecer los encabezados con el siguiente código:

 headers.set("Accept-Language", "en-US,en;q=0.8"); 
 RestTemplate restTemplate = new RestTemplate(); ((SimpleClientHttpRequestFactory)restTemplate.getRequestFactory()).setOutputStreaming(false); restTemplate.postForObject...... 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.