Cancel request early if it’s not an image
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sun, 13 Nov 2016 08:38:40 +0000 (09:38 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sun, 13 Nov 2016 08:38:40 +0000 (09:38 +0100)
src/main/java/net/pterodactylus/sone/core/FreenetInterface.java
src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt
src/test/java/net/pterodactylus/sone/core/FreenetInterfaceTest.java
src/test/kotlin/net/pterodactylus/sone/core/DefaultElementLoaderTest.kt

index c6c4e97..48df732 100644 (file)
@@ -55,12 +55,14 @@ import freenet.client.HighLevelSimpleClient;
 import freenet.client.InsertBlock;
 import freenet.client.InsertContext;
 import freenet.client.InsertException;
+import freenet.client.Metadata;
 import freenet.client.async.BaseClientPutter;
 import freenet.client.async.ClientContext;
 import freenet.client.async.ClientGetCallback;
 import freenet.client.async.ClientGetter;
 import freenet.client.async.ClientPutCallback;
 import freenet.client.async.ClientPutter;
+import freenet.client.async.SnoopMetadata;
 import freenet.client.async.USKCallback;
 import freenet.keys.FreenetURI;
 import freenet.keys.InsertableClientSSK;
@@ -172,15 +174,25 @@ public class FreenetInterface {
                                return imageLoader;
                        }
                };
+               SnoopMetadata snoop = new SnoopMetadata() {
+                       @Override
+                       public boolean snoopMetadata(Metadata meta, ClientContext context) {
+                               String mimeType = meta.getMIMEType();
+                               return (mimeType == null) || backgroundFetchCallback.cancelForMimeType(uri, mimeType);
+                       }
+               };
                FetchContext fetchContext = client.getFetchContext();
                try {
-                       client.fetch(uri, 1048576, callback, fetchContext, RequestStarter.INTERACTIVE_PRIORITY_CLASS);
+                       ClientGetter clientGetter = client.fetch(uri, 1048576, callback, fetchContext, RequestStarter.INTERACTIVE_PRIORITY_CLASS);
+                       clientGetter.setMetaSnoop(snoop);
+                       clientGetter.restart(uri, fetchContext.filterData, node.clientCore.clientContext);
                } catch (FetchException fe) {
                        /* stupid exception that can not actually be thrown! */
                }
        }
 
        public interface BackgroundFetchCallback {
+               boolean cancelForMimeType(@Nonnull FreenetURI uri, @Nonnull String mimeType);
                void loaded(@Nonnull FreenetURI uri, @Nonnull String mimeType, @Nonnull byte[] data);
                void failed(@Nonnull FreenetURI uri);
        }
