diff options
| author | Kenny Root <kenny@the-b.org> | 2009-06-02 01:20:39 +0000 | 
|---|---|---|
| committer | Kenny Root <kenny@the-b.org> | 2009-06-02 01:20:39 +0000 | 
| commit | 0b1f5e775236a489efa28f7c2b8ebecd7c05e758 (patch) | |
| tree | 0ca456e6fcaeb1a045122c4fcc70a0f816f813d2 /src | |
| parent | 3bfe899377534203ff3ee3c06c515733b74b78d9 (diff) | |
| download | connectbot-0b1f5e775236a489efa28f7c2b8ebecd7c05e758.tar.gz connectbot-0b1f5e775236a489efa28f7c2b8ebecd7c05e758.tar.bz2 connectbot-0b1f5e775236a489efa28f7c2b8ebecd7c05e758.zip  | |
Refactor relay into separate class
git-svn-id: https://connectbot.googlecode.com/svn/trunk/connectbot@257 df292f66-193f-0410-a5fc-6d59da041ff2
Diffstat (limited to 'src')
| -rw-r--r-- | src/org/connectbot/service/Relay.java | 218 | ||||
| -rw-r--r-- | src/org/connectbot/service/TerminalBridge.java | 156 | 
2 files changed, 223 insertions, 151 deletions
diff --git a/src/org/connectbot/service/Relay.java b/src/org/connectbot/service/Relay.java new file mode 100644 index 0000000..f713210 --- /dev/null +++ b/src/org/connectbot/service/Relay.java @@ -0,0 +1,218 @@ +/* +	ConnectBot: simple, powerful, open-source SSH client for Android +	Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU General Public License for more details. + +	You should have received a copy of the GNU General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +package org.connectbot.service; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +import android.util.Log; + +import com.trilead.ssh2.ChannelCondition; +import com.trilead.ssh2.Session; + +import de.mud.terminal.vt320; + +/** + * @author Kenny Root + */ +public class Relay implements Runnable { +	private static final String TAG = "ConnectBot.Relay"; + +	private static final int BUFFER_SIZE = 4096; + +	private static final int CONDITIONS = +		ChannelCondition.STDOUT_DATA +		| ChannelCondition.STDERR_DATA +		| ChannelCondition.CLOSED +		| ChannelCondition.EOF; + +	private TerminalBridge bridge; + +	private CharsetDecoder decoder; +	private CharsetDecoder replacer; + +	private Session session; + +	private InputStream stdout; +	private InputStream stderr; + +	private vt320 buffer; + +	private ByteBuffer byteBuffer; +	private CharBuffer charBuffer; + +	private byte[] byteArray; +	private char[] charArray; + +	public Relay(TerminalBridge bridge, Session session, InputStream stdout, InputStream stderr, vt320 buffer, String encoding) { +		setCharset(encoding); +		this.bridge = bridge; +		this.session = session; +		this.stdout = stdout; +		this.stderr = stderr; +		this.buffer = buffer; +	} + +	public void setCharset(String encoding) { +		Charset charset = Charset.forName(encoding); + +		CharsetDecoder newCd = charset.newDecoder(); +		newCd.onUnmappableCharacter(CodingErrorAction.REPLACE); +		newCd.onMalformedInput(CodingErrorAction.REPORT); + +		CharsetDecoder newReplacer = charset.newDecoder(); +		newReplacer.onUnmappableCharacter(CodingErrorAction.REPLACE); +		newReplacer.onMalformedInput(CodingErrorAction.REPLACE); + +		decoder = newCd; +		replacer = newReplacer; +	} + +	public void run() { +		byteBuffer = ByteBuffer.allocate(BUFFER_SIZE); +		charBuffer = CharBuffer.allocate(BUFFER_SIZE); + +		byteArray = byteBuffer.array(); +		charArray = charBuffer.array(); + +		int bytesRead = 0; +		int offset = 0; + +		int newConditions = 0; + +		while((newConditions & ChannelCondition.CLOSED) == 0) { +			try { +				newConditions = session.waitForCondition(CONDITIONS, 0); +				if ((newConditions & ChannelCondition.STDOUT_DATA) != 0) { +					while (stdout.available() > 0) { +						bytesRead = offset + stdout.read(byteArray, offset, BUFFER_SIZE - offset); + +						byteBuffer.position(0); +						byteBuffer.limit(bytesRead); + +						CoderResult coderResult = decoder.decode(byteBuffer, charBuffer, true); + +						while (byteBuffer.position() < bytesRead) { +							if (coderResult.isMalformed()) +								skipMalformedBytes(bytesRead, coderResult); + +							coderResult = decoder.decode(byteBuffer, charBuffer, true); +						} + +						if (coderResult.isMalformed()) +							offset = discardMalformedBytes(bytesRead, coderResult); +						else { +							// No errors at all. +							buffer.putString(charArray, 0, charBuffer.position()); +							offset = 0; +						} + +						charBuffer.clear(); +					} + +					bridge.redraw(); +				} + +				if ((newConditions & ChannelCondition.STDERR_DATA) != 0) +					logAndDiscard(stderr); + +				if ((newConditions & ChannelCondition.EOF) != 0) { +					// The other side closed our channel, so let's disconnect. +					// TODO review whether any tunnel is in use currently. +					bridge.dispatchDisconnect(false); +					break; +				} +			} catch (IOException e) { +				Log.e(TAG, "Problem while handling incoming data in relay thread", e); +				break; +			} +		} +	} + +	/** +	 * @param stream +\	 * @throws IOException +	 */ +	private void logAndDiscard(InputStream stream) throws IOException { +		while (stream.available() > 0) { +			int n = stream.read(byteArray); +			byteBuffer.position(0); +			byteBuffer.limit(n); +			replacer.decode(byteBuffer, charBuffer, false); +			// TODO I don't know.. do we want this? We were ignoring it before +			Log.d(TAG, String.format("Read data from stream: %s", new String(charArray, 0, charBuffer.position()))); +			charBuffer.clear(); +		} +	} + +	/** +	 * @param n +	 * @param cr +	 * @return +	 */ +	private int discardMalformedBytes(int n, CoderResult cr) { +		int offset; +		/* If we still have malformed input, save the bytes for the next +		 * read and try to parse it again. +		 */ +		offset = n - byteBuffer.position() + cr.length(); +		System.arraycopy(byteArray, byteBuffer.position() - cr.length(), byteArray, 0, offset); +		Log.d(TAG, String.format("Copying out %d chars at %d: 0x%02x", +				offset, byteBuffer.position() - cr.length(), +				byteArray[byteBuffer.position() - cr.length()] +		)); +		return offset; +	} + +	/** +	 * @param numReadBytes +	 * @param errorResult +	 */ +	private void skipMalformedBytes(int numReadBytes, CoderResult errorResult) { +		int curpos = byteBuffer.position() - errorResult.length(); + +		if (curpos > 0) { +			/* There is good data before the malformed section, so +			 * pass this on immediately. +			 */ +			buffer.putString(charArray, 0, charBuffer.position()); +		} + +		byteBuffer.position(curpos); +		byteBuffer.limit(curpos + errorResult.length()); + +		charBuffer.clear(); +		replacer.decode(byteBuffer, charBuffer, true); + +		buffer.putString(charArray, 0, charBuffer.position()); + +		curpos += errorResult.length(); + +		byteBuffer.position(curpos); +		byteBuffer.limit(numReadBytes); + +		charBuffer.clear(); +	} +} diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java index 3cf0109..c12eeeb 100644 --- a/src/org/connectbot/service/TerminalBridge.java +++ b/src/org/connectbot/service/TerminalBridge.java @@ -21,12 +21,6 @@ package org.connectbot.service;  import java.io.IOException;  import java.io.InputStream;  import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CoderResult; -import java.nio.charset.CodingErrorAction;  import java.security.NoSuchAlgorithmException;  import java.security.PrivateKey;  import java.security.PublicKey; @@ -62,7 +56,6 @@ import android.view.KeyEvent;  import android.view.View;  import android.view.View.OnKeyListener; -import com.trilead.ssh2.ChannelCondition;  import com.trilead.ssh2.Connection;  import com.trilead.ssh2.ConnectionInfo;  import com.trilead.ssh2.ConnectionMonitor; @@ -92,8 +85,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal  	public final static String TAG = TerminalBridge.class.toString(); -	private final static int BUFFER_SIZE = 4096; -  	public final static int DEFAULT_FONT_SIZE = 10;  	public static final String AUTH_PUBLICKEY = "publickey", @@ -123,7 +114,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal  	private InputStream stderr; -	private Thread relay; +	private Relay relay;  	private final String emulation;  	private final int scrollback; @@ -195,144 +186,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal  	protected ConnectionInfo connectionInfo; -	/** -	 * @author kenny -	 * -	 */ -	private final class Relay implements Runnable { -		final String encoding = host.getEncoding(); - -		public void run() { -			final Charset charset = Charset.forName(encoding); - -			/* Set up character set decoder to report any byte sequences -			 * which are malformed so we can try to resume decoding it -			 * on the next packet received. -			 * -			 * UTF-8 byte sequences have a tendency to get truncated at -			 * times. -			 */ -			final CharsetDecoder cd = charset.newDecoder(); -			cd.onUnmappableCharacter(CodingErrorAction.REPLACE); -			cd.onMalformedInput(CodingErrorAction.REPORT); - -			final CharsetDecoder replacer = charset.newDecoder(); -			replacer.onUnmappableCharacter(CodingErrorAction.REPLACE); -			replacer.onMalformedInput(CodingErrorAction.REPLACE); - -			ByteBuffer bb = ByteBuffer.allocate(BUFFER_SIZE); -			CharBuffer cb = CharBuffer.allocate(BUFFER_SIZE); - -			final byte[] bba = bb.array(); -			final char[] cba = cb.array(); -			final byte[] tmpBuff = new byte[BUFFER_SIZE]; - -			int n = 0; -			int offset = 0; - -			int conditions = ChannelCondition.STDOUT_DATA -					| ChannelCondition.STDERR_DATA -					| ChannelCondition.CLOSED -					| ChannelCondition.EOF; -			int newConditions = 0; - -			while((newConditions & ChannelCondition.CLOSED) == 0) { -				try { -					newConditions = session.waitForCondition(conditions, 0); -					if ((newConditions & ChannelCondition.STDOUT_DATA) != 0) { -						while (stdout.available() > 0) { -							n = offset + stdout.read(bba, offset, BUFFER_SIZE - offset); - -							bb.position(0); -							bb.limit(n); - -							CoderResult cr = cd.decode(bb, cb, true); - -							if (cr.isMalformed()) { -								int curpos = bb.position() - cr.length(); - -								if (curpos > 0) { -									/* There is good data before the malformed section, so -									 * pass this on immediately. -									 */ -									((vt320)buffer).putString(cba, 0, cb.position()); -								} - -								while (bb.position() < n) { -									bb.position(curpos); -									bb.limit(curpos + cr.length()); - -									cb.clear(); -									replacer.decode(bb, cb, true); - -									((vt320) buffer).putString(cba, 0, cb.position()); - -									curpos += cr.length(); - -									bb.position(curpos); -									cb.limit(n); - -									cb.clear(); -									cr = cd.decode(bb, cb, true); -								} - -								if (cr.isMalformed()) { -									/* If we still have malformed input, save the bytes for the next -									 * read and try to parse it again. -									 */ -									offset = n - bb.position() + cr.length(); -									if ((bb.position() - cr.length()) < offset) { -										System.arraycopy(bba, bb.position() - cr.length(), tmpBuff, 0, offset); -										System.arraycopy(tmpBuff, 0, bba, 0, offset); -									} else { -										System.arraycopy(bba, bb.position() - cr.length(), bba, 0, offset); -									} -									Log.d(TAG, String.format("Copying out %d chars at %d: 0x%02x", -											offset, bb.position() - cr.length(), -											bba[bb.position() - cr.length()] -									)); -								} else { -									// After discarding the previous offset, we only have valid data. -									((vt320)buffer).putString(cba, 0, cb.position()); -									offset = 0; -								} -							} else { -								// No errors at all. -								((vt320)buffer).putString(cba, 0, cb.position()); -								offset = 0; -							} - -							cb.clear(); -						} -						redraw(); -					} - -					if ((newConditions & ChannelCondition.STDERR_DATA) != 0) { -						while (stderr.available() > 0) { -							n = stderr.read(bba); -							bb.position(0); -							bb.limit(n); -							replacer.decode(bb, cb, false); -							// TODO I don't know.. do we want this? We were ignoring it before -							Log.d(TAG, String.format("Read data from stderr: %s", new String(cba, 0, cb.position()))); -							cb.clear(); -						} -					} - -					if ((newConditions & ChannelCondition.EOF) != 0) { -						// The other side closed our channel, so let's disconnect. -						// TODO review whether any tunnel is in use currently. -						dispatchDisconnect(false); -						break; -					} -				} catch (IOException e) { -					Log.e(TAG, "Problem while handling incoming data in relay thread", e); -					break; -				} -			} -		} -	} -  	public class HostKeyVerifier implements ServerHostKeyVerifier {  		public boolean verifyServerHostKey(String hostname, int port, @@ -821,9 +674,10 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal  			stderr = session.getStderr();  			// create thread to relay incoming connection data to buffer -			relay = new Thread(new Relay()); -			relay.setName("Relay"); -			relay.start(); +			relay = new Relay(this, session, stdout, stderr, (vt320) buffer, host.getEncoding()); +			Thread relayThread = new Thread(relay); +			relayThread.setName("Relay"); +			relayThread.start();  			// force font-size to make sure we resizePTY as needed  			setFontSize(fontSize);  | 
