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

UndeliverableException due to SocketException when cancel the download #353

Open
Deishelon opened this issue Jun 29, 2022 · 0 comments
Open

Comments

@Deishelon
Copy link
Contributor

Deishelon commented Jun 29, 2022

Hi all,

Summary:

Creating a download task, and cancelling (disposing it) while download is ongoing results in SocketException which is thrown after disposable which results in UndeliverableException.

Version of software affected

  • Library version: 1.1.4
  • Demo app from master branch (commit: 0275362) [if RxJavaPlugins.setErrorHandler is removed from BaseApplication]

Failure Rate

100 %

Regression ?

Yes.
But not sure on the exact version when regression happened. The old lib version (from ~2019 ) does not have this problem.

Steps

  • Create Download Task (val disposable = Task(url).download).subscribeBy(....))
  • Wait until download started (i.e you get onNext callback with progress)
  • Cancel the download by calling dispose on disposable variable
  • The fatal error in the library will crash the application

Expected Result

  • Create Download Task
  • Wait until download started
  • Cancel the download
  • Download is cancelled & App is not crashed

Actual Result

  • Create Download Task
  • Wait until download started
  • Cancel the download
  • App crashed

Crashlogs

Crashlog
2022-06-29 17:48:51.650 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err: io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | java.net.SocketException: Socket closed
2022-06-29 17:48:51.650 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
2022-06-29 17:48:51.650 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.innerError(FlowableFlatMap.java:608)
2022-06-29 17:48:51.651 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onError(FlowableFlatMap.java:672)
2022-06-29 17:48:51.651 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableGenerate$GeneratorSubscription.onError(FlowableGenerate.java:189)
2022-06-29 17:48:51.651 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableGenerate$GeneratorSubscription.request(FlowableGenerate.java:114)
2022-06-29 17:48:51.651 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onSubscribe(FlowableFlatMap.java:656)
2022-06-29 17:48:51.651 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableGenerate.subscribeActual(FlowableGenerate.java:52)
2022-06-29 17:48:51.651 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.Flowable.subscribe(Flowable.java:14918)
2022-06-29 17:48:51.651 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.Flowable.subscribe(Flowable.java:14865)
2022-06-29 17:48:51.651 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.onNext(FlowableFlatMap.java:163)
2022-06-29 17:48:51.651 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.tryEmit(FlowableFlatMap.java:282)
2022-06-29 17:48:51.652 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onNext(FlowableFlatMap.java:663)
2022-06-29 17:48:51.652 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableOnBackpressureLatest$BackpressureLatestSubscriber.drain(FlowableOnBackpressureLatest.java:129)
2022-06-29 17:48:51.652 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableOnBackpressureLatest$BackpressureLatestSubscriber.onNext(FlowableOnBackpressureLatest.java:68)
2022-06-29 17:48:51.652 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFromObservable$SubscriberObserver.onNext(FlowableFromObservable.java:54)
2022-06-29 17:48:51.652 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeOnObserver.onNext(ObservableSubscribeOn.java:58)
2022-06-29 17:48:51.652 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:47)
2022-06-29 17:48:51.652 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.Observable.subscribe(Observable.java:12267)
2022-06-29 17:48:51.652 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
2022-06-29 17:48:51.652 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:578)
2022-06-29 17:48:51.652 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
2022-06-29 17:48:51.652 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
2022-06-29 17:48:51.653 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:266)
2022-06-29 17:48:51.653 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
2022-06-29 17:48:51.653 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
2022-06-29 17:48:51.653 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
2022-06-29 17:48:51.653 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.lang.Thread.run(Thread.java:923)
2022-06-29 17:48:51.653 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err: Caused by: java.net.SocketException: Socket closed
2022-06-29 17:48:51.653 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.net.SocketInputStream.read(SocketInputStream.java:209)
2022-06-29 17:48:51.654 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.net.SocketInputStream.read(SocketInputStream.java:144)
2022-06-29 17:48:51.654 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.readFromSocket(ConscryptEngineSocket.java:936)
2022-06-29 17:48:51.654 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.processDataFromSocket(ConscryptEngineSocket.java:900)
2022-06-29 17:48:51.654 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.readUntilDataAvailable(ConscryptEngineSocket.java:815)
2022-06-29 17:48:51.654 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.read(ConscryptEngineSocket.java:788)
2022-06-29 17:48:51.654 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at okio.InputStreamSource.read(Okio.kt:93)
2022-06-29 17:48:51.654 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:159)
2022-06-29 17:48:51.654 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at okio.RealBufferedSource.read(RealBufferedSource.kt:43)
2022-06-29 17:48:51.654 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at okhttp3.internal.http1.Http1Codec$AbstractSource.read(Http1Codec.java:363)
2022-06-29 17:48:51.656 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at okhttp3.internal.http1.Http1Codec$FixedLengthSource.read(Http1Codec.java:407)
2022-06-29 17:48:51.656 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at okio.RealBufferedSource.read(RealBufferedSource.kt:43)
2022-06-29 17:48:51.656 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at okio.ForwardingSource.read(ForwardingSource.kt:29)
2022-06-29 17:48:51.657 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at retrofit2.OkHttpCall$ExceptionCatchingResponseBody$1.read(OkHttpCall.java:288)
2022-06-29 17:48:51.657 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at okio.RealBufferedSource$inputStream$1.read(RealBufferedSource.kt:440)
2022-06-29 17:48:51.657 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.io.InputStream.read(InputStream.java:101)
2022-06-29 17:48:51.657 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at zlc.season.rxdownload4.downloader.RangeDownloader$InnerDownloader$rangeSave$2.accept(RangeDownloader.kt:152)
2022-06-29 17:48:51.657 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at zlc.season.rxdownload4.downloader.RangeDownloader$InnerDownloader$rangeSave$2.accept(RangeDownloader.kt:123)
2022-06-29 17:48:51.657 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableInternalHelper$SimpleBiGenerator.apply(FlowableInternalHelper.java:62)
2022-06-29 17:48:51.657 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableInternalHelper$SimpleBiGenerator.apply(FlowableInternalHelper.java:53)
2022-06-29 17:48:51.657 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableGenerate$GeneratorSubscription.request(FlowableGenerate.java:109)
2022-06-29 17:48:51.658 4277-4356/zlc.season.rxdownload.kotlin_demo W/System.err: 	... 22 more
2022-06-29 17:48:51.658 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err: io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | java.net.SocketException: Socket closed
2022-06-29 17:48:51.658 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
2022-06-29 17:48:51.659 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.innerError(FlowableFlatMap.java:608)
2022-06-29 17:48:51.659 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onError(FlowableFlatMap.java:672)
2022-06-29 17:48:51.659 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableGenerate$GeneratorSubscription.onError(FlowableGenerate.java:189)
2022-06-29 17:48:51.659 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableGenerate$GeneratorSubscription.request(FlowableGenerate.java:114)
2022-06-29 17:48:51.659 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onSubscribe(FlowableFlatMap.java:656)
2022-06-29 17:48:51.659 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableGenerate.subscribeActual(FlowableGenerate.java:52)
2022-06-29 17:48:51.660 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.Flowable.subscribe(Flowable.java:14918)
2022-06-29 17:48:51.660 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.Flowable.subscribe(Flowable.java:14865)
2022-06-29 17:48:51.660 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.onNext(FlowableFlatMap.java:163)
2022-06-29 17:48:51.660 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.tryEmit(FlowableFlatMap.java:282)
2022-06-29 17:48:51.660 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onNext(FlowableFlatMap.java:663)
2022-06-29 17:48:51.660 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableOnBackpressureLatest$BackpressureLatestSubscriber.drain(FlowableOnBackpressureLatest.java:129)
2022-06-29 17:48:51.660 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableOnBackpressureLatest$BackpressureLatestSubscriber.onNext(FlowableOnBackpressureLatest.java:68)
2022-06-29 17:48:51.660 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.flowable.FlowableFromObservable$SubscriberObserver.onNext(FlowableFromObservable.java:54)
2022-06-29 17:48:51.660 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeOnObserver.onNext(ObservableSubscribeOn.java:58)
2022-06-29 17:48:51.660 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:47)
2022-06-29 17:48:51.661 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.Observable.subscribe(Observable.java:12267)
2022-06-29 17:48:51.661 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
2022-06-29 17:48:51.662 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:578)
2022-06-29 17:48:51.662 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
2022-06-29 17:48:51.662 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
2022-06-29 17:48:51.662 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:266)
2022-06-29 17:48:51.662 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
2022-06-29 17:48:51.662 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
2022-06-29 17:48:51.663 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
2022-06-29 17:48:51.663 4277-4357/zlc.season.rxdownload.kotlin_demo W/System.err:     at java.lang.Thread.run(Thread.java:923)
    
    --------- beginning of crash
