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.net.SocketAddress;
23  import java.nio.charset.Charset;
24  import java.nio.charset.CharsetEncoder;
25  
26  import junit.framework.Assert;
27  import junit.framework.TestCase;
28  
29  import org.apache.mina.common.BufferDataException;
30  import org.apache.mina.common.ByteBuffer;
31  import org.apache.mina.common.IoFilterChain;
32  import org.apache.mina.common.IoHandler;
33  import org.apache.mina.common.IoService;
34  import org.apache.mina.common.IoServiceConfig;
35  import org.apache.mina.common.IoSession;
36  import org.apache.mina.common.IoSessionConfig;
37  import org.apache.mina.common.TransportType;
38  import org.apache.mina.common.support.BaseIoSession;
39  import org.apache.mina.filter.codec.ProtocolDecoderOutput;
40  import org.apache.mina.util.Queue;
41  
42  /**
43   * Tests {@link TextLineDecoder}.
44   *
45   * @author The Apache Directory Project (mina-dev@directory.apache.org)
46   * @version $Rev: 585065 $, $Date: 2007-10-16 16:38:13 +0900 (Tue, 16 Oct 2007) $
47   */
48  public class TextLineDecoderTest extends TestCase {
49      public static void main(String[] args) {
50          junit.textui.TestRunner.run(TextLineDecoderTest.class);
51      }
52  
53      public void testNormalDecode() throws Exception {
54          TextLineDecoder decoder = new TextLineDecoder(Charset.forName("UTF-8"),
55                  LineDelimiter.WINDOWS);
56  
57          CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
58          IoSession session = new DummySession();
59          TestDecoderOutput out = new TestDecoderOutput();
60          ByteBuffer in = ByteBuffer.allocate(16);
61  
62          // Test one decode and one output
63          in.putString("ABC\r\n", encoder);
64          in.flip();
65          decoder.decode(session, in, out);
66          Assert.assertEquals(1, out.getMessageQueue().size());
67          Assert.assertEquals("ABC", out.getMessageQueue().pop());
68  
69          // Test two decode and one output
70          in.clear();
71          in.putString("DEF", encoder);
72          in.flip();
73          decoder.decode(session, in, out);
74          Assert.assertEquals(0, out.getMessageQueue().size());
75          in.clear();
76          in.putString("GHI\r\n", encoder);
77          in.flip();
78          decoder.decode(session, in, out);
79          Assert.assertEquals(1, out.getMessageQueue().size());
80          Assert.assertEquals("DEFGHI", out.getMessageQueue().pop());
81  
82          // Test one decode and two output
83          in.clear();
84          in.putString("JKL\r\nMNO\r\n", encoder);
85          in.flip();
86          decoder.decode(session, in, out);
87          Assert.assertEquals(2, out.getMessageQueue().size());
88          Assert.assertEquals("JKL", out.getMessageQueue().pop());
89          Assert.assertEquals("MNO", out.getMessageQueue().pop());
90  
91          // Test splitted long delimiter
92          decoder = new TextLineDecoder(Charset.forName("UTF-8"),
93                  new LineDelimiter("\n\n\n"));
94          in.clear();
95          in.putString("PQR\n", encoder);
96          in.flip();
97          decoder.decode(session, in, out);
98          Assert.assertEquals(0, out.getMessageQueue().size());
99          in.clear();
100         in.putString("\n", encoder);
101         in.flip();
102         decoder.decode(session, in, out);
103         Assert.assertEquals(0, out.getMessageQueue().size());
104         in.clear();
105         in.putString("\n", encoder);
106         in.flip();
107         decoder.decode(session, in, out);
108         Assert.assertEquals(1, out.getMessageQueue().size());
109         Assert.assertEquals("PQR", out.getMessageQueue().pop());
110 
111         // Test splitted long delimiter which produces two output
112         decoder = new TextLineDecoder(Charset.forName("UTF-8"),
113                 new LineDelimiter("\n\n\n"));
114         in.clear();
115         in.putString("PQR\n", encoder);
116         in.flip();
117         decoder.decode(session, in, out);
118         Assert.assertEquals(0, out.getMessageQueue().size());
119         in.clear();
120         in.putString("\n", encoder);
121         in.flip();
122         decoder.decode(session, in, out);
123         Assert.assertEquals(0, out.getMessageQueue().size());
124         in.clear();
125         in.putString("\nSTU\n\n\n", encoder);
126         in.flip();
127         decoder.decode(session, in, out);
128         Assert.assertEquals(2, out.getMessageQueue().size());
129         Assert.assertEquals("PQR", out.getMessageQueue().pop());
130         Assert.assertEquals("STU", out.getMessageQueue().pop());
131 
132         // Test splitted long delimiter mixed with partial non-delimiter.
133         decoder = new TextLineDecoder(Charset.forName("UTF-8"),
134                 new LineDelimiter("\n\n\n"));
135         in.clear();
136         in.putString("PQR\n", encoder);
137         in.flip();
138         decoder.decode(session, in, out);
139         Assert.assertEquals(0, out.getMessageQueue().size());
140         in.clear();
141         in.putString("X\n", encoder);
142         in.flip();
143         decoder.decode(session, in, out);
144         Assert.assertEquals(0, out.getMessageQueue().size());
145         in.clear();
146         in.putString("\n\nSTU\n\n\n", encoder);
147         in.flip();
148         decoder.decode(session, in, out);
149         Assert.assertEquals(2, out.getMessageQueue().size());
150         Assert.assertEquals("PQR\nX", out.getMessageQueue().pop());
151         Assert.assertEquals("STU", out.getMessageQueue().pop());
152     }
153 
154     public void testAutoDecode() throws Exception {
155         TextLineDecoder decoder = new TextLineDecoder(Charset.forName("UTF-8"),
156                 LineDelimiter.AUTO);
157 
158         CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
159         IoSession session = new DummySession();
160         TestDecoderOutput out = new TestDecoderOutput();
161         ByteBuffer in = ByteBuffer.allocate(16);
162 
163         // Test one decode and one output
164         in.putString("ABC\r\n", encoder);
165         in.flip();
166         decoder.decode(session, in, out);
167         Assert.assertEquals(1, out.getMessageQueue().size());
168         Assert.assertEquals("ABC", out.getMessageQueue().pop());
169 
170         // Test two decode and one output
171         in.clear();
172         in.putString("DEF", encoder);
173         in.flip();
174         decoder.decode(session, in, out);
175         Assert.assertEquals(0, out.getMessageQueue().size());
176         in.clear();
177         in.putString("GHI\r\n", encoder);
178         in.flip();
179         decoder.decode(session, in, out);
180         Assert.assertEquals(1, out.getMessageQueue().size());
181         Assert.assertEquals("DEFGHI", out.getMessageQueue().pop());
182 
183         // Test one decode and two output
184         in.clear();
185         in.putString("JKL\r\nMNO\r\n", encoder);
186         in.flip();
187         decoder.decode(session, in, out);
188         Assert.assertEquals(2, out.getMessageQueue().size());
189         Assert.assertEquals("JKL", out.getMessageQueue().pop());
190         Assert.assertEquals("MNO", out.getMessageQueue().pop());
191 
192         // Test multiple '\n's
193         in.clear();
194         in.putString("\n\n\n", encoder);
195         in.flip();
196         decoder.decode(session, in, out);
197         Assert.assertEquals(3, out.getMessageQueue().size());
198         Assert.assertEquals("", out.getMessageQueue().pop());
199         Assert.assertEquals("", out.getMessageQueue().pop());
200         Assert.assertEquals("", out.getMessageQueue().pop());
201 
202         // Test splitted long delimiter (\r\r\n)
203         in.clear();
204         in.putString("PQR\r", encoder);
205         in.flip();
206         decoder.decode(session, in, out);
207         Assert.assertEquals(0, out.getMessageQueue().size());
208         in.clear();
209         in.putString("\r", encoder);
210         in.flip();
211         decoder.decode(session, in, out);
212         Assert.assertEquals(0, out.getMessageQueue().size());
213         in.clear();
214         in.putString("\n", encoder);
215         in.flip();
216         decoder.decode(session, in, out);
217         Assert.assertEquals(1, out.getMessageQueue().size());
218         Assert.assertEquals("PQR", out.getMessageQueue().pop());
219 
220         // Test splitted long delimiter (\r\r\n) which produces two output
221         in.clear();
222         in.putString("PQR\r", encoder);
223         in.flip();
224         decoder.decode(session, in, out);
225         Assert.assertEquals(0, out.getMessageQueue().size());
226         in.clear();
227         in.putString("\r", encoder);
228         in.flip();
229         decoder.decode(session, in, out);
230         Assert.assertEquals(0, out.getMessageQueue().size());
231         in.clear();
232         in.putString("\nSTU\r\r\n", encoder);
233         in.flip();
234         decoder.decode(session, in, out);
235         Assert.assertEquals(2, out.getMessageQueue().size());
236         Assert.assertEquals("PQR", out.getMessageQueue().pop());
237         Assert.assertEquals("STU", out.getMessageQueue().pop());
238 
239         // Test splitted long delimiter mixed with partial non-delimiter.
240         in.clear();
241         in.putString("PQR\r", encoder);
242         in.flip();
243         decoder.decode(session, in, out);
244         Assert.assertEquals(0, out.getMessageQueue().size());
245         in.clear();
246         in.putString("X\r", encoder);
247         in.flip();
248         decoder.decode(session, in, out);
249         Assert.assertEquals(0, out.getMessageQueue().size());
250         in.clear();
251         in.putString("\r\nSTU\r\r\n", encoder);
252         in.flip();
253         decoder.decode(session, in, out);
254         Assert.assertEquals(2, out.getMessageQueue().size());
255         Assert.assertEquals("PQR\rX", out.getMessageQueue().pop());
256         Assert.assertEquals("STU", out.getMessageQueue().pop());
257     }
258     
259     public void testOverflow() throws Exception {
260         TextLineDecoder decoder = new TextLineDecoder(Charset.forName("UTF-8"),
261                 LineDelimiter.AUTO);
262         decoder.setMaxLineLength(3);
263 
264         CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
265         IoSession session = new DummySession();
266         TestDecoderOutput out = new TestDecoderOutput();
267         ByteBuffer in = ByteBuffer.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         Assert.assertEquals(0, out.getMessageQueue().size());
274         decoder.decode(session, in.reset().mark(), out);
275         Assert.assertEquals(0, out.getMessageQueue().size());
276         decoder.decode(session, in.reset().mark(), out);
277         Assert.assertEquals(0, out.getMessageQueue().size());
278         decoder.decode(session, in.reset().mark(), out);
279         Assert.assertEquals(0, out.getMessageQueue().size());
280         
281         in.clear().putString("A\r\nB\r\n", encoder).flip();
282         try {
283             decoder.decode(session, in, out);
284             Assert.fail();
285         } catch (BufferDataException e) {
286             // Success!
287         }
288         
289         decoder.decode(session, in, out);
290         Assert.assertEquals(1, out.getMessageQueue().size());
291         Assert.assertEquals("B", out.getMessageQueue().poll());
292 
293         // Make sure OOM is not thrown.
294         long oldFreeMemory = Runtime.getRuntime().freeMemory();
295         in = ByteBuffer.allocate(1048576 * 16).mark();
296         for (int i = 0; i < 10; i ++) {
297             decoder.decode(session, in.reset().mark(), out);
298             Assert.assertEquals(0, out.getMessageQueue().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 (BufferDataException e) {
309             // Success!
310         }
311         
312         decoder.decode(session, in, out);
313         Assert.assertEquals(1, out.getMessageQueue().size());
314         Assert.assertEquals("D", out.getMessageQueue().poll());
315 
316         // Memory consumption should be minimal.
317         Assert.assertTrue(Runtime.getRuntime().freeMemory() - oldFreeMemory < 1048576); 
318     }
319     
320 
321     private static class DummySession extends BaseIoSession {
322         protected void updateTrafficMask() {
323         }
324 
325         public IoService getService() {
326             return null;
327         }
328 
329         public IoServiceConfig getServiceConfig() {
330             return null;
331         }
332 
333         public IoHandler getHandler() {
334             return null;
335         }
336 
337         public IoFilterChain getFilterChain() {
338             return null;
339         }
340 
341         public TransportType getTransportType() {
342             return null;
343         }
344 
345         public SocketAddress getRemoteAddress() {
346             return null;
347         }
348 
349         public SocketAddress getLocalAddress() {
350             return null;
351         }
352 
353         public int getScheduledWriteRequests() {
354             return 0;
355         }
356 
357         public IoSessionConfig getConfig() {
358             return null;
359         }
360 
361         public SocketAddress getServiceAddress() {
362             return null;
363         }
364 
365         public int getScheduledWriteBytes() {
366             return 0;
367         }
368     }
369 
370     private static class TestDecoderOutput implements ProtocolDecoderOutput {
371         private Queue messageQueue = new Queue();
372 
373         public void write(Object message) {
374             messageQueue.push(message);
375         }
376 
377         public Queue getMessageQueue() {
378             return messageQueue;
379         }
380 
381         public void flush() {
382         }
383     }
384 }