001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.filter.codec.textline; 021 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.nio.charset.Charset; 027import java.nio.charset.CharsetEncoder; 028 029import org.apache.mina.core.buffer.IoBuffer; 030import org.apache.mina.filter.codec.ProtocolCodecSession; 031import org.apache.mina.filter.codec.ProtocolDecoderOutput; 032import org.apache.mina.filter.codec.RecoverableProtocolDecoderException; 033import org.junit.Test; 034 035/** 036 * Tests {@link TextLineDecoder}. 037 * 038 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 039 */ 040public class TextLineDecoderTest { 041 @Test 042 public void testNormalDecode() throws Exception { 043 TextLineDecoder decoder = new TextLineDecoder(Charset.forName("UTF-8"), LineDelimiter.WINDOWS); 044 045 CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder(); 046 ProtocolCodecSession session = new ProtocolCodecSession(); 047 ProtocolDecoderOutput out = session.getDecoderOutput(); 048 IoBuffer in = IoBuffer.allocate(16); 049 050 // Test one decode and one output 051 in.putString("ABC\r\n", encoder); 052 in.flip(); 053 decoder.decode(session, in, out); 054 assertEquals(1, session.getDecoderOutputQueue().size()); 055 assertEquals("ABC", session.getDecoderOutputQueue().poll()); 056 057 // Test two decode and one output 058 in.clear(); 059 in.putString("DEF", encoder); 060 in.flip(); 061 decoder.decode(session, in, out); 062 assertEquals(0, session.getDecoderOutputQueue().size()); 063 in.clear(); 064 in.putString("GHI\r\n", encoder); 065 in.flip(); 066 decoder.decode(session, in, out); 067 assertEquals(1, session.getDecoderOutputQueue().size()); 068 assertEquals("DEFGHI", session.getDecoderOutputQueue().poll()); 069 070 // Test one decode and two output 071 in.clear(); 072 in.putString("JKL\r\nMNO\r\n", encoder); 073 in.flip(); 074 decoder.decode(session, in, out); 075 assertEquals(2, session.getDecoderOutputQueue().size()); 076 assertEquals("JKL", session.getDecoderOutputQueue().poll()); 077 assertEquals("MNO", session.getDecoderOutputQueue().poll()); 078 079 // Test aborted delimiter (DIRMINA-506) 080 in.clear(); 081 in.putString("ABC\r\r\n", encoder); 082 in.flip(); 083 decoder.decode(session, in, out); 084 assertEquals(1, session.getDecoderOutputQueue().size()); 085 assertEquals("ABC\r", session.getDecoderOutputQueue().poll()); 086 087 // Test splitted long delimiter 088 decoder = new TextLineDecoder(Charset.forName("UTF-8"), new LineDelimiter("\n\n\n")); 089 in.clear(); 090 in.putString("PQR\n", encoder); 091 in.flip(); 092 decoder.decode(session, in, out); 093 assertEquals(0, session.getDecoderOutputQueue().size()); 094 in.clear(); 095 in.putString("\n", encoder); 096 in.flip(); 097 decoder.decode(session, in, out); 098 assertEquals(0, session.getDecoderOutputQueue().size()); 099 in.clear(); 100 in.putString("\n", encoder); 101 in.flip(); 102 decoder.decode(session, in, out); 103 assertEquals(1, session.getDecoderOutputQueue().size()); 104 assertEquals("PQR", session.getDecoderOutputQueue().poll()); 105 106 // Test splitted long delimiter which produces two output 107 decoder = new TextLineDecoder(Charset.forName("UTF-8"), new LineDelimiter("\n\n\n")); 108 in.clear(); 109 in.putString("PQR\n", encoder); 110 in.flip(); 111 decoder.decode(session, in, out); 112 assertEquals(0, session.getDecoderOutputQueue().size()); 113 in.clear(); 114 in.putString("\n", encoder); 115 in.flip(); 116 decoder.decode(session, in, out); 117 assertEquals(0, session.getDecoderOutputQueue().size()); 118 in.clear(); 119 in.putString("\nSTU\n\n\n", encoder); 120 in.flip(); 121 decoder.decode(session, in, out); 122 assertEquals(2, session.getDecoderOutputQueue().size()); 123 assertEquals("PQR", session.getDecoderOutputQueue().poll()); 124 assertEquals("STU", session.getDecoderOutputQueue().poll()); 125 126 // Test splitted long delimiter mixed with partial non-delimiter. 127 decoder = new TextLineDecoder(Charset.forName("UTF-8"), new LineDelimiter("\n\n\n")); 128 in.clear(); 129 in.putString("PQR\n", encoder); 130 in.flip(); 131 decoder.decode(session, in, out); 132 assertEquals(0, session.getDecoderOutputQueue().size()); 133 in.clear(); 134 in.putString("X\n", encoder); 135 in.flip(); 136 decoder.decode(session, in, out); 137 assertEquals(0, session.getDecoderOutputQueue().size()); 138 in.clear(); 139 in.putString("\n\nSTU\n\n\n", encoder); 140 in.flip(); 141 decoder.decode(session, in, out); 142 assertEquals(2, session.getDecoderOutputQueue().size()); 143 assertEquals("PQR\nX", session.getDecoderOutputQueue().poll()); 144 assertEquals("STU", session.getDecoderOutputQueue().poll()); 145 } 146 147 public void testAutoDecode() throws Exception { 148 TextLineDecoder decoder = new TextLineDecoder(Charset.forName("UTF-8"), LineDelimiter.AUTO); 149 150 CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder(); 151 ProtocolCodecSession session = new ProtocolCodecSession(); 152 ProtocolDecoderOutput out = session.getDecoderOutput(); 153 IoBuffer in = IoBuffer.allocate(16); 154 155 // Test one decode and one output 156 in.putString("ABC\r\n", encoder); 157 in.flip(); 158 decoder.decode(session, in, out); 159 assertEquals(1, session.getDecoderOutputQueue().size()); 160 assertEquals("ABC", session.getDecoderOutputQueue().poll()); 161 162 // Test two decode and one output 163 in.clear(); 164 in.putString("DEF", encoder); 165 in.flip(); 166 decoder.decode(session, in, out); 167 assertEquals(0, session.getDecoderOutputQueue().size()); 168 in.clear(); 169 in.putString("GHI\r\n", encoder); 170 in.flip(); 171 decoder.decode(session, in, out); 172 assertEquals(1, session.getDecoderOutputQueue().size()); 173 assertEquals("DEFGHI", session.getDecoderOutputQueue().poll()); 174 175 // Test one decode and two output 176 in.clear(); 177 in.putString("JKL\r\nMNO\r\n", encoder); 178 in.flip(); 179 decoder.decode(session, in, out); 180 assertEquals(2, session.getDecoderOutputQueue().size()); 181 assertEquals("JKL", session.getDecoderOutputQueue().poll()); 182 assertEquals("MNO", session.getDecoderOutputQueue().poll()); 183 184 // Test multiple '\n's 185 in.clear(); 186 in.putString("\n\n\n", encoder); 187 in.flip(); 188 decoder.decode(session, in, out); 189 assertEquals(3, session.getDecoderOutputQueue().size()); 190 assertEquals("", session.getDecoderOutputQueue().poll()); 191 assertEquals("", session.getDecoderOutputQueue().poll()); 192 assertEquals("", session.getDecoderOutputQueue().poll()); 193 194 // Test splitted long delimiter (\r\r\n) 195 in.clear(); 196 in.putString("PQR\r", encoder); 197 in.flip(); 198 decoder.decode(session, in, out); 199 assertEquals(0, session.getDecoderOutputQueue().size()); 200 in.clear(); 201 in.putString("\r", encoder); 202 in.flip(); 203 decoder.decode(session, in, out); 204 assertEquals(0, session.getDecoderOutputQueue().size()); 205 in.clear(); 206 in.putString("\n", encoder); 207 in.flip(); 208 decoder.decode(session, in, out); 209 assertEquals(1, session.getDecoderOutputQueue().size()); 210 assertEquals("PQR", session.getDecoderOutputQueue().poll()); 211 212 // Test splitted long delimiter (\r\r\n) which produces two output 213 in.clear(); 214 in.putString("PQR\r", encoder); 215 in.flip(); 216 decoder.decode(session, in, out); 217 assertEquals(0, session.getDecoderOutputQueue().size()); 218 in.clear(); 219 in.putString("\r", encoder); 220 in.flip(); 221 decoder.decode(session, in, out); 222 assertEquals(0, session.getDecoderOutputQueue().size()); 223 in.clear(); 224 in.putString("\nSTU\r\r\n", encoder); 225 in.flip(); 226 decoder.decode(session, in, out); 227 assertEquals(2, session.getDecoderOutputQueue().size()); 228 assertEquals("PQR", session.getDecoderOutputQueue().poll()); 229 assertEquals("STU", session.getDecoderOutputQueue().poll()); 230 231 // Test splitted long delimiter mixed with partial non-delimiter. 232 in.clear(); 233 in.putString("PQR\r", encoder); 234 in.flip(); 235 decoder.decode(session, in, out); 236 assertEquals(0, session.getDecoderOutputQueue().size()); 237 in.clear(); 238 in.putString("X\r", encoder); 239 in.flip(); 240 decoder.decode(session, in, out); 241 assertEquals(0, session.getDecoderOutputQueue().size()); 242 in.clear(); 243 in.putString("\r\nSTU\r\r\n", encoder); 244 in.flip(); 245 decoder.decode(session, in, out); 246 assertEquals(2, session.getDecoderOutputQueue().size()); 247 assertEquals("PQR\rX", session.getDecoderOutputQueue().poll()); 248 assertEquals("STU", session.getDecoderOutputQueue().poll()); 249 250 in.clear(); 251 String s = new String(new byte[] { 0, 77, 105, 110, 97 }); 252 in.putString(s, encoder); 253 in.flip(); 254 decoder.decode(session, in, out); 255 assertEquals(1, session.getDecoderOutputQueue().size()); 256 assertEquals(s, session.getDecoderOutputQueue().poll()); 257 } 258 259 public void testOverflow() throws Exception { 260 TextLineDecoder decoder = new TextLineDecoder(Charset.forName("UTF-8"), 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 assertEquals(0, session.getDecoderOutputQueue().size()); 273 decoder.decode(session, in.reset().mark(), out); 274 assertEquals(0, session.getDecoderOutputQueue().size()); 275 decoder.decode(session, in.reset().mark(), out); 276 assertEquals(0, session.getDecoderOutputQueue().size()); 277 decoder.decode(session, in.reset().mark(), out); 278 assertEquals(0, session.getDecoderOutputQueue().size()); 279 280 in.clear().putString("A\r\nB\r\n", encoder).flip(); 281 282 try { 283 decoder.decode(session, in, out); 284 fail(); 285 } catch (RecoverableProtocolDecoderException e) { 286 // signifies a successful test execution 287 assertTrue(true); 288 } 289 290 decoder.decode(session, in, out); 291 assertEquals(1, session.getDecoderOutputQueue().size()); 292 assertEquals("B", session.getDecoderOutputQueue().poll()); 293 294 // Make sure OOM is not thrown. 295 System.gc(); 296 long oldFreeMemory = Runtime.getRuntime().freeMemory(); 297 in = IoBuffer.allocate(1048576 * 16).sweep((byte) ' ').mark(); 298 299 for (int i = 0; i < 10; i++) { 300 decoder.decode(session, in.reset().mark(), out); 301 assertEquals(0, session.getDecoderOutputQueue().size()); 302 303 // Memory consumption should be minimal. 304 assertTrue(Runtime.getRuntime().freeMemory() - oldFreeMemory < 1048576); 305 } 306 307 in.clear().putString("C\r\nD\r\n", encoder).flip(); 308 try { 309 decoder.decode(session, in, out); 310 fail(); 311 } catch (RecoverableProtocolDecoderException e) { 312 // signifies a successful test execution 313 assertTrue(true); 314 } 315 316 decoder.decode(session, in, out); 317 assertEquals(1, session.getDecoderOutputQueue().size()); 318 assertEquals("D", session.getDecoderOutputQueue().poll()); 319 320 // Memory consumption should be minimal. 321 assertTrue(Runtime.getRuntime().freeMemory() - oldFreeMemory < 1048576); 322 } 323 324 public void testSMTPDataBounds() throws Exception { 325 TextLineDecoder decoder = new TextLineDecoder(Charset.forName("ISO-8859-1"), 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}