2022-06-29 17:48:51.663 4277-4356/zlc.season.rxdownload.kotlin_demo E/AndroidRuntime: FATAL EXCEPTION: RxCachedThreadScheduler-6
    Process: zlc.season.rxdownload.kotlin_demo, PID: 4277
    io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | java.net.SocketException: Socket closed
        at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
        at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.innerError(FlowableFlatMap.java:608)
        at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onError(FlowableFlatMap.java:672)
        at io.reactivex.internal.operators.flowable.FlowableGenerate$GeneratorSubscription.onError(FlowableGenerate.java:189)
        at io.reactivex.internal.operators.flowable.FlowableGenerate$GeneratorSubscription.request(FlowableGenerate.java:114)
        at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onSubscribe(FlowableFlatMap.java:656)
        at io.reactivex.internal.operators.flowable.FlowableGenerate.subscribeActual(FlowableGenerate.java:52)
        at io.reactivex.Flowable.subscribe(Flowable.java:14918)
        at io.reactivex.Flowable.subscribe(Flowable.java:14865)
        at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.onNext(FlowableFlatMap.java:163)
        at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.tryEmit(FlowableFlatMap.java:282)
        at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onNext(FlowableFlatMap.java:663)
        at io.reactivex.internal.operators.flowable.FlowableOnBackpressureLatest$BackpressureLatestSubscriber.drain(FlowableOnBackpressureLatest.java:129)
        at io.reactivex.internal.operators.flowable.FlowableOnBackpressureLatest$BackpressureLatestSubscriber.onNext(FlowableOnBackpressureLatest.java:68)
        at io.reactivex.internal.operators.flowable.FlowableFromObservable$SubscriberObserver.onNext(FlowableFromObservable.java:54)
        at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeOnObserver.onNext(ObservableSubscribeOn.java:58)
        at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:47)
        at io.reactivex.Observable.subscribe(Observable.java:12267)
        at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
        at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:578)
        at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
        at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)
     Caused by: java.net.SocketException: Socket closed
        at java.net.SocketInputStream.read(SocketInputStream.java:209)
        at java.net.SocketInputStream.read(SocketInputStream.java:144)
        at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.readFromSocket(ConscryptEngineSocket.java:936)
        at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.processDataFromSocket(ConscryptEngineSocket.java:900)
        at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.readUntilDataAvailable(ConscryptEngineSocket.java:815)
        at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.read(ConscryptEngineSocket.java:788)
        at okio.InputStreamSource.read(Okio.kt:93)
        at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:159)
        at okio.RealBufferedSource.read(RealBufferedSource.kt:43)
        at okhttp3.internal.http1.Http1Codec$AbstractSource.read(Http1Codec.java:363)
