Retrofit POST java.io.IOException:java.io.EOFException 导致连接上的流意外结束:\n 未找到:

Retrofit POST java.io.IOException: unexpected end of stream on Connection caused by java.io.EOFException: \n not found:

我已经解决了与此相关的所有问题,但我还没有找到适合我的解决方案。

我正在使用 retrofit 2.8.1OkHttp 4.5.0

我的服务界面如下所示

public interface MlApiService
{
    @POST
    @Multipart
    Call<List<PreprocessedDocument>> postDocument( @Url String apiUrl, @Part MultipartBody.Part document,
    @Part ( "document_id") RequestBody documentId );
}

然后我像下面这样构建客户端,requestTimeoutInSeconds 设置为 90 秒。

public void init()
{
    GsonBuilder gson = new GsonBuilder();
    gson.registerTypeAdapter( new TypeToken<List<PreprocessedDocument>>() {}.getType(), new CustomResponseDeserializer() );

    HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor();
    logInterceptor.setLevel( HttpLoggingInterceptor.Level.HEADERS );

    OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure( true ).addInterceptor( logInterceptor )
        .readTimeout( requestTimeoutInSeconds, TimeUnit.SECONDS ).build();
    //Dummy Base URL must be provided. otherwise client won't get initialized
    Retrofit retrofit = new Retrofit.Builder().baseUrl( "http://thisIsJustDummyUrlForTheSakeOfAddingBaseUrl.com/" )
        .client( client ).addConverterFactory( GsonConverterFactory.create( gson.setLenient().create() ) ).build();
    mlApiService = retrofit.create( MlApiService.class );
}

请求到达服务器,当服务器响应时我收到以下错误:

Caused by: java.io.IOException: unexpected end of stream on Connection{34.XXX.XXX.9:8085, proxy=DIRECT hostAddress=/34.XXX.XXX.9:8085 cipherSuite=none protocol=http/1.1}
    at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:203)
    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
Caused by: java.io.EOFException: \n not found: limit=0 content=…
    at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:227)
    at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:211)
    at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:187)

到目前为止我尝试过的东西很少

  1. retryOnConnectionFailure(真)
  2. .addHeader("连接","关闭")
  3. .header("Accept-Encoding", "身份")

API 在 postman 中工作正常,但当我在代码中尝试时它失败了。所以我尝试了邮递员发送的相同 headers 。仍然没有运气。

少量观察:

  1. 它有时有效。不会总是失败。(同一个文件总是与邮递员一起工作)
  2. 它始终适用于其他文件(从来不是问题)。
  3. 请求到达服务器并在没有任何错误的情况下处理请求并响应。服务器完成处理并响应客户端后立即收到错误。

编辑 1: 我点击的服务器由 gunicorn/20.0.4 支持并使用 Flask。我无权访问服务器代码。而且我怀疑发送的响应是否包含导致错误的垃圾字符。我不知道如何在 retrofit/okhttp.

读取之前记录原始响应

编辑 2:

我 运行 详细的 Curl 命令,这就是我得到的。

< HTTP/1.1 100 Continue

  • Empty reply from server
  • Connection #0 to host xx.xxx.xxx.9 left intact curl: (52) Empty reply from server

您收到没有状态行的空回复。这就是问题。 HTTP 请求通常 return 状态行(例如 HTTP/1.1 200 OK\r\n),其中包含状态代码,请参阅 https://www.ietf.org/rfc/rfc2616.txt 第 6.1 章。 这通常是服务器错误。

短篇小说

问题出在我访问的服务器上。它没有发送任何响应(实际上什么都没有。没有 headers,没有 body,没有)。

说来话长

所以在浏览了 Whosebug 和其他好的网站上的所有相关答案并尝试了我在问题本身中提到的这么多解决方案之后,它并没有解决我的问题。

仔细阅读堆栈跟踪后,我发现了以下行。

okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:203)

客户端(我的代码)正在尝试读取响应 header,那是抛出错误 java.io.EOFException: \n not found: limit=0 content= 的时候。

这提示我问题可能出在服务器上,而不是客户端。所以我想我应该尝试使用不同的客户端,看看我是否可以看到原始响应。

我想到的第一个工具是 Curl(Postman 曾经给通用的 Could not get any response 而这并没有一直发生)。我使用带有详细选项的 curl 访问了服务器,然后砰的一声!我收到以下回复:

curl -v --location --request POST 'http://XX.XXX.XXX.9:8085/psc/document_upload' --form 'document=@/home/user376/Downloads/test-1.pdf' --form 'document_id=22004494_ae7f_4998_a1d8_73249bda9905'
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying XX.XXX.XXX.9...
* Connected to XX.XXX.XXX.9 (XX.XXX.XXX.9) port 8085 (#0)
> POST /psc/document_upload HTTP/1.1
> Host: XX.XXX.XXX.9:8085
> User-Agent: curl/7.49.0
> Accept: */*
> Content-Length: 4684053
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------a8446c7eedb10689
> 
< HTTP/1.1 100 Continue
* Empty reply from server
* Connection #0 to host XX.XXX.XXX.9 left intact
curl: (52) Empty reply from server

确认问题出在服务器上,而不是客户端(Retrofit / http)。

故事的寓意: 有时您必须逐字阅读堆栈跟踪,即使它看起来不值得研究:)

很可能有两件事同时发生。
首先,url 包含一个不常用的端口,其次,您使用的 VPN 或代理不支持该端口。
就我个人而言,我遇到了同样的问题。
我的服务器端口是 45860,我使用的是 pSiphon anti-filter VPN。
在那种情况下,只有当服务器的 relpy 是状态代码大于 0 的错误时,我的邮递员才报告“连接 hang-up”。(当某些文本从服务器返回时没有错误代码,这很好)
然后我将服务器上的 Web 服务端口更改为 8080,哇,成功了!尽管已连接 psiphon vpn。
因此,我的建议是,如果可以更改服务器端口,那么尝试一下,或者检查是否存在代理问题。可能你的 Postman 和 acual app 不在同一台机器上。