Spring Rest模板使用导致EOFException

我在Android上使用Spring REST模板时收到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上。

问题出现并消失。 这也不是由长时间的请求触发的。 服务器部分在本地networking内的一台机器上运行。 当我尝试通过curl运行API调用时,它工作得很好。

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

其余模板configuration如下:

 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; 

我试过的东西:

  • 删除了LoggingInterceptor
  • 由CURL调用所有的API调用
  • 删除了调用BufferingClientHttpRequestFactory – 帮助了一下,但仍然发生错误。
  • testing它在Android 2.3 – 错误不能被复制

我一直在阅读各种论坛post,EOFexception似乎出现如果URL不正确,我在这种情况下,双重检查。

另外值得注意的是,一旦发生EOFexception,呼叫甚至不能到达服务器端。

哪里可以继续寻找解决scheme? 这是一个Android 4.1的不便吗?

在debugging这个问题的时候,我还发现https://jira.springsource.org/browse/ANDROID-102 ,它阻止我看到真正的错误(EOF)之前。


更新:刚刚findhttp://code.google.com/p/google-http-java-client/issues/detail?id=116 – 可能是相关的。

该修补程序也在https://codereview.appspot.com/6225045/中概述 – 因此它可能已经被合并为4.1。

这一点,我也运行果冻豆4.2。 经过研究,似乎是因为Keep-Alive被设置使用标准的J2SE HTTP客户端(我相信它是HttpURLConnection)的组合而发生的。

有两个解决scheme,我可以确认是正确的。

1)closures保持活动。
对我来说,在塞巴斯蒂安的答案给出的解决scheme, System.setProperty(“http.keepAlive”,“false”); 没有工作。 我不得不使用

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

并将这些头文件发送到RestTemplate中的HttpEntity中。
如前所述,这个解决scheme可能会影响性能

2)更改HTTP客户端。
在Android的Spring(在1.0.1.RELEASE上进行了testing,但也可能在较早的版本中),RestTemplate实例的默认HTTP客户端由设备上的Android版本确定。 API 9或更新版本使用HttpURLConnection,旧版本使用HTTPClient。 要明确地将客户端设置为旧的,请使用

 restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory()); 

更多信息可以在这里find: http : //static.springsource.org/spring-android/docs/1.0.1.RELEASE/reference/htmlsingle/#d4e34
我不确定这会对性能产生什么影响,但是我猜测它比不工作的应用更具性能。

无论如何,希望帮助别人。 我只是浪费了一个星期的野鹅 – 追逐这一个。

http://code.google.com/p/google-http-java-client/issues/detail?id=116包含最新评论的解决方法:;

这是通过keepAlive连接以某种方式连接的。

当我使用: System.setProperty(“http.keepAlive”,“false”); 问题消失。

但从我的理解保持活着连接大大提高性能,所以最好不要禁用它们。

我也认为保持活力应该被禁用旧版本,但我的设备是果冻豆。

一旦应用,错误消失。
看起来这并不完全与Spring有关,而是一个JB问题。

最近我遇到了这个问题,并能设置标题后面的一段代码解决了这个问题:

 headers.set("Accept-Language", "en-US,en;q=0.8"); 
 RestTemplate restTemplate = new RestTemplate(); ((SimpleClientHttpRequestFactory)restTemplate.getRequestFactory()).setOutputStreaming(false); restTemplate.postForObject......