2022-06-29 17:48:51.663 4277-4357/? W/System.err: Caused by: java.net.SocketException: Socket closed
2022-06-29 17:48:51.663 4277-4357/? W/System.err:     at java.net.SocketInputStream.read(SocketInputStream.java:209)
2022-06-29 17:48:51.663 4277-4357/? W/System.err:     at java.net.SocketInputStream.read(SocketInputStream.java:144)
2022-06-29 17:48:51.664 4277-4357/? W/System.err:     at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.readFromSocket(ConscryptEngineSocket.java:936)
2022-06-29 17:48:51.664 4277-4357/? W/System.err:     at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.processDataFromSocket(ConscryptEngineSocket.java:900)
2022-06-29 17:48:51.664 4277-4356/? E/AndroidRuntime:     at okhttp3.internal.http1.Http1Codec$FixedLengthSource.read(Http1Codec.java:407)
        at okio.RealBufferedSource.read(RealBufferedSource.kt:43)
        at okio.ForwardingSource.read(ForwardingSource.kt:29)
        at retrofit2.OkHttpCall$ExceptionCatchingResponseBody$1.read(OkHttpCall.java:288)
        at okio.RealBufferedSource$inputStream$1.read(RealBufferedSource.kt:440)
        at java.io.InputStream.read(InputStream.java:101)
        at zlc.season.rxdownload4.downloader.RangeDownloader$InnerDownloader$rangeSave$2.accept(RangeDownloader.kt:152)
        at zlc.season.rxdownload4.downloader.RangeDownloader$InnerDownloader$rangeSave$2.accept(RangeDownloader.kt:123)
        at io.reactivex.internal.operators.flowable.FlowableInternalHelper$SimpleBiGenerator.apply(FlowableInternalHelper.java:62)
        at io.reactivex.internal.operators.flowable.FlowableInternalHelper$SimpleBiGenerator.apply(FlowableInternalHelper.java:53)
        at io.reactivex.internal.operators.flowable.FlowableGenerate$GeneratorSubscription.request(FlowableGenerate.java:109)
        	... 22 more
