package net.i2p.client.impl;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.client.I2PSessionMuxedListener;
import net.i2p.util.Log;
/*
* public domain
*/
/**
* Implement multiplexing with a 1-byte 'protocol' and a two-byte 'port'.
* Listeners register with either addListener() or addMuxedListener(),
* depending on whether they want to hear about the
* protocol, from port, and to port for every received message.
*
* messageAvailable() only calls one listener, not all that apply.
* The others call all listeners.
*
* @author zzz
*/
public class I2PSessionDemultiplexer implements I2PSessionMuxedListener {
private final Log _log;
private final Map<Integer, I2PSessionMuxedListener> _listeners;
public I2PSessionDemultiplexer(I2PAppContext ctx) {
_log = ctx.logManager().getLog(I2PSessionDemultiplexer.class);
_listeners = new ConcurrentHashMap<Integer, I2PSessionMuxedListener>();
}
/** unused */
public void messageAvailable(I2PSession session, int msgId, long size) {}
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport ) {
I2PSessionMuxedListener l = findListener(proto, toport);
if (l != null)
l.messageAvailable(session, msgId, size, proto, fromport, toport);
else {
// no listener, throw it out
if (_listeners.isEmpty()) {
if (_log.shouldLog(Log.WARN))
_log.warn("No listeners for incoming message");
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("No listener found for proto: " + proto + " port: " + toport + " msg id: " + msgId +
" from pool of " + _listeners.size() + " listeners");
}
try {
session.receiveMessage(msgId);
} catch (I2PSessionException ise) {}
}
}
public void reportAbuse(I2PSession session, int severity) {
for (I2PSessionMuxedListener l : _listeners.values())
l.reportAbuse(session, severity);
}
public void disconnected(I2PSession session) {
for (I2PSessionMuxedListener l : _listeners.values())
l.disconnected(session);
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
for (I2PSessionMuxedListener l : _listeners.values())
l.errorOccurred(session, message, error);
}
/**
* For those that don't need to hear about the protocol and ports
* in messageAvailable()
* (Streaming lib)
*/
public void addListener(I2PSessionListener l, int proto, int port) {
I2PSessionListener old = _listeners.put(key(proto, port), new NoPortsListener(l));
if (old != null && _log.shouldLog(Log.WARN))
_log.warn("Listener " + l + " replaces " + old + " for proto: " + proto + " port: " + port);
}
/**
* For those that do care
* UDP perhaps
*/
public void addMuxedListener(I2PSessionMuxedListener l, int proto, int port) {
I2PSessionListener old = _listeners.put(key(proto, port), l);
if (old != null && _log.shouldLog(Log.WARN))
_log.warn("Listener " + l + " replaces " + old + " for proto: " + proto + " port: " + port);
}
public void removeListener(int proto, int port) {
_listeners.remove(key(proto, port));
}
/** find the one listener that most specifically matches the request */
private I2PSessionMuxedListener findListener(int proto, int port) {
I2PSessionMuxedListener rv = getListener(proto, port);
if (rv != null) return rv;
if (port != I2PSession.PORT_ANY) { // try any port
rv = getListener(proto, I2PSession.PORT_ANY);
if (rv != null) return rv;
}
if (proto != I2PSession.PROTO_ANY) { // try any protocol
rv = getListener(I2PSession.PROTO_ANY, port);
if (rv != null) return rv;
}
if (proto != I2PSession.PROTO_ANY && port != I2PSession.PORT_ANY) { // try default
rv = getListener(I2PSession.PROTO_ANY, I2PSession.PORT_ANY);
}
return rv;
}
private I2PSessionMuxedListener getListener(int proto, int port) {
return _listeners.get(key(proto, port));
}
private static Integer key(int proto, int port) {
return Integer.valueOf(((port << 8) & 0xffff00) | proto);
}
/** for those that don't care about proto and ports */
private static class NoPortsListener implements I2PSessionMuxedListener {
private I2PSessionListener _l;
public NoPortsListener(I2PSessionListener l) {
_l = l;
}
public void messageAvailable(I2PSession session, int msgId, long size) {
throw new IllegalArgumentException("no");
}
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport) {
_l.messageAvailable(session, msgId, size);
}
public void reportAbuse(I2PSession session, int severity) {
_l.reportAbuse(session, severity);
}
public void disconnected(I2PSession session) {
_l.disconnected(session);
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
_l.errorOccurred(session, message, error);
}
}
}