| /*
|
| * Copyright 1999-2011 Alibaba Group.
|
| *
|
| * Licensed under the Apache License, Version 2.0 (the "License");
|
| * you may not use this file except in compliance with the License.
|
| * You may obtain a copy of the License at
|
| *
|
| * http://www.apache.org/licenses/LICENSE-2.0
|
| *
|
| * Unless required by applicable law or agreed to in writing, software
|
| * distributed under the License is distributed on an "AS IS" BASIS,
|
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| * See the License for the specific language governing permissions and
|
| * limitations under the License.
|
| */
|
| package com.alibaba.dubbo.remoting.codec; |
| |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.Serializable; |
| import java.util.HashMap; |
| |
| import junit.framework.Assert; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import com.alibaba.dubbo.common.URL; |
| import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream; |
| import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream; |
| import com.alibaba.dubbo.remoting.Channel; |
| import com.alibaba.dubbo.remoting.Codec; |
| import com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec; |
| |
| /** |
| * @author chao.liuc |
| * |
| */ |
| public class TelnetCodecTest { |
| protected Codec codec ; |
| byte[] UP = new byte[] {27, 91, 65}; |
| byte[] DOWN = new byte[] {27, 91, 66}; |
| /** |
| * @throws java.lang.Exception |
| */ |
| @Before |
| public void setUp() throws Exception { |
| codec = new TelnetCodec(); |
| } |
| |
| protected AbstractMockChannel getServerSideChannel(URL url){ |
| url = url.addParameter(AbstractMockChannel.LOCAL_ADDRESS, url.getAddress()) |
| .addParameter(AbstractMockChannel.REMOTE_ADDRESS, "127.0.0.1:12345"); |
| AbstractMockChannel channel = new AbstractMockChannel(url); |
| return channel; |
| } |
| protected AbstractMockChannel getCliendSideChannel(URL url){ |
| url = url.addParameter(AbstractMockChannel.LOCAL_ADDRESS, "127.0.0.1:12345") |
| .addParameter(AbstractMockChannel.REMOTE_ADDRESS, url.getAddress()); |
| AbstractMockChannel channel = new AbstractMockChannel(url); |
| return channel; |
| } |
| |
| protected byte[] join(byte[] in1, byte[] in2){ |
| byte[] ret = new byte[in1.length + in2.length]; |
| System.arraycopy(in1, 0, ret, 0, in1.length); |
| System.arraycopy(in2, 0, ret, in1.length, in2.length); |
| return ret; |
| } |
| |
| protected byte[] objectToByte(Object obj){ |
| byte[] bytes; |
| if (obj instanceof String){ |
| bytes = ((String)obj).getBytes(); |
| } else if (obj instanceof byte[]){ |
| bytes = (byte[]) obj; |
| } else { |
| try { |
| //object to bytearray |
| ByteArrayOutputStream bo = new ByteArrayOutputStream(); |
| ObjectOutputStream oo = new ObjectOutputStream(bo); |
| oo.writeObject(obj); |
| bytes = bo.toByteArray(); |
| bo.close(); |
| oo.close(); |
| } |
| catch(Exception e){ |
| throw new RuntimeException(e); |
| } |
| } |
| return(bytes); |
| } |
| |
| protected Object byteToObject(byte[] objBytes) throws Exception { |
| if (objBytes == null || objBytes.length == 0) { |
| return null; |
| } |
| ByteArrayInputStream bi = new ByteArrayInputStream(objBytes); |
| ObjectInputStream oi = new ObjectInputStream(bi); |
| return oi.readObject(); |
| } |
| |
| //====================================================== |
| public static class Person implements Serializable{ |
| private static final long serialVersionUID = 3362088148941547337L; |
| public String name ; |
| public String sex ; |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + ((name == null) ? 0 : name.hashCode()); |
| result = prime * result + ((sex == null) ? 0 : sex.hashCode()); |
| return result; |
| } |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) |
| return true; |
| if (obj == null) |
| return false; |
| if (getClass() != obj.getClass()) |
| return false; |
| Person other = (Person) obj; |
| if (name == null) { |
| if (other.name != null) |
| return false; |
| } else if (!name.equals(other.name)) |
| return false; |
| if (sex == null) { |
| if (other.sex != null) |
| return false; |
| } else if (!sex.equals(other.sex)) |
| return false; |
| return true; |
| } |
| |
| } |
| |
| protected void testDecode_assertEquals(byte[] request,Object ret) throws IOException{ |
| testDecode_assertEquals(request, ret, true); |
| } |
| |
| protected void testDecode_assertEquals(byte[] request,Object ret, boolean isServerside) throws IOException{ |
| //init channel |
| Channel channel = isServerside? getServerSideChannel(url) : getCliendSideChannel(url); |
| //init request string |
| InputStream input = new UnsafeByteArrayInputStream(request); |
| |
| //decode |
| Object obj = codec.decode(channel, input); |
| Assert.assertEquals(ret, obj); |
| } |
| |
| |
| |
| protected void testEecode_assertEquals(Object request,byte[] ret, boolean isServerside) throws IOException{ |
| //init channel |
| Channel channel = isServerside? getServerSideChannel(url) : getCliendSideChannel(url); |
| |
| UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024); |
| |
| codec.encode(channel, bos, request); |
| bos.flush(); |
| bos.close(); |
| InputStream is = new ByteArrayInputStream(bos.toByteArray()); |
| byte[] data = new byte[is.available()]; |
| is.read(data); |
| |
| Assert.assertEquals(ret.length, data.length); |
| for(int i=0;i<ret.length;i++){ |
| if (ret[i] != data[i]){ |
| Assert.fail(); |
| } |
| } |
| } |
| |
| protected void testDecode_assertEquals(Object request,Object ret) throws IOException{ |
| testDecode_assertEquals(request, ret, null); |
| } |
| |
| private void testDecode_assertEquals(Object request,Object ret, Object channelReceive) throws IOException{ |
| testDecode_assertEquals(null, request, ret, channelReceive); |
| } |
| |
| private void testDecode_assertEquals(AbstractMockChannel channel, Object request,Object expectret, Object channelReceive) throws IOException{ |
| //init channel |
| if (channel == null){ |
| channel = getServerSideChannel(url); |
| } |
| |
| byte[] buf = objectToByte(request); |
| InputStream input = new UnsafeByteArrayInputStream(buf); |
| |
| //decode |
| Object obj = codec.decode(channel, input); |
| Assert.assertEquals(expectret, obj); |
| Assert.assertEquals(channelReceive, channel.getReceivedMessage()); |
| } |
| |
| private void testDecode_PersonWithEnterByte(byte[] enterbytes ,boolean isNeedmore) throws IOException{ |
| //init channel |
| Channel channel = getServerSideChannel(url); |
| //init request string |
| Person request = new Person(); |
| byte[] newbuf = join(objectToByte(request),enterbytes); |
| InputStream input = new UnsafeByteArrayInputStream(newbuf); |
| |
| //decode |
| Object obj = codec.decode(channel, input); |
| if (isNeedmore){ |
| Assert.assertEquals(Codec.NEED_MORE_INPUT , obj); |
| }else { |
| Assert.assertTrue("return must string ", obj instanceof String); |
| } |
| } |
| |
| private void testDecode_WithExitByte(byte[] exitbytes ,boolean isChannelClose) throws IOException{ |
| //init channel |
| Channel channel = getServerSideChannel(url); |
| InputStream input = new UnsafeByteArrayInputStream(exitbytes); |
| |
| //decode |
| codec.decode(channel, input); |
| Assert.assertEquals(isChannelClose, channel.isClosed()); |
| } |
| |
| |
| //====================================================== |
| URL url = URL.valueOf("dubbo://10.20.30.40:20880"); |
| |
| @Test |
| public void testDecode_String_ClientSide() throws IOException{ |
| testDecode_assertEquals("aaa".getBytes(), "aaa",false); |
| } |
| |
| @Test |
| public void testDecode_BlankMessage() throws IOException{ |
| testDecode_assertEquals(new byte[]{}, Codec.NEED_MORE_INPUT); |
| } |
| |
| @Test |
| public void testDecode_String_NoEnter() throws IOException{ |
| testDecode_assertEquals("aaa", Codec.NEED_MORE_INPUT); |
| } |
| |
| @Test |
| public void testDecode_String_WithEnter() throws IOException{ |
| testDecode_assertEquals("aaa\n", "aaa"); |
| } |
| @Test |
| public void testDecode_String_MiddleWithEnter() throws IOException{ |
| testDecode_assertEquals("aaa\r\naaa", Codec.NEED_MORE_INPUT); |
| } |
| |
| @Test |
| public void testDecode_Person_ObjectOnly() throws IOException{ |
| testDecode_assertEquals(new Person(), Codec.NEED_MORE_INPUT); |
| } |
| @Test |
| public void testDecode_Person_WithEnter() throws IOException{ |
| testDecode_PersonWithEnterByte(new byte[] { '\r', '\n' } , false);//windows end |
| testDecode_PersonWithEnterByte(new byte[] { '\n', '\r' } , true); |
| testDecode_PersonWithEnterByte(new byte[] { '\n' } , false); //linux end |
| testDecode_PersonWithEnterByte(new byte[] { '\r' } , true); |
| testDecode_PersonWithEnterByte(new byte[] { '\r', 100 } , true); |
| } |
| @Test |
| public void testDecode_WithExitByte() throws IOException{ |
| HashMap<byte[] , Boolean> exitbytes = new HashMap<byte[] , Boolean>(); |
| exitbytes.put( new byte[] { 3 }, true ); /* Windows Ctrl+C */ |
| exitbytes.put( new byte[] { 1, 3 }, false ); //must equal the bytes |
| exitbytes.put( new byte[] { -1, -12, -1, -3, 6 }, true ); /* Linux Ctrl+C */ |
| exitbytes.put( new byte[] {1, -1, -12, -1, -3, 6 }, false ); //must equal the bytes |
| exitbytes.put( new byte[] { -1, -19, -1, -3, 6 }, true ); /* Linux Pause */ |
| |
| for (byte[] exit : exitbytes.keySet()){ |
| testDecode_WithExitByte(exit ,exitbytes.get(exit)); |
| } |
| } |
| @Test |
| public void testDecode_Backspace() throws IOException{ |
| //32 8 先加空格在补退格. |
| testDecode_assertEquals(new byte[]{'\b'}, Codec.NEED_MORE_INPUT, new String(new byte[] {32, 8})); |
| |
| //测试中文 |
| byte[] chineseBytes = "中".getBytes(); |
| byte[] request = join(chineseBytes, new byte[]{'\b'}); |
| testDecode_assertEquals(request, Codec.NEED_MORE_INPUT, new String(new byte[] {32, 32, 8, 8})); |
| //中文会带来此问题 (-数判断) 忽略此问题,退格键只有在真的telnet程序中才输入有意义. |
| testDecode_assertEquals(new byte[]{'a', 'x', -1, 'x', '\b'}, Codec.NEED_MORE_INPUT, new String(new byte[] {32, 32, 8, 8})); |
| } |
| |
| @Test(expected = IOException.class) |
| public void testDecode_Backspace_WithError() throws IOException{ |
| url = url.addParameter(AbstractMockChannel.ERROR_WHEN_SEND, Boolean.TRUE.toString()); |
| testDecode_Backspace(); |
| url = url.removeParameter(AbstractMockChannel.ERROR_WHEN_SEND); |
| } |
| |
| @Test() |
| public void testDecode_History_UP() throws IOException{ |
| //init channel |
| AbstractMockChannel channel = getServerSideChannel(url); |
| |
| testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, null); |
| |
| String request1 = "aaa\n"; |
| Object expected1 = "aaa"; |
| //init history |
| testDecode_assertEquals(channel, request1, expected1, null); |
| |
| testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1); |
| } |
| |
| @Test(expected = IOException.class) |
| public void testDecode_UPorDOWN_WithError() throws IOException{ |
| url = url.addParameter(AbstractMockChannel.ERROR_WHEN_SEND, Boolean.TRUE.toString()); |
| |
| //init channel |
| AbstractMockChannel channel = getServerSideChannel(url); |
| |
| testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, null); |
| |
| String request1 = "aaa\n"; |
| Object expected1 = "aaa"; |
| //init history |
| testDecode_assertEquals(channel, request1, expected1, null); |
| |
| testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1); |
| |
| url = url.removeParameter(AbstractMockChannel.ERROR_WHEN_SEND); |
| } |
| |
| @Test() |
| public void testDecode_History_UP_DOWN_MULTI() throws IOException{ |
| AbstractMockChannel channel = getServerSideChannel(url); |
| |
| String request1 = "aaa\n"; |
| Object expected1 = request1.replace("\n", ""); |
| //init history |
| testDecode_assertEquals(channel, request1, expected1, null); |
| |
| String request2 = "bbb\n"; |
| Object expected2 = request2.replace("\n", ""); |
| //init history |
| testDecode_assertEquals(channel, request2, expected2, null); |
| |
| String request3 = "ccc\n"; |
| Object expected3= request3.replace("\n", ""); |
| //init history |
| testDecode_assertEquals(channel, request3, expected3, null); |
| |
| byte[] UP = new byte[] {27, 91, 65}; |
| byte[] DOWN = new byte[] {27, 91, 66}; |
| //history[aaa,bbb,ccc] |
| testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected3); |
| testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3); |
| testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected2); |
| testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1); |
| testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1); |
| testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1); |
| testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected2); |
| testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3); |
| testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3); |
| testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3); |
| testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected2); |
| } |
| |
| |
| //============================================================================================================================= |
| @Test |
| public void testEncode_String_ClientSide() throws IOException{ |
| testEecode_assertEquals("aaa", "aaa\r\n".getBytes(), false); |
| } |
| |
| } |