Skip to content

Commit a48effc

Browse files
authored
Merge pull request #57 from ppkarwasz/fix/deflater-inflater-swap
fix: create deflater in DEFLATER mode in CompressionFilter
2 parents d3af7f2 + cfdb045 commit a48effc

3 files changed

Lines changed: 71 additions & 151 deletions

File tree

mina-filter-compression/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@
4545
</dependency>
4646

4747
<dependency>
48-
<groupId>org.easymock</groupId>
49-
<artifactId>easymock</artifactId>
48+
<groupId>org.mockito</groupId>
49+
<artifactId>mockito-core</artifactId>
5050
</dependency>
5151
</dependencies>
5252

mina-filter-compression/src/main/java/org/apache/mina/filter/compression/CompressionFilter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,9 @@ public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) t
277277
throw new IllegalStateException("Only one " + CompressionFilter.class + " is permitted.");
278278
}
279279

280-
Zlib deflater = new Zlib(compressionLevel, Zlib.MODE_INFLATER, maxDecompressedSize,
280+
Zlib deflater = new Zlib(compressionLevel, Zlib.MODE_DEFLATER, maxDecompressedSize,
281281
maxDecompressRatio, decompressRatioMinSize);
282-
Zlib inflater = new Zlib(compressionLevel, Zlib.MODE_INFLATER, maxDecompressedSize,
282+
Zlib inflater = new Zlib(compressionLevel, Zlib.MODE_INFLATER, maxDecompressedSize,
283283
maxDecompressRatio, decompressRatioMinSize);
284284

285285
IoSession session = parent.getSession();

mina-filter-compression/src/test/java/org/apache/mina/filter/compression/CompressionFilterTest.java

Lines changed: 67 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -19,192 +19,112 @@
1919
*/
2020
package org.apache.mina.filter.compression;
2121

22-
import static org.junit.Assert.assertTrue;
22+
import static org.junit.Assert.*;
23+
import static org.mockito.ArgumentMatchers.any;
24+
import static org.mockito.ArgumentMatchers.eq;
25+
import static org.mockito.Mockito.mock;
26+
import static org.mockito.Mockito.verify;
27+
import static org.mockito.Mockito.when;
2328

2429
import java.nio.charset.StandardCharsets;
30+
import java.util.HashMap;
31+
import java.util.Map;
2532

2633
import org.apache.mina.core.buffer.IoBuffer;
27-
import org.apache.mina.core.filterchain.IoFilterChain;
2834
import org.apache.mina.core.filterchain.IoFilter.NextFilter;
35+
import org.apache.mina.core.filterchain.IoFilterChain;
36+
import org.apache.mina.core.session.AttributeKey;
2937
import org.apache.mina.core.session.IoSession;
3038
import org.apache.mina.core.write.DefaultWriteRequest;
3139
import org.apache.mina.core.write.WriteRequest;
3240
import org.junit.Before;
33-
import org.junit.Ignore;
3441
import org.junit.Test;
42+
import org.mockito.ArgumentCaptor;
3543

3644
/**
37-
*
45+
*
3846
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
3947
*/
40-
@Ignore
4148
public class CompressionFilterTest {
42-
/*
43-
private MockControl mockSession;
44-
45-
private MockControl mockNextFilter;
46-
47-
private MockControl mockIoFilterChain;
48-
49-
private IoSession session;
50-
51-
private NextFilter nextFilter;
52-
53-
private IoFilterChain ioFilterChain;
49+
// the sample data to be used for testing
50+
private static final String STR_COMPRESS = repeat("The quick brown fox jumps over the lazy dog. ", 25);
5451

5552
private CompressionFilter filter;
5653

57-
private Zlib deflater;
58-
59-
private Zlib inflater;
54+
private IoSession session;
6055

61-
private Zlib actualDeflater;
56+
private IoFilterChain filterChain;
6257

63-
private Zlib actualInflater;
58+
private NextFilter nextFilter;
6459

65-
// the sample data to be used for testing
66-
String strCompress = "The quick brown fox jumps over the lazy dog. "
67-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "
68-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "
69-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "
70-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "
71-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "
72-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "
73-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "
74-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "
75-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "
76-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "
77-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. "
78-
+ "The quick brown fox jumps over the lazy dog. " + "The quick brown fox jumps over the lazy dog. ";
60+
private static String repeat(String value, int count) {
61+
StringBuilder builder = new StringBuilder(value.length() * count);
62+
for (int i = 0; i < count; i++) {
63+
builder.append(value);
64+
}
65+
return builder.toString();
66+
}
7967

8068
@Before
8169
public void setUp() {
82-
// create the necessary mock controls.
83-
mockSession = MockControl.createControl(IoSession.class);
84-
mockNextFilter = MockControl.createControl(NextFilter.class);
85-
mockIoFilterChain = MockControl.createControl(IoFilterChain.class);
86-
87-
// set the default matcher
88-
mockNextFilter.setDefaultMatcher(new DataMatcher());
89-
90-
session = (IoSession) mockSession.getMock();
91-
nextFilter = (NextFilter) mockNextFilter.getMock();
92-
ioFilterChain = (IoFilterChain) mockIoFilterChain.getMock();
93-
94-
// create an instance of the filter
9570
filter = new CompressionFilter(CompressionFilter.COMPRESSION_MAX);
9671

97-
// deflater and inflater that will be used by the filter
98-
deflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_DEFLATER);
99-
inflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_INFLATER);
100-
101-
// create instances of the deflater and inflater to help test the output
102-
actualDeflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_DEFLATER);
103-
actualInflater = new Zlib(Zlib.COMPRESSION_MAX, Zlib.MODE_INFLATER);
72+
// a mock session whose attributes are stored in a real map, so that the deflater and inflater
73+
// created by onPreAdd() are actually retrieved by filterWrite() and messageReceived().
74+
session = mock(IoSession.class);
75+
final Map<Object, Object> attributes = new HashMap<>();
76+
when(session.setAttribute(any(), any()))
77+
.thenAnswer(invocation -> attributes.put(invocation.getArgument(0), invocation.getArgument(1)));
78+
when(session.getAttribute(any())).thenAnswer(invocation -> attributes.get(invocation.getArgument(0)));
79+
when(session.containsAttribute(any())).thenAnswer(invocation -> attributes.containsKey(invocation.getArgument(0)));
80+
when(session.removeAttribute(any())).thenAnswer(invocation -> attributes.remove(invocation.getArgument(0)));
81+
82+
filterChain = mock(IoFilterChain.class);
83+
when(filterChain.contains(CompressionFilter.class)).thenReturn(false);
84+
when(filterChain.getSession()).thenReturn(session);
85+
86+
nextFilter = mock(NextFilter.class);
10487
}
10588

10689
@Test
107-
public void testCompression() throws Exception {
108-
// prepare the input data
109-
IoBuffer buf = IoBuffer.wrap(strCompress.getBytes(StandardCharsets.UTF_8));
110-
IoBuffer actualOutput = actualDeflater.deflate(buf);
111-
buf.flip();
112-
WriteRequest writeRequest = new DefaultWriteRequest(buf);
90+
public void testCompressionRoundTrip() throws Exception {
91+
filter.onPreAdd(filterChain, "CompressionFilter", nextFilter);
11392

114-
// record all the mock calls
115-
ioFilterChain.contains(CompressionFilter.class);
116-
mockIoFilterChain.setReturnValue(false);
117-
118-
ioFilterChain.getSession();
119-
mockIoFilterChain.setReturnValue(session);
120-
121-
session.setAttribute(CompressionFilter.class.getName() + ".Deflater", deflater);
122-
mockSession.setDefaultMatcher(new DataMatcher());
123-
mockSession.setReturnValue(null, MockControl.ONE);
124-
125-
session.setAttribute(CompressionFilter.class.getName() + ".Inflater", inflater);
126-
mockSession.setReturnValue(null, MockControl.ONE);
127-
128-
session.containsAttribute(CompressionFilter.DISABLE_COMPRESSION_ONCE);
129-
mockSession.setReturnValue(false);
130-
131-
session.getAttribute(CompressionFilter.class.getName() + ".Deflater");
132-
mockSession.setReturnValue(deflater);
133-
134-
nextFilter.filterWrite(session, new DefaultWriteRequest(actualOutput));
135-
136-
// switch to playback mode
137-
mockSession.replay();
138-
mockIoFilterChain.replay();
139-
mockNextFilter.replay();
140-
141-
// make the actual calls on the filter
142-
filter.onPreAdd(ioFilterChain, "CompressionFilter", nextFilter);
93+
IoBuffer input = IoBuffer.wrap(STR_COMPRESS.getBytes(StandardCharsets.UTF_8));
94+
WriteRequest writeRequest = new DefaultWriteRequest(input);
14395
filter.filterWrite(nextFilter, session, writeRequest);
14496

145-
// verify that all the calls happened as recorded
146-
mockNextFilter.verify();
97+
// capture the compressed buffer forwarded down the chain
98+
ArgumentCaptor<WriteRequest> writeCaptor = ArgumentCaptor.forClass(WriteRequest.class);
99+
verify(nextFilter).filterWrite(eq(session), writeCaptor.capture());
100+
IoBuffer compressed = (IoBuffer) writeCaptor.getValue().getMessage();
147101

148-
assertTrue(true);
102+
// feeding the compressed buffer back in must reproduce the original payload
103+
filter.messageReceived(nextFilter, session, compressed);
104+
ArgumentCaptor<Object> receiveCaptor = ArgumentCaptor.forClass(Object.class);
105+
verify(nextFilter).messageReceived(eq(session), receiveCaptor.capture());
106+
IoBuffer decompressed = (IoBuffer) receiveCaptor.getValue();
107+
108+
assertEquals(STR_COMPRESS, decompressed.getString(StandardCharsets.UTF_8.newDecoder()));
149109
}
150110

111+
/**
112+
* Regression guard: onPreAdd() must register the deflater in deflate mode and the inflater in
113+
* inflate mode, not the other way round. Verified by checking each rejects the opposite operation.
114+
*/
151115
@Test
152-
public void testDecompression() throws Exception {
153-
// prepare the input data
154-
IoBuffer buf = IoBuffer.wrap(strCompress.getBytes(StandardCharsets.UTF_8));
155-
IoBuffer byteInput = actualDeflater.deflate(buf);
156-
IoBuffer actualOutput = actualInflater.inflate(byteInput);
116+
public void testDeflaterAndInflaterNotSwapped() throws Exception {
117+
filter.onPreAdd(filterChain, "CompressionFilter", nextFilter);
157118

158-
// record all the mock calls
159-
ioFilterChain.contains(CompressionFilter.class);
160-
mockIoFilterChain.setReturnValue(false);
119+
IoBuffer input = IoBuffer.wrap(STR_COMPRESS.getBytes(StandardCharsets.UTF_8));
161120

162-
ioFilterChain.getSession();
163-
mockIoFilterChain.setReturnValue(session);
121+
Zlib deflater = (Zlib) session.getAttribute(new AttributeKey(CompressionFilter.class, "deflater"));
122+
assertNotNull(deflater);
123+
assertThrows(IllegalStateException.class, () -> deflater.inflate(input));
164124

165-
session.setAttribute(CompressionFilter.class.getName() + ".Deflater", deflater);
166-
mockSession.setDefaultMatcher(new DataMatcher());
167-
mockSession.setReturnValue(null, MockControl.ONE);
168125

169-
session.setAttribute(CompressionFilter.class.getName() + ".Inflater", inflater);
170-
mockSession.setReturnValue(null, MockControl.ONE);
171-
172-
session.getAttribute(CompressionFilter.class.getName() + ".Inflater");
173-
mockSession.setReturnValue(inflater);
174-
175-
nextFilter.messageReceived(session, actualOutput);
176-
177-
// switch to playback mode
178-
mockSession.replay();
179-
mockIoFilterChain.replay();
180-
mockNextFilter.replay();
181-
182-
// make the actual calls on the filter
183-
filter.onPreAdd(ioFilterChain, "CompressionFilter", nextFilter);
184-
filter.messageReceived(nextFilter, session, byteInput);
185-
186-
// verify that all the calls happened as recorded
187-
mockNextFilter.verify();
188-
189-
assertTrue(true);
190-
}
191-
192-
/**
193-
* A matcher used to check if the actual and expected outputs matched
194-
*
195-
class DataMatcher extends AbstractMatcher {
196-
@Override
197-
protected boolean argumentMatches(Object arg0, Object arg1) {
198-
// we need to only verify the ByteBuffer output
199-
if (arg0 instanceof WriteRequest) {
200-
WriteRequest expected = (WriteRequest) arg0;
201-
WriteRequest actual = (WriteRequest) arg1;
202-
IoBuffer bExpected = (IoBuffer) expected.getMessage();
203-
IoBuffer bActual = (IoBuffer) actual.getMessage();
204-
return bExpected.equals(bActual);
205-
}
206-
return true;
207-
}
126+
Zlib inflater = (Zlib) session.getAttribute(new AttributeKey(CompressionFilter.class, "inflater"));
127+
assertNotNull(inflater);
128+
assertThrows(IllegalStateException.class, () -> inflater.deflate(input));
208129
}
209-
*/
210130
}

0 commit comments

Comments
 (0)