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