Files
i2p.i2p/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java
jrandom 48cdf17a4f * revamped locking to block on flush and close until all of the
packets through that point have been ACKed, throwing an
  InterruptedIOException if there was a writeTimeout or an IOException
  if the con failed
* revamped the ack/nack field settings to ack as much as possible
* handle some strange timeout/resend errors on connection
* pass 1/2rtt as the packet 'optional delay' field, and use that to
  schedule the ack time (the 'last' messages in a window set the
  optional delay to 0, asking for immediate ack of all received)
* increase the optional delay to 2 bytes (#ms to delay)
* inject random failures and delays if configured to do so in
  PacketHandler.choke
* fix up the window size adjustment (increment on ack, /= 2 on resend)
* use the highest RTT in the new RTT calculation so that we fit more
  in (via SACK)
* fix up the SACK handling (duh)
* revise the resend time calculation
2004-10-28 02:03:38 +00:00

140 lines
4.8 KiB
Java

package net.i2p.client.streaming;
import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;
/**
* Receive new connection attempts
*/
class ConnectionHandler {
private I2PAppContext _context;
private Log _log;
private ConnectionManager _manager;
private List _synQueue;
private boolean _active;
private int _acceptTimeout;
/** max time after receiveNewSyn() and before the matched accept() */
private static final int DEFAULT_ACCEPT_TIMEOUT = 3*1000;
/** Creates a new instance of ConnectionHandler */
public ConnectionHandler(I2PAppContext context, ConnectionManager mgr) {
_context = context;
_log = context.logManager().getLog(ConnectionHandler.class);
_manager = mgr;
_synQueue = new ArrayList(5);
_active = false;
_acceptTimeout = DEFAULT_ACCEPT_TIMEOUT;
}
public void setActive(boolean active) { _active = active; }
public boolean getActive() { return _active; }
public void receiveNewSyn(Packet packet) {
if (!_active) {
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping new SYN request, as we're not listening");
sendReset(packet);
return;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive new SYN: " + packet + ": timeout in " + _acceptTimeout);
SimpleTimer.getInstance().addEvent(new TimeoutSyn(packet), _acceptTimeout);
synchronized (_synQueue) {
_synQueue.add(packet);
_synQueue.notifyAll();
}
}
public Connection accept(long timeoutMs) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Accept("+ timeoutMs+") called");
long expiration = timeoutMs;
if (expiration > 0)
expiration += _context.clock().now();
Packet syn = null;
synchronized (_synQueue) {
while ( _active && (_synQueue.size() <= 0) ) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Accept("+ timeoutMs+"): active=" + _active + " queue: " + _synQueue.size());
if (timeoutMs <= 0) {
try { _synQueue.wait(); } catch (InterruptedException ie) {}
} else {
long remaining = expiration - _context.clock().now();
if (remaining < 0)
break;
try { _synQueue.wait(remaining); } catch (InterruptedException ie) {}
}
}
if (_active && _synQueue.size() > 0) {
syn = (Packet)_synQueue.remove(0);
}
}
if (syn != null) {
// deal with forged / invalid syn packets
Connection con = _manager.receiveConnection(syn);
if (con != null) {
return con;
} else if (timeoutMs > 0) {
long remaining = expiration - _context.clock().now();
if (remaining <= 0) {
return null;
} else {
return accept(remaining);
}
} else {
return accept(timeoutMs);
}
} else {
return null;
}
}
private void sendReset(Packet packet) {
boolean ok = packet.verifySignature(_context, packet.getOptionalFrom(), null);
if (!ok) {
if (_log.shouldLog(Log.WARN))
_log.warn("Received a spoofed SYN packet: they said they were " + packet.getOptionalFrom());
return;
}
PacketLocal reply = new PacketLocal(_context, packet.getOptionalFrom());
reply.setFlag(Packet.FLAG_RESET);
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
reply.setAckThrough(packet.getSequenceNum());
reply.setSendStreamId(packet.getReceiveStreamId());
reply.setReceiveStreamId(null);
reply.setOptionalFrom(_manager.getSession().getMyDestination());
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending RST: " + reply + " because of " + packet);
// this just sends the packet - no retries or whatnot
_manager.getPacketQueue().enqueue(reply);
}
private class TimeoutSyn implements SimpleTimer.TimedEvent {
private Packet _synPacket;
public TimeoutSyn(Packet packet) {
_synPacket = packet;
}
public void timeReached() {
boolean removed = false;
synchronized (_synQueue) {
removed = _synQueue.remove(_synPacket);
}
if (removed) {
// timeout - send RST
sendReset(_synPacket);
} else {
// handled. noop
}
}
}
}