2022-06-29 17:48:51.665 4277-4357/? W/System.err:     at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.readUntilDataAvailable(ConscryptEngineSocket.java:815)
2022-06-29 17:48:51.665 4277-4357/? W/System.err:     at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.read(ConscryptEngineSocket.java:788)
2022-06-29 17:48:51.665 4277-4357/? W/System.err:     at okio.InputStreamSource.read(Okio.kt:93)
2022-06-29 17:48:51.665 4277-4357/? W/System.err:     at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:159)
2022-06-29 17:48:51.665 4277-4357/? W/System.err:     at okio.RealBufferedSource.read(RealBufferedSource.kt:43)
2022-06-29 17:48:51.665 4277-4357/? W/System.err:     at okhttp3.internal.http1.Http1Codec$AbstractSource.read(Http1Codec.java:363)
2022-06-29 17:48:51.666 4277-4357/? W/System.err:     at okhttp3.internal.http1.Http1Codec$FixedLengthSource.read(Http1Codec.java:407)
2022-06-29 17:48:51.666 4277-4357/? W/System.err:     at okio.RealBufferedSource.read(RealBufferedSource.kt:43)
2022-06-29 17:48:51.666 4277-4357/? W/System.err:     at okio.ForwardingSource.read(ForwardingSource.kt:29)
2022-06-29 17:48:51.666 4277-4357/? W/System.err:     at retrofit2.OkHttpCall$ExceptionCatchingResponseBody$1.read(OkHttpCall.java:288)
2022-06-29 17:48:51.666 4277-4357/? W/System.err:     at okio.RealBufferedSource$inputStream$1.read(RealBufferedSource.kt:440)
2022-06-29 17:48:51.666 4277-4357/? W/System.err:     at java.io.InputStream.read(InputStream.java:101)
2022-06-29 17:48:51.666 4277-4357/? W/System.err:     at zlc.season.rxdownload4.downloader.RangeDownloader$InnerDownloader$rangeSave$2.accept(RangeDownloader.kt:152)
2022-06-29 17:48:51.666 4277-4357/? W/System.err:     at zlc.season.rxdownload4.downloader.RangeDownloader$InnerDownloader$rangeSave$2.accept(RangeDownloader.kt:123)
2022-06-29 17:48:51.667 4277-4357/? W/System.err:     at io.reactivex.internal.operators.flowable.FlowableInternalHelper$SimpleBiGenerator.apply(FlowableInternalHelper.java:62)
2022-06-29 17:48:51.667 4277-4357/? W/System.err:     at io.reactivex.internal.operators.flowable.FlowableInternalHelper$SimpleBiGenerator.apply(FlowableInternalHelper.java:53)
2022-06-29 17:48:51.667 4277-4357/? W/System.err:     at io.reactivex.internal.operators.flowable.FlowableGenerate$GeneratorSubscription.request(FlowableGenerate.java:109)
2022-06-29 17:48:51.667 4277-4357/? W/System.err: 	... 22 more
2022-06-29 17:48:51.667 4277-4357/? I/Process: Sending signal. PID: 4277 SIG: 9
2022-06-29 17:48:51.667 4277-4344/? W/System.err: io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | java.net.SocketException: Socket closed

