Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OkHttp3 - sll exception on reading response data #3596

Closed
hasretsariyer opened this issue Sep 11, 2017 · 13 comments
Closed

OkHttp3 - sll exception on reading response data #3596

hasretsariyer opened this issue Sep 11, 2017 · 13 comments

Comments

@hasretsariyer
Copy link

Trace is here :
javax.net.ssl.SSLException: Read error: ssl=0x8797a780: I/O error during system call, Connection reset by peer
at com.android.org.conscrypt.NativeCrypto.SSL_read(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:758)
at okio.Okio$2.read(Okio.java:139)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:237)
at okio.RealBufferedSource.read(RealBufferedSource.java:46)
at okhttp3.internal.http1.Http1Codec$FixedLengthSource.read(Http1Codec.java:384)
at okio.Buffer.writeAll(Buffer.java:1005)
at okio.RealBufferedSource.readByteArray(RealBufferedSource.java:107)
at okhttp3.ResponseBody.bytes(ResponseBody.java:136)

The library triggers onResponse callback with 304 status code. It is true for my case. I can get content length of body, headers of response. But I couldn't get bytes or string of response body.

            ResponseBody body = response.body();
            int statusCode = response.code();
            byte[] bodyBytes = body.bytes();

The error is thrown on body.bytes() method. Okhttp version is 3.8.1.

@swankjesse
Copy link
Member

Executable test case?

@hasretsariyer
Copy link
Author

hasretsariyer commented Sep 12, 2017

My body content is here

Volley works fine. See the server details .

Full code is below:


int timeout = 200000;
OkHttpClient.Builder clientBuilder, OkHttpClient client;
clientBuilder = new OkHttpClient.Builder();
clientBuilder.connectTimeout(timeout, TimeUnit.MILLISECONDS);
clientBuilder.readTimeout(timeout, TimeUnit.MILLISECONDS);
clientBuilder.writeTimeout(timeout, TimeUnit.MILLISECONDS);
client = clientBuilder.build();
client.retryOnConnectionFailure();

const String URL = "https://portalapi.smartface.io/api/v1/rau/check?v=" + Math.floor(Math.random() * 1000);
final Request request = new Request.Builder().url(URL)
	.post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), fileContentText)).build();

Call call = client.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        Log.i("@@TAG", " Failure " + e.getMessage());
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        String message = response.message();
        
        Headers headers = response.headers();
        for(int i = 0; i < headers.size(); i++) {
            Log.d("@@Headers ", i + " ] " + headers.name(i) + " : " + headers.value(i));
        }

        ResponseBody body = response.body();
        int statusCode = response.code();

        Log.i("@@LOG " , " statusCode: " + statusCode + " contentLength: " + body.contentLength());
        byte[] bodyBytes = body.bytes();
        Log.i("@@LOG " , " bodyLength: " + bodyBytes.length);
    }
}

@hasretsariyer
Copy link
Author

Are you reproducing this error? If yes, is there any solution to fix it?

@yschimke
Copy link
Collaborator

I can reproduce the hang and then timeout, seems like a problem with the response from the server, Content-Length is 30, but I don't see those bytes come back.

$ oksocial -i -H 'Content-Type: application/json; charset=utf-8' -d @Downloads/body.txt 'https://portalapi.smartface.io/api/v1/rau/check?v=0'

HTTP/1.1 304 Not Modified
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 30
Content-Type: application/json; charset=utf-8

Curl seems to handle it more gracefully by giving up.

$ curl -s -X POST -H "User-Agent:oksocial/1.0.53" -H "Content-Type:application/json; charset=utf-8" -H "Content-Length:495803" -H "Host:portalapi.smartface.io" -H "Connection:Keep-Alive" -H "Accept-Encoding:gzip" -d @Downloads/body.txt https://portalapi.smartface.io/api/v1/rau/check?v=0 | wc -c
       0

@hasretsariyer
Copy link
Author

hasretsariyer commented Sep 15, 2017

