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