Workaround

I know by setting the RxJavaPlugins.setErrorHandler we can avoid this crash, but is it a good practice to ignore silently errors like this ?

Investigations

I think there is a bug in the library, specifically in: RangeDownloader::InnerDownloader::rangeSave which located here: rxdownload4/src/main/java/zlc/season/rxdownload4/downloader/RangeDownloader.kt

Function rangeSave creates a Flowable which reads from InputStream in generator and closes the stream in disposeState

Here is the picture with code for the above:
info

The reading from socket is a blocking call (val readLen = source.read(buffer)) which waits/blocks until we have received 8k bytes, then execution continues.

Now, while the reading is ongoing/thread is blocked, it's possible for user to dispose the disposable (which is done on another thread).
In which case, as soon as socket is closed, the blocking read will throw java.net.SocketException: Socket closed

I think this is a bug that we need to solve somehow, if user disposes the download, the socket throws, but this is done after user has disposed the download, which means this error will go to RxJava default error handler, and not to our onError from subscribeBy

So, I think we should catch this error somehow (since this is expected error, as we are closing socket in the middle of the read), or prevent this error from happening ?

This is the closest I've got to solving this problem:
Patch:

--- a/rxdownload4/src/main/java/zlc/season/rxdownload4/downloader/RangeDownloader.kt
+++ b/rxdownload4/src/main/java/zlc/season/rxdownload4/downloader/RangeDownloader.kt
@@ -14,6 +14,7 @@ import zlc.season.rxdownload4.task.TaskInfo
 import zlc.season.rxdownload4.utils.*
 import java.io.File
 import java.io.InputStream
+import java.net.SocketException
 import java.nio.MappedByteBuffer
 import java.nio.channels.FileChannel
 import java.nio.channels.FileChannel.MapMode.READ_WRITE
@@ -149,7 +150,15 @@ class RangeDownloader : Downloader {
                     BiConsumer<InternalState, Emitter<Long>> { internalState, emitter ->
                         internalState.apply {
                             val buffer = ByteArray(8192)
-                            val readLen = source.read(buffer)
+
+                            val readLen = try {
+                                source.read(buffer)
+                            } catch (e: SocketException) {
+                                if (e.message?.contains("Socket closed") == false) {
+                                    throw e
+                                }
+                                null
+                            } ?: return@apply
 
                             if (readLen == -1) {
                                 emitter.onComplete()

Fix explanation:

We know that read will throw SocketException with Socket closed, but we don't wan to propagae this error to the user since user has disposed the download, and we don't want to crash the app. So we look for this specific error, and if it is - ignore it, otherwise re-throw error (We don't want to break a use-case when there was a real socket error while download is going, we want to throw in that case so it can end up in our onError from subscribeBy)

But, to be honest, I don't really like this fix. It looks into the error message, does string == check. But, I'm curios to see what maintainer thinks of this issue & if there is a more elegant way to fix this? Maybe we can somehow close the stream without making it throw?
I found this interesting suggestion on StackOverflow: https://stackoverflow.com/a/23623114 but this does not apply to use, as InputStream does not have shutdownInput method.

PS. This issue looks similar to this reported before, but there was no fix provided as far as I can see: #308 (comment)

PSS. Thanks for making this lib, I think it's an amazing lib, and I've used it in the past, which was a breeze!

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

1 participant