This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch 2.1.X
in repository https://gitbox.apache.org/repos/asf/mina.git
The following commit(s) were added to refs/heads/2.1.X by this push:
new 716411735 Fixed the compression mina-filter
716411735 is described below
commit 716411735ae6842df6ce3349c7360ac6c8ac2a9c
Author: Emmanuel Lécharny <[email protected]>
AuthorDate: Fri Jun 12 11:14:01 2026 +0200
Fixed the compression mina-filter
---
mina-filter-compression/pom.xml | 4 +-
.../mina/filter/compression/CompressionFilter.java | 4 +-
.../filter/compression/CompressionFilterTest.java | 211 +++++++--------------
pom.xml | 9 +
4 files changed, 80 insertions(+), 148 deletions(-)
diff --git a/mina-filter-compression/pom.xml b/mina-filter-compression/pom.xml
index 8a0c9eeca..3e76c01cc 100644
--- a/mina-filter-compression/pom.xml
+++ b/mina-filter-compression/pom.xml
@@ -45,8 +45,8 @@
</dependency>
<dependency>
- <groupId>org.easymock</groupId>
- <artifactId>easymock</artifactId>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
</dependency>
</dependencies>
diff --git
a/mina-filter-compression/src/main/java/org/apache/mina/filter/compression/CompressionFilter.java
b/mina-filter-compression/src/main/java/org/apache/mina/filter/compression/CompressionFilter.java
index f8c08cebb..04a5db152 100644
---
a/mina-filter-compression/src/main/java/org/apache/mina/filter/compression/CompressionFilter.java
+++
b/mina-filter-compression/src/main/java/org/apache/mina/filter/compression/CompressionFilter.java
@@ -273,9 +273,9 @@ public class CompressionFilter extends IoFilterAdapter {
throw new IllegalStateException("Only one " +
CompressionFilter.class + " is permitted.");
}
- Zlib deflater = new Zlib(compressionLevel, Zlib.MODE_INFLATER,
maxDecompressedSize,
+ Zlib deflater = new Zlib(compressionLevel, Zlib.MODE_DEFLATER,
maxDecompressedSize,
maxDecompressRatio, decompressRatioMinSize);
- Zlib inflater = new Zlib(compressionLevel, Zlib.MODE_INFLATER,
maxDecompressedSize,
+ Zlib inflater = new Zlib(compressionLevel, Zlib.MODE_INFLATER,
maxDecompressedSize,
maxDecompressRatio, decompressRatioMinSize);
IoSession session = parent.getSession();
diff --git
a/mina-filter-compression/src/test/java/org/apache/mina/filter/compression/CompressionFilterTest.java
b/mina-filter-compression/src/test/java/org/apache/mina/filter/compression/CompressionFilterTest.java
index 299d8b335..cadd544de 100644
---
a/mina-filter-compression/src/test/java/org/apache/mina/filter/compression/CompressionFilterTest.java
+++
b/mina-filter-compression/src/test/java/org/apache/mina/filter/compression/CompressionFilterTest.java
@@ -19,189 +19,112 @@
*/
package org.apache.mina.filter.compression;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
import org.apache.mina.core.buffer.IoBuffer;
-import org.apache.mina.core.filterchain.IoFilterChain;
import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.core.filterchain.IoFilterChain;
+import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.write.DefaultWriteRequest;
import org.apache.mina.core.write.WriteRequest;
-import org.easymock.AbstractMatcher;
-import org.easymock.MockControl;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
/**
+ *
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
public class CompressionFilterTest {
- private MockControl mockSession;
-
- private MockControl mockNextFilter;
-
- private MockControl mockIoFilterChain;
-
- private IoSession session;
-
- private NextFilter nextFilter;
-
- private IoFilterChain ioFilterChain;
+ // the sample data to be used for testing
+ private static final String STR_COMPRESS = repeat("The quick brown fox
jumps over the lazy dog. ", 25);
private CompressionFilter filter;
- private Zlib deflater;
-
- private Zlib inflater;
+ private IoSession session;
- private Zlib actualDeflater;
+ private IoFilterChain filterChain;
- private Zlib actualInflater;
+ private NextFilter nextFilter;
- // the sample data to be used for testing
- String strCompress = "The quick brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. "
- + "The quick brown fox jumps over the lazy dog. " + "The quick
brown fox jumps over the lazy dog. ";
+ private static String repeat(String value, int count) {
+ StringBuilder builder = new StringBuilder(value.length() * count);
+ for (int i = 0; i < count; i++) {
+ builder.append(value);
+ }
+ return builder.toString();
+ }
@Before
public void setUp() {
- // create the necessary mock controls.
- mockSession = MockControl.createControl(IoSession.class);
- mockNextFilter = MockControl.createControl(NextFilter.class);
- mockIoFilterChain = MockControl.createControl(IoFilterChain.class);
-
- // set the default matcher
- mockNextFilter.setDefaultMatcher(new DataMatcher());
-
- session = (IoSession) mockSession.getMock();
- nextFilter = (NextFilter) mockNextFilter.getMock();
- ioFilterChain = (IoFilterChain) mockIoFilterChain.getMock();
-
- // create an instance of the filter
filter = new CompressionFilter(CompressionFilter.COMPRESSION_MAX);
- // deflater and inflater that will be used by the filter
- deflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_DEFLATER);
- inflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_INFLATER);
-
- // create instances of the deflater and inflater to help test the
output
- actualDeflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_DEFLATER);
- actualInflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_INFLATER);
+ // a mock session whose attributes are stored in a real map, so that
the deflater and inflater
+ // created by onPreAdd() are actually retrieved by filterWrite() and
messageReceived().
+ session = mock(IoSession.class);
+ final Map<Object, Object> attributes = new HashMap<>();
+ when(session.setAttribute(any(), any()))
+ .thenAnswer(invocation ->
attributes.put(invocation.getArgument(0), invocation.getArgument(1)));
+ when(session.getAttribute(any())).thenAnswer(invocation ->
attributes.get(invocation.getArgument(0)));
+ when(session.containsAttribute(any())).thenAnswer(invocation ->
attributes.containsKey(invocation.getArgument(0)));
+ when(session.removeAttribute(any())).thenAnswer(invocation ->
attributes.remove(invocation.getArgument(0)));
+
+ filterChain = mock(IoFilterChain.class);
+ when(filterChain.contains(CompressionFilter.class)).thenReturn(false);
+ when(filterChain.getSession()).thenReturn(session);
+
+ nextFilter = mock(NextFilter.class);
}
@Test
- public void testCompression() throws Exception {
- // prepare the input data
- IoBuffer buf =
IoBuffer.wrap(strCompress.getBytes(StandardCharsets.UTF_8));
- IoBuffer actualOutput = actualDeflater.deflate(buf);
- buf.flip();
- WriteRequest writeRequest = new DefaultWriteRequest(buf);
-
- // record all the mock calls
- ioFilterChain.contains(CompressionFilter.class);
- mockIoFilterChain.setReturnValue(false);
+ public void testCompressionRoundTrip() throws Exception {
+ filter.onPreAdd(filterChain, "CompressionFilter", nextFilter);
- ioFilterChain.getSession();
- mockIoFilterChain.setReturnValue(session);
-
- session.setAttribute(CompressionFilter.class.getName() + ".Deflater",
deflater);
- mockSession.setDefaultMatcher(new DataMatcher());
- mockSession.setReturnValue(null, MockControl.ONE);
-
- session.setAttribute(CompressionFilter.class.getName() + ".Inflater",
inflater);
- mockSession.setReturnValue(null, MockControl.ONE);
-
- session.containsAttribute(CompressionFilter.DISABLE_COMPRESSION_ONCE);
- mockSession.setReturnValue(false);
-
- session.getAttribute(CompressionFilter.class.getName() + ".Deflater");
- mockSession.setReturnValue(deflater);
-
- nextFilter.filterWrite(session, new DefaultWriteRequest(actualOutput));
-
- // switch to playback mode
- mockSession.replay();
- mockIoFilterChain.replay();
- mockNextFilter.replay();
-
- // make the actual calls on the filter
- filter.onPreAdd(ioFilterChain, "CompressionFilter", nextFilter);
+ IoBuffer input =
IoBuffer.wrap(STR_COMPRESS.getBytes(StandardCharsets.UTF_8));
+ WriteRequest writeRequest = new DefaultWriteRequest(input);
filter.filterWrite(nextFilter, session, writeRequest);
- // verify that all the calls happened as recorded
- mockNextFilter.verify();
+ // capture the compressed buffer forwarded down the chain
+ ArgumentCaptor<WriteRequest> writeCaptor =
ArgumentCaptor.forClass(WriteRequest.class);
+ verify(nextFilter).filterWrite(eq(session), writeCaptor.capture());
+ IoBuffer compressed = (IoBuffer) writeCaptor.getValue().getMessage();
+
+ // feeding the compressed buffer back in must reproduce the original
payload
+ filter.messageReceived(nextFilter, session, compressed);
+ ArgumentCaptor<Object> receiveCaptor =
ArgumentCaptor.forClass(Object.class);
+ verify(nextFilter).messageReceived(eq(session),
receiveCaptor.capture());
+ IoBuffer decompressed = (IoBuffer) receiveCaptor.getValue();
- assertTrue(true);
+ assertEquals(STR_COMPRESS,
decompressed.getString(StandardCharsets.UTF_8.newDecoder()));
}
+ /**
+ * Regression guard: onPreAdd() must register the deflater in deflate mode
and the inflater in
+ * inflate mode, not the other way round. Verified by checking each
rejects the opposite operation.
+ */
@Test
- public void testDecompression() throws Exception {
- // prepare the input data
- IoBuffer buf =
IoBuffer.wrap(strCompress.getBytes(StandardCharsets.UTF_8));
- IoBuffer byteInput = actualDeflater.deflate(buf);
- IoBuffer actualOutput = actualInflater.inflate(byteInput);
-
- // record all the mock calls
- ioFilterChain.contains(CompressionFilter.class);
- mockIoFilterChain.setReturnValue(false);
+ public void testDeflaterAndInflaterNotSwapped() throws Exception {
+ filter.onPreAdd(filterChain, "CompressionFilter", nextFilter);
- ioFilterChain.getSession();
- mockIoFilterChain.setReturnValue(session);
+ IoBuffer input =
IoBuffer.wrap(STR_COMPRESS.getBytes(StandardCharsets.UTF_8));
- session.setAttribute(CompressionFilter.class.getName() + ".Deflater",
deflater);
- mockSession.setDefaultMatcher(new DataMatcher());
- mockSession.setReturnValue(null, MockControl.ONE);
+ Zlib deflater = (Zlib) session.getAttribute(new
AttributeKey(CompressionFilter.class, "deflater"));
+ assertNotNull(deflater);
+ assertThrows(IllegalStateException.class, () ->
deflater.inflate(input));
- session.setAttribute(CompressionFilter.class.getName() + ".Inflater",
inflater);
- mockSession.setReturnValue(null, MockControl.ONE);
- session.getAttribute(CompressionFilter.class.getName() + ".Inflater");
- mockSession.setReturnValue(inflater);
-
- nextFilter.messageReceived(session, actualOutput);
-
- // switch to playback mode
- mockSession.replay();
- mockIoFilterChain.replay();
- mockNextFilter.replay();
-
- // make the actual calls on the filter
- filter.onPreAdd(ioFilterChain, "CompressionFilter", nextFilter);
- filter.messageReceived(nextFilter, session, byteInput);
-
- // verify that all the calls happened as recorded
- mockNextFilter.verify();
-
- assertTrue(true);
- }
-
- /**
- * A matcher used to check if the actual and expected outputs matched
- */
- class DataMatcher extends AbstractMatcher {
- @Override
- protected boolean argumentMatches(Object arg0, Object arg1) {
- // we need to only verify the ByteBuffer output
- if (arg0 instanceof WriteRequest) {
- WriteRequest expected = (WriteRequest) arg0;
- WriteRequest actual = (WriteRequest) arg1;
- IoBuffer bExpected = (IoBuffer) expected.getMessage();
- IoBuffer bActual = (IoBuffer) actual.getMessage();
- return bExpected.equals(bActual);
- }
- return true;
- }
+ Zlib inflater = (Zlib) session.getAttribute(new
AttributeKey(CompressionFilter.class, "inflater"));
+ assertNotNull(inflater);
+ assertThrows(IllegalStateException.class, () ->
inflater.deflate(input));
}
}
diff --git a/pom.xml b/pom.xml
index 293a8fd47..95126fed6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -149,6 +149,7 @@
<version.junit>4.13.2</version.junit>
<version.jzlib>1.1.3</version.jzlib>
<version.log4j>1.2.17</version.log4j>
+ <version.mockito>4.11.0</version.mockito>
<!-- DO NOT CHANGE. Breaks the build with more recent versions -->
<version.ognl>3.3.4</version.ognl>
<version.pmd>7.24.0</version.pmd>
@@ -299,6 +300,14 @@
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>${version.mockito}</version>
+ <optional>true</optional>
+ <scope>test</scope>
+ </dependency>
+
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-core</artifactId>