package net.i2p.data.i2np;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import net.i2p.router.RouterContext;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* The I2NPMessageReader reads an InputStream (using
* {@link I2NPMessageHandler I2NPMessageHandler}) and passes out events to a registered
* listener, where events are either messages being received, exceptions being
* thrown, or the connection being closed. Routers should use this rather
* than read from the stream themselves.
*
* Deprecated - unused.
* This was used by the old TCP transport.
* Both the NTCP and SSU transports provide encapsulation
* of I2NP messages, so they use I2NPMessageHandlers directly.
* If we ever add a transport that does not provide encapsulation,
* this will be useful again.
*
* @deprecated unused
*
* @author jrandom
*/
@Deprecated
public class I2NPMessageReader {
private Log _log;
private RouterContext _context;
private InputStream _stream;
private I2NPMessageEventListener _listener;
private I2NPMessageReaderRunner _reader;
private Thread _readerThread;
public I2NPMessageReader(RouterContext context, InputStream stream, I2NPMessageEventListener lsnr) {
this(context, stream, lsnr, "I2NP Reader");
}
public I2NPMessageReader(RouterContext context, InputStream stream, I2NPMessageEventListener lsnr, String name) {
_context = context;
_log = context.logManager().getLog(I2NPMessageReader.class);
_stream = stream;
setListener(lsnr);
_reader = new I2NPMessageReaderRunner();
_readerThread = new I2PThread(_reader);
_readerThread.setName(name);
_readerThread.setDaemon(true);
}
public void setListener(I2NPMessageEventListener lsnr) { _listener = lsnr; }
public I2NPMessageEventListener getListener() { return _listener; }
/**
* Instruct the reader to begin reading messages off the stream
*
*/
public void startReading() { _readerThread.start(); }
/**
* Have the already started reader pause its reading indefinitely
* @deprecated unused
*/
@Deprecated
public void pauseReading() { _reader.pauseRunner(); }
/**
* Resume reading after a pause
* @deprecated unused
*/
@Deprecated
public void resumeReading() { _reader.resumeRunner(); }
/**
* Cancel reading.
*
*/
public void stopReading() { _reader.cancelRunner(); }
/**
* Defines the different events the reader produces while reading the stream
*
*/
public static interface I2NPMessageEventListener {
/**
* Notify the listener that a message has been received from the given
* reader
*
*/
public void messageReceived(I2NPMessageReader reader, I2NPMessage message, long msToRead, int bytesRead);
/**
* Notify the listener that an exception was thrown while reading from the given
* reader
*
*/
public void readError(I2NPMessageReader reader, Exception error);
/**
* Notify the listener that the stream the given reader was running off
* closed
*
*/
public void disconnected(I2NPMessageReader reader);
}
private class I2NPMessageReaderRunner implements Runnable {
private boolean _doRun;
private boolean _stayAlive;
private I2NPMessageHandler _handler;
public I2NPMessageReaderRunner() {
_doRun = true;
_stayAlive = true;
_handler = new I2NPMessageHandler(_context);
}
/** deprecated unused */
public void pauseRunner() { _doRun = false; }
/** deprecated unused */
public void resumeRunner() { _doRun = true; }
public void cancelRunner() {
_doRun = false;
_stayAlive = false;
}
public void run() {
while (_stayAlive) {
while (_doRun) {
while (!_context.throttle().acceptNetworkMessage()) {
try { Thread.sleep(500 + _context.random().nextInt(512)); } catch (InterruptedException ie) {}
}
// do read
try {
I2NPMessage msg = _handler.readMessage(_stream);
if (msg != null) {
long msToRead = _handler.getLastReadTime();
int bytesRead = _handler.getLastSize();
//msToRead += injectLag(bytesRead);
_listener.messageReceived(I2NPMessageReader.this, msg, msToRead, bytesRead);
}
} catch (I2NPMessageException ime) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error handling message", ime);
_listener.readError(I2NPMessageReader.this, ime);
_listener.disconnected(I2NPMessageReader.this);
cancelRunner();
} catch (InterruptedIOException iioe) {
// not all I2NPMessageReaders support this, but some run off sockets which throw
// SocketTimeoutExceptions or InterruptedIOExceptions
if (_log.shouldLog(Log.INFO))
_log.info("Disconnecting due to inactivity", iioe);
_listener.disconnected(I2NPMessageReader.this);
cancelRunner();
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("IO Error handling message", ioe);
_listener.disconnected(I2NPMessageReader.this);
cancelRunner();
} catch (RuntimeException e) {
_log.log(Log.CRIT, "error reading msg!", e);
_listener.readError(I2NPMessageReader.this, e);
_listener.disconnected(I2NPMessageReader.this);
cancelRunner();
}
}
// ??? unused
if (_stayAlive && !_doRun) {
// pause .5 secs when we're paused
try { Thread.sleep(500); } catch (InterruptedException ie) {}
}
}
// boom bye bye bad bwoy
}
/****
private final long injectLag(int size) {
if (true) {
return 0;
} else {
boolean shouldLag = _context.random().nextInt(1000) > size;
if (!shouldLag) return 0;
long readLag = getReadLag();
if (readLag > 0) {
long lag = _context.random().nextLong(readLag);
if (lag > 0) {
try { Thread.sleep(lag); } catch (InterruptedException ie) {}
return lag;
} else {
return 0;
}
} else {
return 0;
}
}
}
private final long getReadLag() {
return _context.getProperty("router.injectLagMs", 0L);
}
****/
}
}