index eabfed8..956d73b 100644 (file)
@@ -14,6 +14,10 @@ class DefaultElementLoader @Inject constructor(private val freenetInterface: Fre
        private val loadingLinks = CacheBuilder.newBuilder().build<String, Boolean>()
        private val imageCache = CacheBuilder.newBuilder().build<String, LinkedImage>()
        private val callback = object : FreenetInterface.BackgroundFetchCallback {
+               override fun cancelForMimeType(uri: FreenetURI, mimeType: String): Boolean {
+                       return !mimeType.startsWith("image/")
+               }
+
                override fun loaded(uri: FreenetURI, mimeType: String, data: ByteArray) {
                        if (!mimeType.startsWith("image/")) {
                                return
index 04b5b67..195876f 100644 (file)
@@ -54,9 +54,12 @@ import freenet.client.InsertBlock;
 import freenet.client.InsertContext;
 import freenet.client.InsertException;
 import freenet.client.InsertException.InsertExceptionMode;
+import freenet.client.Metadata;
+import freenet.client.async.ClientContext;
 import freenet.client.async.ClientGetCallback;
 import freenet.client.async.ClientGetter;
 import freenet.client.async.ClientPutter;
+import freenet.client.async.SnoopMetadata;
 import freenet.client.async.USKCallback;
 import freenet.client.async.USKManager;
 import freenet.crypt.DummyRandomSource;
@@ -101,13 +104,15 @@ public class FreenetInterfaceTest {
        private final FreenetURI uri = new FreenetURI("KSK@pgl.png");
        private final FetchResult fetchResult = mock(FetchResult.class);
        private final BackgroundFetchCallback backgroundFetchCallback = mock(BackgroundFetchCallback.class);
+       private final ClientGetter clientGetter = mock(ClientGetter.class);
 
        public FreenetInterfaceTest() throws MalformedURLException {
        }
 
        @Before
-       public void setupHighLevelSimpleClient() {
+       public void setupHighLevelSimpleClient() throws Exception {
                when(highLevelSimpleClient.getFetchContext()).thenReturn(mock(FetchContext.class));
+               when(highLevelSimpleClient.fetch(eq(uri), anyLong(), any(ClientGetCallback.class), any(FetchContext.class), anyShort())).thenReturn( clientGetter);
        }
 
        @Before
@@ -116,6 +121,7 @@ public class FreenetInterfaceTest {
                setFinalField(node, "clientCore", nodeClientCore);
                setFinalField(node, "random", randomSource);
                setFinalField(nodeClientCore, "uskManager", uskManager);
+               setFinalField(nodeClientCore, "clientContext", mock(ClientContext.class));
                freenetInterface = new FreenetInterface(eventBus, node);
                insertToken = freenetInterface.new InsertToken(image);
                insertToken.setBucket(bucket);
@@ -425,6 +431,49 @@ public class FreenetInterfaceTest {
        }
 
        @Test
+       public void backgroundFetchRegistersSnoopAndRestartsTheRequest() throws Exception {
+               freenetInterface.startFetch(uri, backgroundFetchCallback);
+               verify(clientGetter).setMetaSnoop(any(SnoopMetadata.class));
+               verify(clientGetter).restart(eq(uri), anyBoolean(), any(ClientContext.class));
+       }
+
+       @Test
+       public void requestIsNotCancelledForImageMimeType() {
+               verifySnoopCancelsRequestForMimeType("image/png", false);
+       }
+
+       @Test
+       public void requestIsCancelledForNullMimeType() {
+               verifySnoopCancelsRequestForMimeType(null, true);
+               verify(backgroundFetchCallback, never()).cancelForMimeType(eq(uri), ArgumentMatchers.<String>any());
+       }
+
+       @Test
+       public void requestIsCancelledForVideoMimeType() {
+               verifySnoopCancelsRequestForMimeType("video/mkv", true);
+       }
+
+       @Test
+       public void requestIsCancelledForAudioMimeType() {
+               verifySnoopCancelsRequestForMimeType("audio/mpeg", true);
+       }
+
+       @Test
+       public void requestIsCancelledForTextMimeType() {
+               verifySnoopCancelsRequestForMimeType("text/plain", true);
+       }
+
+       private void verifySnoopCancelsRequestForMimeType(String mimeType, boolean cancel) {
+               when(backgroundFetchCallback.cancelForMimeType(uri, mimeType)).thenReturn(cancel);
+               freenetInterface.startFetch(uri, backgroundFetchCallback);
+               ArgumentCaptor<SnoopMetadata> snoopMetadata = forClass(SnoopMetadata.class);
+               verify(clientGetter).setMetaSnoop(snoopMetadata.capture());
+               Metadata metadata = mock(Metadata.class);
+               when(metadata.getMIMEType()).thenReturn(mimeType);
+               assertThat(snoopMetadata.getValue().snoopMetadata(metadata, mock(ClientContext.class)), is(cancel));
+       }
+
+       @Test
        public void callbackOfBackgroundFetchIsNotifiedOnSuccess() throws Exception {
                freenetInterface.startFetch(uri, backgroundFetchCallback);
                verify(highLevelSimpleClient).fetch(eq(uri), anyLong(), clientGetCallback.capture(), any(FetchContext.class), anyShort());
index adc5bc9..720cd67 100644 (file)
@@ -1,17 +1,13 @@
 package net.pterodactylus.sone.core
 
 import com.google.common.io.ByteStreams
-import com.google.common.io.Files
 import freenet.keys.FreenetURI
 import net.pterodactylus.sone.core.FreenetInterface.BackgroundFetchCallback
 import net.pterodactylus.sone.test.capture
 import net.pterodactylus.sone.test.mock
-import org.hamcrest.MatcherAssert
 import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers
 import org.hamcrest.Matchers.`is`
 import org.hamcrest.Matchers.instanceOf
-import org.hamcrest.Matchers.nullValue
 import org.junit.Test
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.eq
@@ -26,23 +22,25 @@ class DefaultElementLoaderTest {
 
        companion object {
                private const val IMAGE_ID = "KSK@gpl.png"
+               private val freenetURI = FreenetURI(IMAGE_ID)
        }
 
        private val freenetInterface = mock<FreenetInterface>()
        private val elementLoader = DefaultElementLoader(freenetInterface)
        private val callback = capture<BackgroundFetchCallback>()
 
+
        @Test
        fun `image loader starts request for link that is not known`() {
                elementLoader.loadElement(IMAGE_ID)
-               verify(freenetInterface).startFetch(eq(FreenetURI(IMAGE_ID)), any<BackgroundFetchCallback>())
+               verify(freenetInterface).startFetch(eq(freenetURI), any<BackgroundFetchCallback>())
        }
 
        @Test
        fun `element loader only starts request once`() {
                elementLoader.loadElement(IMAGE_ID)
                elementLoader.loadElement(IMAGE_ID)
-               verify(freenetInterface).startFetch(eq(FreenetURI(IMAGE_ID)), any<BackgroundFetchCallback>())
+               verify(freenetInterface).startFetch(eq(freenetURI), any<BackgroundFetchCallback>())
        }
 
        @Test
@@ -51,10 +49,38 @@ class DefaultElementLoaderTest {
        }
 
        @Test
+       fun `element loader does not cancel on image mime type`() {
+               elementLoader.loadElement(IMAGE_ID)
+               verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
+               assertThat(callback.value.cancelForMimeType(freenetURI, "image/png"), `is`(false))
+       }
+
+       @Test
+       fun `element loader does  cancel on audio mime type`() {
+               elementLoader.loadElement(IMAGE_ID)
+               verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
+               assertThat(callback.value.cancelForMimeType(freenetURI, "audio/mpeg"), `is`(true))
+       }
+
+       @Test
+       fun `element loader does not cancel on video mime type`() {
+               elementLoader.loadElement(IMAGE_ID)
+               verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
+               assertThat(callback.value.cancelForMimeType(freenetURI, "video/mkv"), `is`(true))
+       }
+
+       @Test
+       fun `element loader does not cancel on text mime type`() {
+               elementLoader.loadElement(IMAGE_ID)
+               verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
+               assertThat(callback.value.cancelForMimeType(freenetURI, "text/plain"), `is`(true))
+       }
+
+       @Test
        fun `image loader can load image`() {
                elementLoader.loadElement(IMAGE_ID)
-               verify(freenetInterface).startFetch(eq(FreenetURI(IMAGE_ID)), callback.capture())
-           callback.value.loaded(FreenetURI(IMAGE_ID), "image/png", read("/static/images/unknown-image-0.png"))
+               verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
+               callback.value.loaded(freenetURI, "image/png", read("/static/images/unknown-image-0.png"))
                val linkedElement = elementLoader.loadElement(IMAGE_ID)
                assertThat(linkedElement.link, `is`(IMAGE_ID))
                assertThat(linkedElement.loading, `is`(false))
@@ -64,10 +90,10 @@ class DefaultElementLoaderTest {
        @Test
        fun `image can be loaded again after it failed`() {
                elementLoader.loadElement(IMAGE_ID)
-               verify(freenetInterface).startFetch(eq(FreenetURI(IMAGE_ID)), callback.capture())
-               callback.value.failed(FreenetURI(IMAGE_ID))
+               verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
+               callback.value.failed(freenetURI)
                elementLoader.loadElement(IMAGE_ID)
-               verify(freenetInterface, times(2)).startFetch(eq(FreenetURI(IMAGE_ID)), callback.capture())
+               verify(freenetInterface, times(2)).startFetch(eq(freenetURI), callback.capture())
        }
 
        private fun read(resource: String): ByteArray =