2

I'm successfully uploading smaller files < 10MB via okhttp (3.8.1) via https.

  • Testet on different Android 6 and 7 devices.
  • buildtools 27.0.0
  • compiledSDK 27

Using following libraries:

  • com.squareup.okhttp3:okhttp:3.8.1
  • com.squareup.okhttp3:logging-interceptor:3.8.1
  • com.squareup.okhttp3:okhttp-urlconnection:3.8.1

But if I try the same code with a file around 100-200MB the upload fails with the Exception:

javax.net.ssl.SSLException: Write error: ssl=0x559a6311d0: I/O error during system call, Connection reset by peer
    at com.android.org.conscrypt.NativeCrypto.SSL_write(Native Method)
    at com.android.org.conscrypt.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:771)
    at okio.Okio$1.write(Okio.java:79)
    at okio.AsyncTimeout$1.write(AsyncTimeout.java:180)
    at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:171)
    at okio.RealBufferedSink.write(RealBufferedSink.java:41)
    at okhttp3.internal.http1.Http1Codec$ChunkedSink.write(Http1Codec.java:325)
    at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:171)
    at okio.RealBufferedSink.write(RealBufferedSink.java:91)
    at de.fhdo.gobsis.mobile.libraries.ProgressRequestBody.writeTo(ProgressRequestBody.java:73)
    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:62)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
    at okhttp3.RealCall.execute(RealCall.java:69)
    at com.example.services.Uploadervice.onHandleIntent(UploadCaseService.java:274)
    at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:66)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.os.HandlerThread.run(HandlerThread.java:61)

The device is running and app in foreground. I'm using the following code in an Android IntentService:

ProgressRequestBody body = new ProgressRequestBody(
  getApplicationContext(),
  new ProgressRequestBody.UploadInfo(Uri.fromFile(json), json.length()),
      new ProgressRequestBody.ProgressCallback() {
          @Override
          public void onProgress(long progress, long total) {
              publishProgress(progress, total);
          }
      });

OkHttpClient client = new OkHttpClient.Builder() 
  .connectTimeout(10, TimeUnit.MINUTES) 
  .writeTimeout(10, TimeUnit.MINUTES) 
  .readTimeout(10, TimeUnit.MINUTES) 
  .build();
Request request = new Request.Builder()
  .header("X-Client-Type", "Android")
  .addHeader("APIKEY", "SomeRND")
  .url(url)
  .post(body)
  .build();

//Exception occures here:
Response response = client.newCall(request).execute();

The ProgressRequestBody extends the default okhttp RequestBody adds an interface for reporting the current uploadprogress. I overwrote the writeTo(BufferedSink sink)-Method like this:

@Override
public void writeTo(BufferedSink sink) throws IOException {
    long fileLength = mUploadInfo.contentLength;
    byte[] buffer = new byte[65536];
    InputStream in = in();
    long uploaded = 0;

    try {
        int read;
        while((read = in.read(buffer)) != -1) {
            mListener.onProgress(uploaded, fileLength);
            uploaded += read;
            sink.write(buffer, 0, read);
        }
    } finally {
        in.close();
    }
}

The file is read via the in()-method

 private InputStream in() throws IOException {
        InputStream stream = null;

        try {
            stream = getContentResolver().openInputStream(mUploadInfo.contentUri);
        } catch (Exception ex) {
            Log.e(LOG_TAG, "Error getting input stream for upload", ex);
        }

        return stream;
  }

The upload failes after ~3-5 minutes. Can someone tell me how I can get a stable connection or resume it after its broken? In several posts I read that ssl exception occurences are not rare on large file uploads but I found no satisfying solution on how to fix this.

Ben
  • 117
  • 15
  • *"I/O error during system call, Connection reset by peer"* - this is not an SSL exception. This is a problem at the layer below SSL, i.e. TCP. The server closed the connection for an unknown reason, maybe because it does not like large file uploads. It might also be some middlebox like a firewall or IDS/IPS on the path to the server. – Steffen Ullrich Nov 13 '17 at 07:52
  • I tried it on another internet connection and it always stops at 45% with the current file so maybe the server really don't like large files. I'm using a Tomcat 8.0.3 on a remote server and a rest service. I try looking into the tomcat config. Thanks for the suggestion – Ben Nov 13 '17 at 08:56
  • It is actually as you said. In this case the server got an out of memory exception because I send a single large json without streaming. I'll probably switch to MultiPartData with inputstream instead. – Ben Nov 15 '17 at 08:35

0 Answers0