Yeah, the response body is empty for this request. It is an expected behaviour. But okhttp doesn't return null when response.body() is called. So, I don't know the body is empty or not in programmatically if the return value isn't null.

But it throws error response.body().string() or response.body().source() etc. I expect that response.body() returns null if the response body is empty.

ResponseBody responseBody = response.body();
  if(responseBody != null && responseBody.contentLength() > 0) {
        byte[] bytes = responseBody.bytes();
}

@yschimke
Copy link
Collaborator

I believe the server is sending an incorrect header. You can send "Connection: close" as a header to make it clearer what is going on, but OkHttp is doing the right thing here.

$ oksocial -i -H 'Content-Type: application/json; charset=utf-8' -H 'Connection: close'  -d @Downloads/body.txt 'https://portalapi.smartface.io/api/v1/rau/check?v=0'

java.net.ProtocolException: unexpected end of stream
	at okhttp3.internal.http1.Http1Codec$FixedLengthSource.read(Http1Codec.java:387)
	at okio.RealBufferedSource.exhausted(RealBufferedSource.java:55)
	at com.baulsupp.oksocial.output.DownloadHandler.writeToSink(DownloadHandler.java:55)
	at com.baulsupp.oksocial.output.ConsoleHandler.showOutput(ConsoleHandler.java:79)
	at com.baulsupp.oksocial.Main.showOutput(Main.java:592)
	at com.baulsupp.oksocial.Main.processResponses(Main.java:570)
	at com.baulsupp.oksocial.Main.executeRequests(Main.java:558)
	at com.baulsupp.oksocial.Main.run(Main.java:341)
	at com.baulsupp.oksocial.Main.main(Main.java:120)

@hasretsariyer
Copy link
Author

I am not sure. Volley works properly. Also, curl doesn't throw any error.

curl -X POST -H "User-Agent:oksocial/1.0.53" -H "Content-Type:application/json; charset=utf-8" -H "Content-Length:495803" -H "Host:portalapi.smartface.io" -H "Connection:Keep-Alive" -H "Accept-Encoding:gzip" -d @/users/auser/Downloads/body.txt https://portalapi.smartface.io/api/v1/rau/check?v=11 | wc -c
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 484k 0 0 100 484k 0 159k 0:00:03 0:00:03 --:--:-- 159k
0

curl -X POST -H "User-Agent:oksocial/1.0.53" -H "Content-Type:application/json; charset=utf-8" -H "Content-Length:495803" -H "Host:portalapi.smartface.io" -H "Connection:close" -H "Accept-Encoding:gzip" -d @/users/auser/Downloads/body.txt https://portalapi.smartface.io/api/v1/rau/check?v=11 | wc -c
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 484k 0 0 100 484k 0 153k 0:00:03 0:00:03 --:--:-- 153k
0

@yschimke
Copy link
Collaborator

ok. how about you check the status before reading the body?

@hasretsariyer
Copy link
Author

Look at here. It says that "The 304 response MUST NOT contain a message-body". If the status code is 304, we shouldn't read response body content.

@yschimke
Copy link
Collaborator

yschimke commented Sep 15, 2017

@swankjesse what is expected behaviour for 304, when server indicates there is a response, but it doesn't come? OR worse it does come so needs to be drained for keepalive?

@swankjesse
Copy link
Member

We handle 304s properly in our cache code. When we get a 304 when the cache isn’t involved we might be broken. We should confirm that we expect no response body.

@yschimke
Copy link
Collaborator

yschimke commented Sep 15, 2017

I don't think there is a right answer here. If we ignore the header, and the server sends a response the we break some other malformed servers with keepalives etc.

// If the Content-Length or Transfer-Encoding headers disagree with the response code, the
// response is malformed. For best compatibility, we honor the headers.
if (contentLength(response) != -1
|| "chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
return true;
}

@yschimke
Copy link
Collaborator

Going to close this as won't fix. We risk breaking other clients by changing this behaviour, and ultimately the server should be fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants