2005-09-25 Complication
* Better i2paddresshelper handling in the I2PTunnel httpclient, plus a new conflict resolution page if the i2paddresshelper parameter differs from an existing name to destination mapping. 2005-09-25 jrandom * Fix a long standing streaming lib bug (in the inactivity detection code) * Improved handling of initial streaming lib packet retransmissions to kill the "lost first packet" bug (where a page shows up with the first few KB missing) * Add support for initial window sizes greater than 1 - useful for eepsites to transmit e.g. 4 packets full of data along with the initial ACK, thereby cutting down on the rtt latency. The congestion window size can and does still shrink down to 1 packet though. * Adjusted the streaming lib retransmission calculation algorithm to be more TCP-like.
This commit is contained in:
@ -96,6 +96,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
|||||||
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel")
|
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel")
|
||||||
.getBytes();
|
.getBytes();
|
||||||
|
|
||||||
|
private final static byte[] ERR_AHELPER_CONFLICT =
|
||||||
|
("HTTP/1.1 409 Conflict\r\n"+
|
||||||
|
"Content-Type: text/html; charset=iso-8859-1\r\n"+
|
||||||
|
"Cache-control: no-cache\r\n"+
|
||||||
|
"\r\n"+
|
||||||
|
"<html><body><H1>I2P ERROR: Destination key conflict</H1>"+
|
||||||
|
"The addresshelper link you followed specifies a different destination key "+
|
||||||
|
"than a host entry in your host database. "+
|
||||||
|
"Someone could be trying to impersonate another eepsite, "+
|
||||||
|
"or people have given two eepsites identical names.<P/>"+
|
||||||
|
"You can resolve the conflict by considering which key you trust, "+
|
||||||
|
"and either discarding the addresshelper link, "+
|
||||||
|
"discarding the host entry from your host database, "+
|
||||||
|
"or naming one of them differently.<P/>")
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
/** used to assign unique IDs to the threads / clients. no logic or functionality */
|
/** used to assign unique IDs to the threads / clients. no logic or functionality */
|
||||||
private static volatile long __clientId = 0;
|
private static volatile long __clientId = 0;
|
||||||
|
|
||||||
@ -243,50 +259,102 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
|||||||
|
|
||||||
// Quick hack for foo.bar.i2p
|
// Quick hack for foo.bar.i2p
|
||||||
if (host.toLowerCase().endsWith(".i2p")) {
|
if (host.toLowerCase().endsWith(".i2p")) {
|
||||||
|
// Destination gets the host name
|
||||||
destination = host;
|
destination = host;
|
||||||
|
// Host becomes the destination key
|
||||||
host = getHostName(destination);
|
host = getHostName(destination);
|
||||||
if ( (host != null) && ("i2p".equals(host)) ) {
|
|
||||||
int pos2;
|
|
||||||
if ((pos2 = request.indexOf("?")) != -1) {
|
|
||||||
// Try to find an address helper in the fragments
|
|
||||||
// and split the request into it's component parts for rebuilding later
|
|
||||||
String fragments = request.substring(pos2 + 1);
|
|
||||||
String uriPath = request.substring(0, pos2);
|
|
||||||
pos2 = fragments.indexOf(" ");
|
|
||||||
String protocolVersion = fragments.substring(pos2 + 1);
|
|
||||||
String urlEncoding = "";
|
|
||||||
fragments = fragments.substring(0, pos2);
|
|
||||||
fragments = fragments + "&";
|
|
||||||
String fragment;
|
|
||||||
while(fragments.length() > 0) {
|
|
||||||
pos2 = fragments.indexOf("&");
|
|
||||||
fragment = fragments.substring(0, pos2);
|
|
||||||
fragments = fragments.substring(pos2 + 1);
|
|
||||||
if (fragment.startsWith("i2paddresshelper")) {
|
|
||||||
pos2 = fragment.indexOf("=");
|
|
||||||
if (pos2 >= 0) {
|
|
||||||
addressHelpers.put(destination,fragment.substring(pos2 + 1));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// append each fragment unless it's the address helper
|
|
||||||
if ("".equals(urlEncoding)) {
|
|
||||||
urlEncoding = "?" + fragment;
|
|
||||||
} else {
|
|
||||||
urlEncoding = urlEncoding + "&" + fragment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// reconstruct the request minus the i2paddresshelper GET var
|
|
||||||
request = uriPath + urlEncoding + " " + protocolVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
String addressHelper = (String) addressHelpers.get(destination);
|
int pos2;
|
||||||
if (addressHelper != null) {
|
if ((pos2 = request.indexOf("?")) != -1) {
|
||||||
destination = addressHelper;
|
// Try to find an address helper in the fragments
|
||||||
host = getHostName(destination);
|
// and split the request into it's component parts for rebuilding later
|
||||||
ahelper = 1;
|
String ahelperKey = null;
|
||||||
|
boolean ahelperConflict = false;
|
||||||
|
|
||||||
|
String fragments = request.substring(pos2 + 1);
|
||||||
|
String uriPath = request.substring(0, pos2);
|
||||||
|
pos2 = fragments.indexOf(" ");
|
||||||
|
String protocolVersion = fragments.substring(pos2 + 1);
|
||||||
|
String urlEncoding = "";
|
||||||
|
fragments = fragments.substring(0, pos2);
|
||||||
|
String initialFragments = fragments;
|
||||||
|
fragments = fragments + "&";
|
||||||
|
String fragment;
|
||||||
|
while(fragments.length() > 0) {
|
||||||
|
pos2 = fragments.indexOf("&");
|
||||||
|
fragment = fragments.substring(0, pos2);
|
||||||
|
fragments = fragments.substring(pos2 + 1);
|
||||||
|
|
||||||
|
// Fragment looks like addresshelper key
|
||||||
|
if (fragment.startsWith("i2paddresshelper=")) {
|
||||||
|
pos2 = fragment.indexOf("=");
|
||||||
|
ahelperKey = fragment.substring(pos2 + 1);
|
||||||
|
|
||||||
|
// Key contains data, lets not ignore it
|
||||||
|
if (ahelperKey != null) {
|
||||||
|
|
||||||
|
// Host resolvable only with addresshelper
|
||||||
|
if ( (host == null) || ("i2p".equals(host)) )
|
||||||
|
{
|
||||||
|
// Cannot check, use addresshelper key
|
||||||
|
addressHelpers.put(destination,ahelperKey);
|
||||||
|
} else {
|
||||||
|
// Host resolvable from database, verify addresshelper key
|
||||||
|
// Silently bypass correct keys, otherwise alert
|
||||||
|
if (!host.equals(ahelperKey))
|
||||||
|
{
|
||||||
|
// Conflict: handle when URL reconstruction done
|
||||||
|
ahelperConflict = true;
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + host + "], specified key [" + ahelperKey + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Other fragments, just pass along
|
||||||
|
// Append each fragment to urlEncoding
|
||||||
|
if ("".equals(urlEncoding)) {
|
||||||
|
urlEncoding = "?" + fragment;
|
||||||
|
} else {
|
||||||
|
urlEncoding = urlEncoding + "&" + fragment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Reconstruct the request minus the i2paddresshelper GET var
|
||||||
|
request = uriPath + urlEncoding + " " + protocolVersion;
|
||||||
|
|
||||||
|
// Did addresshelper key conflict?
|
||||||
|
if (ahelperConflict)
|
||||||
|
{
|
||||||
|
String str;
|
||||||
|
byte[] header;
|
||||||
|
str = FileUtil.readTextFile("docs/ahelper-conflict-header.ht", 100, true);
|
||||||
|
if (str != null) header = str.getBytes();
|
||||||
|
else header = ERR_AHELPER_CONFLICT;
|
||||||
|
|
||||||
|
if (out != null) {
|
||||||
|
long alias = I2PAppContext.getGlobalContext().random().nextLong();
|
||||||
|
String trustedURL = protocol + uriPath + urlEncoding;
|
||||||
|
String conflictURL = protocol + alias + ".i2p/?" + initialFragments;
|
||||||
|
out.write(header);
|
||||||
|
out.write(("To visit the destination in your host database, click <a href=\"" + trustedURL + "\">here</a>. To visit the conflicting addresshelper link by temporarily giving it a random alias, click <a href=\"" + conflictURL + "\">here</a>.<P/>").getBytes());
|
||||||
|
out.write("</div><p><i>I2P HTTP Proxy Server<br>Generated on: ".getBytes());
|
||||||
|
out.write(new Date().toString().getBytes());
|
||||||
|
out.write("</i></body></html>\n".getBytes());
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
s.close();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String addressHelper = (String) addressHelpers.get(destination);
|
||||||
|
if (addressHelper != null) {
|
||||||
|
destination = addressHelper;
|
||||||
|
host = getHostName(destination);
|
||||||
|
ahelper = 1;
|
||||||
|
}
|
||||||
|
|
||||||
line = method + " " + request.substring(pos);
|
line = method + " " + request.substring(pos);
|
||||||
} else if (host.indexOf(".") != -1) {
|
} else if (host.indexOf(".") != -1) {
|
||||||
// The request must be forwarded to a WWW proxy
|
// The request must be forwarded to a WWW proxy
|
||||||
|
@ -72,7 +72,7 @@ public class Connection {
|
|||||||
private long _lifetimeDupMessageSent;
|
private long _lifetimeDupMessageSent;
|
||||||
private long _lifetimeDupMessageReceived;
|
private long _lifetimeDupMessageReceived;
|
||||||
|
|
||||||
public static final long MAX_RESEND_DELAY = 5*1000;
|
public static final long MAX_RESEND_DELAY = 8*1000;
|
||||||
public static final long MIN_RESEND_DELAY = 3*1000;
|
public static final long MIN_RESEND_DELAY = 3*1000;
|
||||||
|
|
||||||
/** wait up to 5 minutes after disconnection so we can ack/close packets */
|
/** wait up to 5 minutes after disconnection so we can ack/close packets */
|
||||||
@ -257,13 +257,13 @@ public class Connection {
|
|||||||
if (packet.isFlagSet(Packet.FLAG_CLOSE) || (remaining < 2)) {
|
if (packet.isFlagSet(Packet.FLAG_CLOSE) || (remaining < 2)) {
|
||||||
packet.setOptionalDelay(0);
|
packet.setOptionalDelay(0);
|
||||||
} else {
|
} else {
|
||||||
int delay = _options.getRTT() / 2;
|
int delay = _options.getRTO() / 2;
|
||||||
packet.setOptionalDelay(delay);
|
packet.setOptionalDelay(delay);
|
||||||
_log.debug("Requesting ack delay of " + delay + "ms for packet " + packet);
|
_log.debug("Requesting ack delay of " + delay + "ms for packet " + packet);
|
||||||
}
|
}
|
||||||
packet.setFlag(Packet.FLAG_DELAY_REQUESTED);
|
packet.setFlag(Packet.FLAG_DELAY_REQUESTED);
|
||||||
|
|
||||||
long timeout = _options.getRTT() + MIN_RESEND_DELAY;
|
long timeout = _options.getRTO();
|
||||||
if (timeout > MAX_RESEND_DELAY)
|
if (timeout > MAX_RESEND_DELAY)
|
||||||
timeout = MAX_RESEND_DELAY;
|
timeout = MAX_RESEND_DELAY;
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
@ -308,7 +308,7 @@ public class Connection {
|
|||||||
|
|
||||||
List ackPackets(long ackThrough, long nacks[]) {
|
List ackPackets(long ackThrough, long nacks[]) {
|
||||||
if (nacks == null) {
|
if (nacks == null) {
|
||||||
_highestAckedThrough = ackThrough;
|
_highestAckedThrough = ackThrough;
|
||||||
} else {
|
} else {
|
||||||
long lowest = -1;
|
long lowest = -1;
|
||||||
for (int i = 0; i < nacks.length; i++) {
|
for (int i = 0; i < nacks.length; i++) {
|
||||||
@ -463,7 +463,9 @@ public class Connection {
|
|||||||
_receiver.destroy();
|
_receiver.destroy();
|
||||||
if (_activityTimer != null)
|
if (_activityTimer != null)
|
||||||
SimpleTimer.getInstance().removeEvent(_activityTimer);
|
SimpleTimer.getInstance().removeEvent(_activityTimer);
|
||||||
_activityTimer = null;
|
//_activityTimer = null;
|
||||||
|
if (_inputStream != null)
|
||||||
|
_inputStream.streamErrorOccurred(new IOException("disconnected!"));
|
||||||
|
|
||||||
if (_disconnectScheduledOn < 0) {
|
if (_disconnectScheduledOn < 0) {
|
||||||
_disconnectScheduledOn = _context.clock().now();
|
_disconnectScheduledOn = _context.clock().now();
|
||||||
@ -695,11 +697,19 @@ public class Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void resetActivityTimer() {
|
private void resetActivityTimer() {
|
||||||
if (_options.getInactivityTimeout() <= 0) return;
|
if (_options.getInactivityTimeout() <= 0) {
|
||||||
if (_activityTimer == null) return;
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Resetting the inactivity timer, but its gone!", new Exception("where did it go?"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_activityTimer == null) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Resetting the inactivity timer, but its gone!", new Exception("where did it go?"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
long howLong = _activityTimer.getTimeLeft();
|
long howLong = _activityTimer.getTimeLeft();
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Resetting the inactivity timer to " + howLong);
|
_log.debug("Resetting the inactivity timer to " + howLong, new Exception("Reset by"));
|
||||||
// this will get rescheduled, and rescheduled, and rescheduled...
|
// this will get rescheduled, and rescheduled, and rescheduled...
|
||||||
SimpleTimer.getInstance().addEvent(_activityTimer, howLong);
|
SimpleTimer.getInstance().addEvent(_activityTimer, howLong);
|
||||||
}
|
}
|
||||||
@ -707,15 +717,34 @@ public class Connection {
|
|||||||
private class ActivityTimer implements SimpleTimer.TimedEvent {
|
private class ActivityTimer implements SimpleTimer.TimedEvent {
|
||||||
public void timeReached() {
|
public void timeReached() {
|
||||||
// uh, nothing more to do...
|
// uh, nothing more to do...
|
||||||
if (!_connected) return;
|
if (!_connected) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, but we are already closed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
// we got rescheduled already
|
// we got rescheduled already
|
||||||
if (getTimeLeft() > 0) return;
|
long left = getTimeLeft();
|
||||||
|
if (left > 0) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, but there is time left (" + left + ")");
|
||||||
|
SimpleTimer.getInstance().addEvent(ActivityTimer.this, left);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// these are either going to time out or cause further rescheduling
|
// these are either going to time out or cause further rescheduling
|
||||||
if (getUnackedPacketsSent() > 0) return;
|
if (getUnackedPacketsSent() > 0) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, but there are unacked packets");
|
||||||
|
return;
|
||||||
|
}
|
||||||
// wtf, this shouldn't have been scheduled
|
// wtf, this shouldn't have been scheduled
|
||||||
if (_options.getInactivityTimeout() <= 0) return;
|
if (_options.getInactivityTimeout() <= 0) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, but there is no timer...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
// if one of us can't talk...
|
// if one of us can't talk...
|
||||||
if ( (_closeSentOn > 0) || (_closeReceivedOn > 0) ) return;
|
if ( (_closeSentOn > 0) || (_closeReceivedOn > 0) ) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, but we are closing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("Inactivity timeout reached, with action=" + _options.getInactivityAction());
|
||||||
|
|
||||||
// bugger it, might as well do the hard work now
|
// bugger it, might as well do the hard work now
|
||||||
switch (_options.getInactivityAction()) {
|
switch (_options.getInactivityAction()) {
|
||||||
@ -741,7 +770,9 @@ public class Connection {
|
|||||||
_log.debug(buf.toString());
|
_log.debug(buf.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect(true);
|
_inputStream.streamErrorOccurred(new IOException("Inactivity timeout"));
|
||||||
|
_outputStream.streamErrorOccurred(new IOException("Inactivity timeout"));
|
||||||
|
disconnect(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -774,6 +805,7 @@ public class Connection {
|
|||||||
buf.append(" wsize: ").append(_options.getWindowSize());
|
buf.append(" wsize: ").append(_options.getWindowSize());
|
||||||
buf.append(" cwin: ").append(_congestionWindowEnd - _highestAckedThrough);
|
buf.append(" cwin: ").append(_congestionWindowEnd - _highestAckedThrough);
|
||||||
buf.append(" rtt: ").append(_options.getRTT());
|
buf.append(" rtt: ").append(_options.getRTT());
|
||||||
|
buf.append(" rto: ").append(_options.getRTO());
|
||||||
// not synchronized to avoid some kooky races
|
// not synchronized to avoid some kooky races
|
||||||
buf.append(" unacked outbound: ").append(_outboundPackets.size()).append(" ");
|
buf.append(" unacked outbound: ").append(_outboundPackets.size()).append(" ");
|
||||||
/*
|
/*
|
||||||
@ -950,10 +982,10 @@ public class Connection {
|
|||||||
disconnect(false);
|
disconnect(false);
|
||||||
} else {
|
} else {
|
||||||
//long timeout = _options.getResendDelay() << numSends;
|
//long timeout = _options.getResendDelay() << numSends;
|
||||||
long rtt = _options.getRTT();
|
long rto = _options.getRTO();
|
||||||
if (rtt < MIN_RESEND_DELAY)
|
if (rto < MIN_RESEND_DELAY)
|
||||||
rtt = MIN_RESEND_DELAY;
|
rto = MIN_RESEND_DELAY;
|
||||||
long timeout = rtt << (numSends-1);
|
long timeout = rto << (numSends-1);
|
||||||
if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) )
|
if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) )
|
||||||
timeout = MAX_RESEND_DELAY;
|
timeout = MAX_RESEND_DELAY;
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
@ -2,6 +2,7 @@ package net.i2p.client.streaming;
|
|||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.ByteArray;
|
import net.i2p.data.ByteArray;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,6 +167,9 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
|||||||
packet.setOptionalFrom(con.getSession().getMyDestination());
|
packet.setOptionalFrom(con.getSession().getMyDestination());
|
||||||
packet.setOptionalMaxSize(con.getOptions().getMaxMessageSize());
|
packet.setOptionalMaxSize(con.getOptions().getMaxMessageSize());
|
||||||
}
|
}
|
||||||
|
if (DataHelper.eq(con.getSendStreamId(), Packet.STREAM_ID_UNKNOWN)) {
|
||||||
|
packet.setFlag(Packet.FLAG_NO_ACK);
|
||||||
|
}
|
||||||
|
|
||||||
// don't set the closed flag if this is a plain ACK and there are outstanding
|
// don't set the closed flag if this is a plain ACK and there are outstanding
|
||||||
// packets sent, otherwise the other side could receive the CLOSE prematurely,
|
// packets sent, otherwise the other side could receive the CLOSE prematurely,
|
||||||
|
@ -13,6 +13,8 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
private int _receiveWindow;
|
private int _receiveWindow;
|
||||||
private int _profile;
|
private int _profile;
|
||||||
private int _rtt;
|
private int _rtt;
|
||||||
|
private int _rttDev;
|
||||||
|
private int _rto;
|
||||||
private int _trend[];
|
private int _trend[];
|
||||||
private int _resendDelay;
|
private int _resendDelay;
|
||||||
private int _sendAckDelay;
|
private int _sendAckDelay;
|
||||||
@ -52,6 +54,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
public static final String PROP_SLOW_START_GROWTH_RATE_FACTOR = "i2p.streaming.slowStartGrowthRateFactor";
|
public static final String PROP_SLOW_START_GROWTH_RATE_FACTOR = "i2p.streaming.slowStartGrowthRateFactor";
|
||||||
|
|
||||||
private static final int TREND_COUNT = 3;
|
private static final int TREND_COUNT = 3;
|
||||||
|
static final int INITIAL_WINDOW_SIZE = 4;
|
||||||
|
|
||||||
public ConnectionOptions() {
|
public ConnectionOptions() {
|
||||||
super();
|
super();
|
||||||
@ -68,6 +71,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
public ConnectionOptions(ConnectionOptions opts) {
|
public ConnectionOptions(ConnectionOptions opts) {
|
||||||
super(opts);
|
super(opts);
|
||||||
if (opts != null) {
|
if (opts != null) {
|
||||||
|
setMaxWindowSize(opts.getMaxWindowSize());
|
||||||
setConnectDelay(opts.getConnectDelay());
|
setConnectDelay(opts.getConnectDelay());
|
||||||
setProfile(opts.getProfile());
|
setProfile(opts.getProfile());
|
||||||
setRTT(opts.getRTT());
|
setRTT(opts.getRTT());
|
||||||
@ -80,7 +84,6 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
setInactivityTimeout(opts.getInactivityTimeout());
|
setInactivityTimeout(opts.getInactivityTimeout());
|
||||||
setInactivityAction(opts.getInactivityAction());
|
setInactivityAction(opts.getInactivityAction());
|
||||||
setInboundBufferSize(opts.getInboundBufferSize());
|
setInboundBufferSize(opts.getInboundBufferSize());
|
||||||
setMaxWindowSize(opts.getMaxWindowSize());
|
|
||||||
setCongestionAvoidanceGrowthRateFactor(opts.getCongestionAvoidanceGrowthRateFactor());
|
setCongestionAvoidanceGrowthRateFactor(opts.getCongestionAvoidanceGrowthRateFactor());
|
||||||
setSlowStartGrowthRateFactor(opts.getSlowStartGrowthRateFactor());
|
setSlowStartGrowthRateFactor(opts.getSlowStartGrowthRateFactor());
|
||||||
}
|
}
|
||||||
@ -90,6 +93,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
super.init(opts);
|
super.init(opts);
|
||||||
_trend = new int[TREND_COUNT];
|
_trend = new int[TREND_COUNT];
|
||||||
|
|
||||||
|
setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE));
|
||||||
setConnectDelay(getInt(opts, PROP_CONNECT_DELAY, -1));
|
setConnectDelay(getInt(opts, PROP_CONNECT_DELAY, -1));
|
||||||
setProfile(getInt(opts, PROP_PROFILE, PROFILE_BULK));
|
setProfile(getInt(opts, PROP_PROFILE, PROFILE_BULK));
|
||||||
setMaxMessageSize(getInt(opts, PROP_MAX_MESSAGE_SIZE, 4*1024));
|
setMaxMessageSize(getInt(opts, PROP_MAX_MESSAGE_SIZE, 4*1024));
|
||||||
@ -97,22 +101,23 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1));
|
setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1));
|
||||||
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 1000));
|
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 1000));
|
||||||
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 500));
|
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 500));
|
||||||
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, 1));
|
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, INITIAL_WINDOW_SIZE));
|
||||||
setMaxResends(getInt(opts, PROP_MAX_RESENDS, 10));
|
setMaxResends(getInt(opts, PROP_MAX_RESENDS, 10));
|
||||||
setWriteTimeout(getInt(opts, PROP_WRITE_TIMEOUT, -1));
|
setWriteTimeout(getInt(opts, PROP_WRITE_TIMEOUT, -1));
|
||||||
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
|
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 2*60*1000));
|
||||||
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
|
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
|
||||||
setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2));
|
setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2));
|
||||||
setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, 1));
|
setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, 1));
|
||||||
setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, 1));
|
setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, 1));
|
||||||
|
|
||||||
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
||||||
setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProperties(Properties opts) {
|
public void setProperties(Properties opts) {
|
||||||
super.setProperties(opts);
|
super.setProperties(opts);
|
||||||
if (opts == null) return;
|
if (opts == null) return;
|
||||||
|
if (opts.containsKey(PROP_MAX_WINDOW_SIZE))
|
||||||
|
setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE));
|
||||||
if (opts.containsKey(PROP_CONNECT_DELAY))
|
if (opts.containsKey(PROP_CONNECT_DELAY))
|
||||||
setConnectDelay(getInt(opts, PROP_CONNECT_DELAY, -1));
|
setConnectDelay(getInt(opts, PROP_CONNECT_DELAY, -1));
|
||||||
if (opts.containsKey(PROP_PROFILE))
|
if (opts.containsKey(PROP_PROFILE))
|
||||||
@ -124,17 +129,17 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
if (opts.containsKey(PROP_INITIAL_RECEIVE_WINDOW))
|
if (opts.containsKey(PROP_INITIAL_RECEIVE_WINDOW))
|
||||||
setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1));
|
setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1));
|
||||||
if (opts.containsKey(PROP_INITIAL_RESEND_DELAY))
|
if (opts.containsKey(PROP_INITIAL_RESEND_DELAY))
|
||||||
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 500));
|
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 1000));
|
||||||
if (opts.containsKey(PROP_INITIAL_ACK_DELAY))
|
if (opts.containsKey(PROP_INITIAL_ACK_DELAY))
|
||||||
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 500));
|
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 500));
|
||||||
if (opts.containsKey(PROP_INITIAL_WINDOW_SIZE))
|
if (opts.containsKey(PROP_INITIAL_WINDOW_SIZE))
|
||||||
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, 1));
|
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, INITIAL_WINDOW_SIZE));
|
||||||
if (opts.containsKey(PROP_MAX_RESENDS))
|
if (opts.containsKey(PROP_MAX_RESENDS))
|
||||||
setMaxResends(getInt(opts, PROP_MAX_RESENDS, 10));
|
setMaxResends(getInt(opts, PROP_MAX_RESENDS, 10));
|
||||||
if (opts.containsKey(PROP_WRITE_TIMEOUT))
|
if (opts.containsKey(PROP_WRITE_TIMEOUT))
|
||||||
setWriteTimeout(getInt(opts, PROP_WRITE_TIMEOUT, -1));
|
setWriteTimeout(getInt(opts, PROP_WRITE_TIMEOUT, -1));
|
||||||
if (opts.containsKey(PROP_INACTIVITY_TIMEOUT))
|
if (opts.containsKey(PROP_INACTIVITY_TIMEOUT))
|
||||||
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
|
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 2*60*1000));
|
||||||
if (opts.containsKey(PROP_INACTIVITY_ACTION))
|
if (opts.containsKey(PROP_INACTIVITY_ACTION))
|
||||||
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
|
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
|
||||||
setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2));
|
setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2));
|
||||||
@ -145,8 +150,6 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
|
|
||||||
if (opts.containsKey(PROP_CONNECT_TIMEOUT))
|
if (opts.containsKey(PROP_CONNECT_TIMEOUT))
|
||||||
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
||||||
if (opts.containsKey(PROP_MAX_WINDOW_SIZE))
|
|
||||||
setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -191,6 +194,10 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
*/
|
*/
|
||||||
public int getRTT() { return _rtt; }
|
public int getRTT() { return _rtt; }
|
||||||
public void setRTT(int ms) {
|
public void setRTT(int ms) {
|
||||||
|
if (_rto == 0) {
|
||||||
|
_rttDev = ms;
|
||||||
|
_rto = (int)Connection.MAX_RESEND_DELAY;
|
||||||
|
}
|
||||||
synchronized (_trend) {
|
synchronized (_trend) {
|
||||||
_trend[0] = _trend[1];
|
_trend[0] = _trend[1];
|
||||||
_trend[1] = _trend[2];
|
_trend[1] = _trend[2];
|
||||||
@ -201,10 +208,12 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
else
|
else
|
||||||
_trend[2] = 0;
|
_trend[2] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_rtt = ms;
|
_rtt = ms;
|
||||||
if (_rtt > 60*1000)
|
if (_rtt > 60*1000)
|
||||||
_rtt = 60*1000;
|
_rtt = 60*1000;
|
||||||
}
|
}
|
||||||
|
public int getRTO() { return _rto; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If we have 3 consecutive rtt increases, we are trending upwards (1), or if we have
|
* If we have 3 consecutive rtt increases, we are trending upwards (1), or if we have
|
||||||
@ -225,7 +234,15 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
private static final double RTT_DAMPENING = 0.9;
|
private static final double RTT_DAMPENING = 0.9;
|
||||||
|
|
||||||
public void updateRTT(int measuredValue) {
|
public void updateRTT(int measuredValue) {
|
||||||
setRTT((int)(RTT_DAMPENING*_rtt + (1-RTT_DAMPENING)*measuredValue));
|
_rttDev = _rttDev + (int)(0.25d*(Math.abs(measuredValue-_rtt)-_rttDev));
|
||||||
|
int smoothed = (int)(RTT_DAMPENING*_rtt + (1-RTT_DAMPENING)*measuredValue);
|
||||||
|
_rto = smoothed + (_rttDev<<2);
|
||||||
|
if (_rto < Connection.MIN_RESEND_DELAY)
|
||||||
|
_rto = (int)Connection.MIN_RESEND_DELAY;
|
||||||
|
else if (_rto > Connection.MAX_RESEND_DELAY)
|
||||||
|
_rto = (int)Connection.MAX_RESEND_DELAY;
|
||||||
|
|
||||||
|
setRTT(smoothed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** How long after sending a packet will we wait before resending? */
|
/** How long after sending a packet will we wait before resending? */
|
||||||
|
@ -187,6 +187,7 @@ public class ConnectionPacketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean ack(Connection con, long ackThrough, long nacks[], Packet packet, boolean isNew, boolean choke) {
|
private boolean ack(Connection con, long ackThrough, long nacks[], Packet packet, boolean isNew, boolean choke) {
|
||||||
|
if (ackThrough < 0) return false;
|
||||||
//if ( (nacks != null) && (nacks.length > 0) )
|
//if ( (nacks != null) && (nacks.length > 0) )
|
||||||
// con.getOptions().setRTT(con.getOptions().getRTT() + nacks.length*1000);
|
// con.getOptions().setRTT(con.getOptions().getRTT() + nacks.length*1000);
|
||||||
|
|
||||||
@ -315,6 +316,12 @@ public class ConnectionPacketHandler {
|
|||||||
return congested;
|
return congested;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we don't know the send stream id yet (we're just creating a connection), allow
|
||||||
|
* the first three packets to come in. The first of those should be the SYN, of course...
|
||||||
|
*/
|
||||||
|
private static final int MAX_INITIAL_PACKETS = ConnectionOptions.INITIAL_WINDOW_SIZE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make sure this packet is ok and that we can continue processing its data.
|
* Make sure this packet is ok and that we can continue processing its data.
|
||||||
*
|
*
|
||||||
@ -335,7 +342,7 @@ public class ConnectionPacketHandler {
|
|||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// neither RST nor SYN and we dont have the stream id yet?
|
// neither RST nor SYN and we dont have the stream id yet?
|
||||||
if (packet.getSequenceNum() <= 2) {
|
if (packet.getSequenceNum() < MAX_INITIAL_PACKETS) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
|
@ -44,6 +44,7 @@ public class MessageHandler implements I2PSessionListener {
|
|||||||
_log.warn("Error receiving the message", ise);
|
_log.warn("Error receiving the message", ise);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (data == null) return;
|
||||||
Packet packet = new Packet();
|
Packet packet = new Packet();
|
||||||
try {
|
try {
|
||||||
packet.readPacket(data, 0, data.length);
|
packet.readPacket(data, 0, data.length);
|
||||||
|
@ -435,7 +435,9 @@ public class MessageInputStream extends InputStream {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void streamErrorOccurred(IOException ioe) {
|
void streamErrorOccurred(IOException ioe) {
|
||||||
_streamError = ioe;
|
if (_streamError == null)
|
||||||
|
_streamError = ioe;
|
||||||
|
_locallyClosed = true;
|
||||||
synchronized (_dataLock) {
|
synchronized (_dataLock) {
|
||||||
_dataLock.notifyAll();
|
_dataLock.notifyAll();
|
||||||
}
|
}
|
||||||
|
@ -312,11 +312,16 @@ public class MessageOutputStream extends OutputStream {
|
|||||||
/** nonblocking close */
|
/** nonblocking close */
|
||||||
public void closeInternal() {
|
public void closeInternal() {
|
||||||
_closed = true;
|
_closed = true;
|
||||||
_streamError = new IOException("Closed internally");
|
if (_streamError == null)
|
||||||
|
_streamError = new IOException("Closed internally");
|
||||||
|
clearData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearData(boolean shouldFlush) {
|
||||||
ByteArray ba = null;
|
ByteArray ba = null;
|
||||||
synchronized (_dataLock) {
|
synchronized (_dataLock) {
|
||||||
// flush any data, but don't wait for it
|
// flush any data, but don't wait for it
|
||||||
if (_dataReceiver != null)
|
if ( (_dataReceiver != null) && (_valid > 0) && shouldFlush)
|
||||||
_dataReceiver.writeData(_buf, 0, _valid);
|
_dataReceiver.writeData(_buf, 0, _valid);
|
||||||
_written += _valid;
|
_written += _valid;
|
||||||
_valid = 0;
|
_valid = 0;
|
||||||
@ -345,7 +350,9 @@ public class MessageOutputStream extends OutputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void streamErrorOccurred(IOException ioe) {
|
void streamErrorOccurred(IOException ioe) {
|
||||||
_streamError = ioe;
|
if (_streamError == null)
|
||||||
|
_streamError = ioe;
|
||||||
|
clearData(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -136,6 +136,11 @@ public class Packet {
|
|||||||
*/
|
*/
|
||||||
public static final int FLAG_ECHO = (1 << 9);
|
public static final int FLAG_ECHO = (1 << 9);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, this packet doesn't really want to ack anything
|
||||||
|
*/
|
||||||
|
public static final int FLAG_NO_ACK = (1 << 10);
|
||||||
|
|
||||||
public static final int DEFAULT_MAX_SIZE = 32*1024;
|
public static final int DEFAULT_MAX_SIZE = 32*1024;
|
||||||
private static final int MAX_DELAY_REQUEST = 65535;
|
private static final int MAX_DELAY_REQUEST = 65535;
|
||||||
|
|
||||||
@ -181,11 +186,21 @@ public class Packet {
|
|||||||
/**
|
/**
|
||||||
* The highest packet sequence number that received
|
* The highest packet sequence number that received
|
||||||
* on the receiveStreamId. This field is ignored on the initial
|
* on the receiveStreamId. This field is ignored on the initial
|
||||||
* connection packet (where receiveStreamId is the unknown id).
|
* connection packet (where receiveStreamId is the unknown id) or
|
||||||
|
* if FLAG_NO_ACK is set.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getAckThrough() { return _ackThrough; }
|
public long getAckThrough() {
|
||||||
public void setAckThrough(long id) { _ackThrough = id; }
|
if (isFlagSet(FLAG_NO_ACK))
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return _ackThrough;
|
||||||
|
}
|
||||||
|
public void setAckThrough(long id) {
|
||||||
|
if (id < 0)
|
||||||
|
setFlag(FLAG_NO_ACK);
|
||||||
|
_ackThrough = id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of packet sequence numbers below the getAckThrough() value
|
* List of packet sequence numbers below the getAckThrough() value
|
||||||
@ -566,7 +581,7 @@ public class Packet {
|
|||||||
else
|
else
|
||||||
buf.append('\t');
|
buf.append('\t');
|
||||||
buf.append(toFlagString());
|
buf.append(toFlagString());
|
||||||
buf.append(" ACK ").append(_ackThrough);
|
buf.append(" ACK ").append(getAckThrough());
|
||||||
if (_nacks != null) {
|
if (_nacks != null) {
|
||||||
buf.append(" NACK");
|
buf.append(" NACK");
|
||||||
for (int i = 0; i < _nacks.length; i++) {
|
for (int i = 0; i < _nacks.length; i++) {
|
||||||
|
@ -22,19 +22,26 @@ public class PacketHandler {
|
|||||||
private I2PAppContext _context;
|
private I2PAppContext _context;
|
||||||
private Log _log;
|
private Log _log;
|
||||||
private int _lastDelay;
|
private int _lastDelay;
|
||||||
|
private int _dropped;
|
||||||
|
|
||||||
public PacketHandler(I2PAppContext ctx, ConnectionManager mgr) {
|
public PacketHandler(I2PAppContext ctx, ConnectionManager mgr) {
|
||||||
_manager = mgr;
|
_manager = mgr;
|
||||||
_context = ctx;
|
_context = ctx;
|
||||||
|
_dropped = 0;
|
||||||
_log = ctx.logManager().getLog(PacketHandler.class);
|
_log = ctx.logManager().getLog(PacketHandler.class);
|
||||||
_lastDelay = _context.random().nextInt(30*1000);
|
_lastDelay = _context.random().nextInt(30*1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean choke(Packet packet) {
|
private boolean choke(Packet packet) {
|
||||||
if (false) {
|
if (true) return true;
|
||||||
// artificial choke: 2% random drop and a 0-30s
|
//if ( (_dropped == 0) && true ) { //&& (_manager.getSent() <= 0) ) {
|
||||||
|
// _dropped++;
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
if (true) {
|
||||||
|
// artificial choke: 2% random drop and a 0-5s
|
||||||
// random tiered delay from 0-30s
|
// random tiered delay from 0-30s
|
||||||
if (_context.random().nextInt(100) >= 95) {
|
if (_context.random().nextInt(100) >= 98) {
|
||||||
displayPacket(packet, "DROP", null);
|
displayPacket(packet, "DROP", null);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@ -42,7 +49,7 @@ public class PacketHandler {
|
|||||||
/*
|
/*
|
||||||
int delay = _context.random().nextInt(5*1000);
|
int delay = _context.random().nextInt(5*1000);
|
||||||
*/
|
*/
|
||||||
int delay = _context.random().nextInt(6*1000);
|
int delay = _context.random().nextInt(1*1000);
|
||||||
int delayFactor = _context.random().nextInt(100);
|
int delayFactor = _context.random().nextInt(100);
|
||||||
if (delayFactor > 80) {
|
if (delayFactor > 80) {
|
||||||
if (delayFactor > 98)
|
if (delayFactor > 98)
|
||||||
@ -97,7 +104,7 @@ public class PacketHandler {
|
|||||||
Connection con = (sendId != null ? _manager.getConnectionByInboundId(sendId) : null);
|
Connection con = (sendId != null ? _manager.getConnectionByInboundId(sendId) : null);
|
||||||
if (con != null) {
|
if (con != null) {
|
||||||
receiveKnownCon(con, packet);
|
receiveKnownCon(con, packet);
|
||||||
displayPacket(packet, "RECV", "wsize " + con.getOptions().getWindowSize());
|
displayPacket(packet, "RECV", "wsize " + con.getOptions().getWindowSize() + " rto " + con.getOptions().getRTO());
|
||||||
} else {
|
} else {
|
||||||
receiveUnknownCon(packet, sendId);
|
receiveUnknownCon(packet, sendId);
|
||||||
displayPacket(packet, "UNKN", null);
|
displayPacket(packet, "UNKN", null);
|
||||||
|
@ -125,7 +125,7 @@ class PacketQueue {
|
|||||||
_log.debug(msg);
|
_log.debug(msg);
|
||||||
}
|
}
|
||||||
Connection c = packet.getConnection();
|
Connection c = packet.getConnection();
|
||||||
String suffix = (c != null ? "wsize " + c.getOptions().getWindowSize() : null);
|
String suffix = (c != null ? "wsize " + c.getOptions().getWindowSize() + " rto " + c.getOptions().getRTO() : null);
|
||||||
_connectionManager.getPacketHandler().displayPacket(packet, "SEND", suffix);
|
_connectionManager.getPacketHandler().displayPacket(packet, "SEND", suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,6 +273,7 @@
|
|||||||
<copy file="installer/resources/dnf-header.ht" todir="pkg-temp/docs/" />
|
<copy file="installer/resources/dnf-header.ht" todir="pkg-temp/docs/" />
|
||||||
<copy file="installer/resources/dnfp-header.ht" todir="pkg-temp/docs/" />
|
<copy file="installer/resources/dnfp-header.ht" todir="pkg-temp/docs/" />
|
||||||
<copy file="installer/resources/dnfb-header.ht" todir="pkg-temp/docs/" />
|
<copy file="installer/resources/dnfb-header.ht" todir="pkg-temp/docs/" />
|
||||||
|
<copy file="installer/resources/ahelper-conflict-header.ht" todir="pkg-temp/docs/" />
|
||||||
<mkdir dir="pkg-temp/eepsite" />
|
<mkdir dir="pkg-temp/eepsite" />
|
||||||
<mkdir dir="pkg-temp/eepsite/webapps" />
|
<mkdir dir="pkg-temp/eepsite/webapps" />
|
||||||
<mkdir dir="pkg-temp/eepsite/logs" />
|
<mkdir dir="pkg-temp/eepsite/logs" />
|
||||||
@ -339,6 +340,7 @@
|
|||||||
<copy file="installer/resources/dnf-header.ht" todir="pkg-temp/docs/" />
|
<copy file="installer/resources/dnf-header.ht" todir="pkg-temp/docs/" />
|
||||||
<copy file="installer/resources/dnfp-header.ht" todir="pkg-temp/docs/" />
|
<copy file="installer/resources/dnfp-header.ht" todir="pkg-temp/docs/" />
|
||||||
<copy file="installer/resources/dnfb-header.ht" todir="pkg-temp/docs/" />
|
<copy file="installer/resources/dnfb-header.ht" todir="pkg-temp/docs/" />
|
||||||
|
<copy file="installer/resources/ahelper-conflict-header.ht" todir="pkg-temp/docs/" />
|
||||||
<!-- the addressbook handles this for updates -->
|
<!-- the addressbook handles this for updates -->
|
||||||
<!-- <copy file="hosts.txt" todir="pkg-temp/" /> -->
|
<!-- <copy file="hosts.txt" todir="pkg-temp/" /> -->
|
||||||
<mkdir dir="pkg-temp/eepsite" />
|
<mkdir dir="pkg-temp/eepsite" />
|
||||||
|
@ -414,7 +414,12 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
if ( (msgId != null) && (size != null) ) {
|
if ( (msgId != null) && (size != null) ) {
|
||||||
if (_sessionListener != null) {
|
if (_sessionListener != null) {
|
||||||
try {
|
try {
|
||||||
|
long before = System.currentTimeMillis();
|
||||||
_sessionListener.messageAvailable(I2PSessionImpl.this, msgId.intValue(), size.intValue());
|
_sessionListener.messageAvailable(I2PSessionImpl.this, msgId.intValue(), size.intValue());
|
||||||
|
long duration = System.currentTimeMillis() - before;
|
||||||
|
if ((duration > 100) && _log.shouldLog(Log.ERROR))
|
||||||
|
_log.error("Message availability notification for " + msgId.intValue() + " took "
|
||||||
|
+ duration + " to " + _sessionListener);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
_log.log(Log.CRIT, "Error notifying app of message availability", e);
|
_log.log(Log.CRIT, "Error notifying app of message availability", e);
|
||||||
}
|
}
|
||||||
|
@ -93,8 +93,10 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
*/
|
*/
|
||||||
public byte[] receiveMessage(int msgId) throws I2PSessionException {
|
public byte[] receiveMessage(int msgId) throws I2PSessionException {
|
||||||
byte compressed[] = super.receiveMessage(msgId);
|
byte compressed[] = super.receiveMessage(msgId);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (compressed == null) {
|
||||||
_log.debug("receiving message " + msgId + " with " + compressed.length + " compressed bytes");
|
_log.error("Error: message " + msgId + " already received!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (SHOULD_COMPRESS) {
|
if (SHOULD_COMPRESS) {
|
||||||
try {
|
try {
|
||||||
return DataHelper.decompress(compressed);
|
return DataHelper.decompress(compressed);
|
||||||
|
@ -37,7 +37,7 @@ public class ByteArray implements Serializable, Comparable {
|
|||||||
_valid = length;
|
_valid = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final byte[] getData() {
|
public byte[] getData() {
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,10 +50,10 @@ public class ByteArray implements Serializable, Comparable {
|
|||||||
* this property does not necessarily have meaning for all byte
|
* this property does not necessarily have meaning for all byte
|
||||||
* arrays.
|
* arrays.
|
||||||
*/
|
*/
|
||||||
public final int getValid() { return _valid; }
|
public int getValid() { return _valid; }
|
||||||
public final void setValid(int valid) { _valid = valid; }
|
public void setValid(int valid) { _valid = valid; }
|
||||||
public final int getOffset() { return _offset; }
|
public int getOffset() { return _offset; }
|
||||||
public final void setOffset(int offset) { _offset = offset; }
|
public void setOffset(int offset) { _offset = offset; }
|
||||||
|
|
||||||
public final boolean equals(Object o) {
|
public final boolean equals(Object o) {
|
||||||
if (o == null) return false;
|
if (o == null) return false;
|
||||||
@ -83,7 +83,7 @@ public class ByteArray implements Serializable, Comparable {
|
|||||||
return DataHelper.hashCode(getData());
|
return DataHelper.hashCode(getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
public final String toString() {
|
public String toString() {
|
||||||
return super.toString() + "/" + DataHelper.toString(getData(), 32) + "." + _valid;
|
return super.toString() + "/" + DataHelper.toString(getData(), 32) + "." + _valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
19
history.txt
19
history.txt
@ -1,4 +1,21 @@
|
|||||||
$Id: history.txt,v 1.262 2005/09/21 01:43:04 jrandom Exp $
|
$Id: history.txt,v 1.263 2005/09/21 18:01:37 jrandom Exp $
|
||||||
|
|
||||||
|
2005-09-25 Complication
|
||||||
|
* Better i2paddresshelper handling in the I2PTunnel httpclient, plus a new
|
||||||
|
conflict resolution page if the i2paddresshelper parameter differs from
|
||||||
|
an existing name to destination mapping.
|
||||||
|
|
||||||
|
2005-09-25 jrandom
|
||||||
|
* Fix a long standing streaming lib bug (in the inactivity detection code)
|
||||||
|
* Improved handling of initial streaming lib packet retransmissions to
|
||||||
|
kill the "lost first packet" bug (where a page shows up with the first
|
||||||
|
few KB missing)
|
||||||
|
* Add support for initial window sizes greater than 1 - useful for
|
||||||
|
eepsites to transmit e.g. 4 packets full of data along with the initial
|
||||||
|
ACK, thereby cutting down on the rtt latency. The congestion window
|
||||||
|
size can and does still shrink down to 1 packet though.
|
||||||
|
* Adjusted the streaming lib retransmission calculation algorithm to be
|
||||||
|
more TCP-like.
|
||||||
|
|
||||||
2005-09-21 redzara
|
2005-09-21 redzara
|
||||||
* Use ISO-8859-1 for the susidns xml
|
* Use ISO-8859-1 for the susidns xml
|
||||||
|
45
installer/resources/ahelper-conflict-header.ht
Normal file
45
installer/resources/ahelper-conflict-header.ht
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
HTTP/1.1 409 Conflict
|
||||||
|
Content-Type: text/html; charset=iso-8859-1
|
||||||
|
Cache-control: no-cache
|
||||||
|
|
||||||
|
<html><head>
|
||||||
|
<title>Destination key conflict</title>
|
||||||
|
<style type='text/css'>
|
||||||
|
div.warning {
|
||||||
|
margin: 0em 1em 1em 224px;
|
||||||
|
padding: .5em 1em;
|
||||||
|
background-color: #ffefef;
|
||||||
|
border: medium solid #ffafaf;
|
||||||
|
text-align: left;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
div.logo {
|
||||||
|
float: left;
|
||||||
|
width: 200px;
|
||||||
|
left: 1em;
|
||||||
|
top: 1em;
|
||||||
|
margin: 0em;
|
||||||
|
padding: .5em;
|
||||||
|
text-align: left;
|
||||||
|
border: medium solid #efefff;
|
||||||
|
background-color: #fafaff;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class=logo>
|
||||||
|
<a href="http://localhost:7657/index.jsp">Router Console</a><br />
|
||||||
|
[<a href="http://localhost:7657/config.jsp">configuration</a> | <a href="http://localhost:7657/help.jsp">help</a>]
|
||||||
|
</div>
|
||||||
|
<div class=warning id=warning>
|
||||||
|
The addresshelper link you followed specifies a different destination key
|
||||||
|
than a host entry in your host database.
|
||||||
|
Someone could be trying to impersonate another eepsite,
|
||||||
|
or people have given two eepsites identical names.
|
||||||
|
<P/>
|
||||||
|
You can resolve the conflict by considering which key you trust,
|
||||||
|
and either discarding the addresshelper link,
|
||||||
|
discarding the host entry from your host database,
|
||||||
|
or naming one of them differently.
|
||||||
|
<P/>
|
@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RouterVersion {
|
public class RouterVersion {
|
||||||
public final static String ID = "$Revision: 1.242 $ $Date: 2005/09/18 18:08:18 $";
|
public final static String ID = "$Revision: 1.243 $ $Date: 2005/09/21 01:43:04 $";
|
||||||
public final static String VERSION = "0.6.0.6";
|
public final static String VERSION = "0.6.0.6";
|
||||||
public final static long BUILD = 2;
|
public final static long BUILD = 3;
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
||||||
System.out.println("Router ID: " + RouterVersion.ID);
|
System.out.println("Router ID: " + RouterVersion.ID);
|
||||||
|
Reference in New Issue
Block a user