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