Bug 1595144 - Throw IOException when trying to read a closed GeckoInputStream r=geckoview-reviewers,agi
authorJames Willcox <snorp@snorp.net>
Wed, 13 Nov 2019 15:38:38 +0000
changeset 501804 9a4d7144206139470df276d6d0a26a99068aa0ab
parent 501803 e2b8a34e2aac27d21773cb2d3da1278f47550e4f
child 501805 e7a759d68461596d4988c54e8c8e23b9a853e6ae
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgeckoview-reviewers, agi
bugs1595144
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1595144 - Throw IOException when trying to read a closed GeckoInputStream r=geckoview-reviewers,agi Differential Revision: https://phabricator.services.mozilla.com/D52381
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExecutorTest.kt
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoInputStream.java
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExecutorTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExecutorTest.kt
@@ -29,26 +29,25 @@ import java.util.concurrent.CountDownLat
 import org.hamcrest.MatcherAssert.assertThat
 import org.hamcrest.Matchers.*
 
 import org.json.JSONObject
 import org.junit.*
 
 import org.junit.rules.ExpectedException
 import org.junit.runner.RunWith
-import org.mozilla.geckoview.GeckoResult
 
 import org.mozilla.geckoview.GeckoWebExecutor
 import org.mozilla.geckoview.WebRequest
 import org.mozilla.geckoview.WebRequestError
 import org.mozilla.geckoview.WebResponse
 
-import org.mozilla.geckoview.test.util.Environment
 import org.mozilla.geckoview.test.util.HttpBin
 import org.mozilla.geckoview.test.util.RuntimeCreator
+import java.io.IOException
 import java.net.UnknownHostException
 import java.util.*
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 class WebExecutorTest {
     companion object {
         val TEST_ENDPOINT: String = "http://localhost:4242"
@@ -300,16 +299,27 @@ class WebExecutorTest {
 
         assertThat("Byte counts should match", bytes.size, equalTo(expectedCount))
 
         val digest = MessageDigest.getInstance("SHA-256").digest(bytes)
         assertThat("Hashes should match", response.headers["X-SHA-256"],
                 equalTo(String.format("%064x", BigInteger(1, digest))))
     }
 
+    @Test(expected = IOException::class)
+    fun readClosedStream() {
+        val response = executor.fetch(WebRequest("$TEST_ENDPOINT/anything")).pollDefault()!!
+
+        assertThat("Status code should match", response.statusCode, equalTo(200))
+
+        val stream = response.body!!
+        stream.close()
+        stream.readBytes()
+    }
+
     @Test
     fun testFetchStreamCancel() {
         val expectedCount = 1 * 1024 * 1024 // 1MB
         val response = executor.fetch(WebRequest("$TEST_ENDPOINT/bytes/$expectedCount")).pollDefault()!!
 
         assertThat("Status code should match", response.statusCode, equalTo(200))
         assertThat("Content-Length should match", response.headers["Content-Length"]!!.toInt(), equalTo(expectedCount))
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoInputStream.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoInputStream.java
@@ -18,16 +18,17 @@ import java.util.LinkedList;
  */
 @WrapForJNI
 @AnyThread
 /* package */ class GeckoInputStream extends InputStream {
     private static final String LOGTAG = "GeckoInputStream";
 
     private LinkedList<ByteBuffer> mBuffers = new LinkedList<>();
     private boolean mEOF;
+    private boolean mClosed;
     private boolean mResumed;
     private Support mSupport;
 
     /**
      * This is only called via JNI. The support instance provides
      * callbacks for the native counterpart.
      *
      * @param support An instance of {@link Support}, used for native callbacks.
@@ -35,26 +36,39 @@ import java.util.LinkedList;
     private GeckoInputStream(final @NonNull Support support) {
         mSupport = support;
     }
 
     @Override
     public synchronized void close() throws IOException {
         super.close();
         sendEof();
+        mClosed = true;
     }
 
     @Override
     public synchronized int available() throws IOException {
+        if (mClosed) {
+            return 0;
+        }
+
         final ByteBuffer buf = mBuffers.peekFirst();
         return buf != null ? buf.remaining() : 0;
     }
 
+    private void ensureNotClosed() throws IOException {
+        if (mClosed) {
+            throw new IOException("Stream is closed");
+        }
+    }
+
     @Override
     public synchronized int read() throws IOException {
+        ensureNotClosed();
+
         int expect = Integer.SIZE / 8;
         byte[] bytes = new byte[expect];
 
         int count = 0;
         while (count < expect) {
             long bytesRead = read(bytes, count, expect - count);
             if (bytesRead < 0) {
                 return -1;
@@ -70,16 +84,18 @@ import java.util.LinkedList;
     @Override
     public int read(final @NonNull byte[] b) throws IOException {
         return read(b, 0, b.length);
     }
 
     @Override
     public synchronized int read(final @NonNull byte[] dest, final int offset, final int length)
             throws IOException {
+        ensureNotClosed();
+
         while (!mEOF && mBuffers.size() == 0) {
             // The underlying channel is suspended, so resume that before
             // waiting for a buffer.
             if (!mResumed) {
                 mSupport.resume();
                 mResumed = true;
             }