From 6e91e9356231217afcecb12275882c0ebf0f34a8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 13 Nov 2016 09:38:40 +0100 Subject: [PATCH] =?utf8?q?Cancel=20request=20early=20if=20it=E2=80=99s=20n?= =?utf8?q?ot=20an=20image?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../pterodactylus/sone/core/FreenetInterface.java | 14 +++++- .../sone/core/DefaultElementLoader.kt | 4 ++ .../sone/core/FreenetInterfaceTest.java | 51 +++++++++++++++++++++- .../sone/core/DefaultElementLoaderTest.kt | 48 +++++++++++++++----- 4 files changed, 104 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java b/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java index c6c4e97..48df732 100644 --- a/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java +++ b/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java @@ -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); } diff --git a/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt b/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt index eabfed8..956d73b 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt @@ -14,6 +14,10 @@ class DefaultElementLoader @Inject constructor(private val freenetInterface: Fre private val loadingLinks = CacheBuilder.newBuilder().build() private val imageCache = CacheBuilder.newBuilder().build() 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 diff --git a/src/test/java/net/pterodactylus/sone/core/FreenetInterfaceTest.java b/src/test/java/net/pterodactylus/sone/core/FreenetInterfaceTest.java index 04b5b67..195876f 100644 --- a/src/test/java/net/pterodactylus/sone/core/FreenetInterfaceTest.java +++ b/src/test/java/net/pterodactylus/sone/core/FreenetInterfaceTest.java @@ -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.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 = 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()); diff --git a/src/test/kotlin/net/pterodactylus/sone/core/DefaultElementLoaderTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/DefaultElementLoaderTest.kt index adc5bc9..720cd67 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/DefaultElementLoaderTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/DefaultElementLoaderTest.kt @@ -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() private val elementLoader = DefaultElementLoader(freenetInterface) private val callback = capture() + @Test fun `image loader starts request for link that is not known`() { elementLoader.loadElement(IMAGE_ID) - verify(freenetInterface).startFetch(eq(FreenetURI(IMAGE_ID)), any()) + verify(freenetInterface).startFetch(eq(freenetURI), any()) } @Test fun `element loader only starts request once`() { elementLoader.loadElement(IMAGE_ID) elementLoader.loadElement(IMAGE_ID) - verify(freenetInterface).startFetch(eq(FreenetURI(IMAGE_ID)), any()) + verify(freenetInterface).startFetch(eq(freenetURI), any()) } @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 = -- 2.7.4