View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.filter.codec.textline;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.Assert.fail;
25  
26  import java.nio.charset.Charset;
27  import java.nio.charset.CharsetEncoder;
28  import java.nio.charset.StandardCharsets;
29  
30  import org.apache.mina.core.buffer.IoBuffer;
31  import org.apache.mina.filter.codec.ProtocolCodecSession;
32  import org.apache.mina.filter.codec.ProtocolDecoderOutput;
33  import org.apache.mina.filter.codec.RecoverableProtocolDecoderException;
34  import org.junit.Test;
35  
36  /**
37   * Tests {@link TextLineDecoder}.
38   *
39   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
40   */
41  public class TextLineDecoderTest {
42      @Test
43      public void testNormalDecode() throws Exception {
44          TextLineDecoder decoder = new TextLineDecoder(StandardCharsets.UTF_8, LineDelimiter.WINDOWS);
45  
46          CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
47          ProtocolCodecSession session = new ProtocolCodecSession();
48          ProtocolDecoderOutput out = session.getDecoderOutput();
49          IoBuffer in = IoBuffer.allocate(16);
50  
51          // Test one decode and one output
52          in.putString("ABC\r\n", encoder);
53          in.flip();
54          decoder.decode(session, in, out);
55          assertEquals(1, session.getDecoderOutputQueue().size());
56          assertEquals("ABC", session.getDecoderOutputQueue().poll());
57  
58          // Test two decode and one output
59          in.clear();
60          in.putString("DEF", encoder);
61          in.flip();
62          decoder.decode(session, in, out);
63          assertEquals(0, session.getDecoderOutputQueue().size());
64          in.clear();
65          in.putString("GHI\r\n", encoder);
66          in.flip();
67          decoder.decode(session, in, out);
68          assertEquals(1, session.getDecoderOutputQueue().size());
69          assertEquals("DEFGHI", session.getDecoderOutputQueue().poll());
70  
71          // Test one decode and two output
72          in.clear();
73          in.putString("JKL\r\nMNO\r\n", encoder);
74          in.flip();
75          decoder.decode(session, in, out);
76          assertEquals(2, session.getDecoderOutputQueue().size());
77          assertEquals("JKL", session.getDecoderOutputQueue().poll());
78          assertEquals("MNO", session.getDecoderOutputQueue().poll());
79  
80          // Test aborted delimiter (DIRMINA-506)
81          in.clear();
82          in.putString("ABC\r\r\n", encoder);
83          in.flip();
84          decoder.decode(session, in, out);
85          assertEquals(1, session.getDecoderOutputQueue().size());
86          assertEquals("ABC\r", session.getDecoderOutputQueue().poll());
87  
88          // Test splitted long delimiter
89          decoder = new TextLineDecoder(StandardCharsets.UTF_8, new LineDelimiter("\n\n\n"));
90          in.clear();
91          in.putString("PQR\n", encoder);
92          in.flip();
93          decoder.decode(session, in, out);
94          assertEquals(0, session.getDecoderOutputQueue().size());
95          in.clear();
96          in.putString("\n", encoder);
97          in.flip();
98          decoder.decode(session, in, out);
99          assertEquals(0, session.getDecoderOutputQueue().size());
100         in.clear();
101         in.putString("\n", encoder);
102         in.flip();
103         decoder.decode(session, in, out);
104         assertEquals(1, session.getDecoderOutputQueue().size());
105         assertEquals("PQR", session.getDecoderOutputQueue().poll());
106 
107         // Test splitted long delimiter which produces two output
108         decoder = new TextLineDecoder(StandardCharsets.UTF_8, new LineDelimiter("\n\n\n"));
109         in.clear();
110         in.putString("PQR\n", encoder);
111         in.flip();
112         decoder.decode(session, in, out);
113         assertEquals(0, session.getDecoderOutputQueue().size());
114         in.clear();
115         in.putString("\n", encoder);
116         in.flip();
117         decoder.decode(session, in, out);
118         assertEquals(0, session.getDecoderOutputQueue().size());
119         in.clear();
120         in.putString("\nSTU\n\n\n", encoder);
121         in.flip();
122         decoder.decode(session, in, out);
123         assertEquals(2, session.getDecoderOutputQueue().size());
124         assertEquals("PQR", session.getDecoderOutputQueue().poll());
125         assertEquals("STU", session.getDecoderOutputQueue().poll());
126 
127         // Test splitted long delimiter mixed with partial non-delimiter.
128         decoder = new TextLineDecoder(StandardCharsets.UTF_8, new LineDelimiter("\n\n\n"));
129         in.clear();
130         in.putString("PQR\n", encoder);
131         in.flip();
132         decoder.decode(session, in, out);
133         assertEquals(0, session.getDecoderOutputQueue().size());
134         in.clear();
135         in.putString("X\n", encoder);
136         in.flip();
137         decoder.decode(session, in, out);
138         assertEquals(0, session.getDecoderOutputQueue().size());
139         in.clear();
140         in.putString("\n\nSTU\n\n\n", encoder);
141         in.flip();
142         decoder.decode(session, in, out);
143         assertEquals(2, session.getDecoderOutputQueue().size());
144         assertEquals("PQR\nX", session.getDecoderOutputQueue().poll());
145         assertEquals("STU", session.getDecoderOutputQueue().poll());
146     }
147 
148     public void testAutoDecode() throws Exception {
149         TextLineDecoder decoder = new TextLineDecoder(StandardCharsets.UTF_8, LineDelimiter.AUTO);
150 
151         CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
152         ProtocolCodecSession session = new ProtocolCodecSession();
153         ProtocolDecoderOutput out = session.getDecoderOutput();
154         IoBuffer in = IoBuffer.allocate(16);
155 
156         // Test one decode and one output
157         in.putString("ABC\r\n", encoder);
158         in.flip();
159         decoder.decode(session, in, out);
160         assertEquals(1, session.getDecoderOutputQueue().size());
161         assertEquals("ABC", session.getDecoderOutputQueue().poll());
162 
163         // Test two decode and one output
164         in.clear();
165         in.putString("DEF", encoder);
166         in.flip();
167         decoder.decode(session, in, out);
168         assertEquals(0, session.getDecoderOutputQueue().size());
169         in.clear();
170         in.putString("GHI\r\n", encoder);
171         in.flip();
172         decoder.decode(session, in, out);
173         assertEquals(1, session.getDecoderOutputQueue().size());
174         assertEquals("DEFGHI", session.getDecoderOutputQueue().poll());
175 
176         // Test one decode and two output
177         in.clear();
178         in.putString("JKL\r\nMNO\r\n", encoder);
179         in.flip();
180         decoder.decode(session, in, out);
181         assertEquals(2, session.getDecoderOutputQueue().size());
182         assertEquals("JKL", session.getDecoderOutputQueue().poll());
183         assertEquals("MNO", session.getDecoderOutputQueue().poll());
184 
185         // Test multiple '\n's
186         in.clear();
187         in.putString("\n\n\n", encoder);
188         in.flip();
189         decoder.decode(session, in, out);
190         assertEquals(3, session.getDecoderOutputQueue().size());
191         assertEquals("", session.getDecoderOutputQueue().poll());
192         assertEquals("", session.getDecoderOutputQueue().poll());
193         assertEquals("", session.getDecoderOutputQueue().poll());
194 
195         // Test splitted long delimiter (\r\r\n)
196         in.clear();
197         in.putString("PQR\r", encoder);
198         in.flip();
199         decoder.decode(session, in, out);
200         assertEquals(0, session.getDecoderOutputQueue().size());
201         in.clear();
202         in.putString("\r", encoder);
203         in.flip();
204         decoder.decode(session, in, out);
205         assertEquals(0, session.getDecoderOutputQueue().size());
206         in.clear();
207         in.putString("\n", encoder);
208         in.flip();
209         decoder.decode(session, in, out);
210         assertEquals(1, session.getDecoderOutputQueue().size());
211         assertEquals("PQR", session.getDecoderOutputQueue().poll());
212 
213         // Test splitted long delimiter (\r\r\n) which produces two output
214         in.clear();
215         in.putString("PQR\r", encoder);
216         in.flip();
217         decoder.decode(session, in, out);
218         assertEquals(0, session.getDecoderOutputQueue().size());
219         in.clear();
220         in.putString("\r", encoder);
221         in.flip();
222         decoder.decode(session, in, out);
223         assertEquals(0, session.getDecoderOutputQueue().size());
224         in.clear();
225         in.putString("\nSTU\r\r\n", encoder);
226         in.flip();
227         decoder.decode(session, in, out);
228         assertEquals(2, session.getDecoderOutputQueue().size());
229         assertEquals("PQR", session.getDecoderOutputQueue().poll());
230         assertEquals("STU", session.getDecoderOutputQueue().poll());
231 
232         // Test splitted long delimiter mixed with partial non-delimiter.
233         in.clear();
234         in.putString("PQR\r", encoder);
235         in.flip();
236         decoder.decode(session, in, out);
237         assertEquals(0, session.getDecoderOutputQueue().size());
238         in.clear();
239         in.putString("X\r", encoder);
240         in.flip();
241         decoder.decode(session, in, out);
242         assertEquals(0, session.getDecoderOutputQueue().size());
243         in.clear();
244         in.putString("\r\nSTU\r\r\n", encoder);
245         in.flip();
246         decoder.decode(session, in, out);
247         assertEquals(2, session.getDecoderOutputQueue().size());
248         assertEquals("PQR\rX", session.getDecoderOutputQueue().poll());
249         assertEquals("STU", session.getDecoderOutputQueue().poll());
250 
251         in.clear();
252         String s = new String(new byte[] { 0, 77, 105, 110, 97 });
253         in.putString(s, encoder);
254         in.flip();
255         decoder.decode(session, in, out);
256         assertEquals(1, session.getDecoderOutputQueue().size());
257         assertEquals(s, session.getDecoderOutputQueue().poll());
258     }
259 
260     public void testOverflow() throws Exception {
261         TextLineDecoder decoder = new TextLineDecoder(StandardCharsets.UTF_8, LineDelimiter.AUTO);
262         decoder.setMaxLineLength(3);
263 
264         CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
265         ProtocolCodecSession session = new ProtocolCodecSession();
266         ProtocolDecoderOutput out = session.getDecoderOutput();
267         IoBuffer in = IoBuffer.allocate(16);
268 
269         // Make sure the overflow exception is not thrown until
270         // the delimiter is encountered.
271         in.putString("A", encoder).flip().mark();
272         decoder.decode(session, in.reset().mark(), out);
273         assertEquals(0, session.getDecoderOutputQueue().size());
274         decoder.decode(session, in.reset().mark(), out);
275         assertEquals(0, session.getDecoderOutputQueue().size());
276         decoder.decode(session, in.reset().mark(), out);
277         assertEquals(0, session.getDecoderOutputQueue().size());
278         decoder.decode(session, in.reset().mark(), out);
279         assertEquals(0, session.getDecoderOutputQueue().size());
280 
281         in.clear().putString("A\r\nB\r\n", encoder).flip();
282 
283         try {
284             decoder.decode(session, in, out);
285             fail();
286         } catch (RecoverableProtocolDecoderException e) {
287             // signifies a successful test execution
288             assertTrue(true);
289         }
290 
291         decoder.decode(session, in, out);
292         assertEquals(1, session.getDecoderOutputQueue().size());
293         assertEquals("B", session.getDecoderOutputQueue().poll());
294 
295         // Make sure OOM is not thrown.
296         System.gc();
297         long oldFreeMemory = Runtime.getRuntime().freeMemory();
298         in = IoBuffer.allocate(1048576 * 16).sweep((byte) ' ').mark();
299 
300         for (int i = 0; i < 10; i++) {
301             decoder.decode(session, in.reset().mark(), out);
302             assertEquals(0, session.getDecoderOutputQueue().size());
303 
304             // Memory consumption should be minimal.
305             assertTrue(Runtime.getRuntime().freeMemory() - oldFreeMemory < 1048576);
306         }
307 
308         in.clear().putString("C\r\nD\r\n", encoder).flip();
309         try {
310             decoder.decode(session, in, out);
311             fail();
312         } catch (RecoverableProtocolDecoderException e) {
313             // signifies a successful test execution
314             assertTrue(true);
315         }
316 
317         decoder.decode(session, in, out);
318         assertEquals(1, session.getDecoderOutputQueue().size());
319         assertEquals("D", session.getDecoderOutputQueue().poll());
320 
321         // Memory consumption should be minimal.
322         assertTrue(Runtime.getRuntime().freeMemory() - oldFreeMemory < 1048576);
323     }
324 
325     public void testSMTPDataBounds() throws Exception {
326         TextLineDecoder decoder = new TextLineDecoder(Charset.forName("ISO-8859-1"), new LineDelimiter("\r\n.\r\n"));
327 
328         CharsetEncoder encoder = Charset.forName("ISO-8859-1").newEncoder();
329         ProtocolCodecSession session = new ProtocolCodecSession();
330         IoBuffer in = IoBuffer.allocate(16).setAutoExpand(true);
331 
332         in.putString("\r\n", encoder).flip().mark();
333         decoder.decode(session, in.reset().mark(), session.getDecoderOutput());
334         assertEquals(0, session.getDecoderOutputQueue().size());
335         in.putString("Body\r\n.\r\n", encoder).flip().mark();
336         decoder.decode(session, in.reset().mark(), session.getDecoderOutput());
337         assertEquals(1, session.getDecoderOutputQueue().size());
338         assertEquals("\r\n\r\nBody", session.getDecoderOutputQueue().poll());
339     }
340 }