    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 09:38:13 +0200 (Tue, 16 Oct 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 }