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