/*
* Copyright 2014 Julien Viet
*
* Julien Viet licenses this file to you 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 io.termd.core.telnet;
import org.apache.commons.net.telnet.EchoOptionHandler;
import org.apache.commons.net.telnet.SimpleOptionHandler;
import org.apache.commons.net.telnet.SuppressGAOptionHandler;
import org.apache.commons.net.telnet.TelnetNotificationHandler;
import org.apache.commons.net.telnet.TelnetOptionHandler;
import org.apache.commons.net.telnet.WindowSizeOptionHandler;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
/**
* See <a href="http://commons.apache.org/proper/commons-net/examples/telnet/TelnetClientExample.java>for more possibilities</a>
*
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
*/
public abstract class TelnetHandlerTest extends TelnetTestBase {
private void testOptionValue(Supplier<TelnetHandler> factory, TelnetOptionHandler optionHandler) throws Exception {
server.start(factory);
client.setOptionHandler(optionHandler);
client.connect("localhost", 4000);
await();
}
@Test
public void testRejectEcho() throws Exception {
final AtomicReference<Boolean> serverValue = new AtomicReference<>();
EchoOptionHandler optionHandler = new EchoOptionHandler(false, false, false, false);
testOptionValue(() -> new TelnetHandler() {
@Override
protected void onOpen(TelnetConnection conn) {
conn.writeWillOption(Option.ECHO);
}
@Override
protected void onEcho(boolean echo) {
serverValue.set(echo);
testComplete();
}
}, optionHandler);
assertEquals(false, serverValue.get());
assertEquals(false, optionHandler.getAcceptRemote());
}
@Test
public void testAcceptEcho() throws Exception {
final AtomicReference<Boolean> serverValue = new AtomicReference<>();
EchoOptionHandler optionHandler = new EchoOptionHandler(false, false, false, true);
testOptionValue(() -> new TelnetHandler() {
@Override
protected void onOpen(TelnetConnection conn) {
conn.writeWillOption(Option.ECHO);
}
@Override
protected void onEcho(boolean echo) {
serverValue.set(echo);
testComplete();
}
}, optionHandler);
assertEquals(true, serverValue.get());
assertEquals(true, optionHandler.getAcceptRemote());
}
@Test
public void testRejectSGA() throws Exception {
final AtomicReference<Boolean> serverValue = new AtomicReference<>();
SuppressGAOptionHandler optionHandler = new SuppressGAOptionHandler(false, false, false, false);
testOptionValue(() -> new TelnetHandler() {
@Override
protected void onOpen(TelnetConnection conn) {
conn.writeWillOption(Option.SGA);
}
@Override
protected void onSGA(boolean sga) {
serverValue.set(sga);
testComplete();
}
}, optionHandler);
assertEquals(false, serverValue.get());
assertEquals(false, optionHandler.getAcceptRemote());
}
@Test
public void testAcceptSGA() throws Exception {
final AtomicReference<Boolean> serverValue = new AtomicReference<>();
SuppressGAOptionHandler optionHandler = new SuppressGAOptionHandler(false, false, false, true);
testOptionValue(() -> new TelnetHandler() {
@Override
protected void onOpen(TelnetConnection conn) {
conn.writeWillOption(Option.SGA);
}
@Override
protected void onSGA(boolean sga) {
serverValue.set(sga);
testComplete();
}
}, optionHandler);
assertEquals(true, serverValue.get());
assertEquals(true, optionHandler.getAcceptRemote());
}
@Test
public void testRejectNAWS() throws Exception {
final AtomicReference<Boolean> serverValue = new AtomicReference<>();
WindowSizeOptionHandler optionHandler = new WindowSizeOptionHandler(20, 10, false, false, false, false);
testOptionValue(new Supplier<TelnetHandler>() {
@Override
public TelnetHandler get() {
return new TelnetHandler() {
@Override
protected void onOpen(TelnetConnection conn) {
conn.writeDoOption(Option.NAWS);
}
@Override
protected void onNAWS(boolean naws) {
serverValue.set(naws);
testComplete();
}
@Override
protected void onSize(int width, int height) {
super.onSize(width, height);
}
};
}
}, optionHandler);
assertEquals(false, serverValue.get());
assertEquals(false, optionHandler.getAcceptLocal());
}
@Test
public void testAcceptNAWS() throws Exception {
final AtomicReference<Boolean> serverValue = new AtomicReference<>();
final AtomicReference<int[]> size = new AtomicReference<>();
WindowSizeOptionHandler optionHandler = new WindowSizeOptionHandler(20, 10, false, false, true, false);
testOptionValue(() -> new TelnetHandler() {
@Override
protected void onOpen(TelnetConnection conn) {
conn.writeDoOption(Option.NAWS);
}
@Override
protected void onNAWS(boolean naws) {
serverValue.set(naws);
}
@Override
protected void onSize(int width, int height) {
size.set(new int[]{width, height});
testComplete();
}
}, optionHandler);
assertEquals(true, serverValue.get());
assertEquals(true, optionHandler.getAcceptLocal());
assertEquals(2, size.get().length);
assertEquals(20, size.get()[0]);
assertEquals(10, size.get()[1]);
}
@Test
public void testOpen() throws Exception {
server.start(() -> new TelnetHandler() {
@Override
protected void onOpen(TelnetConnection conn) {
testComplete();
}
});
client.connect("localhost", 4000);
await();
}
@Test
public void testClientDisconnect() throws Exception {
server.start(() -> new TelnetHandler() {
@Override
protected void onClose() {
testComplete();
}
});
try {
client.connect("localhost", 4000);
} finally {
client.disconnect();
}
await();
}
@Test
public void testServerClose() throws Exception {
server.start(() -> new TelnetHandler() {
@Override
protected void onOpen(TelnetConnection conn) {
conn.close();
}
@Override
protected void onClose() {
testComplete();
}
});
client.connect("localhost", 4000);
await();
}
@Test
public void testSend() throws Exception {
server.start(() -> new TelnetHandler() {
@Override
protected void onOpen(TelnetConnection conn) {
conn.write(new byte[]{0, 1, 2, 3, 127, (byte) 0x80, (byte) 0x81, -1});
}
});
client.connect("localhost", 4000);
byte[] data = client.assertReadBytes(8);
assertEquals((byte)0, data[0]);
assertEquals((byte)1, data[1]);
assertEquals((byte)2, data[2]);
assertEquals((byte)3, data[3]);
assertEquals((byte)127, data[4]);
assertEquals((byte)0, data[5]);
assertEquals((byte)1, data[6]);
assertEquals((byte)127, data[7]);
}
@Test
public void testReceive() throws Exception {
server.start(() -> new TelnetHandler() {
@Override
protected void onData(byte[] data) {
assertEquals(7, data.length);
assertEquals((byte) 0, data[0]);
assertEquals((byte) 1, data[1]);
assertEquals((byte) 2, data[2]);
assertEquals((byte) 3, data[3]);
assertEquals((byte) 127, data[4]);
assertEquals((byte) 0x80, data[5]);
assertEquals((byte) 0x81, data[6]);
testComplete();
}
});
client.connect("localhost", 4000).write(new byte[]{0, 1, 2, 3, 127, (byte) 0x80, (byte) 0x81}).flush();
await();
}
@Test
public void testWillUnknownOption() throws Exception {
server.start(TelnetHandler::new);
client.registerNotifHandler((negotiation_code, option_code) -> {
if (option_code == 47) {
assertEquals(TelnetNotificationHandler.RECEIVED_DONT, negotiation_code);
testComplete();
}
});
client.setOptionHandler(new SimpleOptionHandler(47, true, false, false, false));
client.connect("localhost", 4000);
await();
}
@Test
public void testDoUnknownOption() throws Exception {
server.start(TelnetHandler::new);
client.registerNotifHandler((negotiation_code, option_code) -> {
if (option_code == 47) {
assertEquals(TelnetNotificationHandler.RECEIVED_WONT, negotiation_code);
testComplete();
}
});
client.setOptionHandler(new SimpleOptionHandler(47, false, true, false, false));
client.connect("localhost", 4000);
await();
}
@Test
public void testReceiveBinary() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
server.start(() -> new TelnetHandler() {
@Override
protected void onOpen(TelnetConnection conn) {
conn.writeDoOption(Option.BINARY);
}
@Override
protected void onSendBinary(boolean binary) {
if (binary) {
fail("Was not expecting a do for binary option");
}
}
@Override
protected void onReceiveBinary(boolean binary) {
if (binary) {
latch.countDown();
} else {
fail("Was not expecting a won't for binary option");
}
}
@Override
protected void onData(byte[] data) {
assertEquals(1, data.length);
assertEquals((byte) -1, data[0]);
testComplete();
}
});
client.setOptionHandler(new SimpleOptionHandler(0, false, false, true, false));
client.connect("localhost", 4000);
latch.await();
client.writeDirectAndFlush((byte)-1, (byte)-1);
await();
}
@Test
public void testSendBinary() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
server.start(new Supplier<TelnetHandler>() {
@Override
public TelnetHandler get() {
return new TelnetHandler() {
private TelnetConnection conn;
@Override
protected void onOpen(TelnetConnection conn) {
this.conn = conn;
conn.writeWillOption(Option.BINARY);
}
@Override
protected void onSendBinary(boolean binary) {
if (binary) {
conn.write(new byte[]{'h', 'e', 'l', 'l', 'o', -1});
latch.countDown();
} else {
fail("Was not expecting a don't for binary option");
}
}
@Override
protected void onReceiveBinary(boolean binary) {
if (binary) {
fail("Was not expecting a will for binary option");
}
}
};
}
});
client.setOptionHandler(new SimpleOptionHandler(0, false, false, false, true));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
client.client.registerSpyStream(baos);
client.connect("localhost", 4000);
latch.await();
Reader reader = new InputStreamReader(client.client.getInputStream());
int expectedLen = 5;
char[] hello = new char[expectedLen];
int num = 0;
while (num < expectedLen) {
int read = reader.read(hello, num, expectedLen - num);
if (read == -1) {
fail("Unexpected");
}
num += read;
}
assertEquals(5, num);
assertEquals("hello", new String(hello));
byte[] data = baos.toByteArray();
assertEquals(10, data.length);
assertEquals((byte)'h', data[3]);
assertEquals((byte)'e', data[4]);
assertEquals((byte)'l', data[5]);
assertEquals((byte)'l', data[6]);
assertEquals((byte)'o', data[7]);
assertEquals((byte)-1, data[8]);
assertEquals((byte)-1, data[9]);
}
}