/* * Copyright 2015 Julien Viet * * 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 io.termd.core.telnet; import io.termd.core.TestBase; import org.apache.commons.net.telnet.InvalidTelnetOptionException; import org.apache.commons.net.telnet.TelnetClient; import org.apache.commons.net.telnet.TelnetOptionHandler; import org.junit.rules.ExternalResource; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.net.SocketException; import java.util.function.BiConsumer; /** * @author <a href="mailto:julien@julienviet.com">Julien Viet</a> */ public class TelnetClientRule extends ExternalResource { private Socket socket; private OutputStream directOutput; public TelnetClient client; public String assertReadString(int length) throws Exception { return new String(assertReadBytes(length), 0, length, "UTF-8"); } private void checkNotConnected() { if (client.isConnected()) { throw TestBase.failure("Already connected"); } } public TelnetClientRule registerNotifHandler(BiConsumer<Integer, Integer> handler) { checkNotConnected(); client.registerNotifHandler(handler::accept); return this; } public void setOptionHandler(TelnetOptionHandler handler) throws IOException, InvalidTelnetOptionException { checkNotConnected(); client.addOptionHandler(handler); } public TelnetClientRule connect(String host, int port) throws IOException { checkNotConnected(); client.connect(host, port); return this; } public void disconnect() throws IOException { disconnect(true); } /** * Check if the client is disconnected, this affects the input stream of the socket by reading bytes from it. * * @return if the client is disconnected */ public boolean checkDisconnected() { try { return socket != null && socket.getInputStream().read() == -1; } catch (SocketException e) { if (e.getMessage().equals("Connection reset")) { return true; } throw TestBase.failure(e); } catch (IOException e) { throw TestBase.failure(e); } } public void disconnect(boolean clean) throws IOException { if (client.isConnected()) { if (clean) { client.disconnect(); } else { socket.close(); } } } public byte[] assertReadBytes(int length) throws Exception { byte[] bytes = new byte[length]; while (length > 0) { int i = client.getInputStream().read(bytes, bytes.length - length, length); if (i == -1) { throw TestBase.failure("Closed"); } length -= i; } return bytes; } public TelnetClientRule write(byte... bytes) throws IOException { client.getOutputStream().write(bytes); return this; } public TelnetClientRule flush() throws IOException { client.getOutputStream().flush(); return this; } public void writeDirect(byte... bytes) throws IOException { synchronized (client) { directOutput.write(bytes); } } public void writeDirectAndFlush(byte... bytes) throws IOException { synchronized (client) { directOutput.write(bytes); directOutput.flush(); } } @Override protected void before() throws Throwable { client = new TelnetClient() { @Override protected void _connectAction_() throws IOException { super._connectAction_(); socket = _socket_; directOutput = _output_; setKeepAlive(false); } }; } @Override protected void after() { if (client != null && client.isConnected()) { try { client.disconnect(); } catch (Exception ignore) { } finally { client = null; } } } }