package com.github.sdbg.debug.core.internal.forwarder;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.Channel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.WritableByteChannel;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Tunnel {
private Logger logger;
private String id;
private ByteChannel leftChannel, rightChannel;
private ByteBuffer leftToRight, rightToLeft;
public Tunnel(Logger logger, String id) {
this.logger = logger;
this.id = id;
}
public void close() {
close(leftChannel);
close(rightChannel);
}
public ByteChannel getLeftChannel() {
return leftChannel;
}
public ByteBuffer getLeftToRight() {
if (leftToRight == null) {
leftToRight = ByteBuffer.allocate(8192);
}
return leftToRight;
}
public ByteChannel getRightChannel() {
return rightChannel;
}
public ByteBuffer getRightToLeft() {
if (rightToLeft == null) {
rightToLeft = ByteBuffer.allocate(8192);
}
return rightToLeft;
}
public void setLeftChannel(ByteChannel leftChannel) {
this.leftChannel = leftChannel;
}
public void setRightChannel(ByteChannel rightChannel) {
this.rightChannel = rightChannel;
}
public boolean spool(SelectionKey key) throws IOException {
if (key.isReadable()) {
if (key.channel() == leftChannel) {
return spoolLeftToRight(key.selector());
} else {
return spoolRightToLeft(key.selector());
}
} else if (key.isWritable()) {
if (key.channel() == rightChannel) {
return spoolLeftToRight(key.selector());
} else {
return spoolRightToLeft(key.selector());
}
} else {
return true;
}
}
public boolean spoolLeftToRight(Selector selector) throws IOException {
return spool(selector, leftChannel, rightChannel, getLeftToRight());
}
public boolean spoolRightToLeft(Selector selector) throws IOException {
return spool(selector, rightChannel, leftChannel, getRightToLeft());
}
private void close(Channel channel) {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
// Best effort
}
}
}
private boolean spool(Selector selector, ReadableByteChannel from, WritableByteChannel to,
ByteBuffer buff) throws IOException {
int read = 0, written = 0;
do {
if (read > -1 && buff.hasRemaining()) {
int oldPos = buff.position();
read = from.read(buff);
if (logger != null && logger.isLoggable(Level.FINEST) && oldPos < buff.position()) {
logger.finest("Tunnel " + id + " spooling:\n" + "==== BEGIN DUMP ====\n"
+ new String(buff.array(), oldPos, buff.position()) + "\n==== END DUMP ====");
}
} else {
read = 0;
}
if (buff.position() > 0) {
buff.flip();
written = to.write(buff);
buff.compact();
} else {
written = 0;
}
} while (read > 0 || written > 0);
SelectionKey fromKey = null;
if (selector != null && from instanceof SelectableChannel) {
fromKey = ((SelectableChannel) from).keyFor(selector);
}
if (fromKey != null) {
if (!buff.hasRemaining()) {
fromKey.interestOps(fromKey.interestOps() & ~SelectionKey.OP_READ);
} else {
fromKey.interestOps(fromKey.interestOps() | SelectionKey.OP_READ);
}
}
SelectionKey toKey = null;
if (selector != null && to instanceof SelectableChannel) {
toKey = ((SelectableChannel) to).keyFor(selector);
}
if (toKey != null) {
if (buff.position() == 0) {
toKey.interestOps(toKey.interestOps() & ~SelectionKey.OP_WRITE);
} else {
toKey.interestOps(toKey.interestOps() | SelectionKey.OP_WRITE);
}
}
return (read > -1 || buff.position() > 0) && written > -1;
}
}