forked from I2P_Developers/i2p.i2p
Compare commits
81 Commits
i2p_0_4_1_
...
i2p_0_4_2_
Author | SHA1 | Date | |
---|---|---|---|
![]() |
97e8ab7c5b | ||
![]() |
cb930a7ab5 | ||
![]() |
610f1f7dd4 | ||
![]() |
516d0b4db8 | ||
![]() |
df61ae5c6f | ||
![]() |
9f6584b55e | ||
![]() |
e4b41f5bb0 | ||
![]() |
8d0cea93e9 | ||
![]() |
d294d07919 | ||
![]() |
153eea2bd5 | ||
![]() |
571e3c5c13 | ||
![]() |
a2d268f3d6 | ||
![]() |
02d456d7a0 | ||
![]() |
b3626ad86f | ||
![]() |
9b6eab451f | ||
![]() |
72be9b5f04 | ||
![]() |
35e94a7f65 | ||
![]() |
8e02586cc9 | ||
![]() |
0b5a640896 | ||
![]() |
64b5089909 | ||
![]() |
e0e09bfa45 | ||
![]() |
8c7f9f2c65 | ||
![]() |
aff5cea949 | ||
![]() |
8bd99f699f | ||
![]() |
b0513fff8a | ||
![]() |
f10db9d91a | ||
![]() |
9b5fb17068 | ||
![]() |
608d713dca | ||
![]() |
6d5fc8ca21 | ||
![]() |
8c3145b70f | ||
![]() |
12a6f3e938 | ||
![]() |
2c59435762 | ||
![]() |
7336bf5c55 | ||
![]() |
2b21b97277 | ||
![]() |
603bc99a2f | ||
![]() |
426ede1c99 | ||
![]() |
21506c1d1b | ||
![]() |
0b48b18e7e | ||
![]() |
c8f6d9c7a1 | ||
![]() |
ed8eced9dd | ||
![]() |
4a029b7853 | ||
![]() |
6bd9e58ece | ||
![]() |
cd075fc8a6 | ||
![]() |
e733427920 | ||
![]() |
107da0ae22 | ||
![]() |
3629d7a32c | ||
![]() |
71e1152cde | ||
![]() |
d01ab7fd23 | ||
![]() |
f46d0a720c | ||
![]() |
d943b4993a | ||
![]() |
4a4f57d6ac | ||
![]() |
085da16268 | ||
![]() |
306f6b0037 | ||
![]() |
3780d290fa | ||
![]() |
ad7dc66f90 | ||
![]() |
258244fed8 | ||
![]() |
5f7982540f | ||
![]() |
b1c0de4b77 | ||
![]() |
45b3fecfff | ||
![]() |
9774ded4dd | ||
![]() |
7ec027854e | ||
![]() |
b457001b42 | ||
![]() |
6fc6866eb4 | ||
![]() |
299e5528bc | ||
![]() |
881524a5e4 | ||
![]() |
ffc405138d | ||
![]() |
f6ff74af16 | ||
![]() |
73a12d47de | ||
![]() |
83165df7e5 | ||
![]() |
30074be5a5 | ||
![]() |
16715aa309 | ||
![]() |
53f3802a81 | ||
![]() |
07626b5cc2 | ||
![]() |
9ea603caf2 | ||
![]() |
18ab9b80d2 | ||
![]() |
71c1cb4e12 | ||
![]() |
0c049f39d9 | ||
![]() |
096b807c37 | ||
![]() |
9018af4765 | ||
![]() |
323f28e306 | ||
![]() |
e9dbd00f42 |
@@ -284,7 +284,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("textserver <host> <port> <privkey>");
|
||||
l.log("genkeys <privkeyfile> [<pubkeyfile>]");
|
||||
l.log("gentextkeys");
|
||||
l.log("client <port> <pubkey>|file:<pubkeyfile>");
|
||||
l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile>");
|
||||
l.log("httpclient <port>");
|
||||
l.log("lookup <name>");
|
||||
l.log("quit");
|
||||
@@ -449,9 +449,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
notifyEvent("clientTaskId", new Integer(-1));
|
||||
}
|
||||
} else {
|
||||
l.log("client <port> <pubkey>|file:<pubkeyfile>");
|
||||
l.log("client <port> <pubkey>[,<pubkey>]|file:<pubkeyfile>");
|
||||
l.log(" creates a client that forwards port to the pubkey.\n"
|
||||
+ " use 0 as port to get a free port assigned.");
|
||||
+ " use 0 as port to get a free port assigned. If you specify\n"
|
||||
+ " a comma delimited list of pubkeys, it will rotate among them\n"
|
||||
+ " randomlyl");
|
||||
notifyEvent("clientTaskId", new Integer(-1));
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,11 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
@@ -15,15 +19,17 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
|
||||
private static final Log _log = new Log(I2PTunnelClient.class);
|
||||
|
||||
protected Destination dest;
|
||||
/** list of Destination objects that we point at */
|
||||
protected List dests;
|
||||
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
|
||||
protected long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
/**
|
||||
* @param destinations comma delimited list of peers we target
|
||||
* @throws IllegalArgumentException if the I2PTunnel does not contain
|
||||
* valid config to contact the router
|
||||
*/
|
||||
public I2PTunnelClient(int localPort, String destination, Logging l,
|
||||
public I2PTunnelClient(int localPort, String destinations, Logging l,
|
||||
boolean ownDest, EventDispatcher notifyThis,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||
super(localPort, ownDest, l, notifyThis, "SynSender", tunnel);
|
||||
@@ -33,19 +39,28 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
dest = I2PTunnel.destFromName(destination);
|
||||
if (dest == null) {
|
||||
l.log("Could not resolve " + destination + ".");
|
||||
return;
|
||||
StringTokenizer tok = new StringTokenizer(destinations, ",");
|
||||
dests = new ArrayList(1);
|
||||
while (tok.hasMoreTokens()) {
|
||||
String destination = tok.nextToken();
|
||||
try {
|
||||
Destination dest = I2PTunnel.destFromName(destination);
|
||||
if (dest == null)
|
||||
l.log("Could not resolve " + destination);
|
||||
else
|
||||
dests.add(dest);
|
||||
} catch (DataFormatException dfe) {
|
||||
l.log("Bad format parsing \"" + destination + "\"");
|
||||
}
|
||||
} catch (DataFormatException e) {
|
||||
l.log("Bad format in destination \"" + destination + "\".");
|
||||
}
|
||||
|
||||
if (dests.size() <= 0) {
|
||||
l.log("No target destinations found");
|
||||
notifyEvent("openClientResult", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
setName(getLocalPort() + " -> " + destination);
|
||||
setName(getLocalPort() + " -> " + destinations);
|
||||
|
||||
startRunning();
|
||||
|
||||
@@ -56,14 +71,34 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
public long getReadTimeout() { return readTimeout; }
|
||||
|
||||
protected void clientConnectionRun(Socket s) {
|
||||
Destination dest = pickDestination();
|
||||
I2PSocket i2ps = null;
|
||||
try {
|
||||
I2PSocket i2ps = createI2PSocket(dest);
|
||||
i2ps = createI2PSocket(dest);
|
||||
i2ps.setReadTimeout(readTimeout);
|
||||
new I2PTunnelRunner(s, i2ps, sockLock, null);
|
||||
new I2PTunnelRunner(s, i2ps, sockLock, null, mySockets);
|
||||
} catch (Exception ex) {
|
||||
_log.info("Error connecting", ex);
|
||||
l.log(ex.getMessage());
|
||||
closeSocket(s);
|
||||
if (i2ps != null) {
|
||||
synchronized (sockLock) {
|
||||
mySockets.remove(sockLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Destination pickDestination() {
|
||||
int size = dests.size();
|
||||
if (size <= 0) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("No client targets?!");
|
||||
return null;
|
||||
}
|
||||
if (size == 1) // skip the rand in the most common case
|
||||
return (Destination)dests.get(0);
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
return (Destination)dests.get(index);
|
||||
}
|
||||
}
|
||||
|
@@ -33,13 +33,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
private static final Log _log = new Log(I2PTunnelClientBase.class);
|
||||
protected Logging l;
|
||||
|
||||
private static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
|
||||
static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
|
||||
|
||||
private static volatile long __clientId = 0;
|
||||
protected long _clientId;
|
||||
protected Object sockLock = new Object(); // Guards sockMgr and mySockets
|
||||
private I2PSocketManager sockMgr;
|
||||
private List mySockets = new ArrayList();
|
||||
protected I2PSocketManager sockMgr;
|
||||
protected List mySockets = new ArrayList();
|
||||
|
||||
protected Destination dest = null;
|
||||
private int localPort;
|
||||
@@ -57,6 +57,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
|
||||
private String handlerName;
|
||||
|
||||
private Object conLock = new Object();
|
||||
private int pendingConnections = 0;
|
||||
|
||||
//public I2PTunnelClientBase(int localPort, boolean ownDest,
|
||||
// Logging l) {
|
||||
// I2PTunnelClientBase(localPort, ownDest, l, (EventDispatcher)null);
|
||||
@@ -96,7 +99,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
t.start();
|
||||
open = true;
|
||||
synchronized (this) {
|
||||
while (!listenerReady) {
|
||||
while (!listenerReady && open) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
@@ -109,7 +112,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
l.log("Ready! Port " + getLocalPort());
|
||||
notifyEvent("openBaseClientResult", "ok");
|
||||
} else {
|
||||
l.log("Error!");
|
||||
l.log("Error listening - please see the logs!");
|
||||
notifyEvent("openBaseClientResult", "error");
|
||||
}
|
||||
}
|
||||
@@ -181,9 +184,24 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
private I2PSocketOptions getDefaultOptions() {
|
||||
I2PSocketOptions opts = new I2PSocketOptions();
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
protected I2PSocketOptions getDefaultOptions() {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
defaultOpts.putAll(overrides);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
@@ -227,7 +245,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
public final void run() {
|
||||
try {
|
||||
InetAddress addr = getListenHost(l);
|
||||
if (addr == null) return;
|
||||
if (addr == null) {
|
||||
open = false;
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
return;
|
||||
}
|
||||
ss = new ServerSocket(localPort, 0, addr);
|
||||
|
||||
// If a free port was requested, find out what we got
|
||||
@@ -258,8 +282,15 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
manageConnection(s);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error listening for connections", ex);
|
||||
_log.error("Error listening for connections on " + localPort, ex);
|
||||
notifyEvent("openBaseClientResult", "error");
|
||||
synchronized (sockLock) {
|
||||
mySockets.clear();
|
||||
}
|
||||
open = false;
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +300,16 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
* @param s Socket to take care of
|
||||
*/
|
||||
protected void manageConnection(Socket s) {
|
||||
new ClientConnectionRunner(s, handlerName);
|
||||
boolean useBlocking = false;
|
||||
synchronized (conLock) {
|
||||
pendingConnections++;
|
||||
if (pendingConnections > 5)
|
||||
useBlocking = true;
|
||||
}
|
||||
if (useBlocking)
|
||||
clientConnectionRun(s);
|
||||
else
|
||||
new ClientConnectionRunner(s, handlerName);
|
||||
}
|
||||
|
||||
public boolean close(boolean forced) {
|
||||
@@ -326,6 +366,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
|
||||
public void run() {
|
||||
clientConnectionRun(s);
|
||||
synchronized (conLock) {
|
||||
pendingConnections--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -14,10 +14,12 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Clock;
|
||||
@@ -142,14 +144,49 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int DEFAULT_READ_TIMEOUT = 60*1000;
|
||||
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions() {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT);
|
||||
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
defaultOpts.putAll(overrides);
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT);
|
||||
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
private static long __requestId = 0;
|
||||
protected void clientConnectionRun(Socket s) {
|
||||
OutputStream out = null;
|
||||
String targetRequest = null;
|
||||
boolean usingWWWProxy = false;
|
||||
String currentProxy = null;
|
||||
InactivityTimeoutThread timeoutThread = null;
|
||||
long requestId = ++__requestId;
|
||||
try {
|
||||
out = s.getOutputStream();
|
||||
@@ -295,7 +332,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix(requestId) + "Setting host = " + host);
|
||||
} else if (line.startsWith("User-Agent: ")) {
|
||||
line = "User-Agent: MYOB/6.66 (AN/ON)";
|
||||
// always stripped, added back at the end
|
||||
line = null;
|
||||
continue;
|
||||
} else if (line.startsWith("Accept")) {
|
||||
// strip the accept-blah headers, as they vary dramatically from
|
||||
// browser to browser
|
||||
line = null;
|
||||
continue;
|
||||
} else if (line.startsWith("Referer: ")) {
|
||||
// Shouldn't we be more specific, like accepting in-site referers ?
|
||||
//line = "Referer: i2p";
|
||||
@@ -313,6 +357,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
if (line.length() == 0) {
|
||||
newRequest.append("User-Agent: MYOB/6.66 (AN/ON)\r\n");
|
||||
newRequest.append("Connection: close\r\n\r\n");
|
||||
break;
|
||||
} else {
|
||||
@@ -354,25 +399,26 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
return;
|
||||
}
|
||||
String remoteID;
|
||||
I2PSocket i2ps = createI2PSocket(dest);
|
||||
|
||||
Properties opts = new Properties();
|
||||
opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
|
||||
// 1 == disconnect. see ConnectionOptions in the new streaming lib, which i
|
||||
// dont want to hard link to here
|
||||
opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
|
||||
I2PSocket i2ps = createI2PSocket(dest, getDefaultOptions(opts));
|
||||
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
|
||||
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data);
|
||||
timeoutThread = new InactivityTimeoutThread(runner, out, targetRequest, usingWWWProxy, currentProxy, s, requestId);
|
||||
timeoutThread.start();
|
||||
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets);
|
||||
} catch (SocketException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (IOException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (I2PException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info("getPrefix(requestId) + Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
@@ -380,91 +426,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
private static final long INACTIVITY_TIMEOUT = 120 * 1000;
|
||||
private static volatile long __timeoutId = 0;
|
||||
|
||||
private class InactivityTimeoutThread extends I2PThread {
|
||||
|
||||
private Socket s;
|
||||
private I2PTunnelRunner _runner;
|
||||
private OutputStream _out;
|
||||
private String _targetRequest;
|
||||
private boolean _useWWWProxy;
|
||||
private String _currentProxy;
|
||||
private long _requestId;
|
||||
private boolean _disabled;
|
||||
private Object _disableLock = new Object();
|
||||
|
||||
public InactivityTimeoutThread(I2PTunnelRunner runner, OutputStream out, String targetRequest,
|
||||
boolean useWWWProxy, String currentProxy, Socket s, long requestId) {
|
||||
this.s = s;
|
||||
_runner = runner;
|
||||
_out = out;
|
||||
_targetRequest = targetRequest;
|
||||
_useWWWProxy = useWWWProxy;
|
||||
_currentProxy = currentProxy;
|
||||
_disabled = false;
|
||||
_requestId = requestId;
|
||||
long timeoutId = ++__timeoutId;
|
||||
setName("InactivityThread " + getPrefix(requestId) + timeoutId);
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
_disabled = true;
|
||||
synchronized (_disableLock) {
|
||||
_disableLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (!_disabled) {
|
||||
if (_runner.isFinished()) {
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(_requestId) + "HTTP client request completed prior to timeout");
|
||||
return;
|
||||
}
|
||||
if (_runner.getLastActivityOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
|
||||
if (_runner.getStartedOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix(_requestId) + "HTTP client request timed out (lastActivity: "
|
||||
+ new Date(_runner.getLastActivityOn()) + ", startedOn: "
|
||||
+ new Date(_runner.getStartedOn()) + ")");
|
||||
timeout();
|
||||
return;
|
||||
} else {
|
||||
// runner hasn't been going to long enough
|
||||
}
|
||||
} else {
|
||||
// there has been activity in the period
|
||||
}
|
||||
synchronized (_disableLock) {
|
||||
try {
|
||||
_disableLock.wait(INACTIVITY_TIMEOUT);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void timeout() {
|
||||
_log.info(getPrefix(_requestId) + "Inactivity timeout reached");
|
||||
l.log("Inactivity timeout reached");
|
||||
if (_out != null) {
|
||||
try {
|
||||
if (_runner.getLastActivityOn() > 0) {
|
||||
// some data has been sent, so don't 404 it
|
||||
} else {
|
||||
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest, _useWWWProxy, _currentProxy);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.warn(getPrefix(_requestId) + "Error writing out the 'timeout' message", ioe);
|
||||
}
|
||||
} else {
|
||||
_log.warn(getPrefix(_requestId) + "Client disconnected before we could say we timed out");
|
||||
}
|
||||
closeSocket(s);
|
||||
}
|
||||
}
|
||||
|
||||
private final static String getHostName(String host) {
|
||||
if (host == null) return null;
|
||||
try {
|
||||
|
@@ -11,9 +11,11 @@ import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
@@ -38,14 +40,17 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
Object slock, finishLock = new Object();
|
||||
boolean finished = false;
|
||||
HashMap ostreams, sockets;
|
||||
I2PSession session;
|
||||
byte[] initialData;
|
||||
/** when the last data was sent/received (or -1 if never) */
|
||||
private long lastActivityOn;
|
||||
/** when the runner started up */
|
||||
private long startedOn;
|
||||
private List sockList;
|
||||
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialData) {
|
||||
private volatile long __forwarderId;
|
||||
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialData, List sockList) {
|
||||
this.sockList = sockList;
|
||||
this.s = s;
|
||||
this.i2ps = i2ps;
|
||||
this.slock = slock;
|
||||
@@ -55,6 +60,7 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("I2PTunnelRunner started");
|
||||
_runnerId = ++__runnerId;
|
||||
__forwarderId = i2ps.hashCode();
|
||||
setName("I2PTunnelRunner " + _runnerId);
|
||||
start();
|
||||
}
|
||||
@@ -91,50 +97,68 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
}
|
||||
|
||||
public void run() {
|
||||
boolean closedCleanly = false;
|
||||
try {
|
||||
InputStream in = s.getInputStream();
|
||||
OutputStream out = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
|
||||
OutputStream out = s.getOutputStream(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
|
||||
i2ps.setSocketErrorListener(this);
|
||||
InputStream i2pin = i2ps.getInputStream();
|
||||
OutputStream i2pout = new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
|
||||
OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
|
||||
if (initialData != null) {
|
||||
synchronized (slock) {
|
||||
i2pout.write(initialData);
|
||||
i2pout.flush();
|
||||
//i2pout.flush();
|
||||
}
|
||||
}
|
||||
Thread t1 = new StreamForwarder(in, i2pout);
|
||||
Thread t2 = new StreamForwarder(i2pin, out);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Initial data " + (initialData != null ? initialData.length : 0)
|
||||
+ " written, starting forwarders");
|
||||
Thread t1 = new StreamForwarder(in, i2pout, "toI2P");
|
||||
Thread t2 = new StreamForwarder(i2pin, out, "fromI2P");
|
||||
synchronized (finishLock) {
|
||||
while (!finished) {
|
||||
finishLock.wait();
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("At least one forwarder completed, closing and joining");
|
||||
// now one connection is dead - kill the other as well.
|
||||
s.close();
|
||||
s = null;
|
||||
i2ps.close();
|
||||
i2ps = null;
|
||||
t1.join();
|
||||
t2.join();
|
||||
t1.join(30*1000);
|
||||
t2.join(30*1000);
|
||||
closedCleanly = true;
|
||||
} catch (InterruptedException ex) {
|
||||
_log.error("Interrupted", ex);
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Interrupted", ex);
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
_log.debug("Error forwarding", ex);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Error forwarding", ex);
|
||||
} catch (Exception e) {
|
||||
_log.error("Internal error", e);
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Internal error", e);
|
||||
} finally {
|
||||
removeRef();
|
||||
try {
|
||||
if (s != null) s.close();
|
||||
if (i2ps != null) i2ps.close();
|
||||
if ( (s != null) && (!closedCleanly) )
|
||||
s.close();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
_log.error("Could not close socket", ex);
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close java socket", ex);
|
||||
}
|
||||
try {
|
||||
if (i2ps != null) {
|
||||
if (!closedCleanly)
|
||||
i2ps.close();
|
||||
i2ps.setSocketErrorListener(null);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close I2PSocket", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void errorOccurred() {
|
||||
synchronized (finishLock) {
|
||||
finished = true;
|
||||
@@ -142,22 +166,43 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
}
|
||||
}
|
||||
|
||||
private volatile long __forwarderId = 0;
|
||||
private void removeRef() {
|
||||
if (sockList != null) {
|
||||
synchronized (slock) {
|
||||
boolean removed = sockList.remove(i2ps);
|
||||
//System.out.println("Removal of i2psocket " + i2ps + " successful? "
|
||||
// + removed + " remaining: " + sockList.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StreamForwarder extends I2PThread {
|
||||
|
||||
InputStream in;
|
||||
OutputStream out;
|
||||
String direction;
|
||||
private ByteCache _cache;
|
||||
|
||||
private StreamForwarder(InputStream in, OutputStream out) {
|
||||
private StreamForwarder(InputStream in, OutputStream out, String dir) {
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
direction = dir;
|
||||
_cache = ByteCache.getInstance(256, NETWORK_BUFFER_SIZE);
|
||||
setName("StreamForwarder " + _runnerId + "." + (++__forwarderId));
|
||||
start();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
byte[] buffer = new byte[NETWORK_BUFFER_SIZE];
|
||||
String from = i2ps.getThisDestination().calculateHash().toBase64().substring(0,6);
|
||||
String to = i2ps.getPeerDestination().calculateHash().toBase64().substring(0,6);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(direction + ": Forwarding between "
|
||||
+ from + " and " + to);
|
||||
}
|
||||
|
||||
ByteArray ba = _cache.acquire();
|
||||
byte[] buffer = ba.getData(); // new byte[NETWORK_BUFFER_SIZE];
|
||||
try {
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
@@ -166,45 +211,65 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
if (len > 0) updateActivity();
|
||||
|
||||
if (in.available() == 0) {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Flushing after sending " + len + " bytes through");
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(direction + ": " + len + " bytes flushed through to "
|
||||
+ i2ps.getPeerDestination().calculateHash().toBase64().substring(0,6));
|
||||
try {
|
||||
Thread.sleep(I2PTunnel.PACKET_DELAY);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (in.available() == 0) {
|
||||
out.flush(); // make sure the data get though
|
||||
|
||||
if (in.available() <= 0)
|
||||
out.flush(); // make sure the data get though
|
||||
}
|
||||
}
|
||||
//out.flush(); // close() flushes
|
||||
} catch (SocketException ex) {
|
||||
// this *will* occur when the other threads closes the socket
|
||||
synchronized (finishLock) {
|
||||
if (!finished) {
|
||||
_log.debug("Socket closed - error reading and writing",
|
||||
ex);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(direction + ": Socket closed - error reading and writing",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedIOException ex) {
|
||||
_log.warn("Closing connection due to timeout (error: \""
|
||||
+ ex.getMessage() + "\")");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(direction + ": Closing connection due to timeout (error: \""
|
||||
+ ex.getMessage() + "\")");
|
||||
} catch (IOException ex) {
|
||||
if (!finished)
|
||||
_log.error("Error forwarding", ex);
|
||||
if (!finished) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error(direction + ": Error forwarding", ex);
|
||||
}
|
||||
//else
|
||||
// _log.warn("You may ignore this", ex);
|
||||
} finally {
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
_log.info(direction + ": done forwarding between "
|
||||
+ from + " and " + to);
|
||||
}
|
||||
try {
|
||||
out.close();
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error closing streams", ex);
|
||||
_log.warn(direction + ": Error closing input stream", ex);
|
||||
}
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(direction + ": Error closing output stream", ex);
|
||||
}
|
||||
synchronized (finishLock) {
|
||||
finished = true;
|
||||
finishLock.notifyAll();
|
||||
// the main thread will close sockets etc. now
|
||||
}
|
||||
_cache.release(ba);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -179,7 +179,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
_handleSocket.setReadTimeout(readTimeout);
|
||||
Socket s = new Socket(remoteHost, remotePort);
|
||||
afterSocket = I2PAppContext.getGlobalContext().clock().now();
|
||||
new I2PTunnelRunner(s, _handleSocket, slock, null);
|
||||
new I2PTunnelRunner(s, _handleSocket, slock, null, null);
|
||||
} catch (SocketException ex) {
|
||||
try {
|
||||
_handleSocket.close();
|
||||
|
@@ -31,6 +31,7 @@ public class TunnelController implements Logging {
|
||||
private List _messages;
|
||||
private List _sessions;
|
||||
private boolean _running;
|
||||
private boolean _starting;
|
||||
|
||||
/**
|
||||
* Create a new controller for a tunnel out of the specific config options.
|
||||
@@ -58,8 +59,7 @@ public class TunnelController implements Logging {
|
||||
_running = false;
|
||||
if (createKey && ("server".equals(getType())) )
|
||||
createPrivateKey();
|
||||
if (getStartOnLoad())
|
||||
startTunnel();
|
||||
_starting = getStartOnLoad();
|
||||
}
|
||||
|
||||
private void createPrivateKey() {
|
||||
@@ -104,12 +104,14 @@ public class TunnelController implements Logging {
|
||||
*
|
||||
*/
|
||||
public void startTunnel() {
|
||||
_starting = true;
|
||||
try {
|
||||
doStartTunnel();
|
||||
} catch (Exception e) {
|
||||
_log.error("Error starting up the tunnel", e);
|
||||
log("Error starting up the tunnel - " + e.getMessage());
|
||||
}
|
||||
_starting = false;
|
||||
}
|
||||
private void doStartTunnel() {
|
||||
if (_running) {
|
||||
@@ -299,6 +301,7 @@ public class TunnelController implements Logging {
|
||||
public boolean getStartOnLoad() { return "true".equalsIgnoreCase(_config.getProperty("startOnLoad", "true")); }
|
||||
|
||||
public boolean getIsRunning() { return _running; }
|
||||
public boolean getIsStarting() { return _starting; }
|
||||
|
||||
public void getSummary(StringBuffer buf) {
|
||||
String type = getType();
|
||||
|
@@ -8,6 +8,7 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -20,6 +21,8 @@ import java.util.TreeMap;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@@ -43,28 +46,34 @@ public class TunnelControllerGroup {
|
||||
*/
|
||||
private Map _sessions;
|
||||
|
||||
public static TunnelControllerGroup getInstance() { return _instance; }
|
||||
public static TunnelControllerGroup getInstance() {
|
||||
synchronized (TunnelControllerGroup.class) {
|
||||
if (_instance == null)
|
||||
_instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private TunnelControllerGroup(String configFile) {
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelControllerGroup.class);
|
||||
_instance = this;
|
||||
_controllers = new ArrayList();
|
||||
_controllers = Collections.synchronizedList(new ArrayList());
|
||||
_configFile = configFile;
|
||||
_sessions = new HashMap(4);
|
||||
loadControllers(_configFile);
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
if ( (args == null) || (args.length <= 0) ) {
|
||||
new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
|
||||
} else if (args.length == 1) {
|
||||
if (DEFAULT_CONFIG_FILE.equals(args[0]))
|
||||
new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
|
||||
else
|
||||
new TunnelControllerGroup(args[0]);
|
||||
} else {
|
||||
System.err.println("Usage: TunnelControllerGroup [filename]");
|
||||
return;
|
||||
synchronized (TunnelControllerGroup.class) {
|
||||
if (_instance != null) return; // already loaded through the web
|
||||
|
||||
if ( (args == null) || (args.length <= 0) ) {
|
||||
_instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
|
||||
} else if (args.length == 1) {
|
||||
_instance = new TunnelControllerGroup(args[0]);
|
||||
} else {
|
||||
System.err.println("Usage: TunnelControllerGroup [filename]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,10 +98,24 @@ public class TunnelControllerGroup {
|
||||
_controllers.add(controller);
|
||||
i++;
|
||||
}
|
||||
I2PThread startupThread = new I2PThread(new StartControllers(), "Startup tunnels");
|
||||
startupThread.start();
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(i + " controllers loaded from " + configFile);
|
||||
}
|
||||
|
||||
private class StartControllers implements Runnable {
|
||||
public void run() {
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
if (controller.getStartOnLoad())
|
||||
controller.startTunnel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void reloadControllers() {
|
||||
unloadControllers();
|
||||
loadControllers(_configFile);
|
||||
@@ -143,7 +166,6 @@ public class TunnelControllerGroup {
|
||||
controller.stopTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers stopped");
|
||||
return msgs;
|
||||
@@ -161,7 +183,7 @@ public class TunnelControllerGroup {
|
||||
controller.startTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers started");
|
||||
return msgs;
|
||||
@@ -259,33 +281,13 @@ public class TunnelControllerGroup {
|
||||
}
|
||||
|
||||
Properties props = new Properties();
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(cfgFile);
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
|
||||
String line = null;
|
||||
while ( (line = in.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.length() <= 0) continue;
|
||||
if (line.startsWith("#") || line.startsWith(";"))
|
||||
continue;
|
||||
int eq = line.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= line.length() - 1) )
|
||||
continue;
|
||||
String key = line.substring(0, eq);
|
||||
String val = line.substring(eq+1);
|
||||
props.setProperty(key, val);
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Props loaded with " + props.size() + " lines");
|
||||
DataHelper.loadProps(props, cfgFile);
|
||||
return props;
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error reading the controllers from " + configFile, ioe);
|
||||
return null;
|
||||
} finally {
|
||||
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -60,6 +60,9 @@ class WebEditPageFormGenerator {
|
||||
buf.append("value=\"squid.i2p\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
buf.append("<hr />Note: the following options are shared across all client tunnels and");
|
||||
buf.append(" HTTP proxies<br />\n");
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
@@ -80,6 +83,9 @@ class WebEditPageFormGenerator {
|
||||
buf.append("value=\"").append(controller.getTargetDestination()).append("\" ");
|
||||
buf.append(" /> (either the hosts.txt name or the full base64 destination)<br />\n");
|
||||
|
||||
buf.append("<hr />Note: the following options are shared across all client tunnels and");
|
||||
buf.append(" HTTP proxies<br />\n");
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\"><br />\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
@@ -145,6 +151,13 @@ class WebEditPageFormGenerator {
|
||||
if ( (controller != null) && (controller.getDescription() != null) )
|
||||
buf.append("value=\"").append(controller.getDescription()).append("\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
buf.append("<b>Start automatically?</b> \n");
|
||||
buf.append("<input type=\"checkbox\" name=\"startOnLoad\" value=\"true\" ");
|
||||
if ( (controller != null) && (controller.getStartOnLoad()) )
|
||||
buf.append(" checked=\"true\" />\n<br />\n");
|
||||
else
|
||||
buf.append(" />\n<br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,6 +207,7 @@ class WebEditPageFormGenerator {
|
||||
private static void addOptions(StringBuffer buf, TunnelController controller) {
|
||||
int tunnelDepth = 2;
|
||||
int numTunnels = 2;
|
||||
int connectDelay = 0;
|
||||
Properties opts = getOptions(controller);
|
||||
if (opts != null) {
|
||||
String depth = opts.getProperty("tunnels.depthInbound");
|
||||
@@ -212,6 +226,14 @@ class WebEditPageFormGenerator {
|
||||
numTunnels = 2;
|
||||
}
|
||||
}
|
||||
String delay = opts.getProperty("i2p.streaming.connectDelay");
|
||||
if (delay != null) {
|
||||
try {
|
||||
connectDelay = Integer.parseInt(delay);
|
||||
} catch (NumberFormatException nfe) {
|
||||
connectDelay = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.append("<b>Tunnel depth:</b> ");
|
||||
@@ -251,6 +273,13 @@ class WebEditPageFormGenerator {
|
||||
}
|
||||
buf.append("</select><br />\n");
|
||||
|
||||
buf.append("<b>Delay connection briefly? </b> ");
|
||||
buf.append("<input type=\"checkbox\" name=\"connectDelay\" value=\"");
|
||||
buf.append((connectDelay > 0 ? connectDelay : 1000)).append("\" ");
|
||||
if (connectDelay > 0)
|
||||
buf.append("checked=\"true\" ");
|
||||
buf.append("/> (useful for brief request/response connections)<br />\n");
|
||||
|
||||
buf.append("<b>I2CP host:</b> ");
|
||||
buf.append("<input type=\"text\" name=\"clientHost\" size=\"20\" value=\"");
|
||||
if ( (controller != null) && (controller.getI2CPHost() != null) )
|
||||
@@ -275,18 +304,13 @@ class WebEditPageFormGenerator {
|
||||
String val = opts.getProperty(key);
|
||||
if ("tunnels.depthInbound".equals(key)) continue;
|
||||
if ("tunnels.numInbound".equals(key)) continue;
|
||||
if ("i2p.streaming.connectDelay".equals(key)) continue;
|
||||
if (i != 0) buf.append(' ');
|
||||
buf.append(key).append('=').append(val);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
buf.append("\" /><br />\n");
|
||||
buf.append("<b>Start automatically?</b> \n");
|
||||
buf.append("<input type=\"checkbox\" name=\"startOnLoad\" value=\"true\" ");
|
||||
if ( (controller != null) && (controller.getStartOnLoad()) )
|
||||
buf.append(" checked=\"true\" />\n<br />\n");
|
||||
else
|
||||
buf.append(" />\n<br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -29,6 +29,7 @@ public class WebEditPageHelper {
|
||||
private String _i2cpPort;
|
||||
private String _tunnelDepth;
|
||||
private String _tunnelCount;
|
||||
private boolean _connectDelay;
|
||||
private String _customOptions;
|
||||
private String _proxyList;
|
||||
private String _port;
|
||||
@@ -164,6 +165,9 @@ public class WebEditPageHelper {
|
||||
public void setStartOnLoad(String moo) {
|
||||
_startOnLoad = true;
|
||||
}
|
||||
public void setConnectDelay(String moo) {
|
||||
_connectDelay = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the form and display any resulting messages
|
||||
@@ -252,6 +256,27 @@ public class WebEditPageHelper {
|
||||
cur.setConfig(config, "");
|
||||
}
|
||||
|
||||
if ("httpclient".equals(cur.getType()) || "client".equals(cur.getType())) {
|
||||
// all clients use the same I2CP session, and as such, use the same
|
||||
// I2CP options
|
||||
List controllers = TunnelControllerGroup.getInstance().getControllers();
|
||||
for (int i = 0; i < controllers.size(); i++) {
|
||||
TunnelController c = (TunnelController)controllers.get(i);
|
||||
if (c == cur) continue;
|
||||
if ("httpclient".equals(c.getType()) || "client".equals(c.getType())) {
|
||||
Properties cOpt = c.getConfig("");
|
||||
if (_tunnelCount != null)
|
||||
cOpt.setProperty("option.tunnels.numInbound", _tunnelCount);
|
||||
if (_tunnelDepth != null)
|
||||
cOpt.setProperty("option.tunnels.depthInbound", _tunnelDepth);
|
||||
if (_connectDelay)
|
||||
cOpt.setProperty("option.i2p.streaming.connectDelay", "1000");
|
||||
else
|
||||
cOpt.setProperty("option.i2p.streaming.connectDelay", "0");
|
||||
c.setConfig(cOpt, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getMessages(doSave());
|
||||
}
|
||||
@@ -324,6 +349,7 @@ public class WebEditPageHelper {
|
||||
String val = pair.substring(eq+1);
|
||||
if ("tunnels.numInbound".equals(key)) continue;
|
||||
if ("tunnels.depthInbound".equals(key)) continue;
|
||||
if ("i2p.streaming.connectDelay".equals(key)) continue;
|
||||
config.setProperty("option." + key, val);
|
||||
}
|
||||
}
|
||||
@@ -334,6 +360,10 @@ public class WebEditPageHelper {
|
||||
config.setProperty("option.tunnels.numInbound", _tunnelCount);
|
||||
if (_tunnelDepth != null)
|
||||
config.setProperty("option.tunnels.depthInbound", _tunnelDepth);
|
||||
if (_connectDelay)
|
||||
config.setProperty("option.i2p.streaming.connectDelay", "1000");
|
||||
else
|
||||
config.setProperty("option.i2p.streaming.connectDelay", "0");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -68,6 +68,8 @@ public class WebStatusPageHelper {
|
||||
if (controller.getIsRunning()) {
|
||||
buf.append("<i>running</i> ");
|
||||
buf.append("<a href=\"index.jsp?num=").append(num).append("&action=stop\">stop</a> ");
|
||||
} else if (controller.getIsStarting()) {
|
||||
buf.append("<i>startup in progress (please be patient)</i>");
|
||||
} else {
|
||||
buf.append("<i>not running</i> ");
|
||||
buf.append("<a href=\"index.jsp?num=").append(num).append("&action=start\">start</a> ");
|
||||
|
@@ -47,7 +47,7 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
|
||||
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
|
||||
Socket clientSock = serv.getClientSocket();
|
||||
I2PSocket destSock = serv.getDestinationI2PSocket();
|
||||
new I2PTunnelRunner(clientSock, destSock, sockLock, null);
|
||||
new I2PTunnelRunner(clientSock, destSock, sockLock, null, mySockets);
|
||||
} catch (SOCKSException e) {
|
||||
_log.error("Error from SOCKS connection: " + e.getMessage());
|
||||
closeSocket(s);
|
||||
|
@@ -81,7 +81,7 @@ public abstract class SOCKSServer {
|
||||
if (connHostName.toLowerCase().endsWith(".i2p")) {
|
||||
_log.debug("connecting to " + connHostName + "...");
|
||||
I2PSocketManager sm = I2PSocketManagerFactory.createManager();
|
||||
destSock = sm.connect(I2PTunnel.destFromName(connHostName), new I2PSocketOptions());
|
||||
destSock = sm.connect(I2PTunnel.destFromName(connHostName), null);
|
||||
confirmConnection();
|
||||
_log.debug("connection confirmed - exchanging data...");
|
||||
} else {
|
||||
|
@@ -5,7 +5,7 @@ package net.i2p.client.streaming;
|
||||
* so care should be taken when using in a multithreaded environment.
|
||||
*
|
||||
*/
|
||||
public class ByteCollector {
|
||||
class ByteCollector {
|
||||
byte[] contents;
|
||||
int size;
|
||||
|
||||
|
@@ -49,6 +49,9 @@ public interface I2PSocketManager {
|
||||
public void setDefaultOptions(I2PSocketOptions options);
|
||||
public I2PSocketOptions getDefaultOptions();
|
||||
public I2PServerSocket getServerSocket();
|
||||
|
||||
public I2PSocketOptions buildOptions();
|
||||
public I2PSocketOptions buildOptions(Properties opts);
|
||||
|
||||
/**
|
||||
* Create a new connected socket (block until the socket is created)
|
||||
|
@@ -26,8 +26,8 @@ public class I2PSocketManagerFactory {
|
||||
private final static Log _log = new Log(I2PSocketManagerFactory.class);
|
||||
|
||||
public static final String PROP_MANAGER = "i2p.streaming.manager";
|
||||
public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerImpl";
|
||||
//public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerFull";
|
||||
//public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerImpl";
|
||||
public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerFull";
|
||||
|
||||
/**
|
||||
* Create a socket manager using a brand new destination connected to the
|
||||
@@ -90,7 +90,7 @@ public class I2PSocketManagerFactory {
|
||||
opts.setProperty(name, System.getProperty(name));
|
||||
}
|
||||
boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
|
||||
if (oldLib) {
|
||||
if (oldLib && false) {
|
||||
// for the old streaming lib
|
||||
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
|
||||
//opts.setProperty("tunnels.depthInbound", "0");
|
||||
@@ -106,7 +106,10 @@ public class I2PSocketManagerFactory {
|
||||
try {
|
||||
I2PSession session = client.createSession(myPrivateKeyStream, opts);
|
||||
session.connect();
|
||||
return createManager(session, opts, "manager");
|
||||
I2PSocketManager sockMgr = createManager(session, opts, "manager");
|
||||
if (sockMgr != null)
|
||||
sockMgr.setDefaultOptions(sockMgr.buildOptions(opts));
|
||||
return sockMgr;
|
||||
} catch (I2PSessionException ise) {
|
||||
_log.error("Error creating session for socket manager", ise);
|
||||
return null;
|
||||
@@ -117,7 +120,7 @@ public class I2PSocketManagerFactory {
|
||||
if (false) {
|
||||
I2PSocketManagerImpl mgr = new I2PSocketManagerImpl();
|
||||
mgr.setSession(session);
|
||||
mgr.setDefaultOptions(new I2PSocketOptions());
|
||||
//mgr.setDefaultOptions(new I2PSocketOptions());
|
||||
return mgr;
|
||||
} else {
|
||||
String classname = opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER);
|
||||
|
@@ -35,7 +35,7 @@ import net.i2p.util.Log;
|
||||
* or receive any messages with its .receiveMessage
|
||||
*
|
||||
*/
|
||||
public class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
|
||||
class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private I2PSession _session;
|
||||
@@ -77,7 +77,7 @@ public class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListene
|
||||
_outSockets = new HashMap(16);
|
||||
_acceptTimeout = ACCEPT_TIMEOUT_DEFAULT;
|
||||
setSession(session);
|
||||
setDefaultOptions(new I2PSocketOptions());
|
||||
setDefaultOptions(buildOptions(opts));
|
||||
_context.statManager().createRateStat("streaming.lifetime", "How long before the socket is closed?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("streaming.sent", "How many bytes are sent in the stream?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("streaming.received", "How many bytes are received in the stream?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
@@ -433,6 +433,11 @@ public class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListene
|
||||
public I2PSocketOptions getDefaultOptions() {
|
||||
return _defaultOptions;
|
||||
}
|
||||
|
||||
public I2PSocketOptions buildOptions() { return buildOptions(null); }
|
||||
public I2PSocketOptions buildOptions(Properties opts) {
|
||||
return new I2PSocketOptionsImpl(opts);
|
||||
}
|
||||
|
||||
public I2PServerSocket getServerSocket() {
|
||||
if (_serverSocket == null) {
|
||||
|
@@ -6,67 +6,38 @@ import java.util.Properties;
|
||||
* Define the configuration for streaming and verifying data on the socket.
|
||||
*
|
||||
*/
|
||||
public class I2PSocketOptions {
|
||||
private long _connectTimeout;
|
||||
private long _readTimeout;
|
||||
private long _writeTimeout;
|
||||
private int _maxBufferSize;
|
||||
|
||||
public static final int DEFAULT_BUFFER_SIZE = 1024*64;
|
||||
public static final int DEFAULT_WRITE_TIMEOUT = 60*1000;
|
||||
|
||||
public I2PSocketOptions() {
|
||||
_connectTimeout = -1;
|
||||
_readTimeout = -1;
|
||||
_writeTimeout = DEFAULT_WRITE_TIMEOUT;
|
||||
_maxBufferSize = DEFAULT_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
public I2PSocketOptions(I2PSocketOptions opts) {
|
||||
_connectTimeout = opts.getConnectTimeout();
|
||||
_readTimeout = opts.getReadTimeout();
|
||||
_writeTimeout = opts.getWriteTimeout();
|
||||
_maxBufferSize = opts.getMaxBufferSize();
|
||||
}
|
||||
|
||||
public I2PSocketOptions(Properties opts) {
|
||||
|
||||
}
|
||||
public interface I2PSocketOptions {
|
||||
public static final String PROP_BUFFER_SIZE = "i2p.streaming.bufferSize";
|
||||
public static final String PROP_CONNECT_TIMEOUT = "i2p.streaming.connectTimeout";
|
||||
public static final String PROP_READ_TIMEOUT = "i2p.streaming.readTimeout";
|
||||
public static final String PROP_WRITE_TIMEOUT = "i2p.streaming.writeTimeout";
|
||||
|
||||
/**
|
||||
* How long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
* @return milliseconds to wait, or -1 if we will wait indefinitely
|
||||
*/
|
||||
public long getConnectTimeout() {
|
||||
return _connectTimeout;
|
||||
}
|
||||
public long getConnectTimeout();
|
||||
|
||||
/**
|
||||
* Define how long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
*/
|
||||
public void setConnectTimeout(long ms) {
|
||||
_connectTimeout = ms;
|
||||
}
|
||||
public void setConnectTimeout(long ms);
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public long getReadTimeout() {
|
||||
return _readTimeout;
|
||||
}
|
||||
public long getReadTimeout();
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public void setReadTimeout(long ms) {
|
||||
_readTimeout = ms;
|
||||
}
|
||||
public void setReadTimeout(long ms);
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
@@ -76,9 +47,7 @@ public class I2PSocketOptions {
|
||||
*
|
||||
* @return buffer size limit, in bytes
|
||||
*/
|
||||
public int getMaxBufferSize() {
|
||||
return _maxBufferSize;
|
||||
}
|
||||
public int getMaxBufferSize();
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
@@ -87,9 +56,7 @@ public class I2PSocketOptions {
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
*/
|
||||
public void setMaxBufferSize(int numBytes) {
|
||||
_maxBufferSize = numBytes;
|
||||
}
|
||||
public void setMaxBufferSize(int numBytes);
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
@@ -97,9 +64,7 @@ public class I2PSocketOptions {
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public long getWriteTimeout() {
|
||||
return _writeTimeout;
|
||||
}
|
||||
public long getWriteTimeout();
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
@@ -107,7 +72,5 @@ public class I2PSocketOptions {
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public void setWriteTimeout(long ms) {
|
||||
_writeTimeout = ms;
|
||||
}
|
||||
public void setWriteTimeout(long ms);
|
||||
}
|
||||
|
@@ -0,0 +1,136 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Define the configuration for streaming and verifying data on the socket.
|
||||
*
|
||||
*/
|
||||
class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
private long _connectTimeout;
|
||||
private long _readTimeout;
|
||||
private long _writeTimeout;
|
||||
private int _maxBufferSize;
|
||||
|
||||
public static final int DEFAULT_BUFFER_SIZE = 1024*64;
|
||||
public static final int DEFAULT_WRITE_TIMEOUT = 60*1000;
|
||||
public static final int DEFAULT_CONNECT_TIMEOUT = 60*1000;
|
||||
|
||||
public I2PSocketOptionsImpl() {
|
||||
this(System.getProperties());
|
||||
}
|
||||
|
||||
public I2PSocketOptionsImpl(I2PSocketOptions opts) {
|
||||
this(System.getProperties());
|
||||
if (opts != null) {
|
||||
_connectTimeout = opts.getConnectTimeout();
|
||||
_readTimeout = opts.getReadTimeout();
|
||||
_writeTimeout = opts.getWriteTimeout();
|
||||
_maxBufferSize = opts.getMaxBufferSize();
|
||||
}
|
||||
}
|
||||
|
||||
public I2PSocketOptionsImpl(Properties opts) {
|
||||
init(opts);
|
||||
}
|
||||
|
||||
protected void init(Properties opts) {
|
||||
_maxBufferSize = getInt(opts, PROP_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
|
||||
_connectTimeout = getInt(opts, PROP_CONNECT_TIMEOUT, DEFAULT_CONNECT_TIMEOUT);
|
||||
_readTimeout = getInt(opts, PROP_READ_TIMEOUT, -1);
|
||||
_writeTimeout = getInt(opts, PROP_WRITE_TIMEOUT, DEFAULT_WRITE_TIMEOUT);
|
||||
}
|
||||
|
||||
protected int getInt(Properties opts, String name, int defaultVal) {
|
||||
if (opts == null) return defaultVal;
|
||||
String val = opts.getProperty(name);
|
||||
if (val == null) {
|
||||
return defaultVal;
|
||||
} else {
|
||||
try {
|
||||
return Integer.parseInt(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* How long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
* @return milliseconds to wait, or -1 if we will wait indefinitely
|
||||
*/
|
||||
public long getConnectTimeout() {
|
||||
return _connectTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define how long we will wait for the ACK from a SYN, in milliseconds.
|
||||
*
|
||||
*/
|
||||
public void setConnectTimeout(long ms) {
|
||||
_connectTimeout = ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public long getReadTimeout() {
|
||||
return _readTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public void setReadTimeout(long ms) {
|
||||
_readTimeout = ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
* this amount has been exceeded, subsequent .write calls will block until
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
* @return buffer size limit, in bytes
|
||||
*/
|
||||
public int getMaxBufferSize() {
|
||||
return _maxBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
* this amount has been exceeded, subsequent .write calls will block until
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
*/
|
||||
public void setMaxBufferSize(int numBytes) {
|
||||
_maxBufferSize = numBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public long getWriteTimeout() {
|
||||
return _writeTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public void setWriteTimeout(long ms) {
|
||||
_writeTimeout = ms;
|
||||
}
|
||||
}
|
@@ -118,11 +118,15 @@ public class StreamSinkServer {
|
||||
try {
|
||||
InputStream in = _sock.getInputStream();
|
||||
byte buf[] = new byte[4096];
|
||||
long written = 0;
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
_fos.write(buf, 0, read);
|
||||
written += read;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read and wrote " + read);
|
||||
}
|
||||
_log.error("Got EOF from client socket");
|
||||
_log.error("Got EOF from client socket [written=" + written + "]");
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing the sink", ioe);
|
||||
} finally {
|
||||
@@ -143,6 +147,9 @@ public class StreamSinkServer {
|
||||
public static void main(String args[]) {
|
||||
StreamSinkServer server = null;
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
server = new StreamSinkServer("dataDir", "server.key", "localhost", 10001);
|
||||
break;
|
||||
case 2:
|
||||
server = new StreamSinkServer(args[0], args[1]);
|
||||
break;
|
||||
|
@@ -217,7 +217,10 @@ public class ConfigNetHandler extends FormHandler {
|
||||
}
|
||||
if ( (_port != null) && (_port.length() > 0) ) {
|
||||
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT);
|
||||
if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_port)) ) {
|
||||
if ( (oldPort == null) && (_port.equals("8887")) ) {
|
||||
// still on default.. noop
|
||||
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_port)) ) {
|
||||
// its not the default OR it has changed
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _port);
|
||||
addFormNotice("Updating TCP port from " + oldPort + " to " + _port);
|
||||
restartRequired = true;
|
||||
|
@@ -120,15 +120,17 @@ public class ConfigNetHelper {
|
||||
private static String getBurstFactor(int numSeconds, String name) {
|
||||
StringBuffer buf = new StringBuffer(256);
|
||||
buf.append("<select name=\"").append(name).append("\">\n");
|
||||
for (int i = 1; i < 10; i++) {
|
||||
boolean found = false;
|
||||
for (int i = 10; i <= 60; i += 10) {
|
||||
buf.append("<option value=\"").append(i).append("\" ");
|
||||
if ( (i == numSeconds) || (i == 10) )
|
||||
if (i == numSeconds) {
|
||||
buf.append("selected ");
|
||||
found = true;
|
||||
} else if ( (i == 60) && (!found) ) {
|
||||
buf.append("selected ");
|
||||
}
|
||||
buf.append(">");
|
||||
if (i == 1)
|
||||
buf.append("1 second (no burst)</option>\n");
|
||||
else
|
||||
buf.append(i).append(" seconds</option>\n");
|
||||
buf.append(i).append(" seconds</option>\n");
|
||||
}
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
|
@@ -1,10 +1,17 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.router.ClientTunnelSettings;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.apps.systray.SysTray;
|
||||
import net.i2p.apps.systray.UrlLauncher;
|
||||
import org.tanukisoftware.wrapper.WrapperManager;
|
||||
|
||||
/**
|
||||
@@ -86,6 +93,12 @@ public class ConfigServiceHandler extends FormHandler {
|
||||
} catch (Throwable t) {
|
||||
addFormError("Warning: unable to contact the systray manager - " + t.getMessage());
|
||||
}
|
||||
} else if ("View console on startup".equals(_action)) {
|
||||
browseOnStartup(true);
|
||||
addFormNotice("Console is to be shown on startup");
|
||||
} else if ("Do not view console on startup".equals(_action)) {
|
||||
browseOnStartup(false);
|
||||
addFormNotice("Console is not to be shown on startup");
|
||||
} else {
|
||||
addFormNotice("Blah blah blah. whatever. I'm not going to " + _action);
|
||||
}
|
||||
@@ -107,4 +120,81 @@ public class ConfigServiceHandler extends FormHandler {
|
||||
addFormError("Warning: unable to remove the service - " + ioe.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private final static String NL = System.getProperty("line.separator");
|
||||
private void browseOnStartup(boolean shouldLaunchBrowser) {
|
||||
File f = new File("clients.config");
|
||||
Properties p = new Properties();
|
||||
try {
|
||||
DataHelper.loadProps(p, f);
|
||||
|
||||
int i = 0;
|
||||
int launchIndex = -1;
|
||||
while (true) {
|
||||
String className = p.getProperty("clientApp." + i + ".main");
|
||||
if (className == null) break;
|
||||
if (UrlLauncher.class.getName().equals(className)) {
|
||||
launchIndex = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if ((launchIndex >= 0) && shouldLaunchBrowser)
|
||||
return;
|
||||
if ((launchIndex < 0) && !shouldLaunchBrowser)
|
||||
return;
|
||||
|
||||
if (shouldLaunchBrowser) {
|
||||
p.setProperty("clientApp." + i + ".main", UrlLauncher.class.getName());
|
||||
p.setProperty("clientApp." + i + ".name", "BrowserLauncher");
|
||||
p.setProperty("clientApp." + i + ".args", "http://localhost:7657/index.jsp");
|
||||
p.setProperty("clientApp." + i + ".delay", "5");
|
||||
} else {
|
||||
p.remove("clientApp." + launchIndex + ".main");
|
||||
p.remove("clientApp." + launchIndex + ".name");
|
||||
p.remove("clientApp." + launchIndex + ".args");
|
||||
p.remove("clientApp." + launchIndex + ".onBoot");
|
||||
p.remove("clientApp." + launchIndex + ".delay");
|
||||
|
||||
i = launchIndex + 1;
|
||||
while (true) {
|
||||
String main = p.getProperty("clientApp." + i + ".main");
|
||||
String name = p.getProperty("clientApp." + i + ".name");
|
||||
String args = p.getProperty("clientApp." + i + ".args");
|
||||
String boot = p.getProperty("clientApp." + i + ".onBoot");
|
||||
String delay= p.getProperty("clientApp." + i + ".delay");
|
||||
|
||||
if (main == null) break;
|
||||
|
||||
p.setProperty("clientApp." + (i-1) + ".main", main);
|
||||
p.setProperty("clientApp." + (i-1) + ".name", name);
|
||||
p.setProperty("clientApp." + (i-1) + ".args", args);
|
||||
if (boot != null)
|
||||
p.setProperty("clientApp." + (i-1) + ".onBoot", boot);
|
||||
if (delay != null)
|
||||
p.setProperty("clientApp." + (i-1) + ".delay", delay);
|
||||
|
||||
p.remove("clientApp." + i + ".main");
|
||||
p.remove("clientApp." + i + ".name");
|
||||
p.remove("clientApp." + i + ".args");
|
||||
p.remove("clientApp." + i + ".onBoot");
|
||||
p.remove("clientApp." + i + ".delay");
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
TreeMap sorted = new TreeMap(p);
|
||||
FileWriter out = new FileWriter(f);
|
||||
for (Iterator iter = sorted.keySet().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
String val = (String)sorted.get(name);
|
||||
out.write(name + "=" + val + NL);
|
||||
}
|
||||
out.close();
|
||||
} catch (IOException ioe) {
|
||||
addFormError("Error updating the client config");
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,6 +10,7 @@ import net.i2p.util.FileUtil;
|
||||
public class ContentHelper {
|
||||
private String _page;
|
||||
private int _maxLines;
|
||||
private boolean _startAtBeginning;
|
||||
private RouterContext _context;
|
||||
/**
|
||||
* Configure this bean to query a particular router context
|
||||
@@ -28,6 +29,10 @@ public class ContentHelper {
|
||||
public ContentHelper() {}
|
||||
|
||||
public void setPage(String page) { _page = page; }
|
||||
public void setStartAtBeginning(String moo) {
|
||||
_startAtBeginning = Boolean.valueOf(""+moo).booleanValue();
|
||||
}
|
||||
|
||||
public void setMaxLines(String lines) {
|
||||
if (lines != null) {
|
||||
try {
|
||||
@@ -40,14 +45,14 @@ public class ContentHelper {
|
||||
}
|
||||
}
|
||||
public String getContent() {
|
||||
String str = FileUtil.readTextFile(_page, _maxLines);
|
||||
String str = FileUtil.readTextFile(_page, _maxLines, _startAtBeginning);
|
||||
if (str == null)
|
||||
return "";
|
||||
else
|
||||
return str;
|
||||
}
|
||||
public String getTextContent() {
|
||||
String str = FileUtil.readTextFile(_page, _maxLines);
|
||||
String str = FileUtil.readTextFile(_page, _maxLines, _startAtBeginning);
|
||||
if (str == null)
|
||||
return "";
|
||||
else
|
||||
|
@@ -42,7 +42,7 @@ public class LogsHelper {
|
||||
}
|
||||
|
||||
public String getServiceLogs() {
|
||||
String str = FileUtil.readTextFile("wrapper.log", 500);
|
||||
String str = FileUtil.readTextFile("wrapper.log", 500, false);
|
||||
if (str == null)
|
||||
return "";
|
||||
else
|
||||
|
@@ -0,0 +1,129 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Handler to deal with reseed requests. This reseed from the URL
|
||||
* http://dev.i2p.net/i2pdb/ unless the java env property "i2p.reseedURL" is
|
||||
* set. It always writes to ./netDb/, so don't mess with that.
|
||||
*
|
||||
*/
|
||||
public class ReseedHandler {
|
||||
private static ReseedRunner _reseedRunner = new ReseedRunner();
|
||||
|
||||
public void setReseedNonce(String nonce) {
|
||||
if (nonce == null) return;
|
||||
if (nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.nonce")) ||
|
||||
nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.noncePrev"))) {
|
||||
synchronized (_reseedRunner) {
|
||||
if (_reseedRunner.isRunning()) {
|
||||
return;
|
||||
} else {
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "true");
|
||||
I2PThread reseed = new I2PThread(_reseedRunner, "Reseed");
|
||||
reseed.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ReseedRunner implements Runnable {
|
||||
private boolean _isRunning;
|
||||
public ReseedRunner() { _isRunning = false; }
|
||||
public boolean isRunning() { return _isRunning; }
|
||||
public void run() {
|
||||
_isRunning = true;
|
||||
reseed();
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false");
|
||||
_isRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String DEFAULT_SEED_URL = "http://dev.i2p.net/i2pdb/";
|
||||
/**
|
||||
* Reseed has been requested, so lets go ahead and do it. Fetch all of
|
||||
* the routerInfo-*.dat files from the specified URL (or the default) and
|
||||
* save them into this router's netDb dir.
|
||||
*
|
||||
*/
|
||||
private static void reseed() {
|
||||
String seedURL = System.getProperty("i2p.reseedURL", DEFAULT_SEED_URL);
|
||||
if ( (seedURL == null) || (seedURL.trim().length() <= 0) )
|
||||
seedURL = DEFAULT_SEED_URL;
|
||||
try {
|
||||
URL dir = new URL(seedURL);
|
||||
String content = new String(readURL(dir));
|
||||
Set urls = new HashSet();
|
||||
int cur = 0;
|
||||
while (true) {
|
||||
int start = content.indexOf("href=\"routerInfo-", cur);
|
||||
if (start < 0)
|
||||
break;
|
||||
|
||||
int end = content.indexOf(".dat\">", start);
|
||||
String name = content.substring(start+"href=\"routerInfo-".length(), end);
|
||||
urls.add(name);
|
||||
cur = end + 1;
|
||||
}
|
||||
|
||||
int fetched = 0;
|
||||
int errors = 0;
|
||||
for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
|
||||
try {
|
||||
fetchSeed(seedURL, (String)iter.next());
|
||||
fetched++;
|
||||
} catch (Exception e) {
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
I2PAppContext.getGlobalContext().logManager().getLog(ReseedHandler.class).error("Error reseeding", t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void fetchSeed(String seedURL, String peer) throws Exception {
|
||||
URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + "routerInfo-" + peer + ".dat");
|
||||
|
||||
byte data[] = readURL(url);
|
||||
writeSeed(peer, data);
|
||||
}
|
||||
|
||||
private static byte[] readURL(URL url) throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
|
||||
URLConnection con = url.openConnection();
|
||||
InputStream in = con.getInputStream();
|
||||
byte buf[] = new byte[1024];
|
||||
while (true) {
|
||||
int read = in.read(buf);
|
||||
if (read < 0)
|
||||
break;
|
||||
baos.write(buf, 0, read);
|
||||
}
|
||||
in.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
private static void writeSeed(String name, byte data[]) throws Exception {
|
||||
String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb");
|
||||
File netDbDir = new File(dirName);
|
||||
if (!netDbDir.exists()) {
|
||||
boolean ok = netDbDir.mkdirs();
|
||||
}
|
||||
FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat"));
|
||||
fos.write(data);
|
||||
fos.close();
|
||||
}
|
||||
}
|
@@ -28,44 +28,58 @@
|
||||
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigNetHandler.nonce")%>" />
|
||||
<input type="hidden" name="action" value="blah" />
|
||||
|
||||
<b>External hostname/IP address:</b>
|
||||
<input name="hostname" type="text" size="32" value="<jsp:getProperty name="nethelper" property="hostname" />" />
|
||||
<input type="submit" name="guesshost" value="Guess" /><br />
|
||||
<b>Externally reachable TCP port:</b>
|
||||
TCP port:
|
||||
<input name="port" type="text" size="4" value="<jsp:getProperty name="nethelper" property="port" />" /> <br />
|
||||
<i>The hostname/IP address and TCP port must be reachable from the outside world. If
|
||||
you are behind a firewall or NAT, this means you must poke a hole for this port. If
|
||||
you are using DHCP and do not have a static IP address, you should either use a service like
|
||||
<a href="http://dyndns.org/">dyndns</a> or leave the hostname blank. If you leave it blank,
|
||||
your router will autodetect the 'correct' IP address by asking a peer (and unconditionally
|
||||
believing them if the address is routable and you don't have any established connections yet).
|
||||
The "guess" functionality makes an HTTP request
|
||||
to <a href="http://www.whatismyip.com/">www.whatismyip.com</a>.</i>
|
||||
<hr />
|
||||
<b>Enable internal time synchronization?</b> <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br />
|
||||
<i>If disabled, your machine <b>must</b> be NTP synchronized - your clock must always
|
||||
be within a few seconds of "correct".</i>
|
||||
<b>You must poke a hole in your firewall or NAT (if applicable) so that you can receive inbound TCP
|
||||
connections on it.</b> Nothing will work if you don't. Sorry. We know how to make it so
|
||||
this restriction won't be necessary, but its later on in the
|
||||
<a href="http://www.i2p.net/roadmap">roadmap</a> and we only have so many coder-hours (but if you want
|
||||
to help, please <a href="http://www.i2p.net/getinvolved">get involved!</a>)
|
||||
<hr />
|
||||
|
||||
<b>Bandwidth limiter</b><br />
|
||||
<b>Inbound rate</b>:
|
||||
Inbound rate:
|
||||
<input name="inboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundRate" />" /> KBytes per second<br />
|
||||
<b>Inbound burst duration:</b>
|
||||
Inbound burst duration:
|
||||
<jsp:getProperty name="nethelper" property="inboundBurstFactorBox" /><br />
|
||||
<b>Outbound rate:</b>
|
||||
Outbound rate:
|
||||
<input name="outboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundRate" />" /> KBytes per second<br />
|
||||
<b>Outbound burst duration:</b>
|
||||
Outbound burst duration:
|
||||
<jsp:getProperty name="nethelper" property="outboundBurstFactorBox" /><br />
|
||||
<i>A negative rate means there is no limit</i><br />
|
||||
<hr />
|
||||
<b>Reseed</b> (from <input name="reseedfrom" type="text" size="40" value="http://dev.i2p.net/i2pdb/" />):
|
||||
<input type="submit" name="reseed" value="now" /><br />
|
||||
<i>May take some time to download the peer references</i>
|
||||
Enable internal time synchronization? <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br />
|
||||
<i>If disabled, your machine <b>must</b> be NTP synchronized - your clock must always
|
||||
be within a few seconds of "correct". You will need to be able to send outbound UDP
|
||||
packets on port 123 to one of the pool.ntp.org machines (or some other SNTP server).</i>
|
||||
<hr />
|
||||
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
|
||||
<i>Changing the hostname or TCP port will force a 'soft restart' - dropping your connections
|
||||
and clients as if the router was stopped and restarted. <b>Please be patient</b> - it may take
|
||||
<i>Changing the TCP port will force a 'soft restart' - dropping your connections and clients as
|
||||
if the router was stopped and restarted. <b>Please be patient</b> - it may take
|
||||
a few seconds to complete.</i>
|
||||
</form>
|
||||
<hr />
|
||||
<b>Advanced network config:</b>
|
||||
<p>
|
||||
There are two other network settings, but no one reads this text so there's no reason
|
||||
to tell you about them. In case you actually do read this, here's the deal: by default,
|
||||
I2P will attempt to guess your IP address by having whomever it talks to tell it what
|
||||
address they think you are. If and only if you have no working TCP connections and you
|
||||
have not overridden the IP address, your router will believe them. If that doesn't sound
|
||||
ok to you, thats fine - go to the <a href="/configadvanced.jsp">advanced config</a> page
|
||||
and add "i2np.tcp.hostname=yourHostname", then go to the
|
||||
<a href="/configservice.jsp">service</a> page and do a graceful restart. We used to make
|
||||
people enter a hostname/IP address on this page, but too many people got it wrong ;)</p>
|
||||
|
||||
<p>The other advanced network option has to do with reseeding - you should never need to
|
||||
reseed your router as long as you can find at least one other peer on the network. However,
|
||||
when you do need to reseed, a link will show up on the left hand side which will
|
||||
fetch all of the routerInfo-* files from http://dev.i2p.net/i2pdb/. That URL is just an
|
||||
apache folder pointing at the netDb/ directory of a router - anyone can run one, and you can
|
||||
configure your router to seed off an alternate URL by adding the java environmental property
|
||||
"i2p.reseedURL=someURL" (e.g. java -Di2p.reseedURL=http://dev.i2p.net/i2pdb/ ...). You can
|
||||
also do it manually by getting routerInfo-*.dat files from someone (a friend, someone on IRC,
|
||||
whatever) and saving them to your netDb/ directory.</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
@@ -29,10 +29,12 @@
|
||||
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigAdvancedHandler.nonce")%>" />
|
||||
<input type="hidden" name="action" value="blah" />
|
||||
<textarea rows="20" cols="100" name="config"><jsp:getProperty name="advancedhelper" property="settings" /></textarea><br />
|
||||
<input type="submit" name="shouldsave" value="Apply" /> <input type="reset" value="Cancel" /> <br />
|
||||
<input type="submit" name="shouldsave" value="Apply" /> <input type="reset" value="Cancel" /><!-- <br />
|
||||
<b>Force restart:</b> <input type="checkbox" name="restart" value="force" /> <i>(specify this
|
||||
if the changes made above require the router to reset itself - e.g. you are updating TCP ports
|
||||
or hostnames, etc)</i>
|
||||
or hostnames, etc)</i>-->
|
||||
If you are changing any of the I2NP settings, you should go to the
|
||||
<a href="configservice.jsp">service config</a> page and do a graceful restart after saving.
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@@ -67,6 +67,14 @@
|
||||
please select the following option and review the thread dumped to
|
||||
<a href="logs.jsp#servicelogs">wrapper.log</a>.</p>
|
||||
<input type="submit" name="action" value="Dump threads" />
|
||||
|
||||
<h4>Launch browser on router startup?</h4>
|
||||
<p>I2P's main configuration interface is this web console, so for your convenience
|
||||
I2P can launch a web browser pointing at
|
||||
<a href="http://localhost:7657/index.jsp">http://localhost:7657/index.jsp</a> whenever
|
||||
the router starts up.</p>
|
||||
<input type="submit" name="action" value="View console on startup" />
|
||||
<input type="submit" name="action" value="Do not view console on startup" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
BIN
apps/routerconsole/jsp/favicon.ico
Normal file
BIN
apps/routerconsole/jsp/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@@ -37,7 +37,9 @@ by their binary code license. This product includes software developed by the A
|
||||
lets you tunnel normal TCP/IP traffic over I2P (such as the eepproxy and the irc proxy).</p>
|
||||
|
||||
<p>The router by default also includes human's public domain <a href="http://www.i2p.net/sam">SAM</a> bridge,
|
||||
which other client applications (such as aum's <a href="http://stasher.i2p/">stasher</a>) can use. For
|
||||
which other client applications (such the <a href="http://duck.i2p/i2p-bt/">bittorrent port</a>) can use.
|
||||
There is also an optimized library for doing large number calculations - jbigi - which in turn uses the
|
||||
LGPL licensed <a href="http://swox.com/gmp/">GMP</a> library, tuned for various PC architectures. For
|
||||
details on other applications available, as well as their licenses, please see the
|
||||
<a href="http://www.i2p.net/licenses">license policy</a>. Source for the I2P code and most bundled
|
||||
client applications can be found on our <a href="http://www.i2p.net/download">download page</a>, and is
|
||||
@@ -47,7 +49,14 @@ in <a href="http://www.i2p.net/cvs">cvs</a>.</p>
|
||||
<jsp:useBean class="net.i2p.router.web.ContentHelper" id="contenthelper" scope="request" />
|
||||
<jsp:setProperty name="contenthelper" property="page" value="history.txt" />
|
||||
<jsp:setProperty name="contenthelper" property="maxLines" value="500" />
|
||||
<jsp:setProperty name="contenthelper" property="startAtBeginning" value="true" />
|
||||
<jsp:getProperty name="contenthelper" property="textContent" />
|
||||
|
||||
<p>
|
||||
A more complete list of updates can be found
|
||||
<a href="http://dev.i2p.net/cgi-bin/cvsweb.cgi/i2p/history.txt?rev=HEAD">online</a>
|
||||
(<a href="http://dev.i2p/cgi-bin/cvsweb.cgi/i2p/history.txt?rev=HEAD">anonymously</a>)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
@@ -2,6 +2,9 @@
|
||||
<jsp:useBean class="net.i2p.router.web.SummaryHelper" id="helper" scope="request" />
|
||||
<jsp:setProperty name="helper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ReseedHandler" id="reseed" scope="request" />
|
||||
<jsp:setProperty name="reseed" property="*" />
|
||||
|
||||
<div class="routersummary">
|
||||
<u><b>General</b></u><br />
|
||||
<b>Ident:</b> <jsp:getProperty name="helper" property="ident" /><br />
|
||||
@@ -16,8 +19,27 @@
|
||||
<b>High capacity:</b> <jsp:getProperty name="helper" property="highCapacityPeers" /><br />
|
||||
<b>Well integrated:</b> <jsp:getProperty name="helper" property="wellIntegratedPeers" /><br />
|
||||
<b>Failing:</b> <jsp:getProperty name="helper" property="failingPeers" /><br />
|
||||
<b>Shitlisted:</b> <jsp:getProperty name="helper" property="shitlistedPeers" /><br />
|
||||
<hr />
|
||||
<b>Shitlisted:</b> <jsp:getProperty name="helper" property="shitlistedPeers" /><br /><%
|
||||
if (helper.getActivePeers() <= 0) {
|
||||
%><b><a href="config.jsp">check your NAT/firewall</a></b><br /><%
|
||||
}
|
||||
if (helper.getActiveProfiles() <= 4) { // 4 is the min fallback
|
||||
if ("true".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) {
|
||||
out.print(" <i>reseeding</i>");
|
||||
} else {
|
||||
long nonce = new java.util.Random().nextLong();
|
||||
String prev = System.getProperty("net.i2p.router.web.ReseedHandler.nonce");
|
||||
if (prev != null) System.setProperty("net.i2p.router.web.ReseedHandler.noncePrev", prev);
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.nonce", nonce+"");
|
||||
String uri = request.getRequestURI();
|
||||
if (uri.indexOf('?') > 0)
|
||||
uri = uri + "&reseedNonce=" + nonce;
|
||||
else
|
||||
uri = uri + "?reseedNonce=" + nonce;
|
||||
out.print(" <a href=\"" + uri + "\">reseed</a>");
|
||||
}
|
||||
}
|
||||
%><hr />
|
||||
|
||||
<u><b>Bandwidth in/out</b></u><br />
|
||||
<b>1m:</b> <jsp:getProperty name="helper" property="inboundMinuteKBps" />/<jsp:getProperty name="helper" property="outboundMinuteKBps" />KBps<br />
|
||||
|
@@ -4,6 +4,7 @@ I need to do these things:
|
||||
* Write an instruction manual
|
||||
* Make dest a dynamic string
|
||||
* Change SAM parser to use a hashmap
|
||||
* Switch to GNU Autoconf (?)
|
||||
|
||||
Anyone can help with these things:
|
||||
|
||||
|
@@ -1,7 +1,10 @@
|
||||
/* vi:set ts=4: */
|
||||
|
||||
v1.30
|
||||
* Improved WIRETAP to do more logging
|
||||
* Added "pinger.sh" shell script example for using i2p-ping
|
||||
* Added SAM_BAD_STYLE error
|
||||
* Added exit values for i2p-ping from xolo
|
||||
|
||||
v1.25 2004-07-31
|
||||
* Created I2P-Ping, a new example program (it's a clone of I2Ping). Works
|
||||
|
@@ -38,9 +38,11 @@ extern "C" {
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/*
|
||||
* Lengths
|
||||
*/
|
||||
|
||||
/* The maximum length a SAM command can be */
|
||||
#define SAM_CMD_LEN 128
|
||||
/* The maximum size of a single datagram packet */
|
||||
@@ -49,18 +51,22 @@ extern "C" {
|
||||
#define SAM_LOGMSG_LEN 256
|
||||
/* The longest `name' arg for the naming lookup callback */
|
||||
#define SAM_NAME_LEN 256
|
||||
/* The max size of a single stream packet */
|
||||
/* The maximum size of a single stream packet */
|
||||
#define SAM_STREAM_PAYLOAD_MAX (32 * 1024)
|
||||
/* The length of a base 64 public key - it's actually 516, but +1 for '\0' */
|
||||
#define SAM_PUBKEY_LEN 517
|
||||
/* A public key SAM command's length */
|
||||
/* The maximum length of a SAM command with a public key */
|
||||
#define SAM_PKCMD_LEN (SAM_PUBKEY_LEN + SAM_CMD_LEN)
|
||||
/* The maximum size of a single raw packet */
|
||||
#define SAM_RAW_PAYLOAD_MAX (32 * 1024)
|
||||
/* The maximum length a SAM non-data reply can be */
|
||||
#define SAM_REPLY_LEN 1024
|
||||
|
||||
|
||||
/*
|
||||
* Shorten some standard variable types
|
||||
* Some LibSAM variable types
|
||||
*/
|
||||
|
||||
typedef signed char schar_t;
|
||||
typedef unsigned char uchar_t;
|
||||
typedef unsigned int uint_t;
|
||||
@@ -102,57 +108,62 @@ typedef enum { /* see sam_strerror() for detailed descriptions of these */
|
||||
SAM_TOO_BIG
|
||||
} samerr_t;
|
||||
|
||||
|
||||
/*
|
||||
* Public functions
|
||||
*/
|
||||
|
||||
/* SAM controls - sessions */
|
||||
extern sam_sess_t *sam_session_init(sam_sess_t *session);
|
||||
extern void sam_session_free(sam_sess_t **session);
|
||||
sam_sess_t *sam_session_init(sam_sess_t *session);
|
||||
void sam_session_free(sam_sess_t **session);
|
||||
/* SAM controls - connection */
|
||||
extern bool sam_close(sam_sess_t *session);
|
||||
extern samerr_t sam_connect(sam_sess_t *session, const char *samhost,
|
||||
uint16_t samport, const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth);
|
||||
bool sam_close(sam_sess_t *session);
|
||||
samerr_t sam_connect(sam_sess_t *session, const char *samhost,
|
||||
uint16_t samport, const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth);
|
||||
/* SAM controls - utilities */
|
||||
extern void sam_naming_lookup(sam_sess_t *session, const char *name);
|
||||
extern bool sam_read_buffer(sam_sess_t *session);
|
||||
extern const char *sam_strerror(samerr_t code);
|
||||
void sam_naming_lookup(sam_sess_t *session, const char *name);
|
||||
bool sam_read_buffer(sam_sess_t *session);
|
||||
const char *sam_strerror(samerr_t code);
|
||||
/* SAM controls - callbacks */
|
||||
extern void (*sam_diedback)(sam_sess_t *session);
|
||||
extern void (*sam_logback)(char *str);
|
||||
extern void (*sam_namingback)(char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result);
|
||||
void (*sam_diedback)(sam_sess_t *session);
|
||||
void (*sam_logback)(char *str);
|
||||
void (*sam_namingback)(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
|
||||
/* Stream commands */
|
||||
extern void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
|
||||
extern sam_sid_t sam_stream_connect(sam_sess_t *session,
|
||||
const sam_pubkey_t dest);
|
||||
extern samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
const void *data, size_t size);
|
||||
void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
|
||||
sam_sid_t sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest);
|
||||
samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
const void *data, size_t size);
|
||||
/* Stream commands - callbacks */
|
||||
extern void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason);
|
||||
extern void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
extern void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
void *data, size_t size);
|
||||
extern void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason);
|
||||
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
void *data, size_t size);
|
||||
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
|
||||
/* Stream send queue (experimental) */
|
||||
extern void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq, const void *data, size_t dsize);
|
||||
extern void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq);
|
||||
void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq, const void *data, size_t dsize);
|
||||
void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq);
|
||||
|
||||
/* Datagram commands */
|
||||
extern samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size);
|
||||
|
||||
samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size);
|
||||
/* Datagram commands - callbacks */
|
||||
extern void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest,
|
||||
void *data, size_t size);
|
||||
void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size);
|
||||
|
||||
/* Raw commands */
|
||||
samerr_t sam_raw_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size);
|
||||
/* Raw commands - callbacks */
|
||||
void (*sam_rawback)(sam_sess_t *session, void *data, size_t size);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -55,28 +55,40 @@ static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n);
|
||||
* Callback functions
|
||||
* Note: if you add a new callback be sure to check for non-NULL in sam_connect
|
||||
*/
|
||||
|
||||
/* a peer closed the connection */
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
|
||||
= NULL;
|
||||
|
||||
/* a peer connected to us */
|
||||
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest) = NULL;
|
||||
|
||||
/* a peer sent some stream data (`data' MUST be freed) */
|
||||
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id, void *data,
|
||||
size_t size) = NULL;
|
||||
|
||||
/* a peer sent some datagram data (`data' MUST be freed) */
|
||||
void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size) = NULL;
|
||||
|
||||
/* we lost the connection to the SAM host */
|
||||
void (*sam_diedback)(sam_sess_t *session) = NULL;
|
||||
|
||||
/* logging callback */
|
||||
void (*sam_logback)(char *str) = NULL;
|
||||
|
||||
/* naming lookup reply - `pubkey' will be NULL if `result' isn't SAM_OK */
|
||||
void (*sam_namingback)(char *name, sam_pubkey_t pubkey, samerr_t result) = NULL;
|
||||
|
||||
/* our connection to a peer has completed */
|
||||
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result) = NULL;
|
||||
|
||||
/* a peer sent some raw data (`data' MUST be freed) */
|
||||
void (*sam_rawback)(sam_sess_t *session, void *data, size_t size) = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Closes the connection to the SAM host
|
||||
*
|
||||
@@ -155,7 +167,11 @@ samerr_t sam_connect(sam_sess_t *session, const char *samhost, uint16_t samport,
|
||||
return SAM_CALLBACKS_UNSET;
|
||||
}
|
||||
} else if (style == SAM_RAW) {
|
||||
abort(); /* not implemented yet */
|
||||
if (sam_diedback == NULL || sam_logback == NULL
|
||||
|| sam_namingback == NULL || sam_rawback == NULL) {
|
||||
SAMLOGS("Please set callback functions before connecting");
|
||||
return SAM_CALLBACKS_UNSET;
|
||||
}
|
||||
} else {
|
||||
SAMLOGS("Unknown connection style");
|
||||
return SAM_BAD_STYLE;
|
||||
@@ -197,7 +213,7 @@ samerr_t sam_connect(sam_sess_t *session, const char *samhost, uint16_t samport,
|
||||
* data - the data we're sending
|
||||
* size - the size of the data
|
||||
*
|
||||
* Returns: true on success, false on failure
|
||||
* Returns: SAM_OK on success
|
||||
*/
|
||||
samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size)
|
||||
@@ -295,6 +311,7 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
#define SAM_NAMING_REPLY_OK "NAMING REPLY RESULT=OK"
|
||||
#define SAM_NAMING_REPLY_IK "NAMING REPLY RESULT=INVALID_KEY"
|
||||
#define SAM_NAMING_REPLY_KNF "NAMING REPLY RESULT=KEY_NOT_FOUND"
|
||||
#define SAM_RAW_RECEIVED_REPLY "RAW RECEIVED"
|
||||
#define SAM_STREAM_CLOSED_REPLY "STREAM CLOSED"
|
||||
#define SAM_STREAM_CONNECTED_REPLY "STREAM CONNECTED"
|
||||
#define SAM_STREAM_RECEIVED_REPLY "STREAM RECEIVED"
|
||||
@@ -305,6 +322,10 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
#define SAM_STREAM_STATUS_REPLY_IK "STREAM STATUS RESULT=INVALID_KEY"
|
||||
#define SAM_STREAM_STATUS_REPLY_TO "STREAM STATUS RESULT=TIMEOUT"
|
||||
|
||||
/*
|
||||
* TODO: add raw parsing
|
||||
*/
|
||||
|
||||
if (strncmp(s, SAM_DGRAM_RECEIVED_REPLY,
|
||||
strlen(SAM_DGRAM_RECEIVED_REPLY)) == 0) {
|
||||
char *p;
|
||||
@@ -518,6 +539,42 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends data to a destination in a raw packet
|
||||
*
|
||||
* dest - base 64 destination of who we're sending to
|
||||
* data - the data we're sending
|
||||
* size - the size of the data
|
||||
*
|
||||
* Returns: SAM_OK on success
|
||||
*/
|
||||
samerr_t sam_raw_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_PKCMD_LEN];
|
||||
|
||||
if (size < 1 || size > SAM_RAW_PAYLOAD_MAX) {
|
||||
#ifdef NO_Z_FORMAT
|
||||
SAMLOG("Invalid data send size (%u bytes)", size);
|
||||
#else
|
||||
SAMLOG("Invalid data send size (%zu bytes)", size);
|
||||
#endif
|
||||
return SAM_TOO_BIG;
|
||||
}
|
||||
#ifdef NO_Z_FORMAT
|
||||
snprintf(cmd, sizeof cmd, "RAW SEND DESTINATION=%s SIZE=%u\n",
|
||||
dest, size);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "RAW SEND DESTINATION=%s SIZE=%zu\n",
|
||||
dest, size);
|
||||
#endif
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
sam_write(session, data, size);
|
||||
|
||||
return SAM_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads and callbacks everything in the SAM network buffer until it is clear
|
||||
*
|
||||
@@ -598,7 +655,7 @@ static ssize_t sam_read1(sam_sess_t *session, char *buf, size_t n)
|
||||
if (*p == '\n') { /* end of SAM response */
|
||||
*p = '\0';
|
||||
#if SAM_WIRETAP
|
||||
printf("<<<< %s\n", buf);
|
||||
printf("*RR* %s\n", buf);
|
||||
#endif
|
||||
return n - nleft;
|
||||
}
|
||||
@@ -663,7 +720,16 @@ static ssize_t sam_read2(sam_sess_t *session, void *buf, size_t n)
|
||||
p += nread;
|
||||
}
|
||||
#if SAM_WIRETAP
|
||||
printf("<<<< (read2() %d bytes)\n", n);
|
||||
p = buf;
|
||||
printf("*RR* ");
|
||||
for (size_t x = 0; x < n; x++) {
|
||||
if (isprint(((uchar_t*)p)[x]))
|
||||
printf("%c,", ((uchar_t*)p)[x]);
|
||||
else
|
||||
printf("%03d,", ((uint8_t*)p)[x]);
|
||||
}
|
||||
printf("\n");
|
||||
printf("*RR* (read2() read %d bytes)\n", n);
|
||||
#endif
|
||||
assert(nleft == 0);/* <---\ */
|
||||
return n - nleft; /* should be equal to initial n */
|
||||
@@ -689,7 +755,7 @@ static bool sam_readable(sam_sess_t *session)
|
||||
FD_ZERO(&rset);
|
||||
FD_SET(session->sock, &rset);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10;
|
||||
tv.tv_usec = 0;
|
||||
rc = select(session->sock + 1, &rset, NULL, NULL, &tv);
|
||||
if (rc == 0)
|
||||
return false;
|
||||
@@ -1334,6 +1400,7 @@ static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n)
|
||||
}
|
||||
#if SAM_WIRETAP
|
||||
const uchar_t *cp = buf;
|
||||
printf("*WW* ");
|
||||
for (size_t x = 0; x < n; x++) {
|
||||
if (isprint(cp[x]))
|
||||
printf("%c,", cp[x]);
|
||||
@@ -1365,7 +1432,7 @@ static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n)
|
||||
p += nwritten;
|
||||
}
|
||||
#if SAM_WIRETAP
|
||||
printf(">>>> (write() %d bytes)\n", n);
|
||||
printf("*WW* (write() wrote %d bytes)\n", n);
|
||||
#endif
|
||||
|
||||
return n;
|
||||
|
@@ -250,6 +250,7 @@ public class SAMBridge implements Runnable {
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if (serverSocket == null) return;
|
||||
try {
|
||||
while (acceptConnections) {
|
||||
Socket s = serverSocket.accept();
|
||||
@@ -280,8 +281,11 @@ public class SAMBridge implements Runnable {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("SAM Error sending error reply", ioe);
|
||||
}
|
||||
s.close();
|
||||
}
|
||||
try { s.close(); } catch (IOException ioe) {}
|
||||
} catch (Exception ee) {
|
||||
try { s.close(); } catch (IOException ioe) {}
|
||||
_log.log(Log.CRIT, "Unexpected error handling SAM connection", ee);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
@@ -290,7 +294,8 @@ public class SAMBridge implements Runnable {
|
||||
try {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Shutting down, closing server socket");
|
||||
serverSocket.close();
|
||||
if (serverSocket != null)
|
||||
serverSocket.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
}
|
||||
|
@@ -100,6 +100,14 @@ public abstract class SAMHandler implements Runnable {
|
||||
socketOS.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If you're crazy enough to write to the raw socket, grab the write lock
|
||||
* with getWriteLock(), synchronize against it, and write to the getOut()
|
||||
*
|
||||
*/
|
||||
protected Object getWriteLock() { return socketWLock; }
|
||||
protected OutputStream getOut() { return socketOS; }
|
||||
|
||||
/**
|
||||
* Write a string to the handler's socket. This method must
|
||||
|
@@ -152,6 +152,8 @@ public class SAMHandlerFactory {
|
||||
|
||||
/* Get the major protocol version from a string */
|
||||
private static int getMajor(String ver) {
|
||||
if ( (ver == null) || (ver.indexOf('.') < 0) )
|
||||
return -1;
|
||||
try {
|
||||
String major = ver.substring(0, ver.indexOf("."));
|
||||
return Integer.parseInt(major);
|
||||
@@ -164,6 +166,8 @@ public class SAMHandlerFactory {
|
||||
|
||||
/* Get the minor protocol version from a string */
|
||||
private static int getMinor(String ver) {
|
||||
if ( (ver == null) || (ver.indexOf('.') < 0) )
|
||||
return -1;
|
||||
try {
|
||||
String major = ver.substring(ver.indexOf(".") + 1);
|
||||
return Integer.parseInt(major);
|
||||
|
@@ -180,9 +180,9 @@ public class SAMStreamSession {
|
||||
Destination d = new Destination();
|
||||
d.fromBase64(dest);
|
||||
|
||||
// FIXME: we should config I2PSocketOptions here
|
||||
I2PSocketOptions opts = new I2PSocketOptions();
|
||||
opts.setConnectTimeout(60 * 1000);
|
||||
I2PSocketOptions opts = socketMgr.buildOptions(props);
|
||||
if (props.getProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT) == null)
|
||||
opts.setConnectTimeout(60 * 1000);
|
||||
|
||||
_log.debug("Connecting new I2PSocket...");
|
||||
I2PSocket i2ps = socketMgr.connect(d, opts);
|
||||
@@ -391,6 +391,8 @@ public class SAMStreamSession {
|
||||
while (stillRunning) {
|
||||
try {
|
||||
i2ps = serverSocket.accept();
|
||||
if (i2ps == null)
|
||||
break;
|
||||
|
||||
_log.debug("New incoming connection");
|
||||
|
||||
@@ -467,6 +469,7 @@ public class SAMStreamSession {
|
||||
}
|
||||
try {
|
||||
i2pSocketOS.write(data);
|
||||
//i2pSocketOS.flush();
|
||||
} catch (IOException e) {
|
||||
_log.error("Error sending data through I2P socket", e);
|
||||
return false;
|
||||
|
@@ -14,6 +14,7 @@ import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.NoRouteToHostException;
|
||||
@@ -685,9 +686,12 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
|
||||
ByteArrayOutputStream msg = new ByteArrayOutputStream();
|
||||
|
||||
msg.write(("RAW RECEIVED SIZE=" + data.length
|
||||
+ "\n").getBytes("ISO-8859-1"));
|
||||
String msgText = "RAW RECEIVED SIZE=" + data.length + "\n";
|
||||
msg.write(msgText.getBytes("ISO-8859-1"));
|
||||
msg.write(data);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("sending to client: " + msgText);
|
||||
|
||||
writeBytes(msg.toByteArray());
|
||||
}
|
||||
@@ -716,9 +720,12 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
|
||||
ByteArrayOutputStream msg = new ByteArrayOutputStream();
|
||||
|
||||
msg.write(("DATAGRAM RECEIVED DESTINATION=" + sender.toBase64()
|
||||
+ " SIZE=" + data.length
|
||||
+ "\n").getBytes("ISO-8859-1"));
|
||||
String msgText = "DATAGRAM RECEIVED DESTINATION=" + sender.toBase64()
|
||||
+ " SIZE=" + data.length + "\n";
|
||||
msg.write(msgText.getBytes("ISO-8859-1"));
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("sending to client: " + msgText);
|
||||
msg.write(data);
|
||||
|
||||
writeBytes(msg.toByteArray());
|
||||
@@ -759,13 +766,24 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
throw new NullPointerException("BUG! STREAM session is null!");
|
||||
}
|
||||
|
||||
ByteArrayOutputStream msg = new ByteArrayOutputStream();
|
||||
|
||||
msg.write(("STREAM RECEIVED ID=" + id
|
||||
+" SIZE=" + len + "\n").getBytes("ISO-8859-1"));
|
||||
msg.write(data, 0, len);
|
||||
|
||||
writeBytes(msg.toByteArray());
|
||||
String msgText = "STREAM RECEIVED ID=" + id +" SIZE=" + len + "\n";
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("sending to client: " + msgText);
|
||||
|
||||
byte prefix[] = msgText.getBytes("ISO-8859-1");
|
||||
|
||||
// dont waste so much memory
|
||||
//ByteArrayOutputStream msg = new ByteArrayOutputStream();
|
||||
//msg.write(msgText.getBytes("ISO-8859-1"));
|
||||
//msg.write(data, 0, len);
|
||||
// writeBytes(msg.toByteArray());
|
||||
Object writeLock = getWriteLock();
|
||||
OutputStream out = getOut();
|
||||
synchronized (writeLock) {
|
||||
out.write(prefix);
|
||||
out.write(data, 0, len);
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyStreamDisconnection(int id, String result, String msg) throws IOException {
|
||||
|
@@ -53,8 +53,30 @@ public class Connection {
|
||||
private I2PSocketFull _socket;
|
||||
/** set to an error cause if the connection could not be established */
|
||||
private String _connectionError;
|
||||
private boolean _disconnectScheduled;
|
||||
private long _lastReceivedOn;
|
||||
private ActivityTimer _activityTimer;
|
||||
/** window size when we last saw congestion */
|
||||
private int _lastCongestionSeenAt;
|
||||
private boolean _ackSinceCongestion;
|
||||
/** Notify this on connection (or connection failure) */
|
||||
private Object _connectLock;
|
||||
/** how many messages have been resent and not yet ACKed? */
|
||||
private int _activeResends;
|
||||
|
||||
private long _lifetimeBytesSent;
|
||||
private long _lifetimeBytesReceived;
|
||||
private long _lifetimeDupMessageSent;
|
||||
private long _lifetimeDupMessageReceived;
|
||||
|
||||
public static final long MAX_RESEND_DELAY = 60*1000;
|
||||
public static final long MIN_RESEND_DELAY = 40*1000;
|
||||
|
||||
/** wait up to 5 minutes after disconnection so we can ack/close packets */
|
||||
public static int DISCONNECT_TIMEOUT = 5*60*1000;
|
||||
|
||||
/** lets be sane- no more than 32 packets in the air in each dir */
|
||||
public static final int MAX_WINDOW_SIZE = 32;
|
||||
|
||||
public Connection(I2PAppContext ctx, ConnectionManager manager, SchedulerChooser chooser, PacketQueue queue, ConnectionPacketHandler handler) {
|
||||
this(ctx, manager, chooser, queue, handler, null);
|
||||
@@ -79,9 +101,17 @@ public class Connection {
|
||||
_unackedPacketsReceived = 0;
|
||||
_congestionWindowEnd = 0;
|
||||
_highestAckedThrough = -1;
|
||||
_lastCongestionSeenAt = MAX_WINDOW_SIZE;
|
||||
_connectionManager = manager;
|
||||
_resetReceived = false;
|
||||
_connected = true;
|
||||
_disconnectScheduled = false;
|
||||
_lastReceivedOn = -1;
|
||||
_activityTimer = new ActivityTimer();
|
||||
_ackSinceCongestion = true;
|
||||
_connectLock = new Object();
|
||||
_activeResends = 0;
|
||||
_context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
}
|
||||
|
||||
public long getNextOutboundPacketNum() {
|
||||
@@ -93,6 +123,7 @@ public class Connection {
|
||||
void closeReceived() {
|
||||
setCloseReceivedOn(_context.clock().now());
|
||||
_inputStream.closeReceived();
|
||||
synchronized (_connectLock) { _connectLock.notifyAll(); }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,6 +178,8 @@ public class Connection {
|
||||
}
|
||||
|
||||
void sendPacket(PacketLocal packet) {
|
||||
if (packet == null) return;
|
||||
|
||||
setNextSendTime(-1);
|
||||
_unackedPacketsReceived = 0;
|
||||
if (_options.getRequireFullySigned()) {
|
||||
@@ -178,7 +211,7 @@ public class Connection {
|
||||
}
|
||||
packet.setFlag(Packet.FLAG_DELAY_REQUESTED);
|
||||
|
||||
long timeout = (_options.getRTT() < 10000 ? 10000 : _options.getRTT());
|
||||
long timeout = (_options.getRTT() < MIN_RESEND_DELAY ? MIN_RESEND_DELAY : _options.getRTT());
|
||||
if (timeout > MAX_RESEND_DELAY)
|
||||
timeout = MAX_RESEND_DELAY;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -189,13 +222,14 @@ public class Connection {
|
||||
|
||||
_lastSendTime = _context.clock().now();
|
||||
_outboundQueue.enqueue(packet);
|
||||
resetActivityTimer();
|
||||
|
||||
if (ackOnly) {
|
||||
// ACK only, don't schedule this packet for retries
|
||||
// however, if we are running low on sessionTags we want to send
|
||||
// something that will get a reply so that we can deliver some new tags -
|
||||
// ACKs don't get ACKed, but pings do.
|
||||
if (packet.getTagsSent().size() > 0) {
|
||||
if ( (packet.getTagsSent() != null) && (packet.getTagsSent().size() > 0) ) {
|
||||
_log.warn("Sending a ping since the ACK we just sent has " + packet.getTagsSent().size() + " tags");
|
||||
_connectionManager.ping(_remotePeer, _options.getRTT()*2, false, packet.getKeyUsed(), packet.getTagsSent(), new PingNotifier());
|
||||
}
|
||||
@@ -260,21 +294,57 @@ public class Connection {
|
||||
PacketLocal p = (PacketLocal)acked.get(i);
|
||||
_outboundPackets.remove(new Long(p.getSequenceNum()));
|
||||
_ackedPackets++;
|
||||
if (p.getNumSends() > 1) {
|
||||
_activeResends--;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Active resend of " + p + " successful, # active left: " + _activeResends);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( (_outboundPackets.size() <= 0) && (_activeResends != 0) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("All outbound packets acked, clearing " + _activeResends);
|
||||
_activeResends = 0;
|
||||
}
|
||||
_outboundPackets.notifyAll();
|
||||
}
|
||||
if ((acked != null) && (acked.size() > 0) )
|
||||
_ackSinceCongestion = true;
|
||||
return acked;
|
||||
}
|
||||
|
||||
private long _occurredTime;
|
||||
private long _occurredEventCount;
|
||||
void eventOccurred() {
|
||||
_chooser.getScheduler(this).eventOccurred(this);
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
TaskScheduler sched = _chooser.getScheduler(this);
|
||||
|
||||
now = now - now % 1000;
|
||||
if (_occurredTime == now) {
|
||||
_occurredEventCount++;
|
||||
} else {
|
||||
_occurredTime = now;
|
||||
if (_occurredEventCount > 100) {
|
||||
_log.log(Log.CRIT, "More than 100 events (" + _occurredEventCount + ") in a second on "
|
||||
+ toString() + ": scheduler = " + sched);
|
||||
}
|
||||
_occurredEventCount = 0;
|
||||
}
|
||||
|
||||
sched.eventOccurred(this);
|
||||
}
|
||||
|
||||
void resetReceived() {
|
||||
_resetReceived = true;
|
||||
_outputStream.streamErrorOccurred(new IOException("Reset received"));
|
||||
_inputStream.streamErrorOccurred(new IOException("Reset received"));
|
||||
MessageOutputStream mos = _outputStream;
|
||||
MessageInputStream mis = _inputStream;
|
||||
if (mos != null)
|
||||
mos.streamErrorOccurred(new IOException("Reset received"));
|
||||
if (mis != null)
|
||||
mis.streamErrorOccurred(new IOException("Reset received"));
|
||||
_connectionError = "Connection reset";
|
||||
synchronized (_connectLock) { _connectLock.notifyAll(); }
|
||||
}
|
||||
public boolean getResetReceived() { return _resetReceived; }
|
||||
|
||||
@@ -284,37 +354,91 @@ public class Connection {
|
||||
disconnect(cleanDisconnect, true);
|
||||
}
|
||||
void disconnect(boolean cleanDisconnect, boolean removeFromConMgr) {
|
||||
if (!_connected) return;
|
||||
_connected = false;
|
||||
synchronized (_connectLock) { _connectLock.notifyAll(); }
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Disconnecting " + toString(), new Exception("discon"));
|
||||
|
||||
if (cleanDisconnect) {
|
||||
if (cleanDisconnect && _connected) {
|
||||
// send close packets and schedule stuff...
|
||||
_outputStream.closeInternal();
|
||||
_inputStream.close();
|
||||
} else {
|
||||
doClose();
|
||||
synchronized (_outboundPackets) {
|
||||
for (Iterator iter = _outboundPackets.values().iterator(); iter.hasNext(); ) {
|
||||
PacketLocal pl = (PacketLocal)iter.next();
|
||||
pl.cancelled();
|
||||
}
|
||||
_outboundPackets.clear();
|
||||
_outboundPackets.notifyAll();
|
||||
}
|
||||
if (removeFromConMgr)
|
||||
_connectionManager.removeConnection(this);
|
||||
if (_connected)
|
||||
doClose();
|
||||
killOutstandingPackets();
|
||||
}
|
||||
if (removeFromConMgr) {
|
||||
if (!_disconnectScheduled) {
|
||||
_disconnectScheduled = true;
|
||||
SimpleTimer.getInstance().addEvent(new DisconnectEvent(), DISCONNECT_TIMEOUT);
|
||||
}
|
||||
}
|
||||
_connected = false;
|
||||
}
|
||||
|
||||
void disconnectComplete() {
|
||||
_connectionManager.removeConnection(this);
|
||||
_connected = false;
|
||||
if (_socket != null)
|
||||
_socket.destroy();
|
||||
_socket = null;
|
||||
_inputStream = null;
|
||||
if (_outputStream != null)
|
||||
_outputStream.destroy();
|
||||
_outputStream = null;
|
||||
_outboundQueue = null;
|
||||
if (_receiver != null)
|
||||
_receiver.destroy();
|
||||
if (_activityTimer != null)
|
||||
SimpleTimer.getInstance().addEvent(_activityTimer, 1);
|
||||
_activityTimer = null;
|
||||
|
||||
if (!_disconnectScheduled) {
|
||||
_disconnectScheduled = true;
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Connection disconnect complete from dead, drop the con "
|
||||
+ toString());
|
||||
_connectionManager.removeConnection(this);
|
||||
}
|
||||
|
||||
killOutstandingPackets();
|
||||
}
|
||||
|
||||
private void killOutstandingPackets() {
|
||||
boolean tagsCancelled = false;
|
||||
synchronized (_outboundPackets) {
|
||||
for (Iterator iter = _outboundPackets.values().iterator(); iter.hasNext(); ) {
|
||||
PacketLocal pl = (PacketLocal)iter.next();
|
||||
if ( (pl.getTagsSent() != null) && (pl.getTagsSent().size() > 0) )
|
||||
tagsCancelled = true;
|
||||
pl.cancelled();
|
||||
}
|
||||
_outboundPackets.clear();
|
||||
_outboundPackets.notifyAll();
|
||||
}
|
||||
if (tagsCancelled)
|
||||
_context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
|
||||
}
|
||||
|
||||
private class DisconnectEvent implements SimpleTimer.TimedEvent {
|
||||
public DisconnectEvent() {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Connection disconnect timer initiated: 5 minutes to drop "
|
||||
+ Connection.this.toString());
|
||||
}
|
||||
public void timeReached() {
|
||||
killOutstandingPackets();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Connection disconnect timer complete, drop the con "
|
||||
+ Connection.this.toString());
|
||||
_connectionManager.removeConnection(Connection.this);
|
||||
}
|
||||
}
|
||||
|
||||
private void doClose() {
|
||||
_outputStream.streamErrorOccurred(new IOException("Hard disconnect"));
|
||||
_inputStream.closeReceived();
|
||||
synchronized (_connectLock) { _connectLock.notifyAll(); }
|
||||
}
|
||||
|
||||
/** who are we talking with */
|
||||
@@ -327,7 +451,10 @@ public class Connection {
|
||||
|
||||
/** stream the peer sends data to us on. (may be null) */
|
||||
public byte[] getReceiveStreamId() { return _receiveStreamId; }
|
||||
public void setReceiveStreamId(byte[] id) { _receiveStreamId = id; }
|
||||
public void setReceiveStreamId(byte[] id) {
|
||||
_receiveStreamId = id;
|
||||
synchronized (_connectLock) { _connectLock.notifyAll(); }
|
||||
}
|
||||
|
||||
/** when did we last send anything to the peer? */
|
||||
public long getLastSendTime() { return _lastSendTime; }
|
||||
@@ -347,8 +474,24 @@ public class Connection {
|
||||
public String getConnectionError() { return _connectionError; }
|
||||
public void setConnectionError(String err) { _connectionError = err; }
|
||||
|
||||
public long getLifetime() {
|
||||
if (_closeSentOn <= 0)
|
||||
return _context.clock().now() - _createdOn;
|
||||
else
|
||||
return _closeSentOn - _createdOn;
|
||||
}
|
||||
|
||||
public ConnectionPacketHandler getPacketHandler() { return _handler; }
|
||||
|
||||
public long getLifetimeBytesSent() { return _lifetimeBytesSent; }
|
||||
public long getLifetimeBytesReceived() { return _lifetimeBytesReceived; }
|
||||
public long getLifetimeDupMessagesSent() { return _lifetimeDupMessageSent; }
|
||||
public long getLifetimeDupMessagesReceived() { return _lifetimeDupMessageReceived; }
|
||||
public void incrementBytesSent(int bytes) { _lifetimeBytesSent += bytes; }
|
||||
public void incrementDupMessagesSent(int msgs) { _lifetimeDupMessageSent += msgs; }
|
||||
public void incrementBytesReceived(int bytes) { _lifetimeBytesReceived += bytes; }
|
||||
public void incrementDupMessagesReceived(int msgs) { _lifetimeDupMessageReceived += msgs; }
|
||||
|
||||
/**
|
||||
* Time when the scheduler next want to send a packet, or -1 if
|
||||
* never. This should be set when we want to send on timeout, for
|
||||
@@ -397,6 +540,129 @@ public class Connection {
|
||||
public long getHighestAckedThrough() { return _highestAckedThrough; }
|
||||
public void setHighestAckedThrough(long msgNum) { _highestAckedThrough = msgNum; }
|
||||
|
||||
public long getLastActivityOn() {
|
||||
return (_lastSendTime > _lastReceivedOn ? _lastSendTime : _lastReceivedOn);
|
||||
}
|
||||
|
||||
public int getLastCongestionSeenAt() { return _lastCongestionSeenAt; }
|
||||
|
||||
void congestionOccurred() {
|
||||
// if we hit congestion and e.g. 5 packets are resent,
|
||||
// dont set the size to (winSize >> 4). only set the
|
||||
if (_ackSinceCongestion) {
|
||||
_lastCongestionSeenAt = _options.getWindowSize();
|
||||
_ackSinceCongestion = false;
|
||||
}
|
||||
}
|
||||
|
||||
void packetReceived() {
|
||||
_lastReceivedOn = _context.clock().now();
|
||||
resetActivityTimer();
|
||||
synchronized (_connectLock) { _connectLock.notifyAll(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* wait until a connection is made or the connection fails within the
|
||||
* timeout period, setting the error accordingly.
|
||||
*/
|
||||
void waitForConnect() {
|
||||
long expiration = _context.clock().now() + _options.getConnectTimeout();
|
||||
while (true) {
|
||||
if (_connected && (_receiveStreamId != null) && (_sendStreamId != null) ) {
|
||||
// w00t
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("waitForConnect(): Connected and we have stream IDs");
|
||||
return;
|
||||
}
|
||||
if (_connectionError != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("waitForConnect(): connection error found: " + _connectionError);
|
||||
return;
|
||||
}
|
||||
if (!_connected) {
|
||||
_connectionError = "Connection failed";
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("waitForConnect(): not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
long timeLeft = expiration - _context.clock().now();
|
||||
if ( (timeLeft <= 0) && (_options.getConnectTimeout() > 0) ) {
|
||||
if (_connectionError == null) {
|
||||
_connectionError = "Connection timed out";
|
||||
disconnect(false);
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("waitForConnect(): timed out: " + _connectionError);
|
||||
return;
|
||||
}
|
||||
if (timeLeft > 60*1000)
|
||||
timeLeft = 60*1000;
|
||||
if (_options.getConnectTimeout() <= 0)
|
||||
timeLeft = 60*1000;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("waitForConnect(): wait " + timeLeft);
|
||||
try {
|
||||
synchronized (_connectLock) {
|
||||
_connectLock.wait(timeLeft);
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
private void resetActivityTimer() {
|
||||
if (_options.getInactivityTimeout() <= 0) return;
|
||||
if (_activityTimer == null) return;
|
||||
long howLong = _activityTimer.getTimeLeft();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Resetting the inactivity timer to " + howLong);
|
||||
// this will get rescheduled, and rescheduled, and rescheduled...
|
||||
SimpleTimer.getInstance().addEvent(_activityTimer, howLong);
|
||||
}
|
||||
|
||||
private class ActivityTimer implements SimpleTimer.TimedEvent {
|
||||
public void timeReached() {
|
||||
// uh, nothing more to do...
|
||||
if (!_connected) return;
|
||||
// we got rescheduled already
|
||||
if (getTimeLeft() > 0) return;
|
||||
// these are either going to time out or cause further rescheduling
|
||||
if (getUnackedPacketsSent() > 0) return;
|
||||
// wtf, this shouldn't have been scheduled
|
||||
if (_options.getInactivityTimeout() <= 0) return;
|
||||
// if one of us can't talk...
|
||||
if ( (_closeSentOn > 0) || (_closeReceivedOn > 0) ) return;
|
||||
|
||||
// bugger it, might as well do the hard work now
|
||||
switch (_options.getInactivityAction()) {
|
||||
case ConnectionOptions.INACTIVITY_ACTION_SEND:
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Sending some data due to inactivity");
|
||||
_receiver.send(null, 0, 0, true);
|
||||
break;
|
||||
case ConnectionOptions.INACTIVITY_ACTION_NOOP:
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Inactivity timer expired, but we aint doin' shit");
|
||||
break;
|
||||
case ConnectionOptions.INACTIVITY_ACTION_DISCONNECT:
|
||||
// fall through
|
||||
default:
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Closing connection due to inactivity");
|
||||
disconnect(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public final long getTimeLeft() {
|
||||
if (getLastActivityOn() > 0)
|
||||
return getLastActivityOn() + _options.getInactivityTimeout() - _context.clock().now();
|
||||
else
|
||||
return _createdOn + _options.getInactivityTimeout() - _context.clock().now();
|
||||
}
|
||||
}
|
||||
|
||||
/** stream that the local peer receives data on */
|
||||
public MessageInputStream getInputStream() { return _inputStream; }
|
||||
/** stream that the local peer sends data to the remote peer on */
|
||||
@@ -426,12 +692,15 @@ public class Connection {
|
||||
buf.append("] ");
|
||||
}
|
||||
buf.append("unacked inbound? ").append(getUnackedPacketsReceived());
|
||||
buf.append(" [high ").append(_inputStream.getHighestBlockId());
|
||||
long nacks[] = _inputStream.getNacks();
|
||||
if (nacks != null)
|
||||
for (int i = 0; i < nacks.length; i++)
|
||||
buf.append(" ").append(nacks[i]);
|
||||
buf.append("]");
|
||||
if (_inputStream != null) {
|
||||
buf.append(" [high ");
|
||||
buf.append(_inputStream.getHighestBlockId());
|
||||
long nacks[] = _inputStream.getNacks();
|
||||
if (nacks != null)
|
||||
for (int i = 0; i < nacks.length; i++)
|
||||
buf.append(" ").append(nacks[i]);
|
||||
buf.append("]");
|
||||
}
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
@@ -441,12 +710,20 @@ public class Connection {
|
||||
*/
|
||||
private class ResendPacketEvent implements SimpleTimer.TimedEvent {
|
||||
private PacketLocal _packet;
|
||||
private boolean _currentIsActiveResend;
|
||||
public ResendPacketEvent(PacketLocal packet) {
|
||||
_packet = packet;
|
||||
_currentIsActiveResend = false;
|
||||
}
|
||||
|
||||
public void timeReached() {
|
||||
if (!_connected) return;
|
||||
if (_packet.getAckTime() > 0)
|
||||
return;
|
||||
|
||||
if (!_connected) {
|
||||
_packet.cancelled();
|
||||
return;
|
||||
}
|
||||
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Resend period reached for " + _packet);
|
||||
@@ -456,6 +733,16 @@ public class Connection {
|
||||
resend = true;
|
||||
}
|
||||
if ( (resend) && (_packet.getAckTime() < 0) ) {
|
||||
if ( (_activeResends > 0) && (!_currentIsActiveResend) ) {
|
||||
// we want to resend this packet, but there are already active
|
||||
// resends in the air and we dont want to make a bad situation
|
||||
// worse. wait another second
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Delaying resend of " + _packet + " as there are "
|
||||
+ _activeResends + " active resends already in play");
|
||||
SimpleTimer.getInstance().addEvent(ResendPacketEvent.this, 1000);
|
||||
return;
|
||||
}
|
||||
// revamp various fields, in case we need to ack more, etc
|
||||
_inputStream.updateAcks(_packet);
|
||||
_packet.setOptionalDelay(getOptions().getChoke());
|
||||
@@ -466,6 +753,8 @@ public class Connection {
|
||||
|
||||
// shrink the window
|
||||
int newWindowSize = getOptions().getWindowSize();
|
||||
congestionOccurred();
|
||||
_context.statManager().addRateData("stream.con.windowSizeAtCongestion", newWindowSize, _packet.getLifetime());
|
||||
newWindowSize /= 2;
|
||||
if (newWindowSize <= 0)
|
||||
newWindowSize = 1;
|
||||
@@ -473,13 +762,25 @@ public class Connection {
|
||||
|
||||
int numSends = _packet.getNumSends() + 1;
|
||||
|
||||
if (numSends == 2) {
|
||||
// first resend for this packet
|
||||
_activeResends++;
|
||||
_currentIsActiveResend = true;
|
||||
}
|
||||
|
||||
// in case things really suck, the other side may have lost thier
|
||||
// session tags (e.g. they restarted), so jump back to ElGamal.
|
||||
if ( (newWindowSize == 1) && (numSends > 2) )
|
||||
int failTagsAt = _options.getMaxResends() - 1;
|
||||
if ( (newWindowSize == 1) && (numSends == failTagsAt) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Optimistically failing tags at resend " + numSends);
|
||||
_context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Resend packet " + _packet + " time " + numSends + " (wsize "
|
||||
_log.warn("Resend packet " + _packet + " time " + numSends +
|
||||
" activeResends: " + _activeResends +
|
||||
" (wsize "
|
||||
+ newWindowSize + " lifetime "
|
||||
+ (_context.clock().now() - _packet.getCreatedOn()) + "ms)");
|
||||
_outboundQueue.enqueue(_packet);
|
||||
@@ -487,10 +788,14 @@ public class Connection {
|
||||
if (numSends > _options.getMaxResends()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Too many resends");
|
||||
_packet.cancelled();
|
||||
disconnect(false);
|
||||
} else {
|
||||
//long timeout = _options.getResendDelay() << numSends;
|
||||
long timeout = _options.getRTT() << (numSends-1);
|
||||
long rtt = _options.getRTT();
|
||||
if (rtt < MIN_RESEND_DELAY)
|
||||
rtt = MIN_RESEND_DELAY;
|
||||
long timeout = rtt << (numSends-1);
|
||||
if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) )
|
||||
timeout = MAX_RESEND_DELAY;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
|
@@ -6,6 +6,11 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Receive data from the MessageOutputStream, build a packet,
|
||||
* and send it through a connection. The write calls on this
|
||||
* do NOT block, but they also do not necessary imply immediate
|
||||
* delivery, or even the generation of a new packet. This class
|
||||
* is the only one that builds useful outbound Packet objects.
|
||||
*
|
||||
*/
|
||||
class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
@@ -21,6 +26,17 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
_dummyStatus = new DummyStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send some data through the connection, or if there is no new data, this
|
||||
* may generate a packet with a plain ACK/NACK or CLOSE, or nothing whatsoever
|
||||
* if there's nothing new to send.
|
||||
*
|
||||
* @param buf data to be sent - may be null
|
||||
* @param off offset into the buffer to start writing from
|
||||
* @param size how many bytes of the buffer to write (may be 0)
|
||||
* @return an object to allow optional blocking for data acceptance or
|
||||
* delivery.
|
||||
*/
|
||||
public MessageOutputStream.WriteStatus writeData(byte[] buf, int off, int size) {
|
||||
boolean doSend = true;
|
||||
if ( (size <= 0) && (_connection.getLastSendId() >= 0) ) {
|
||||
@@ -40,22 +56,46 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
if (_connection.getUnackedPacketsReceived() > 0)
|
||||
doSend = true;
|
||||
|
||||
if (_log.shouldLog(Log.ERROR) && !doSend)
|
||||
_log.error("writeData called: size="+size + " doSend=" + doSend
|
||||
if (_log.shouldLog(Log.INFO) && !doSend)
|
||||
_log.info("writeData called: size="+size + " doSend=" + doSend
|
||||
+ " unackedReceived: " + _connection.getUnackedPacketsReceived()
|
||||
+ " con: " + _connection, new Exception("write called by"));
|
||||
|
||||
if (doSend) {
|
||||
PacketLocal packet = send(buf, off, size);
|
||||
return packet;
|
||||
//dont wait for non-acks
|
||||
if ( (packet.getPayloadSize() > 0) || (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) )
|
||||
return packet;
|
||||
else
|
||||
return _dummyStatus;
|
||||
} else {
|
||||
return _dummyStatus;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send some data through the connection, attaching any appropriate flags
|
||||
* onto the packet.
|
||||
*
|
||||
* @param buf data to be sent - may be null
|
||||
* @param off offset into the buffer to start writing from
|
||||
* @param size how many bytes of the buffer to write (may be 0)
|
||||
* @return the packet sent
|
||||
*/
|
||||
public PacketLocal send(byte buf[], int off, int size) {
|
||||
PacketLocal packet = buildPacket(buf, off, size);
|
||||
return send(buf, off, size, false);
|
||||
}
|
||||
/**
|
||||
* @param buf data to be sent - may be null
|
||||
* @param off offset into the buffer to start writing from
|
||||
* @param size how many bytes of the buffer to write (may be 0)
|
||||
* @param forceIncrement even if the buffer is empty, increment the packetId
|
||||
* so we get an ACK back
|
||||
* @return the packet sent
|
||||
*/
|
||||
public PacketLocal send(byte buf[], int off, int size, boolean forceIncrement) {
|
||||
PacketLocal packet = buildPacket(buf, off, size, forceIncrement);
|
||||
_connection.sendPacket(packet);
|
||||
return packet;
|
||||
}
|
||||
@@ -69,14 +109,14 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
return ackOnly;
|
||||
}
|
||||
|
||||
private PacketLocal buildPacket(byte buf[], int off, int size) {
|
||||
private PacketLocal buildPacket(byte buf[], int off, int size, boolean forceIncrement) {
|
||||
boolean ackOnly = isAckOnly(size);
|
||||
PacketLocal packet = new PacketLocal(_context, _connection.getRemotePeer(), _connection);
|
||||
byte data[] = new byte[size];
|
||||
if (size > 0)
|
||||
System.arraycopy(buf, off, data, 0, size);
|
||||
packet.setPayload(data);
|
||||
if (ackOnly)
|
||||
if (ackOnly && !forceIncrement)
|
||||
packet.setSequenceNum(0);
|
||||
else
|
||||
packet.setSequenceNum(_connection.getNextOutboundPacketNum());
|
||||
@@ -100,7 +140,11 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
packet.setOptionalFrom(_connection.getSession().getMyDestination());
|
||||
}
|
||||
|
||||
if (_connection.getOutputStream().getClosed()) {
|
||||
// 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,
|
||||
// since this ACK could arrive before the unacked payload message.
|
||||
if (_connection.getOutputStream().getClosed() &&
|
||||
( (size > 0) || (_connection.getUnackedPacketsSent() <= 0) ) ) {
|
||||
packet.setFlag(Packet.FLAG_CLOSE);
|
||||
_connection.setCloseSentOn(_context.clock().now());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -112,7 +156,9 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used if no new packet was sent.
|
||||
*/
|
||||
private static final class DummyStatus implements MessageOutputStream.WriteStatus {
|
||||
public final void waitForAccept(int maxWaitMs) { return; }
|
||||
public final void waitForCompletion(int maxWaitMs) { return; }
|
||||
@@ -120,4 +166,8 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
public final boolean writeFailed() { return false; }
|
||||
public final boolean writeSuccessful() { return true; }
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
_connection = null;
|
||||
}
|
||||
}
|
||||
|
@@ -31,7 +31,14 @@ class ConnectionHandler {
|
||||
_acceptTimeout = DEFAULT_ACCEPT_TIMEOUT;
|
||||
}
|
||||
|
||||
public void setActive(boolean active) { _active = active; }
|
||||
public void setActive(boolean active) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("setActive(" + active + ") called");
|
||||
synchronized (_synQueue) {
|
||||
_active = active;
|
||||
_synQueue.notifyAll(); // so we break from the accept()
|
||||
}
|
||||
}
|
||||
public boolean getActive() { return _active; }
|
||||
|
||||
public void receiveNewSyn(Packet packet) {
|
||||
@@ -50,49 +57,61 @@ class ConnectionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive an incoming connection (built from a received SYN)
|
||||
*
|
||||
* @param timeoutMs max amount of time to wait for a connection (if less
|
||||
* than 1ms, wait indefinitely)
|
||||
* @return connection received, or null if there was a timeout or the
|
||||
* handler was shut down
|
||||
*/
|
||||
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) {}
|
||||
long expiration = timeoutMs + _context.clock().now();
|
||||
while (true) {
|
||||
if ( (timeoutMs > 0) && (expiration < _context.clock().now()) )
|
||||
return null;
|
||||
if (!_active) {
|
||||
// fail all the ones we had queued up
|
||||
synchronized (_synQueue) {
|
||||
for (int i = 0; i < _synQueue.size(); i++) {
|
||||
Packet packet = (Packet)_synQueue.get(i);
|
||||
sendReset(packet);
|
||||
}
|
||||
_synQueue.clear();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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 (_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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// keep looping...
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -10,6 +10,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
@@ -35,9 +36,11 @@ public class ConnectionManager {
|
||||
/** Ping ID (ByteArray) to PingRequest */
|
||||
private Map _pendingPings;
|
||||
private boolean _allowIncoming;
|
||||
private int _maxConcurrentStreams;
|
||||
private volatile int _numWaiting;
|
||||
private Object _connectionLock;
|
||||
|
||||
public ConnectionManager(I2PAppContext context, I2PSession session) {
|
||||
public ConnectionManager(I2PAppContext context, I2PSession session, int maxConcurrent) {
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(ConnectionManager.class);
|
||||
_connectionByInboundId = new HashMap(32);
|
||||
@@ -50,8 +53,19 @@ public class ConnectionManager {
|
||||
_conPacketHandler = new ConnectionPacketHandler(context);
|
||||
_session = session;
|
||||
session.setSessionListener(_messageHandler);
|
||||
_outboundQueue = new PacketQueue(context, session);
|
||||
_outboundQueue = new PacketQueue(context, session, this);
|
||||
_allowIncoming = false;
|
||||
_maxConcurrentStreams = maxConcurrent;
|
||||
_numWaiting = 0;
|
||||
_context.statManager().createRateStat("stream.con.lifetimeMessagesSent", "How many messages do we send on a stream?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.lifetimeMessagesReceived", "How many messages do we receive on a stream?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.lifetimeBytesSent", "How many bytes do we send on a stream?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.lifetimeBytesReceived", "How many bytes do we receive on a stream?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.lifetimeDupMessagesSent", "How many duplicate messages do we send on a stream?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.lifetimeDupMessagesReceived", "How many duplicate messages do we receive on a stream?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.lifetimeRTT", "What is the final RTT when a stream closes?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.lifetimeCongestionSeenAt", "When was the last congestion seen at when a stream closes?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.lifetimeSendWindowSize", "What is the final send window size when a stream closes?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
}
|
||||
|
||||
Connection getConnectionByInboundId(byte[] id) {
|
||||
@@ -59,10 +73,25 @@ public class ConnectionManager {
|
||||
return (Connection)_connectionByInboundId.get(new ByteArray(id));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* not guaranteed to be unique, but in case we receive more than one packet
|
||||
* on an inbound connection that we havent ack'ed yet...
|
||||
*/
|
||||
Connection getConnectionByOutboundId(byte[] id) {
|
||||
synchronized (_connectionLock) {
|
||||
for (Iterator iter = _connectionByInboundId.values().iterator(); iter.hasNext(); ) {
|
||||
Connection con = (Connection)iter.next();
|
||||
if (DataHelper.eq(con.getSendStreamId(), id))
|
||||
return con;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setAllowIncomingConnections(boolean allow) {
|
||||
_connectionHandler.setActive(allow);
|
||||
}
|
||||
/** should we acceot connections, or just reject everyone? */
|
||||
public boolean getAllowIncomingConnections() {
|
||||
return _connectionHandler.getActive();
|
||||
}
|
||||
@@ -77,19 +106,41 @@ public class ConnectionManager {
|
||||
Connection con = new Connection(_context, this, _schedulerChooser, _outboundQueue, _conPacketHandler);
|
||||
byte receiveId[] = new byte[4];
|
||||
_context.random().nextBytes(receiveId);
|
||||
boolean reject = false;
|
||||
synchronized (_connectionLock) {
|
||||
while (true) {
|
||||
Connection oldCon = (Connection)_connectionByInboundId.put(new ByteArray(receiveId), con);
|
||||
if (oldCon == null) {
|
||||
break;
|
||||
} else {
|
||||
_connectionByInboundId.put(new ByteArray(receiveId), oldCon);
|
||||
// receiveId already taken, try another
|
||||
_context.random().nextBytes(receiveId);
|
||||
if (locked_tooManyStreams()) {
|
||||
reject = true;
|
||||
} else {
|
||||
while (true) {
|
||||
ByteArray ba = new ByteArray(receiveId);
|
||||
Connection oldCon = (Connection)_connectionByInboundId.put(ba, con);
|
||||
if (oldCon == null) {
|
||||
break;
|
||||
} else {
|
||||
_connectionByInboundId.put(ba, oldCon);
|
||||
// receiveId already taken, try another
|
||||
_context.random().nextBytes(receiveId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reject) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Refusing connection since we have exceeded our max of "
|
||||
+ _maxConcurrentStreams + " connections");
|
||||
PacketLocal reply = new PacketLocal(_context, synPacket.getOptionalFrom());
|
||||
reply.setFlag(Packet.FLAG_RESET);
|
||||
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
|
||||
reply.setAckThrough(synPacket.getSequenceNum());
|
||||
reply.setSendStreamId(synPacket.getReceiveStreamId());
|
||||
reply.setReceiveStreamId(null);
|
||||
reply.setOptionalFrom(_session.getMyDestination());
|
||||
// this just sends the packet - no retries or whatnot
|
||||
_outboundQueue.enqueue(reply);
|
||||
return null;
|
||||
}
|
||||
|
||||
con.setReceiveStreamId(receiveId);
|
||||
try {
|
||||
con.getPacketHandler().receivePacket(synPacket, con);
|
||||
@@ -99,30 +150,97 @@ public class ConnectionManager {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_context.statManager().addRateData("stream.connectionReceived", 1, 0);
|
||||
return con;
|
||||
}
|
||||
|
||||
private static final long DEFAULT_STREAM_DELAY_MAX = 10*1000;
|
||||
|
||||
/**
|
||||
* Build a new connection to the given peer
|
||||
* Build a new connection to the given peer. This blocks if there is no
|
||||
* connection delay, otherwise it returns immediately.
|
||||
*
|
||||
* @return new connection, or null if we have exceeded our limit
|
||||
*/
|
||||
public Connection connect(Destination peer, ConnectionOptions opts) {
|
||||
Connection con = new Connection(_context, this, _schedulerChooser, _outboundQueue, _conPacketHandler, opts);
|
||||
con.setRemotePeer(peer);
|
||||
Connection con = null;
|
||||
byte receiveId[] = new byte[4];
|
||||
_context.random().nextBytes(receiveId);
|
||||
synchronized (_connectionLock) {
|
||||
ByteArray ba = new ByteArray(receiveId);
|
||||
while (_connectionByInboundId.containsKey(ba)) {
|
||||
_context.random().nextBytes(receiveId);
|
||||
long expiration = _context.clock().now() + opts.getConnectTimeout();
|
||||
if (opts.getConnectTimeout() <= 0)
|
||||
expiration = _context.clock().now() + DEFAULT_STREAM_DELAY_MAX;
|
||||
_numWaiting++;
|
||||
while (true) {
|
||||
long remaining = expiration - _context.clock().now();
|
||||
if (remaining <= 0) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Refusing to connect since we have exceeded our max of "
|
||||
+ _maxConcurrentStreams + " connections");
|
||||
_numWaiting--;
|
||||
return null;
|
||||
}
|
||||
boolean reject = false;
|
||||
synchronized (_connectionLock) {
|
||||
if (locked_tooManyStreams()) {
|
||||
// allow a full buffer of pending/waiting streams
|
||||
if (_numWaiting > _maxConcurrentStreams) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Refusing connection since we have exceeded our max of "
|
||||
+ _maxConcurrentStreams + " and there are " + _numWaiting
|
||||
+ " waiting already");
|
||||
_numWaiting--;
|
||||
return null;
|
||||
}
|
||||
|
||||
// no remaining streams, lets wait a bit
|
||||
try { _connectionLock.wait(remaining); } catch (InterruptedException ie) {}
|
||||
} else {
|
||||
con = new Connection(_context, this, _schedulerChooser, _outboundQueue, _conPacketHandler, opts);
|
||||
con.setRemotePeer(peer);
|
||||
|
||||
ByteArray ba = new ByteArray(receiveId);
|
||||
while (_connectionByInboundId.containsKey(ba)) {
|
||||
_context.random().nextBytes(receiveId);
|
||||
}
|
||||
_connectionByInboundId.put(ba, con);
|
||||
break; // stop looping as a psuedo-wait
|
||||
}
|
||||
}
|
||||
_connectionByInboundId.put(ba, con);
|
||||
}
|
||||
|
||||
|
||||
// ok we're in...
|
||||
con.setReceiveStreamId(receiveId);
|
||||
con.eventOccurred();
|
||||
|
||||
_log.debug("Connect() conDelay = " + opts.getConnectDelay());
|
||||
if (opts.getConnectDelay() <= 0) {
|
||||
con.waitForConnect();
|
||||
}
|
||||
if (_numWaiting > 0)
|
||||
_numWaiting--;
|
||||
|
||||
_context.statManager().addRateData("stream.connectionCreated", 1, 0);
|
||||
return con;
|
||||
}
|
||||
|
||||
private boolean locked_tooManyStreams() {
|
||||
int active = 0;
|
||||
for (Iterator iter = _connectionByInboundId.values().iterator(); iter.hasNext(); ) {
|
||||
Connection con = (Connection)iter.next();
|
||||
if (con.getIsConnected())
|
||||
active++;
|
||||
}
|
||||
|
||||
if ( (_connectionByInboundId.size() > 100) && (_log.shouldLog(Log.INFO)) )
|
||||
_log.info("More than 100 connections! " + active
|
||||
+ " total: " + _connectionByInboundId.size());
|
||||
|
||||
if (_maxConcurrentStreams <= 0) return false;
|
||||
if (_connectionByInboundId.size() < _maxConcurrentStreams) return false;
|
||||
return (active >= _maxConcurrentStreams);
|
||||
}
|
||||
|
||||
public MessageHandler getMessageHandler() { return _messageHandler; }
|
||||
public PacketHandler getPacketHandler() { return _packetHandler; }
|
||||
public ConnectionHandler getConnectionHandler() { return _connectionHandler; }
|
||||
@@ -141,15 +259,40 @@ public class ConnectionManager {
|
||||
con.disconnect(false, false);
|
||||
}
|
||||
_connectionByInboundId.clear();
|
||||
_connectionLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the (already closed) connection on the floor.
|
||||
*
|
||||
*/
|
||||
public void removeConnection(Connection con) {
|
||||
boolean removed = false;
|
||||
synchronized (_connectionLock) {
|
||||
_connectionByInboundId.remove(new ByteArray(con.getReceiveStreamId()));
|
||||
Object o = _connectionByInboundId.remove(new ByteArray(con.getReceiveStreamId()));
|
||||
removed = (o == con);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Connection removed? " + removed + " remaining: "
|
||||
+ _connectionByInboundId.size() + ": " + con);
|
||||
if (!removed && _log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Failed to remove " + con +"\n" + _connectionByInboundId.values());
|
||||
_connectionLock.notifyAll();
|
||||
}
|
||||
if (removed) {
|
||||
_context.statManager().addRateData("stream.con.lifetimeMessagesSent", con.getLastSendId(), con.getLifetime());
|
||||
_context.statManager().addRateData("stream.con.lifetimeMessagesReceived", con.getHighestAckedThrough(), con.getLifetime());
|
||||
_context.statManager().addRateData("stream.con.lifetimeBytesSent", con.getLifetimeBytesSent(), con.getLifetime());
|
||||
_context.statManager().addRateData("stream.con.lifetimeBytesReceived", con.getLifetimeBytesReceived(), con.getLifetime());
|
||||
_context.statManager().addRateData("stream.con.lifetimeDupMessagesSent", con.getLifetimeDupMessagesSent(), con.getLifetime());
|
||||
_context.statManager().addRateData("stream.con.lifetimeDupMessagesReceived", con.getLifetimeDupMessagesReceived(), con.getLifetime());
|
||||
_context.statManager().addRateData("stream.con.lifetimeRTT", con.getOptions().getRTT(), con.getLifetime());
|
||||
_context.statManager().addRateData("stream.con.lifetimeCongestionSeenAt", con.getLastCongestionSeenAt(), con.getLifetime());
|
||||
_context.statManager().addRateData("stream.con.lifetimeSendWindowSize", con.getOptions().getWindowSize(), con.getLifetime());
|
||||
}
|
||||
}
|
||||
|
||||
/** return a set of Connection objects */
|
||||
public Set listConnections() {
|
||||
synchronized (_connectionLock) {
|
||||
return new HashSet(_connectionByInboundId.values());
|
||||
|
@@ -6,7 +6,7 @@ import java.util.Properties;
|
||||
* Define the current options for the con (and allow custom tweaking midstream)
|
||||
*
|
||||
*/
|
||||
public class ConnectionOptions extends I2PSocketOptions {
|
||||
public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
private int _connectDelay;
|
||||
private boolean _fullySigned;
|
||||
private int _windowSize;
|
||||
@@ -18,26 +18,46 @@ public class ConnectionOptions extends I2PSocketOptions {
|
||||
private int _maxMessageSize;
|
||||
private int _choke;
|
||||
private int _maxResends;
|
||||
private int _inactivityTimeout;
|
||||
private int _inactivityAction;
|
||||
private int _inboundBufferSize;
|
||||
|
||||
public static final int PROFILE_BULK = 1;
|
||||
public static final int PROFILE_INTERACTIVE = 2;
|
||||
|
||||
/** on inactivity timeout, do nothing */
|
||||
public static final int INACTIVITY_ACTION_NOOP = 0;
|
||||
/** on inactivity timeout, close the connection */
|
||||
public static final int INACTIVITY_ACTION_DISCONNECT = 1;
|
||||
/** on inactivity timeout, send a payload message */
|
||||
public static final int INACTIVITY_ACTION_SEND = 2;
|
||||
|
||||
public static final String PROP_CONNECT_DELAY = "i2p.streaming.connectDelay";
|
||||
public static final String PROP_PROFILE = "i2p.streaming.profile";
|
||||
public static final String PROP_MAX_MESSAGE_SIZE = "i2p.streaming.maxMessageSize";
|
||||
public static final String PROP_MAX_RESENDS = "i2p.streaming.maxResends";
|
||||
public static final String PROP_INITIAL_RTT = "i2p.streaming.initialRTT";
|
||||
public static final String PROP_INITIAL_RESEND_DELAY = "i2p.streaming.initialResendDelay";
|
||||
public static final String PROP_INITIAL_ACK_DELAY = "i2p.streaming.initialAckDelay";
|
||||
public static final String PROP_INITIAL_WINDOW_SIZE = "i2p.streaming.initialWindowSize";
|
||||
public static final String PROP_INITIAL_RECEIVE_WINDOW = "i2p.streaming.initialReceiveWindow";
|
||||
public static final String PROP_INACTIVITY_TIMEOUT = "i2p.streaming.inactivityTimeout";
|
||||
public static final String PROP_INACTIVITY_ACTION = "i2p.streaming.inactivityAction";
|
||||
|
||||
public ConnectionOptions() {
|
||||
super();
|
||||
init(null);
|
||||
}
|
||||
|
||||
public ConnectionOptions(Properties opts) {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
public ConnectionOptions(I2PSocketOptions opts) {
|
||||
super(opts);
|
||||
init(null);
|
||||
}
|
||||
|
||||
public ConnectionOptions(ConnectionOptions opts) {
|
||||
super(opts);
|
||||
init(opts);
|
||||
}
|
||||
|
||||
private void init(ConnectionOptions opts) {
|
||||
if (opts != null) {
|
||||
setConnectDelay(opts.getConnectDelay());
|
||||
setProfile(opts.getProfile());
|
||||
@@ -48,23 +68,27 @@ public class ConnectionOptions extends I2PSocketOptions {
|
||||
setMaxMessageSize(opts.getMaxMessageSize());
|
||||
setChoke(opts.getChoke());
|
||||
setMaxResends(opts.getMaxResends());
|
||||
} else {
|
||||
setConnectDelay(2*1000);
|
||||
setProfile(PROFILE_BULK);
|
||||
setMaxMessageSize(Packet.MAX_PAYLOAD_SIZE);
|
||||
setRTT(30*1000);
|
||||
setReceiveWindow(1);
|
||||
setResendDelay(5*1000);
|
||||
setSendAckDelay(2*1000);
|
||||
setWindowSize(1);
|
||||
setMaxResends(5);
|
||||
setWriteTimeout(-1);
|
||||
setInactivityTimeout(opts.getInactivityTimeout());
|
||||
setInactivityAction(opts.getInactivityAction());
|
||||
setInboundBufferSize(opts.getInboundBufferSize());
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionOptions(Properties opts) {
|
||||
super(opts);
|
||||
// load the options;
|
||||
protected void init(Properties opts) {
|
||||
super.init(opts);
|
||||
setConnectDelay(getInt(opts, PROP_CONNECT_DELAY, -1));
|
||||
setProfile(getInt(opts, PROP_PROFILE, PROFILE_BULK));
|
||||
setMaxMessageSize(getInt(opts, PROP_MAX_MESSAGE_SIZE, Packet.MAX_PAYLOAD_SIZE));
|
||||
setRTT(getInt(opts, PROP_INITIAL_RTT, 30*1000));
|
||||
setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1));
|
||||
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 5*1000));
|
||||
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 2*1000));
|
||||
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, 1));
|
||||
setMaxResends(getInt(opts, PROP_MAX_RESENDS, 5));
|
||||
setWriteTimeout(getInt(opts, PROP_WRITE_TIMEOUT, -1));
|
||||
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
|
||||
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_SEND));
|
||||
setInboundBufferSize((getMaxMessageSize() + 2) * Connection.MAX_WINDOW_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,15 +111,14 @@ public class ConnectionOptions extends I2PSocketOptions {
|
||||
public boolean getRequireFullySigned() { return _fullySigned; }
|
||||
public void setRequireFullySigned(boolean sign) { _fullySigned = sign; }
|
||||
|
||||
private static final int MAX_WINDOW_SIZE = 32;
|
||||
/**
|
||||
* How many messages will we send before waiting for an ACK?
|
||||
*
|
||||
*/
|
||||
public int getWindowSize() { return _windowSize; }
|
||||
public void setWindowSize(int numMsgs) {
|
||||
if (numMsgs > MAX_WINDOW_SIZE)
|
||||
numMsgs = MAX_WINDOW_SIZE;
|
||||
if (numMsgs > Connection.MAX_WINDOW_SIZE)
|
||||
numMsgs = Connection.MAX_WINDOW_SIZE;
|
||||
_windowSize = numMsgs;
|
||||
}
|
||||
|
||||
@@ -151,7 +174,11 @@ public class ConnectionOptions extends I2PSocketOptions {
|
||||
*
|
||||
*/
|
||||
public int getProfile() { return _profile; }
|
||||
public void setProfile(int profile) { _profile = profile; }
|
||||
public void setProfile(int profile) {
|
||||
if (profile != PROFILE_BULK)
|
||||
throw new IllegalArgumentException("Only bulk is supported so far");
|
||||
_profile = profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* How many times will we try to send a message before giving up?
|
||||
@@ -159,4 +186,48 @@ public class ConnectionOptions extends I2PSocketOptions {
|
||||
*/
|
||||
public int getMaxResends() { return _maxResends; }
|
||||
public void setMaxResends(int numSends) { _maxResends = numSends; }
|
||||
|
||||
/**
|
||||
* What period of inactivity qualifies as "too long"?
|
||||
*
|
||||
*/
|
||||
public int getInactivityTimeout() { return _inactivityTimeout; }
|
||||
public void setInactivityTimeout(int timeout) { _inactivityTimeout = timeout; }
|
||||
|
||||
public int getInactivityAction() { return _inactivityAction; }
|
||||
public void setInactivityAction(int action) { _inactivityAction = action; }
|
||||
|
||||
/**
|
||||
* how much data are we willing to accept in our buffer?
|
||||
*
|
||||
*/
|
||||
public int getInboundBufferSize() { return _inboundBufferSize; }
|
||||
public void setInboundBufferSize(int bytes) { _inboundBufferSize = bytes; }
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
buf.append("conDelay=").append(_connectDelay);
|
||||
buf.append(" maxSize=").append(_maxMessageSize);
|
||||
buf.append(" rtt=").append(_rtt);
|
||||
buf.append(" rwin=").append(_receiveWindow);
|
||||
buf.append(" resendDelay=").append(_resendDelay);
|
||||
buf.append(" ackDelay=").append(_sendAckDelay);
|
||||
buf.append(" cwin=").append(_windowSize);
|
||||
buf.append(" maxResends=").append(_maxResends);
|
||||
buf.append(" writeTimeout=").append(getWriteTimeout());
|
||||
buf.append(" inactivityTimeout=").append(_inactivityTimeout);
|
||||
buf.append(" inboundBuffer=").append(_inboundBufferSize);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
Properties p = new Properties();
|
||||
|
||||
p.setProperty(PROP_CONNECT_DELAY, "1000");
|
||||
ConnectionOptions c = new ConnectionOptions(p);
|
||||
System.out.println("opts: " + c);
|
||||
|
||||
c = new ConnectionOptions(new I2PSocketOptionsImpl(p));
|
||||
System.out.println("opts: " + c);
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import net.i2p.I2PException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/**
|
||||
* Receive a packet for a particular connection - placing the data onto the
|
||||
@@ -20,13 +21,35 @@ public class ConnectionPacketHandler {
|
||||
public ConnectionPacketHandler(I2PAppContext context) {
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(ConnectionPacketHandler.class);
|
||||
_context.statManager().createRateStat("stream.con.receiveMessageSize", "Size of a message received on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.receiveDuplicateSize", "Size of a duplicate message received on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.packetsAckedPerMessageReceived", "Size of a duplicate message received on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.sendsBeforeAck", "How many times a message was sent before it was ACKed?", "Stream", new long[] { 10*60*1000, 60*60*1000 });
|
||||
}
|
||||
|
||||
/** distribute a packet to the connection specified */
|
||||
void receivePacket(Packet packet, Connection con) throws I2PException {
|
||||
boolean ok = verifyPacket(packet, con);
|
||||
if (!ok) return;
|
||||
con.packetReceived();
|
||||
|
||||
if (con.getInputStream().getTotalQueuedSize() > con.getOptions().getInboundBufferSize()) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Inbound buffer exceeded on connection " + con + ": dropping " + packet);
|
||||
con.getOptions().setChoke(5*1000);
|
||||
return;
|
||||
}
|
||||
con.getOptions().setChoke(0);
|
||||
|
||||
_context.statManager().addRateData("stream.con.receiveMessageSize", packet.getPayloadSize(), 0);
|
||||
|
||||
boolean isNew = con.getInputStream().messageReceived(packet.getSequenceNum(), packet.getPayload());
|
||||
|
||||
if ( (packet.getSequenceNum() == 0) && (packet.getPayloadSize() > 0) ) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("seq=0 && size=" + packet.getPayloadSize() + ": isNew? " + isNew
|
||||
+ " packet: " + packet + " con: " + con);
|
||||
}
|
||||
|
||||
// close *after* receiving the data, as well as after verifying the signatures / etc
|
||||
if (packet.isFlagSet(Packet.FLAG_CLOSE) && packet.isFlagSet(Packet.FLAG_SIGNATURE_INCLUDED))
|
||||
@@ -34,6 +57,8 @@ public class ConnectionPacketHandler {
|
||||
|
||||
if (isNew) {
|
||||
con.incrementUnackedPacketsReceived();
|
||||
con.incrementBytesReceived(packet.getPayloadSize());
|
||||
|
||||
if (packet.isFlagSet(Packet.FLAG_DELAY_REQUESTED) && (packet.getOptionalDelay() <= 0) ) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Scheduling immediate ack for " + packet);
|
||||
@@ -47,17 +72,21 @@ public class ConnectionPacketHandler {
|
||||
_log.debug("Scheduling ack in " + delay + "ms for received packet " + packet);
|
||||
}
|
||||
} else {
|
||||
if (packet.getSequenceNum() > 0) {
|
||||
if ( (packet.getSequenceNum() > 0) || (packet.getPayloadSize() > 0) ) {
|
||||
_context.statManager().addRateData("stream.con.receiveDuplicateSize", packet.getPayloadSize(), 0);
|
||||
con.incrementDupMessagesReceived(1);
|
||||
|
||||
// take note of congestion
|
||||
con.getOptions().setResendDelay(con.getOptions().getResendDelay()*2);
|
||||
//con.getOptions().setResendDelay(con.getOptions().getResendDelay()*2);
|
||||
//con.getOptions().setWindowSize(con.getOptions().getWindowSize()/2);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("congestion.. dup " + packet);
|
||||
con.incrementUnackedPacketsReceived();
|
||||
_log.warn("congestion.. dup " + packet);
|
||||
SimpleTimer.getInstance().addEvent(new AckDup(con), con.getOptions().getSendAckDelay());
|
||||
//con.incrementUnackedPacketsReceived();
|
||||
con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
|
||||
} else {
|
||||
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
|
||||
con.incrementUnackedPacketsReceived();
|
||||
//con.incrementUnackedPacketsReceived();
|
||||
con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -80,6 +109,7 @@ public class ConnectionPacketHandler {
|
||||
//if (p.getNumSends() <= 1)
|
||||
highestRTT = p.getAckTime();
|
||||
}
|
||||
_context.statManager().addRateData("stream.sendsBeforeAck", p.getNumSends(), p.getAckTime());
|
||||
|
||||
if (p.getNumSends() > 1)
|
||||
numResends++;
|
||||
@@ -97,6 +127,7 @@ public class ConnectionPacketHandler {
|
||||
if (highestRTT > 0) {
|
||||
con.getOptions().updateRTT(highestRTT);
|
||||
}
|
||||
_context.statManager().addRateData("stream.con.packetsAckedPerMessageReceived", acked.size(), highestRTT);
|
||||
}
|
||||
|
||||
boolean fastAck = adjustWindow(con, isNew, packet.getSequenceNum(), numResends, (acked != null ? acked.size() : 0));
|
||||
@@ -114,12 +145,19 @@ public class ConnectionPacketHandler {
|
||||
if ( (!isNew) && (sequenceNum > 0) ) {
|
||||
// dup real packet
|
||||
int oldSize = con.getOptions().getWindowSize();
|
||||
con.congestionOccurred();
|
||||
oldSize >>>= 1;
|
||||
if (oldSize <= 0)
|
||||
oldSize = 1;
|
||||
con.getOptions().setWindowSize(oldSize);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Congestion occurred - new windowSize " + oldSize + " congestionSeenAt: "
|
||||
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
|
||||
+ ") for " + con);
|
||||
|
||||
return true;
|
||||
} else if (numResends > 0) {
|
||||
//} else if (numResends > 0) {
|
||||
// window sizes are shrunk on resend, not on ack
|
||||
} else {
|
||||
if (acked > 0) {
|
||||
@@ -127,9 +165,25 @@ public class ConnectionPacketHandler {
|
||||
if (lowest >= con.getCongestionWindowEnd()) {
|
||||
// new packet that ack'ed uncongested data, or an empty ack
|
||||
int newWindowSize = con.getOptions().getWindowSize();
|
||||
newWindowSize += 1; // acked; // 1
|
||||
|
||||
if (numResends <= 0) {
|
||||
if (newWindowSize > con.getLastCongestionSeenAt() / 2) {
|
||||
// congestion avoidance
|
||||
|
||||
// we can't use newWindowSize += 1/newWindowSize, since we're
|
||||
// integers, so lets use a random distribution instead
|
||||
int shouldIncrement = _context.random().nextInt(newWindowSize);
|
||||
if (shouldIncrement <= 0)
|
||||
newWindowSize += 1;
|
||||
} else {
|
||||
// slow start
|
||||
newWindowSize += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("New window size " + newWindowSize + " (#resends: " + numResends
|
||||
_log.debug("New window size " + newWindowSize + " congestionSeenAt: "
|
||||
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
|
||||
+ ") for " + con);
|
||||
con.getOptions().setWindowSize(newWindowSize);
|
||||
con.setCongestionWindowEnd(newWindowSize + lowest);
|
||||
@@ -248,4 +302,24 @@ public class ConnectionPacketHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AckDup implements SimpleTimer.TimedEvent {
|
||||
private long _created;
|
||||
private Connection _con;
|
||||
public AckDup(Connection con) {
|
||||
_created = _context.clock().now();
|
||||
_con = con;
|
||||
}
|
||||
public void timeReached() {
|
||||
if (_con.getLastSendTime() <= _created) {
|
||||
if (!_con.getIsConnected()) return;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Last sent was a while ago, and we want to ack a dup");
|
||||
// we haven't done anything since receiving the dup, send an
|
||||
// ack now
|
||||
_con.ackImmediately();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,12 +19,17 @@ public class I2PSocketFull implements I2PSocket {
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (_connection.getIsConnected()) {
|
||||
_connection.getOutputStream().close();
|
||||
_connection.disconnect(true);
|
||||
Connection c = _connection;
|
||||
if (c == null) return;
|
||||
if (c.getIsConnected()) {
|
||||
OutputStream out = c.getOutputStream();
|
||||
if (out != null)
|
||||
out.close();
|
||||
c.disconnect(true);
|
||||
} else {
|
||||
throw new IOException("Not connected");
|
||||
//throw new IOException("Not connected");
|
||||
}
|
||||
destroy();
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
@@ -65,4 +70,9 @@ public class I2PSocketFull implements I2PSocket {
|
||||
public void setSocketErrorListener(I2PSocket.SocketErrorListener lsnr) {
|
||||
_listener = lsnr;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
_connection = null;
|
||||
_listener = null;
|
||||
}
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
private ConnectionOptions _defaultOptions;
|
||||
private long _acceptTimeout;
|
||||
private String _name;
|
||||
private int _maxStreams;
|
||||
private static int __managerId = 0;
|
||||
private ConnectionManager _connectionManager;
|
||||
|
||||
@@ -54,6 +55,9 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
init(context, session, opts, name);
|
||||
}
|
||||
|
||||
/** how many streams will we allow at once? */
|
||||
public static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams";
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -61,13 +65,33 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
_context = context;
|
||||
_session = session;
|
||||
_log = _context.logManager().getLog(I2PSocketManagerFull.class);
|
||||
_connectionManager = new ConnectionManager(_context, _session);
|
||||
|
||||
_maxStreams = -1;
|
||||
try {
|
||||
String num = (opts != null ? opts.getProperty(PROP_MAX_STREAMS, "-1") : "-1");
|
||||
_maxStreams = Integer.parseInt(num);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Invalid max # of concurrent streams, defaulting to unlimited", nfe);
|
||||
_maxStreams = -1;
|
||||
}
|
||||
_connectionManager = new ConnectionManager(_context, _session, _maxStreams);
|
||||
_name = name + " " + (++__managerId);
|
||||
_acceptTimeout = ACCEPT_TIMEOUT_DEFAULT;
|
||||
_defaultOptions = new ConnectionOptions(opts);
|
||||
_serverSocket = new I2PServerSocketFull(this);
|
||||
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
_log.info("Socket manager created. \ndefault options: " + _defaultOptions
|
||||
+ "\noriginal properties: " + opts);
|
||||
}
|
||||
}
|
||||
|
||||
public I2PSocketOptions buildOptions() { return buildOptions(null); }
|
||||
public I2PSocketOptions buildOptions(Properties opts) {
|
||||
return new ConnectionOptions(opts);
|
||||
}
|
||||
|
||||
public I2PSession getSession() {
|
||||
return _session;
|
||||
}
|
||||
@@ -134,7 +158,16 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
throws I2PException, NoRouteToHostException {
|
||||
if (_connectionManager.getSession().isClosed())
|
||||
throw new I2PException("Session is closed");
|
||||
Connection con = _connectionManager.connect(peer, new ConnectionOptions(options));
|
||||
if (options == null)
|
||||
options = _defaultOptions;
|
||||
ConnectionOptions opts = null;
|
||||
if (options instanceof ConnectionOptions)
|
||||
opts = (ConnectionOptions)options;
|
||||
else
|
||||
opts = new ConnectionOptions(options);
|
||||
Connection con = _connectionManager.connect(peer, opts);
|
||||
if (con == null)
|
||||
throw new TooManyStreamsException("Too many streams (max " + _maxStreams + ")");
|
||||
I2PSocketFull socket = new I2PSocketFull(con);
|
||||
con.setSocket(socket);
|
||||
if (con.getConnectionError() != null) {
|
||||
@@ -165,6 +198,14 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
_connectionManager.disconnectAllHard();
|
||||
_connectionManager.setAllowIncomingConnections(false);
|
||||
// should we destroy the _session too?
|
||||
// yes, since the old lib did (and SAM wants it to, and i dont know why not)
|
||||
if ( (_session != null) && (!_session.isClosed()) ) {
|
||||
try {
|
||||
_session.destroySession();
|
||||
} catch (I2PSessionException ise) {
|
||||
_log.warn("Unable to destroy the session", ise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -7,6 +7,8 @@ import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Receive raw information from the I2PSession and turn it into
|
||||
* Packets, if we can.
|
||||
*
|
||||
*/
|
||||
public class MessageHandler implements I2PSessionListener {
|
||||
@@ -18,6 +20,7 @@ public class MessageHandler implements I2PSessionListener {
|
||||
_manager = mgr;
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(MessageHandler.class);
|
||||
_context.statManager().createRateStat("stream.packetReceiveFailure", "When do we fail to decrypt or otherwise receive a packet sent to us?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
}
|
||||
|
||||
/** Instruct the client that the given session has received a message with
|
||||
@@ -31,6 +34,7 @@ public class MessageHandler implements I2PSessionListener {
|
||||
try {
|
||||
data = session.receiveMessage(msgId);
|
||||
} catch (I2PSessionException ise) {
|
||||
_context.statManager().addRateData("stream.packetReceiveFailure", 1, 0);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error receiving the message", ise);
|
||||
return;
|
||||
@@ -40,6 +44,7 @@ public class MessageHandler implements I2PSessionListener {
|
||||
packet.readPacket(data, 0, data.length);
|
||||
_manager.getPacketHandler().receivePacket(packet);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_context.statManager().addRateData("stream.packetReceiveFailure", 1, 0);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received an invalid packet", iae);
|
||||
}
|
||||
|
@@ -53,6 +53,8 @@ public class MessageInputStream extends InputStream {
|
||||
private IOException _streamError;
|
||||
private long _readTotal;
|
||||
|
||||
private byte[] _oneByte = new byte[1];
|
||||
|
||||
private Object _dataLock;
|
||||
|
||||
public MessageInputStream(I2PAppContext ctx) {
|
||||
@@ -205,6 +207,7 @@ public class MessageInputStream extends InputStream {
|
||||
if (messageId <= _highestReadyBlockId) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("ignoring dup message " + messageId);
|
||||
_dataLock.notifyAll();
|
||||
return false; // already received
|
||||
}
|
||||
if (messageId > _highestBlockId)
|
||||
@@ -238,76 +241,121 @@ public class MessageInputStream extends InputStream {
|
||||
_notYetReadyBlocks.put(new Long(messageId), new ByteArray(null));
|
||||
else
|
||||
_notYetReadyBlocks.put(new Long(messageId), new ByteArray(payload));
|
||||
_dataLock.notifyAll();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
int read = read(_oneByte, 0, 1);
|
||||
if (read < 0)
|
||||
return -1;
|
||||
else
|
||||
return _oneByte[0];
|
||||
}
|
||||
|
||||
public int read(byte target[]) throws IOException {
|
||||
return read(target, 0, target.length);
|
||||
}
|
||||
|
||||
public int read(byte target[], int offset, int length) throws IOException {
|
||||
if (_locallyClosed) throw new IOException("Already locally closed");
|
||||
throwAnyError();
|
||||
long expiration = -1;
|
||||
if (_readTimeout > 0)
|
||||
expiration = _readTimeout + System.currentTimeMillis();
|
||||
synchronized (_dataLock) {
|
||||
while (_readyDataBlocks.size() <= 0) {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("read() with readyBlocks.size = " + _readyDataBlocks.size() + " on " + toString());
|
||||
|
||||
if ( (_notYetReadyBlocks.size() <= 0) && (_closeReceived) ) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read() got EOF after " + _readTotal + " " + toString());
|
||||
return -1;
|
||||
} else {
|
||||
if (_readTimeout < 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read() with no timeout: " + toString());
|
||||
try { _dataLock.wait(); } catch (InterruptedException ie) { }
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read() with no timeout complete: " + toString());
|
||||
throwAnyError();
|
||||
} else if (_readTimeout > 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read() with timeout: " + _readTimeout + ": " + toString());
|
||||
try { _dataLock.wait(_readTimeout); } catch (InterruptedException ie) { }
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read() with timeout complete: " + _readTimeout + ": " + toString());
|
||||
throwAnyError();
|
||||
} else { // readTimeout == 0
|
||||
// noop, don't block
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read() with nonblocking setup: " + toString());
|
||||
for (int i = 0; i < length; i++) {
|
||||
if ( (_readyDataBlocks.size() <= 0) && (i == 0) ) {
|
||||
// ok, we havent found anything, so lets block until we get
|
||||
// at least one byte
|
||||
|
||||
while (_readyDataBlocks.size() <= 0) {
|
||||
if (_locallyClosed)
|
||||
throw new IOException("Already closed, you wanker");
|
||||
|
||||
if ( (_notYetReadyBlocks.size() <= 0) && (_closeReceived) ) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("read(...," + offset + ", " + length + ")[" + i
|
||||
+ "] got EOF after " + _readTotal + " " + toString());
|
||||
return -1;
|
||||
} else {
|
||||
if (_readTimeout < 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read(...," + offset+", " + length+ ")[" + i
|
||||
+ ") with no timeout: " + toString());
|
||||
try { _dataLock.wait(); } catch (InterruptedException ie) { }
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read(...," + offset+", " + length+ ")[" + i
|
||||
+ ") with no timeout complete: " + toString());
|
||||
throwAnyError();
|
||||
} else if (_readTimeout > 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read(...," + offset+", " + length+ ")[" + i
|
||||
+ ") with timeout: " + _readTimeout + ": " + toString());
|
||||
try { _dataLock.wait(_readTimeout); } catch (InterruptedException ie) { }
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read(...," + offset+", " + length+ ")[" + i
|
||||
+ ") with timeout complete: " + _readTimeout + ": " + toString());
|
||||
throwAnyError();
|
||||
} else { // readTimeout == 0
|
||||
// noop, don't block
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("read(...," + offset+", " + length+ ")[" + i
|
||||
+ ") with nonblocking setup: " + toString());
|
||||
return i;
|
||||
}
|
||||
if (_readyDataBlocks.size() <= 0) {
|
||||
if ( (_readTimeout > 0) && (expiration < System.currentTimeMillis()) ) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("read(...," + offset+", " + length+ ")[" + i
|
||||
+ ") expired: " + toString());
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_readyDataBlocks.size() <= 0) {
|
||||
if ( (_readTimeout > 0) && (expiration > System.currentTimeMillis()) )
|
||||
throw new InterruptedIOException("Timeout reading (timeout=" + _readTimeout + ")");
|
||||
// we looped a few times then got data, so this pass doesnt count
|
||||
i--;
|
||||
} else if (_readyDataBlocks.size() <= 0) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("read(...," + offset+", " + length+ ")[" + i
|
||||
+ "] no more ready blocks, returning");
|
||||
return i;
|
||||
} else {
|
||||
// either was already ready, or we wait()ed and it arrived
|
||||
ByteArray cur = (ByteArray)_readyDataBlocks.get(0);
|
||||
byte rv = cur.getData()[_readyDataBlockIndex];
|
||||
_readyDataBlockIndex++;
|
||||
if (cur.getData().length <= _readyDataBlockIndex) {
|
||||
_readyDataBlockIndex = 0;
|
||||
_readyDataBlocks.remove(0);
|
||||
}
|
||||
_readTotal++;
|
||||
target[offset + i] = rv; // rv < 0 ? rv + 256 : rv
|
||||
if ( (_readyDataBlockIndex <= 3) || (_readyDataBlockIndex >= cur.getData().length - 5) ) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("read(...," + offset+", " + length+ ")[" + i
|
||||
+ "] after ready data: readyDataBlockIndex=" + _readyDataBlockIndex
|
||||
+ " readyBlocks=" + _readyDataBlocks.size()
|
||||
+ " readTotal=" + _readTotal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("read() readyBlocks = " + _readyDataBlocks.size() + ": " + toString());
|
||||
|
||||
// either was already ready, or we wait()ed and it arrived
|
||||
ByteArray cur = (ByteArray)_readyDataBlocks.get(0);
|
||||
byte rv = cur.getData()[_readyDataBlockIndex];
|
||||
_readyDataBlockIndex++;
|
||||
if (cur.getData().length <= _readyDataBlockIndex) {
|
||||
_readyDataBlockIndex = 0;
|
||||
_readyDataBlocks.remove(0);
|
||||
}
|
||||
_readTotal++;
|
||||
return (rv < 0 ? rv + 256 : rv);
|
||||
}
|
||||
} // for (int i = 0; i < length; i++) {
|
||||
} // synchronized (_dataLock)
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.info("read(...," + offset+", " + length+ ") read fully total read: " +_readTotal);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
if (_locallyClosed) throw new IOException("Already closed, you wanker");
|
||||
throwAnyError();
|
||||
int numBytes = 0;
|
||||
synchronized (_dataLock) {
|
||||
if (_readyDataBlocks.size() <= 0)
|
||||
return 0;
|
||||
int numBytes = 0;
|
||||
for (int i = 0; i < _readyDataBlocks.size(); i++) {
|
||||
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
|
||||
if (i == 0)
|
||||
@@ -315,8 +363,11 @@ public class MessageInputStream extends InputStream {
|
||||
else
|
||||
numBytes += cur.getData().length;
|
||||
}
|
||||
return numBytes;
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.info("available(): " + numBytes + " " + toString());
|
||||
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,6 +405,7 @@ public class MessageInputStream extends InputStream {
|
||||
ba.setData(null);
|
||||
}
|
||||
_locallyClosed = true;
|
||||
_dataLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,10 +5,15 @@ import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/**
|
||||
*
|
||||
* A stream that we can shove data into that fires off those bytes
|
||||
* on flush or when the buffer is full. It also blocks according
|
||||
* to the data receiver's needs.
|
||||
*/
|
||||
public class MessageOutputStream extends OutputStream {
|
||||
private I2PAppContext _context;
|
||||
@@ -21,20 +26,31 @@ public class MessageOutputStream extends OutputStream {
|
||||
private boolean _closed;
|
||||
private long _written;
|
||||
private int _writeTimeout;
|
||||
private ByteCache _dataCache;
|
||||
private Flusher _flusher;
|
||||
private long _lastFlushed;
|
||||
private long _lastBuffered;
|
||||
/** if we enqueue data but don't flush it in this period, flush it passively */
|
||||
private int _passiveFlushDelay;
|
||||
|
||||
public MessageOutputStream(I2PAppContext ctx, DataReceiver receiver) {
|
||||
this(ctx, receiver, Packet.MAX_PAYLOAD_SIZE);
|
||||
}
|
||||
public MessageOutputStream(I2PAppContext ctx, DataReceiver receiver, int bufSize) {
|
||||
super();
|
||||
_dataCache = ByteCache.getInstance(128, bufSize);
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(MessageOutputStream.class);
|
||||
_buf = new byte[bufSize];
|
||||
_buf = _dataCache.acquire().getData(); // new byte[bufSize];
|
||||
_dataReceiver = receiver;
|
||||
_dataLock = new Object();
|
||||
_written = 0;
|
||||
_closed = false;
|
||||
_writeTimeout = -1;
|
||||
_passiveFlushDelay = 5*1000;
|
||||
_flusher = new Flusher();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("MessageOutputStream created");
|
||||
}
|
||||
|
||||
public void setWriteTimeout(int ms) { _writeTimeout = ms; }
|
||||
@@ -45,10 +61,11 @@ public class MessageOutputStream extends OutputStream {
|
||||
}
|
||||
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("write(b[], " + off + ", " + len + ")");
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("write(b[], " + off + ", " + len + ") ");
|
||||
int cur = off;
|
||||
int remaining = len;
|
||||
long begin = _context.clock().now();
|
||||
while (remaining > 0) {
|
||||
WriteStatus ws = null;
|
||||
// we do any waiting outside the synchronized() block because we
|
||||
@@ -56,6 +73,7 @@ public class MessageOutputStream extends OutputStream {
|
||||
// this is the only method that *adds* to the _buf, and all
|
||||
// code that reads from it is synchronized
|
||||
synchronized (_dataLock) {
|
||||
if (_buf == null) throw new IOException("closed (buffer went away)");
|
||||
if (_valid + remaining < _buf.length) {
|
||||
// simply buffer the data, no flush
|
||||
System.arraycopy(b, cur, _buf, _valid, remaining);
|
||||
@@ -63,6 +81,11 @@ public class MessageOutputStream extends OutputStream {
|
||||
cur += remaining;
|
||||
_written += remaining;
|
||||
remaining = 0;
|
||||
_lastBuffered = _context.clock().now();
|
||||
if (_passiveFlushDelay > 0) {
|
||||
// if it is already enqueued, this just pushes it further out
|
||||
SimpleTimer.getInstance().addEvent(_flusher, _passiveFlushDelay);
|
||||
}
|
||||
} else {
|
||||
// buffer whatever we can fit then flush,
|
||||
// repeating until we've pushed all of the
|
||||
@@ -72,13 +95,20 @@ public class MessageOutputStream extends OutputStream {
|
||||
remaining -= toWrite;
|
||||
cur += toWrite;
|
||||
_valid = _buf.length;
|
||||
if (_dataReceiver == null) {
|
||||
throwAnyError();
|
||||
return;
|
||||
}
|
||||
ws = _dataReceiver.writeData(_buf, 0, _valid);
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
throwAnyError();
|
||||
_lastFlushed = _context.clock().now();
|
||||
}
|
||||
}
|
||||
if (ws != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Waiting " + _writeTimeout + "ms for accept of " + ws);
|
||||
// ok, we've actually added a new packet - lets wait until
|
||||
// its accepted into the queue before moving on (so that we
|
||||
// dont fill our buffer instantly)
|
||||
@@ -89,8 +119,14 @@ public class MessageOutputStream extends OutputStream {
|
||||
else
|
||||
throw new IOException("Write not accepted into the queue");
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Queued " + len + " without sending to the receiver");
|
||||
}
|
||||
}
|
||||
long elapsed = _context.clock().now() - begin;
|
||||
if ( (elapsed > 10*1000) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("wtf, took " + elapsed + "ms to write to the stream?", new Exception("foo"));
|
||||
throwAnyError();
|
||||
}
|
||||
|
||||
@@ -99,40 +135,119 @@ public class MessageOutputStream extends OutputStream {
|
||||
throwAnyError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush data that has been enqued but not flushed after a certain
|
||||
* period of inactivity
|
||||
*/
|
||||
private class Flusher implements SimpleTimer.TimedEvent {
|
||||
public void timeReached() {
|
||||
boolean sent = false;
|
||||
WriteStatus ws = null;
|
||||
synchronized (_dataLock) {
|
||||
if ( (_valid > 0) && (_lastBuffered + _passiveFlushDelay > _context.clock().now()) ) {
|
||||
if ( (_buf != null) && (_dataReceiver != null) ) {
|
||||
ws = _dataReceiver.writeData(_buf, 0, _valid);
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
_lastFlushed = _context.clock().now();
|
||||
_dataLock.notifyAll();
|
||||
sent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ignore the ws
|
||||
if (sent && _log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Passive flush of " + ws);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the data already queued up, blocking until it has been
|
||||
* delivered.
|
||||
*
|
||||
* @throws IOException if the write fails
|
||||
* @throws InterruptedIOException if the write times out
|
||||
*/
|
||||
public void flush() throws IOException {
|
||||
long begin = _context.clock().now();
|
||||
WriteStatus ws = null;
|
||||
synchronized (_dataLock) {
|
||||
if (_buf == null) throw new IOException("closed (buffer went away)");
|
||||
if (_dataReceiver == null) {
|
||||
throwAnyError();
|
||||
return;
|
||||
}
|
||||
ws = _dataReceiver.writeData(_buf, 0, _valid);
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
_lastFlushed = _context.clock().now();
|
||||
_dataLock.notifyAll();
|
||||
}
|
||||
|
||||
ws.waitForCompletion(_writeTimeout);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("before waiting " + _writeTimeout + "ms for completion of " + ws);
|
||||
if (_closed &&
|
||||
( (_writeTimeout > Connection.DISCONNECT_TIMEOUT) ||
|
||||
(_writeTimeout <= 0) ) )
|
||||
ws.waitForCompletion(Connection.DISCONNECT_TIMEOUT);
|
||||
else if ( (_writeTimeout <= 0) || (_writeTimeout > Connection.DISCONNECT_TIMEOUT) )
|
||||
ws.waitForCompletion(Connection.DISCONNECT_TIMEOUT);
|
||||
else
|
||||
ws.waitForCompletion(_writeTimeout);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("after waiting " + _writeTimeout + "ms for completion of " + ws);
|
||||
if (ws.writeFailed() && (_writeTimeout > 0) )
|
||||
throw new InterruptedIOException("Timed out during write");
|
||||
else if (ws.writeFailed())
|
||||
throw new IOException("Write failed");
|
||||
|
||||
long elapsed = _context.clock().now() - begin;
|
||||
if ( (elapsed > 10*1000) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("wtf, took " + elapsed + "ms to flush the stream?\n" + ws, new Exception("bar"));
|
||||
throwAnyError();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (_closed) return;
|
||||
_closed = true;
|
||||
flush();
|
||||
_log.debug("Output stream closed after writing " + _written);
|
||||
ByteArray ba = null;
|
||||
synchronized (_dataLock) {
|
||||
if (_buf != null) {
|
||||
ba = new ByteArray(_buf);
|
||||
_buf = null;
|
||||
_valid = 0;
|
||||
}
|
||||
}
|
||||
if (ba != null) {
|
||||
_dataCache.release(ba);
|
||||
}
|
||||
}
|
||||
/** nonblocking close */
|
||||
public void closeInternal() {
|
||||
_closed = true;
|
||||
_streamError = new IOException("Closed internally");
|
||||
ByteArray ba = null;
|
||||
synchronized (_dataLock) {
|
||||
// flush any data, but don't wait for it
|
||||
if (_valid > 0) {
|
||||
if (_dataReceiver != null)
|
||||
_dataReceiver.writeData(_buf, 0, _valid);
|
||||
_written += _valid;
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
|
||||
if (_buf != null) {
|
||||
ba = new ByteArray(_buf);
|
||||
_buf = null;
|
||||
_valid = 0;
|
||||
}
|
||||
_lastFlushed = _context.clock().now();
|
||||
_dataLock.notifyAll();
|
||||
}
|
||||
if (ba != null) {
|
||||
_dataCache.release(ba);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getClosed() { return _closed; }
|
||||
@@ -161,12 +276,15 @@ public class MessageOutputStream extends OutputStream {
|
||||
void flushAvailable(DataReceiver target, boolean blocking) throws IOException {
|
||||
WriteStatus ws = null;
|
||||
synchronized (_dataLock) {
|
||||
// _buf may be null, but the data receiver can handle that just fine,
|
||||
// deciding whether or not to send a packet
|
||||
ws = target.writeData(_buf, 0, _valid);
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
_dataLock.notifyAll();
|
||||
_lastFlushed = _context.clock().now();
|
||||
}
|
||||
if (blocking) {
|
||||
if (blocking && ws != null) {
|
||||
ws.waitForAccept(_writeTimeout);
|
||||
if (ws.writeFailed())
|
||||
throw new IOException("Flush available failed");
|
||||
@@ -176,6 +294,11 @@ public class MessageOutputStream extends OutputStream {
|
||||
return;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
_dataReceiver = null;
|
||||
}
|
||||
|
||||
/** Define a component to receive data flushed from this stream */
|
||||
public interface DataReceiver {
|
||||
/**
|
||||
* Nonblocking write
|
||||
@@ -183,6 +306,7 @@ public class MessageOutputStream extends OutputStream {
|
||||
public WriteStatus writeData(byte buf[], int off, int size);
|
||||
}
|
||||
|
||||
/** Define a way to detect the status of a write */
|
||||
public interface WriteStatus {
|
||||
/** wait until the data written either fails or succeeds */
|
||||
public void waitForCompletion(int maxWaitMs);
|
||||
|
@@ -206,6 +206,9 @@ public class Packet {
|
||||
if ( (payload != null) && (payload.length > MAX_PAYLOAD_SIZE) )
|
||||
throw new IllegalArgumentException("Too large payload: " + payload.length);
|
||||
}
|
||||
public int getPayloadSize() {
|
||||
return (_payload == null ? 0 : _payload.length);
|
||||
}
|
||||
|
||||
/** is a particular flag set on this packet? */
|
||||
public boolean isFlagSet(int flag) { return 0 != (_flags & flag); }
|
||||
@@ -273,12 +276,12 @@ public class Packet {
|
||||
*/
|
||||
private int writePacket(byte buffer[], int offset, boolean includeSig) throws IllegalStateException {
|
||||
int cur = offset;
|
||||
if (_sendStreamId != null)
|
||||
if ( (_sendStreamId != null) && (_sendStreamId.length == 4) )
|
||||
System.arraycopy(_sendStreamId, 0, buffer, cur, _sendStreamId.length);
|
||||
else
|
||||
System.arraycopy(STREAM_ID_UNKNOWN, 0, buffer, cur, STREAM_ID_UNKNOWN.length);
|
||||
cur += 4;
|
||||
if (_receiveStreamId != null)
|
||||
if ( (_receiveStreamId != null) && (_receiveStreamId.length == 4) )
|
||||
System.arraycopy(_receiveStreamId, 0, buffer, cur, _receiveStreamId.length);
|
||||
else
|
||||
System.arraycopy(STREAM_ID_UNKNOWN, 0, buffer, cur, STREAM_ID_UNKNOWN.length);
|
||||
@@ -354,8 +357,8 @@ public class Packet {
|
||||
*/
|
||||
public int writtenSize() throws IllegalStateException {
|
||||
int size = 0;
|
||||
size += _sendStreamId.length;
|
||||
size += _receiveStreamId.length;
|
||||
size += 4; // _sendStreamId.length;
|
||||
size += 4; // _receiveStreamId.length;
|
||||
size += 4; // sequenceNum
|
||||
size += 4; // ackThrough
|
||||
if (_nacks != null) {
|
||||
@@ -396,6 +399,10 @@ public class Packet {
|
||||
* @throws IllegalArgumentException if the data is b0rked
|
||||
*/
|
||||
public void readPacket(byte buffer[], int offset, int length) throws IllegalArgumentException {
|
||||
if (buffer.length - offset < length)
|
||||
throw new IllegalArgumentException("len=" + buffer.length + " off=" + offset + " length=" + length);
|
||||
if (length < 22) // min header size
|
||||
throw new IllegalArgumentException("Too small: len=" + buffer.length);
|
||||
int cur = offset;
|
||||
_sendStreamId = new byte[4];
|
||||
System.arraycopy(buffer, cur, _sendStreamId, 0, 4);
|
||||
@@ -409,6 +416,8 @@ public class Packet {
|
||||
cur += 4;
|
||||
int numNacks = (int)DataHelper.fromLong(buffer, cur, 1);
|
||||
cur++;
|
||||
if (length < 22 + numNacks*4)
|
||||
throw new IllegalArgumentException("Too small with " + numNacks + " nacks: " + length);
|
||||
if (numNacks > 0) {
|
||||
_nacks = new long[numNacks];
|
||||
for (int i = 0; i < numNacks; i++) {
|
||||
@@ -425,13 +434,19 @@ public class Packet {
|
||||
|
||||
int optionSize = (int)DataHelper.fromLong(buffer, cur, 2);
|
||||
cur += 2;
|
||||
|
||||
if (length < 22 + numNacks*4 + optionSize)
|
||||
throw new IllegalArgumentException("Too small with " + numNacks + " nacks and "
|
||||
+ optionSize + " options: " + length);
|
||||
|
||||
int payloadBegin = cur + optionSize;
|
||||
int payloadSize = length - payloadBegin;
|
||||
if ( (payloadSize < 0) || (payloadSize > MAX_PAYLOAD_SIZE) )
|
||||
throw new IllegalArgumentException("length: " + length + " offset: " + offset + " begin: " + payloadBegin);
|
||||
|
||||
// skip ahead to the payload
|
||||
_payload = new byte[offset + length - payloadBegin];
|
||||
if (_payload.length > MAX_PAYLOAD_SIZE)
|
||||
throw new IllegalArgumentException("length: " + length + " offset: " + offset + " begin: " + payloadBegin);
|
||||
System.arraycopy(buffer, payloadBegin, _payload, 0, _payload.length);
|
||||
_payload = new byte[payloadSize];
|
||||
System.arraycopy(buffer, payloadBegin, _payload, 0, payloadSize);
|
||||
|
||||
// ok now lets go back and deal with the options
|
||||
if (isFlagSet(FLAG_DELAY_REQUESTED)) {
|
||||
@@ -480,7 +495,7 @@ public class Packet {
|
||||
}
|
||||
boolean ok = ctx.dsa().verifySignature(_optionSignature, buffer, 0, size, from.getSigningPublicKey());
|
||||
if (!ok) {
|
||||
ctx.logManager().getLog(Packet.class).error("Signature failed with sig " + Base64.encode(_optionSignature.getData()), new Exception("moo"));
|
||||
ctx.logManager().getLog(Packet.class).error("Signature failed on " + toString(), new Exception("moo"));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
@@ -105,12 +105,13 @@ public class PacketHandler {
|
||||
}
|
||||
|
||||
private static final SimpleDateFormat _fmt = new SimpleDateFormat("hh:mm:ss.SSS");
|
||||
static void displayPacket(Packet packet, String prefix) {
|
||||
void displayPacket(Packet packet, String prefix) {
|
||||
String msg = null;
|
||||
synchronized (_fmt) {
|
||||
msg = _fmt.format(new Date()) + ": " + prefix + " " + packet.toString();
|
||||
}
|
||||
System.out.println(msg);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
private void receiveKnownCon(Connection con, Packet packet) {
|
||||
@@ -173,22 +174,20 @@ public class PacketHandler {
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Packet received on an unknown stream (and not a SYN): " + packet);
|
||||
_log.debug("Packet received on an unknown stream (and not an ECHO): " + packet);
|
||||
if (sendId == null) {
|
||||
for (Iterator iter = _manager.listConnections().iterator(); iter.hasNext(); ) {
|
||||
Connection con = (Connection)iter.next();
|
||||
if (DataHelper.eq(con.getSendStreamId(), packet.getReceiveStreamId())) {
|
||||
if (con.getAckedPackets() <= 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received additional packets before the syn on " + con + ": " + packet);
|
||||
receiveKnownCon(con, packet);
|
||||
return;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("hrmph, received while ack of syn was in flight on " + con + ": " + packet + " acked: " + con.getAckedPackets());
|
||||
receiveKnownCon(con, packet);
|
||||
return;
|
||||
}
|
||||
Connection con = _manager.getConnectionByOutboundId(packet.getReceiveStreamId());
|
||||
if (con != null) {
|
||||
if (con.getAckedPackets() <= 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received additional packets before the syn on " + con + ": " + packet);
|
||||
receiveKnownCon(con, packet);
|
||||
return;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("hrmph, received while ack of syn was in flight on " + con + ": " + packet + " acked: " + con.getAckedPackets());
|
||||
receiveKnownCon(con, packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -56,7 +56,8 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
public boolean shouldSign() {
|
||||
return isFlagSet(FLAG_SIGNATURE_INCLUDED) ||
|
||||
isFlagSet(FLAG_SYNCHRONIZE) ||
|
||||
isFlagSet(FLAG_CLOSE);
|
||||
isFlagSet(FLAG_CLOSE) ||
|
||||
isFlagSet(FLAG_ECHO);
|
||||
}
|
||||
|
||||
/** last minute update of ack fields, just before write/sign */
|
||||
@@ -66,6 +67,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
}
|
||||
|
||||
public long getCreatedOn() { return _createdOn; }
|
||||
public long getLifetime() { return _context.clock().now() - _createdOn; }
|
||||
public void incrementSends() {
|
||||
_numSends++;
|
||||
_lastSend = _context.clock().now();
|
||||
@@ -76,12 +78,14 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
_ackOn = _context.clock().now();
|
||||
notifyAll();
|
||||
}
|
||||
_connection = null;
|
||||
}
|
||||
public void cancelled() {
|
||||
synchronized (this) {
|
||||
_cancelledOn = _context.clock().now();
|
||||
notifyAll();
|
||||
}
|
||||
_connection = null;
|
||||
}
|
||||
|
||||
/** how long after packet creation was it acked? */
|
||||
@@ -112,18 +116,26 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
_acceptedOn = _context.clock().now();
|
||||
else
|
||||
_acceptedOn = -1;
|
||||
_connection = null;
|
||||
}
|
||||
|
||||
public void waitForCompletion(int maxWaitMs) {
|
||||
_connection = null;
|
||||
long expiration = _context.clock().now()+maxWaitMs;
|
||||
while ((maxWaitMs <= 0) || (expiration < _context.clock().now())) {
|
||||
synchronized (this) {
|
||||
if (_ackOn > 0)
|
||||
return;
|
||||
if (_cancelledOn > 0)
|
||||
return;
|
||||
try { wait(); } catch (InterruptedException ie) {}
|
||||
}
|
||||
while (true) {
|
||||
long timeRemaining = expiration - _context.clock().now();
|
||||
if ( (timeRemaining <= 0) && (maxWaitMs > 0) ) return;
|
||||
try {
|
||||
synchronized (this) {
|
||||
if (_ackOn > 0) return;
|
||||
if (_cancelledOn > 0) return;
|
||||
if (timeRemaining > 60*1000)
|
||||
timeRemaining = 60*1000;
|
||||
else if (timeRemaining <= 0)
|
||||
timeRemaining = 10*1000;
|
||||
wait(timeRemaining);
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,28 +1,45 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Queue out packets to be sent through the session.
|
||||
* Well, thats the theory at least... in practice we just
|
||||
* send them immediately with no blocking, since the
|
||||
* mode=bestEffort doesnt block in the SDK.
|
||||
*
|
||||
*/
|
||||
class PacketQueue {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private I2PSession _session;
|
||||
private ConnectionManager _connectionManager;
|
||||
private byte _buf[];
|
||||
private ByteCache _cache = ByteCache.getInstance(64, 36*1024);
|
||||
|
||||
public PacketQueue(I2PAppContext context, I2PSession session) {
|
||||
public PacketQueue(I2PAppContext context, I2PSession session, ConnectionManager mgr) {
|
||||
_context = context;
|
||||
_session = session;
|
||||
_buf = new byte[36*1024];
|
||||
_connectionManager = mgr;
|
||||
_buf = _cache.acquire().getData(); // new byte[36*1024];
|
||||
_log = context.logManager().getLog(PacketQueue.class);
|
||||
_context.statManager().createRateStat("stream.con.sendMessageSize", "Size of a message sent on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.sendDuplicateSize", "Size of a message resent on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
_cache.release(new ByteArray(_buf));
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,11 +47,6 @@ class PacketQueue {
|
||||
*/
|
||||
public void enqueue(PacketLocal packet) {
|
||||
packet.prepare();
|
||||
int size = 0;
|
||||
if (packet.shouldSign())
|
||||
size = packet.writeSignedPacket(_buf, 0, _context, _session.getPrivateKey());
|
||||
else
|
||||
size = packet.writePacket(_buf, 0);
|
||||
|
||||
SessionKey keyUsed = packet.getKeyUsed();
|
||||
if (keyUsed == null)
|
||||
@@ -42,42 +54,70 @@ class PacketQueue {
|
||||
Set tagsSent = packet.getTagsSent();
|
||||
if (tagsSent == null)
|
||||
tagsSent = new HashSet();
|
||||
|
||||
// cache this from before sendMessage
|
||||
String conStr = (packet.getConnection() != null ? packet.getConnection().toString() : "");
|
||||
if (packet.getAckTime() > 0) {
|
||||
_log.debug("Not resending " + packet);
|
||||
return;
|
||||
} else {
|
||||
_log.debug("Sending... " + packet);
|
||||
}
|
||||
|
||||
long begin = 0;
|
||||
long end = 0;
|
||||
boolean sent = false;
|
||||
try {
|
||||
// cache this from before sendMessage
|
||||
String conStr = (packet.getConnection() != null ? packet.getConnection().toString() : "");
|
||||
if (packet.getAckTime() > 0) {
|
||||
_log.debug("Not resending " + packet);
|
||||
return;
|
||||
} else {
|
||||
_log.debug("Sending... " + packet);
|
||||
int size = 0;
|
||||
synchronized (this) {
|
||||
Arrays.fill(_buf, (byte)0x0);
|
||||
if (packet.shouldSign())
|
||||
size = packet.writeSignedPacket(_buf, 0, _context, _session.getPrivateKey());
|
||||
else
|
||||
size = packet.writePacket(_buf, 0);
|
||||
|
||||
// this should not block!
|
||||
begin = _context.clock().now();
|
||||
sent = _session.sendMessage(packet.getTo(), _buf, 0, size, keyUsed, tagsSent);
|
||||
end = _context.clock().now();
|
||||
}
|
||||
// this should not block!
|
||||
long begin = _context.clock().now();
|
||||
boolean sent = _session.sendMessage(packet.getTo(), _buf, 0, size, keyUsed, tagsSent);
|
||||
long end = _context.clock().now();
|
||||
if (!sent) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Send failed for " + packet);
|
||||
packet.getConnection().disconnect(false);
|
||||
} else {
|
||||
packet.setKeyUsed(keyUsed);
|
||||
packet.setTagsSent(tagsSent);
|
||||
packet.incrementSends();
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
String msg = "SEND " + packet + (tagsSent.size() > 0
|
||||
? " with " + tagsSent.size() + " tags"
|
||||
: "")
|
||||
+ " send # " + packet.getNumSends()
|
||||
+ " sendTime: " + (end-begin)
|
||||
+ " con: " + conStr;
|
||||
_log.debug(msg);
|
||||
}
|
||||
PacketHandler.displayPacket(packet, "SEND");
|
||||
_context.statManager().addRateData("stream.con.sendMessageSize", size, packet.getLifetime());
|
||||
if (packet.getNumSends() > 1)
|
||||
_context.statManager().addRateData("stream.con.sendDuplicateSize", size, packet.getLifetime());
|
||||
|
||||
|
||||
Connection con = packet.getConnection();
|
||||
if (con != null) {
|
||||
con.incrementBytesSent(size);
|
||||
if (packet.getNumSends() > 1)
|
||||
con.incrementDupMessagesSent(1);
|
||||
}
|
||||
} catch (I2PSessionException ise) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to send the packet " + packet, ise);
|
||||
}
|
||||
|
||||
if (!sent) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Send failed for " + packet);
|
||||
Connection c = packet.getConnection();
|
||||
if (c != null) // handle race on b0rk
|
||||
c.disconnect(false);
|
||||
} else {
|
||||
packet.setKeyUsed(keyUsed);
|
||||
packet.setTagsSent(tagsSent);
|
||||
packet.incrementSends();
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
String msg = "SEND " + packet + (tagsSent.size() > 0
|
||||
? " with " + tagsSent.size() + " tags"
|
||||
: "")
|
||||
+ " send # " + packet.getNumSends()
|
||||
+ " sendTime: " + (end-begin)
|
||||
+ " con: " + conStr;
|
||||
_log.debug(msg);
|
||||
}
|
||||
_connectionManager.getPacketHandler().displayPacket(packet, "SEND");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -31,21 +31,24 @@ class SchedulerClosed extends SchedulerImpl {
|
||||
_log = ctx.logManager().getLog(SchedulerClosed.class);
|
||||
}
|
||||
|
||||
static final long CLOSE_TIMEOUT = 30*1000;
|
||||
|
||||
public boolean accept(Connection con) {
|
||||
boolean ok = (con != null) &&
|
||||
(con.getCloseSentOn() > 0) &&
|
||||
if (con == null) return false;
|
||||
long timeSinceClose = _context.clock().now() - con.getCloseSentOn();
|
||||
boolean ok = (con.getCloseSentOn() > 0) &&
|
||||
(con.getCloseReceivedOn() > 0) &&
|
||||
(con.getUnackedPacketsReceived() <= 0) &&
|
||||
(con.getUnackedPacketsSent() <= 0) &&
|
||||
(!con.getResetReceived()) &&
|
||||
(con.getCloseSentOn() + CLOSE_TIMEOUT > _context.clock().now());
|
||||
return ok;
|
||||
(timeSinceClose < Connection.DISCONNECT_TIMEOUT);
|
||||
boolean conTimeout = (con.getOptions().getConnectTimeout() < con.getLifetime()) &&
|
||||
con.getSendStreamId() == null &&
|
||||
con.getLifetime() < Connection.DISCONNECT_TIMEOUT;
|
||||
return (ok || conTimeout);
|
||||
}
|
||||
|
||||
public void eventOccurred(Connection con) {
|
||||
long timeLeft = con.getCloseSentOn() + CLOSE_TIMEOUT - _context.clock().now();
|
||||
reschedule(timeLeft, con);
|
||||
// noop. we do the timeout through the simpleTimer anyway
|
||||
//long timeLeft = con.getCloseSentOn() + Connection.DISCONNECT_TIMEOUT - _context.clock().now();
|
||||
//reschedule(timeLeft, con);
|
||||
}
|
||||
}
|
||||
|
@@ -45,9 +45,20 @@ class SchedulerClosing extends SchedulerImpl {
|
||||
if (con.getNextSendTime() <= 0)
|
||||
con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
|
||||
long remaining = con.getNextSendTime() - _context.clock().now();
|
||||
if (remaining <= 0)
|
||||
con.sendAvailable();
|
||||
else
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Event occurred w/ remaining: " + remaining + " on " + con);
|
||||
if (remaining <= 0) {
|
||||
if (con.getCloseSentOn() <= 0) {
|
||||
con.sendAvailable();
|
||||
con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
|
||||
} else {
|
||||
con.ackImmediately();
|
||||
}
|
||||
} else {
|
||||
//if (remaining < 5*1000)
|
||||
// remaining = 5*1000;
|
||||
//con.setNextSendTime(when
|
||||
reschedule(remaining, con);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -43,9 +43,9 @@ class SchedulerConnectedBulk extends SchedulerImpl {
|
||||
(!con.getResetReceived()) &&
|
||||
( (con.getCloseSentOn() <= 0) || (con.getCloseReceivedOn() <= 0) );
|
||||
if (!ok) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("con: " + con + " closeSentOn: " + con.getCloseSentOn()
|
||||
+ " closeReceivedOn: " + con.getCloseReceivedOn());
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("con: " + con + " closeSentOn: " + con.getCloseSentOn()
|
||||
// + " closeReceivedOn: " + con.getCloseReceivedOn());
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
@@ -35,10 +35,13 @@ class SchedulerConnecting extends SchedulerImpl {
|
||||
}
|
||||
|
||||
public boolean accept(Connection con) {
|
||||
return (con != null) &&
|
||||
(con.getLastSendId() >= 0) &&
|
||||
(con.getAckedPackets() <= 0) &&
|
||||
(!con.getResetReceived());
|
||||
if (con == null) return false;
|
||||
boolean notYetConnected = (con.getIsConnected()) &&
|
||||
(con.getSendStreamId() == null) &&
|
||||
(con.getLastSendId() >= 0) &&
|
||||
(con.getAckedPackets() <= 0) &&
|
||||
(!con.getResetReceived());
|
||||
return notYetConnected;
|
||||
}
|
||||
|
||||
public void eventOccurred(Connection con) {
|
||||
|
@@ -31,14 +31,17 @@ class SchedulerDead extends SchedulerImpl {
|
||||
}
|
||||
|
||||
public boolean accept(Connection con) {
|
||||
boolean ok = (con != null) &&
|
||||
(con.getResetReceived()) ||
|
||||
((con.getCloseSentOn() > 0) &&
|
||||
(con.getCloseReceivedOn() > 0) &&
|
||||
(con.getUnackedPacketsReceived() <= 0) &&
|
||||
(con.getUnackedPacketsSent() <= 0) &&
|
||||
(con.getCloseSentOn() + SchedulerClosed.CLOSE_TIMEOUT <= _context.clock().now()));
|
||||
return ok;
|
||||
if (con == null) return false;
|
||||
long timeSinceClose = _context.clock().now() - con.getCloseSentOn();
|
||||
boolean nothingLeftToDo = (con.getCloseSentOn() > 0) &&
|
||||
(con.getCloseReceivedOn() > 0) &&
|
||||
(con.getUnackedPacketsReceived() <= 0) &&
|
||||
(con.getUnackedPacketsSent() <= 0) &&
|
||||
(timeSinceClose >= Connection.DISCONNECT_TIMEOUT);
|
||||
boolean timedOut = (con.getOptions().getConnectTimeout() < con.getLifetime()) &&
|
||||
con.getSendStreamId() == null &&
|
||||
con.getLifetime() >= Connection.DISCONNECT_TIMEOUT;
|
||||
return con.getResetReceived() || nothingLeftToDo || timedOut;
|
||||
}
|
||||
|
||||
public void eventOccurred(Connection con) {
|
||||
|
@@ -31,10 +31,15 @@ class SchedulerReceived extends SchedulerImpl {
|
||||
|
||||
long timeTillSend = con.getNextSendTime() - _context.clock().now();
|
||||
if (timeTillSend <= 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("received con... send a packet");
|
||||
con.sendAvailable();
|
||||
con.setNextSendTime(-1);
|
||||
if (con.getNextSendTime() > 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("received con... send a packet");
|
||||
con.sendAvailable();
|
||||
con.setNextSendTime(-1);
|
||||
} else {
|
||||
con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
|
||||
reschedule(con.getOptions().getSendAckDelay(), con);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("received con... time till next send: " + timeTillSend);
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import net.i2p.I2PException;
|
||||
|
||||
/**
|
||||
* We attempted to have more open streams than we are willing to put up with
|
||||
*
|
||||
*/
|
||||
public class TooManyStreamsException extends I2PException {
|
||||
public TooManyStreamsException(String message, Throwable parent) {
|
||||
super(message, parent);
|
||||
}
|
||||
|
||||
public TooManyStreamsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TooManyStreamsException() {
|
||||
super();
|
||||
}
|
||||
}
|
@@ -0,0 +1,131 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ConnectInactivityTest {
|
||||
private Log _log;
|
||||
private I2PSession _client;
|
||||
private I2PSession _server;
|
||||
public void test() {
|
||||
try {
|
||||
I2PAppContext context = I2PAppContext.getGlobalContext();
|
||||
_log = context.logManager().getLog(ConnectTest.class);
|
||||
_log.debug("creating server session");
|
||||
_server = createSession();
|
||||
_log.debug("running server");
|
||||
runServer(context, _server);
|
||||
_log.debug("creating client session");
|
||||
_client = createSession();
|
||||
_log.debug("running client");
|
||||
runClient(context, _client);
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void runClient(I2PAppContext ctx, I2PSession session) {
|
||||
Thread t = new Thread(new ClientRunner(ctx, session));
|
||||
t.setName("client");
|
||||
t.setDaemon(false);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void runServer(I2PAppContext ctx, I2PSession session) {
|
||||
Thread t = new Thread(new ServerRunner(ctx, session));
|
||||
t.setName("server");
|
||||
t.setDaemon(false);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private class ServerRunner implements Runnable {
|
||||
private I2PAppContext _context;
|
||||
private I2PSession _session;
|
||||
private Log _log;
|
||||
public ServerRunner(I2PAppContext ctx, I2PSession session) {
|
||||
_context = ctx;
|
||||
_session = session;
|
||||
_log = ctx.logManager().getLog(ServerRunner.class);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Properties opts = new Properties();
|
||||
I2PSocketManager mgr = new I2PSocketManagerFull(_context, _session, opts, "client");
|
||||
_log.debug("manager created");
|
||||
I2PServerSocket ssocket = mgr.getServerSocket();
|
||||
_log.debug("server socket created");
|
||||
I2PSocket socket = ssocket.accept();
|
||||
_log.debug("socket accepted: " + socket);
|
||||
try { Thread.sleep(10*60*1000); } catch (InterruptedException ie) {}
|
||||
socket.close();
|
||||
ssocket.close();
|
||||
_session.destroySession();
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ClientRunner implements Runnable {
|
||||
private I2PAppContext _context;
|
||||
private I2PSession _session;
|
||||
private Log _log;
|
||||
public ClientRunner(I2PAppContext ctx, I2PSession session) {
|
||||
_context = ctx;
|
||||
_session = session;
|
||||
_log = ctx.logManager().getLog(ClientRunner.class);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Properties opts = new Properties();
|
||||
I2PSocketManager mgr = new I2PSocketManagerFull(_context, _session, opts, "client");
|
||||
_log.debug("manager created");
|
||||
I2PSocket socket = mgr.connect(_server.getMyDestination());
|
||||
_log.debug("socket created");
|
||||
try { Thread.sleep(10*60*1000); } catch (InterruptedException ie) {}
|
||||
socket.close();
|
||||
_log.debug("socket closed");
|
||||
//_session.destroySession();
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private I2PSession createSession() {
|
||||
try {
|
||||
I2PClient client = I2PClientFactory.createClient();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||
Destination dest = client.createDestination(baos);
|
||||
Properties p = new Properties();
|
||||
p.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
||||
p.setProperty(I2PClient.PROP_TCP_PORT, "10001");
|
||||
I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), p);
|
||||
sess.connect();
|
||||
return sess;
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
throw new RuntimeException("b0rk b0rk b0rk");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
ConnectInactivityTest ct = new ConnectInactivityTest();
|
||||
ct.test();
|
||||
}
|
||||
}
|
@@ -16,7 +16,6 @@ import net.i2p.util.Log;
|
||||
*/
|
||||
public class ConnectTest {
|
||||
private Log _log;
|
||||
private I2PSession _client;
|
||||
private I2PSession _server;
|
||||
public void test() {
|
||||
try {
|
||||
@@ -26,14 +25,14 @@ public class ConnectTest {
|
||||
_server = createSession();
|
||||
_log.debug("running server");
|
||||
runServer(context, _server);
|
||||
_log.debug("creating client session");
|
||||
_client = createSession();
|
||||
_log.debug("running client");
|
||||
runClient(context, _client);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
_log.debug("running client");
|
||||
runClient(context, createSession());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
try { Thread.sleep(30*1000); } catch (Exception e) {}
|
||||
try { Thread.sleep(10*60*1000); } catch (Exception e) {}
|
||||
}
|
||||
|
||||
private void runClient(I2PAppContext ctx, I2PSession session) {
|
||||
@@ -70,7 +69,7 @@ public class ConnectTest {
|
||||
while (true) {
|
||||
I2PSocket socket = ssocket.accept();
|
||||
_log.debug("socket accepted: " + socket);
|
||||
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
socket.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -97,8 +96,12 @@ public class ConnectTest {
|
||||
_log.debug("manager created");
|
||||
I2PSocket socket = mgr.connect(_server.getMyDestination());
|
||||
_log.debug("socket created");
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
socket.close();
|
||||
_log.debug("socket closed");
|
||||
mgr.destroySocketManager();
|
||||
mgr = null;
|
||||
socket = null;
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
@@ -111,7 +114,7 @@ public class ConnectTest {
|
||||
I2PClient client = I2PClientFactory.createClient();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||
Destination dest = client.createDestination(baos);
|
||||
I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), new Properties());
|
||||
I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), System.getProperties());
|
||||
sess.connect();
|
||||
return sess;
|
||||
} catch (Exception e) {
|
||||
@@ -121,6 +124,8 @@ public class ConnectTest {
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
||||
System.setProperty(I2PClient.PROP_TCP_PORT, "11001");
|
||||
ConnectTest ct = new ConnectTest();
|
||||
ct.test();
|
||||
}
|
||||
|
@@ -0,0 +1,110 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Try to connect to a new nonexistant peer and, of course,
|
||||
* timeout.
|
||||
*/
|
||||
public class ConnectTimeoutTest {
|
||||
private Log _log;
|
||||
private I2PSession _client;
|
||||
private I2PSession _server;
|
||||
private Destination _serverDest;
|
||||
|
||||
public void testNonexistant() {
|
||||
try {
|
||||
I2PAppContext context = I2PAppContext.getGlobalContext();
|
||||
_log = context.logManager().getLog(ConnectTest.class);
|
||||
_log.debug("creating server dest");
|
||||
try {
|
||||
_serverDest = I2PClientFactory.createClient().createDestination(new ByteArrayOutputStream());
|
||||
} catch (Exception e) {}
|
||||
_log.debug("creating client session");
|
||||
_client = createSession();
|
||||
_log.debug("running client");
|
||||
runClient(context, _client);
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
while (true) { synchronized (this) { try { wait(); } catch (Exception e) {} } }
|
||||
}
|
||||
|
||||
private void runClient(I2PAppContext ctx, I2PSession session) {
|
||||
Thread t = new Thread(new ClientRunner(ctx, session));
|
||||
t.setName("client");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private class ClientRunner implements Runnable {
|
||||
private I2PAppContext _context;
|
||||
private I2PSession _session;
|
||||
private Log _log;
|
||||
public ClientRunner(I2PAppContext ctx, I2PSession session) {
|
||||
_context = ctx;
|
||||
_session = session;
|
||||
_log = ctx.logManager().getLog(ClientRunner.class);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
I2PSocketManager mgr = I2PSocketManagerFactory.createManager("localhost", 10001, getProps());
|
||||
_log.debug("manager created");
|
||||
_log.debug("options: " + mgr.getDefaultOptions());
|
||||
I2PSocket socket = mgr.connect(_serverDest);
|
||||
_log.debug("socket created");
|
||||
socket.getOutputStream().write("you smell".getBytes());
|
||||
socket.getOutputStream().flush();
|
||||
_log.error("wtf, shouldn't have flushed");
|
||||
socket.close();
|
||||
_log.debug("socket closed");
|
||||
} catch (Exception e) {
|
||||
_log.error("error running (yay!)", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private I2PSession createSession() {
|
||||
try {
|
||||
I2PClient client = I2PClientFactory.createClient();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||
Destination dest = client.createDestination(baos);
|
||||
Properties p = getProps();
|
||||
|
||||
I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), p);
|
||||
sess.connect();
|
||||
return sess;
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
throw new RuntimeException("b0rk b0rk b0rk");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
ConnectTimeoutTest ct = new ConnectTimeoutTest();
|
||||
ct.testNonexistant();
|
||||
}
|
||||
|
||||
private static Properties getProps() {
|
||||
Properties p = new Properties();
|
||||
p.setProperty(I2PSocketManagerFactory.PROP_MANAGER, I2PSocketManagerFull.class.getName());
|
||||
p.setProperty("tunnels.depthInbound", "0");
|
||||
p.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
||||
p.setProperty(I2PClient.PROP_TCP_PORT, "10001");
|
||||
p.setProperty(ConnectionOptions.PROP_CONNECT_TIMEOUT, "30000");
|
||||
//p.setProperty(ConnectionOptions.PROP_CONNECT_DELAY, "10000");
|
||||
p.setProperty(ConnectionOptions.PROP_CONNECT_DELAY, "0");
|
||||
return p;
|
||||
}
|
||||
}
|
@@ -110,12 +110,52 @@ public class MessageInputStreamTest {
|
||||
}
|
||||
}
|
||||
|
||||
public void testStaggered() {
|
||||
byte orig[] = new byte[256*1024];
|
||||
byte read[] = new byte[orig.length];
|
||||
_context.random().nextBytes(orig);
|
||||
|
||||
MessageInputStream in = new MessageInputStream(_context);
|
||||
ArrayList order = new ArrayList(32);
|
||||
for (int i = 0; i < orig.length / 1024; i++)
|
||||
order.add(new Integer(i));
|
||||
Collections.shuffle(order);
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < orig.length / 1024; i++) {
|
||||
byte msg[] = new byte[1024];
|
||||
Integer cur = (Integer)order.get(i);
|
||||
System.arraycopy(orig, cur.intValue()*1024, msg, 0, 1024);
|
||||
in.messageReceived(cur.intValue(), msg);
|
||||
_log.debug("Injecting " + cur);
|
||||
|
||||
try {
|
||||
if (in.available() > 0) {
|
||||
int curRead = in.read(read, offset, read.length-offset);
|
||||
_log.debug("read " + curRead);
|
||||
if (curRead == -1)
|
||||
throw new RuntimeException("EOF with offset " + offset);
|
||||
else
|
||||
offset += curRead;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("IOE: " + ioe.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (!DataHelper.eq(orig, read))
|
||||
throw new RuntimeException("Failed test: data read is not equal");
|
||||
|
||||
_log.info("Passed test: staggered");
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
MessageInputStreamTest t = new MessageInputStreamTest();
|
||||
try {
|
||||
t.testInOrder();
|
||||
t.testRandomOrder();
|
||||
t.testRandomDups();
|
||||
t.testStaggered();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ public class PingTest {
|
||||
try {
|
||||
I2PAppContext context = I2PAppContext.getGlobalContext();
|
||||
I2PSession session = createSession();
|
||||
ConnectionManager mgr = new ConnectionManager(context, session);
|
||||
ConnectionManager mgr = new ConnectionManager(context, session, -1);
|
||||
Log log = context.logManager().getLog(PingTest.class);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
log.debug("ping " + i);
|
||||
|
@@ -46,8 +46,8 @@ public class SysTray implements SysTrayMenuListener {
|
||||
_portString = _configFile.getProperty("port", "7657");
|
||||
_showIcon = Boolean.valueOf(_configFile.getProperty("visible", "true")).booleanValue();
|
||||
|
||||
if (!(new File("router.config")).exists())
|
||||
openRouterConsole("http://localhost:" + _portString + "/index.jsp");
|
||||
//if (!(new File("router.config")).exists())
|
||||
// openRouterConsole("http://localhost:" + _portString + "/index.jsp");
|
||||
|
||||
if ( (System.getProperty("os.name").startsWith("Windows")) && (!Boolean.getBoolean("systray.disable")) )
|
||||
_instance = new SysTray();
|
||||
|
@@ -147,4 +147,14 @@ public class UrlLauncher {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
UrlLauncher launcher = new UrlLauncher();
|
||||
try {
|
||||
if (args.length > 0)
|
||||
launcher.openUrl(args[0]);
|
||||
else
|
||||
launcher.openUrl("http://localhost:7657/index.jsp");
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
|
17
build.xml
17
build.xml
@@ -181,7 +181,6 @@
|
||||
<copy file="installer/resources/clients.config" todir="pkg-temp/" />
|
||||
<copy file="installer/resources/i2prouter" todir="pkg-temp/" />
|
||||
<copy file="installer/resources/i2prouter.bat" todir="pkg-temp/" />
|
||||
<copy file="installer/resources/i2prouter_win9x.bat" todir="pkg-temp/" />
|
||||
<copy file="installer/resources/i2ptunnel.config" todir="pkg-temp/" />
|
||||
<!-- <copy file="installer/resources/install_i2p_service_unix" todir="pkg-temp/" /> -->
|
||||
<copy file="installer/resources/install_i2p_service_winnt.bat" todir="pkg-temp/" />
|
||||
@@ -212,11 +211,16 @@
|
||||
<copy file="history.txt" todir="pkg-temp/" />
|
||||
<mkdir dir="pkg-temp/docs" />
|
||||
<copy file="readme.html" todir="pkg-temp/docs/" />
|
||||
<copy file="installer/resources/startconsole.html" todir="pkg-temp/docs/" />
|
||||
<copy file="installer/resources/start.ico" todir="pkg-temp/docs/" />
|
||||
<copy file="installer/resources/console.ico" todir="pkg-temp/docs/" />
|
||||
<copy file="installer/resources/uninstall.ico" todir="pkg-temp/docs/" />
|
||||
<mkdir dir="pkg-temp/eepsite" />
|
||||
<mkdir dir="pkg-temp/eepsite/webapps" />
|
||||
<mkdir dir="pkg-temp/eepsite/logs" />
|
||||
<mkdir dir="pkg-temp/eepsite/docroot" />
|
||||
<copy file="installer/resources/eepsite_index.html" tofile="pkg-temp/eepsite/docroot/index.html" />
|
||||
<copy file="installer/resources/favicon.ico" tofile="pkg-temp/eepsite/docroot/favicon.ico" />
|
||||
<copy file="installer/resources/jetty.xml" tofile="pkg-temp/eepsite/jetty.xml" />
|
||||
</target>
|
||||
<target name="tarball" depends="preppkg">
|
||||
@@ -235,6 +239,8 @@
|
||||
<copy file="build/routerconsole.jar" todir="pkg-temp/lib/" />
|
||||
<copy file="build/i2ptunnel.war" todir="pkg-temp/webapps/" />
|
||||
<copy file="build/routerconsole.war" todir="pkg-temp/webapps/" />
|
||||
<copy file="installer/resources/wrapper.config" todir="pkg-temp/" />
|
||||
<copy file="installer/resources/wrapper.config" tofile="pkg-temp/wrapper.config.updated" />
|
||||
<copy file="history.txt" todir="pkg-temp/" />
|
||||
<copy file="hosts.txt" todir="pkg-temp/" />
|
||||
<mkdir dir="pkg-temp/eepsite" />
|
||||
@@ -244,6 +250,15 @@
|
||||
</target>
|
||||
<taskdef name="izpack" classpath="${basedir}/installer/lib/izpack/standalone-compiler.jar" classname="com.izforge.izpack.ant.IzPackTask" />
|
||||
<target name="installer" depends="preppkg">
|
||||
<jar destfile="./pkg-temp/lib/copy.jar" basedir="./core/java/build/obj" includes="net/i2p/util/*.class">
|
||||
<manifest><attribute name="Main-Class" value="net.i2p.util.Copy" /></manifest>
|
||||
</jar>
|
||||
<jar destfile="./pkg-temp/lib/delete.jar" basedir="./core/java/build/obj" includes="net/i2p/util/*.class">
|
||||
<manifest><attribute name="Main-Class" value="net.i2p.util.Delete" /></manifest>
|
||||
</jar>
|
||||
<jar destfile="./pkg-temp/lib/exec.jar" basedir="./core/java/build/obj" includes="net/i2p/util/*.class">
|
||||
<manifest><attribute name="Main-Class" value="net.i2p.util.Exec" /></manifest>
|
||||
</jar>
|
||||
<izpack input="${basedir}/installer/install.xml" output="${basedir}/install.jar" installerType="standard" basedir="${basedir}" />
|
||||
</target>
|
||||
</project>
|
||||
|
@@ -14,8 +14,8 @@ package net.i2p;
|
||||
*
|
||||
*/
|
||||
public class CoreVersion {
|
||||
public final static String ID = "$Revision: 1.24 $ $Date: 2004/10/18 14:08:00 $";
|
||||
public final static String VERSION = "0.4.1.4";
|
||||
public final static String ID = "$Revision: 1.25 $ $Date: 2004/11/06 22:00:57 $";
|
||||
public final static String VERSION = "0.4.2";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Core version: " + VERSION);
|
||||
|
@@ -118,15 +118,23 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
if ( (tagsSent == null) || (tagsSent.size() <= 0) ) {
|
||||
if (oldTags < 10) {
|
||||
sentTags = createNewTags(50);
|
||||
//_log.error("** sendBestEffort only had " + oldTags + " adding 50");
|
||||
} else if (availTimeLeft < 30 * 1000) {
|
||||
// if we have > 10 tags, but they expire in under 30 seconds, we want more
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("** sendBestEffort only had " + oldTags + " with " + availTimeLeft + ", adding 50");
|
||||
} else if (availTimeLeft < 2 * 60 * 1000) {
|
||||
// if we have > 10 tags, but they expire in under 2 minutes, we want more
|
||||
sentTags = createNewTags(50);
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Tags are almost expired, adding 50 new ones");
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Tags expiring in " + availTimeLeft + ", adding 50 new ones");
|
||||
//_log.error("** sendBestEffort available time left " + availTimeLeft);
|
||||
} else {
|
||||
//_log.error("sendBestEffort old tags: " + oldTags + " available time left: " + availTimeLeft);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("sendBestEffort old tags: " + oldTags + " available time left: " + availTimeLeft);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("sendBestEffort is sending " + tagsSent.size() + " with " + availTimeLeft
|
||||
+ "ms left, " + oldTags + " tags known and "
|
||||
+ (tag == null ? "no tag" : " a valid tag"));
|
||||
}
|
||||
|
||||
SessionKey newKey = null;
|
||||
@@ -184,8 +192,14 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
long afterRemovingSync = _context.clock().now();
|
||||
boolean found = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "After waitFor sending state " + state.getMessageId().getMessageId()
|
||||
_log.debug(getPrefix() + "After waitFor sending state " + state.getMessageId()
|
||||
+ " / " + state.getNonce() + " found = " + found);
|
||||
|
||||
long timeToSend = afterRemovingSync - beforeSendingSync;
|
||||
if ( (timeToSend > 10*1000) && (_log.shouldLog(Log.WARN)) ) {
|
||||
_log.warn("wtf, took " + timeToSend + "ms to send the message?!", new Exception("baz"));
|
||||
}
|
||||
|
||||
if (found) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix() + "Message sent after " + state.getElapsed() + "ms with "
|
||||
@@ -210,7 +224,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
Set sentTags = null;
|
||||
if (_context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key) < 10) {
|
||||
sentTags = createNewTags(50);
|
||||
} else if (_context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key) < 30 * 1000) {
|
||||
} else if (_context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key) < 2 * 60 * 1000) {
|
||||
// if we have > 10 tags, but they expire in under 30 seconds, we want more
|
||||
sentTags = createNewTags(50);
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Tags are almost expired, adding 50 new ones");
|
||||
@@ -267,9 +281,10 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
_sendingStates.remove(state);
|
||||
}
|
||||
long afterRemovingSync = _context.clock().now();
|
||||
boolean guaranteed = isGuaranteed();
|
||||
boolean found = false;
|
||||
boolean accepted = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED);
|
||||
if (isGuaranteed())
|
||||
if (guaranteed)
|
||||
found = state.received(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS);
|
||||
else
|
||||
found = accepted;
|
||||
@@ -286,7 +301,8 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
+ ")");
|
||||
//if (true)
|
||||
// throw new OutOfMemoryError("not really an OOM, but more of jr fucking shit up");
|
||||
nackTags(state);
|
||||
if (guaranteed)
|
||||
nackTags(state);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -294,19 +310,24 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
_log.debug(getPrefix() + "After waitFor sending state " + state.getMessageId().getMessageId()
|
||||
+ " / " + state.getNonce() + " found = " + found);
|
||||
|
||||
// WARNING: this will always be false for mode=BestEffort, even though the message may go
|
||||
// through, causing every datagram to be ElGamal encrypted!
|
||||
// TODO: Fix this to include support for acks received after the sendMessage completes
|
||||
if (found) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix() + "Message sent after " + state.getElapsed() + "ms with "
|
||||
+ payload.length + " bytes");
|
||||
ackTags(state);
|
||||
// the 'found' value is only useful for mode=Guaranteed, as mode=BestEffort
|
||||
// doesn't block
|
||||
if (guaranteed) {
|
||||
if (found) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix() + "Message sent after " + state.getElapsed() + "ms with "
|
||||
+ payload.length + " bytes");
|
||||
ackTags(state);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix() + "Message send failed after " + state.getElapsed() + "ms with "
|
||||
+ payload.length + " bytes");
|
||||
nackTags(state);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix() + "Message send failed after " + state.getElapsed() + "ms with "
|
||||
_log.info(getPrefix() + "Message send enqueued after " + state.getElapsed() + "ms with "
|
||||
+ payload.length + " bytes");
|
||||
nackTags(state);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
@@ -57,37 +57,41 @@ public class DSAEngine {
|
||||
public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) {
|
||||
long start = _context.clock().now();
|
||||
|
||||
byte[] sigbytes = signature.getData();
|
||||
byte rbytes[] = new byte[20];
|
||||
byte sbytes[] = new byte[20];
|
||||
for (int x = 0; x < 40; x++) {
|
||||
if (x < 20) {
|
||||
rbytes[x] = sigbytes[x];
|
||||
} else {
|
||||
sbytes[x - 20] = sigbytes[x];
|
||||
try {
|
||||
byte[] sigbytes = signature.getData();
|
||||
byte rbytes[] = new byte[20];
|
||||
byte sbytes[] = new byte[20];
|
||||
for (int x = 0; x < 40; x++) {
|
||||
if (x < 20) {
|
||||
rbytes[x] = sigbytes[x];
|
||||
} else {
|
||||
sbytes[x - 20] = sigbytes[x];
|
||||
}
|
||||
}
|
||||
BigInteger s = new NativeBigInteger(1, sbytes);
|
||||
BigInteger r = new NativeBigInteger(1, rbytes);
|
||||
BigInteger y = new NativeBigInteger(1, verifyingKey.getData());
|
||||
BigInteger w = s.modInverse(CryptoConstants.dsaq);
|
||||
byte data[] = calculateHash(signedData, offset, size).getData();
|
||||
NativeBigInteger bi = new NativeBigInteger(1, data);
|
||||
BigInteger u1 = bi.multiply(w).mod(CryptoConstants.dsaq);
|
||||
BigInteger u2 = r.multiply(w).mod(CryptoConstants.dsaq);
|
||||
BigInteger modval = CryptoConstants.dsag.modPow(u1, CryptoConstants.dsap);
|
||||
BigInteger modmulval = modval.multiply(y.modPow(u2,CryptoConstants.dsap));
|
||||
BigInteger v = (modmulval).mod(CryptoConstants.dsap).mod(CryptoConstants.dsaq);
|
||||
|
||||
boolean ok = v.compareTo(r) == 0;
|
||||
|
||||
long diff = _context.clock().now() - start;
|
||||
if (diff > 1000) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Took too long to verify the signature (" + diff + "ms)");
|
||||
}
|
||||
return ok;
|
||||
} catch (Exception e) {
|
||||
_log.log(Log.CRIT, "Error verifying the signature", e);
|
||||
return false;
|
||||
}
|
||||
BigInteger s = new NativeBigInteger(1, sbytes);
|
||||
BigInteger r = new NativeBigInteger(1, rbytes);
|
||||
BigInteger y = new NativeBigInteger(1, verifyingKey.getData());
|
||||
BigInteger w = s.modInverse(CryptoConstants.dsaq);
|
||||
byte data[] = calculateHash(signedData, offset, size).getData();
|
||||
NativeBigInteger bi = new NativeBigInteger(1, data);
|
||||
BigInteger u1 = bi.multiply(w).mod(CryptoConstants.dsaq);
|
||||
BigInteger u2 = r.multiply(w).mod(CryptoConstants.dsaq);
|
||||
BigInteger modval = CryptoConstants.dsag.modPow(u1, CryptoConstants.dsap);
|
||||
BigInteger modmulval = modval.multiply(y.modPow(u2,CryptoConstants.dsap));
|
||||
BigInteger v = (modmulval).mod(CryptoConstants.dsap).mod(CryptoConstants.dsaq);
|
||||
|
||||
boolean ok = v.compareTo(r) == 0;
|
||||
|
||||
long diff = _context.clock().now() - start;
|
||||
if (diff > 1000) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Took too long to verify the signature (" + diff + "ms)");
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
public Signature sign(byte data[], SigningPrivateKey signingKey) {
|
||||
|
@@ -156,7 +156,7 @@ public class PersistentSessionKeyManager extends TransientSessionKeyManager {
|
||||
tag.setData(val);
|
||||
tags.add(tag);
|
||||
}
|
||||
TagSet ts = new TagSet(tags, key);
|
||||
TagSet ts = new TagSet(tags, key, _context.clock().now());
|
||||
ts.setDate(date.getTime());
|
||||
return ts;
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
private Log _log;
|
||||
private Map _outboundSessions; // PublicKey --> OutboundSession
|
||||
private Map _inboundTagSets; // SessionTag --> TagSet
|
||||
protected I2PAppContext _context;
|
||||
|
||||
/**
|
||||
* Let session tags sit around for 10 minutes before expiring them. We can now have such a large
|
||||
@@ -62,6 +63,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
public TransientSessionKeyManager(I2PAppContext context) {
|
||||
super(context);
|
||||
_log = context.logManager().getLog(TransientSessionKeyManager.class);
|
||||
_context = context;
|
||||
_outboundSessions = new HashMap(64);
|
||||
_inboundTagSets = new HashMap(1024);
|
||||
}
|
||||
@@ -116,12 +118,14 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
public SessionKey getCurrentKey(PublicKey target) {
|
||||
OutboundSession sess = getSession(target);
|
||||
if (sess == null) return null;
|
||||
long now = Clock.getInstance().now();
|
||||
if (sess.getEstablishedDate() < now - SESSION_LIFETIME_MAX_MS) {
|
||||
long now = _context.clock().now();
|
||||
if (sess.getLastUsedDate() < now - SESSION_LIFETIME_MAX_MS) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Expiring old session key established on "
|
||||
+ new Date(sess.getEstablishedDate())
|
||||
+ " with target " + target);
|
||||
+ " but not used for "
|
||||
+ (now-sess.getLastUsedDate())
|
||||
+ "ms with target " + target);
|
||||
return null;
|
||||
}
|
||||
return sess.getCurrentKey();
|
||||
@@ -185,7 +189,11 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
OutboundSession sess = getSession(target);
|
||||
if (sess == null) { return 0; }
|
||||
if (sess.getCurrentKey().equals(key)) {
|
||||
return (sess.getLastExpirationDate() + SESSION_TAG_DURATION_MS) - Clock.getInstance().now();
|
||||
long end = sess.getLastExpirationDate();
|
||||
if (end <= 0)
|
||||
return 0;
|
||||
else
|
||||
return end - _context.clock().now();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -203,7 +211,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
sess = getSession(target);
|
||||
}
|
||||
sess.setCurrentKey(key);
|
||||
TagSet set = new TagSet(sessionTags, key);
|
||||
TagSet set = new TagSet(sessionTags, key, _context.clock().now());
|
||||
sess.addTags(set);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Tags delivered to set " + set + " on session " + sess);
|
||||
@@ -226,7 +234,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
*
|
||||
*/
|
||||
public void tagsReceived(SessionKey key, Set sessionTags) {
|
||||
TagSet tagSet = new TagSet(sessionTags, key);
|
||||
TagSet tagSet = new TagSet(sessionTags, key, _context.clock().now());
|
||||
for (Iterator iter = sessionTags.iterator(); iter.hasNext();) {
|
||||
SessionTag tag = (SessionTag) iter.next();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -285,9 +293,14 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
|
||||
private void removeSession(PublicKey target) {
|
||||
if (target == null) return;
|
||||
OutboundSession session = null;
|
||||
synchronized (_outboundSessions) {
|
||||
_outboundSessions.remove(target);
|
||||
session = (OutboundSession)_outboundSessions.remove(target);
|
||||
}
|
||||
if ( (session != null) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("Removing session tags with " + session.availableTags() + " available for "
|
||||
+ (session.getLastExpirationDate()-_context.clock().now())
|
||||
+ "ms more", new Exception("Removed by"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,32 +310,47 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
*/
|
||||
public int aggressiveExpire() {
|
||||
int removed = 0;
|
||||
long now = Clock.getInstance().now();
|
||||
Set tagsToDrop = new HashSet(64);
|
||||
long now = _context.clock().now();
|
||||
Set tagsToDrop = null; // new HashSet(64);
|
||||
synchronized (_inboundTagSets) {
|
||||
for (Iterator iter = _inboundTagSets.keySet().iterator(); iter.hasNext();) {
|
||||
SessionTag tag = (SessionTag) iter.next();
|
||||
TagSet ts = (TagSet) _inboundTagSets.get(tag);
|
||||
if (ts.getDate() < now - SESSION_LIFETIME_MAX_MS) {
|
||||
if (tagsToDrop == null)
|
||||
tagsToDrop = new HashSet(4);
|
||||
tagsToDrop.add(tag);
|
||||
}
|
||||
}
|
||||
removed += tagsToDrop.size();
|
||||
for (Iterator iter = tagsToDrop.iterator(); iter.hasNext();)
|
||||
_inboundTagSets.remove(iter.next());
|
||||
if (tagsToDrop != null) {
|
||||
removed += tagsToDrop.size();
|
||||
for (Iterator iter = tagsToDrop.iterator(); iter.hasNext();)
|
||||
_inboundTagSets.remove(iter.next());
|
||||
}
|
||||
}
|
||||
//_log.warn("Expiring tags: [" + tagsToDrop + "]");
|
||||
|
||||
synchronized (_outboundSessions) {
|
||||
Set sessionsToDrop = new HashSet(64);
|
||||
Set sessionsToDrop = null;
|
||||
for (Iterator iter = _outboundSessions.keySet().iterator(); iter.hasNext();) {
|
||||
PublicKey key = (PublicKey) iter.next();
|
||||
OutboundSession sess = (OutboundSession) _outboundSessions.get(key);
|
||||
removed += sess.expireTags();
|
||||
if (sess.getTagSets().size() <= 0) sessionsToDrop.add(key);
|
||||
if (sess.getTagSets().size() <= 0) {
|
||||
if (sessionsToDrop == null)
|
||||
sessionsToDrop = new HashSet(4);
|
||||
sessionsToDrop.add(key);
|
||||
}
|
||||
}
|
||||
if (sessionsToDrop != null) {
|
||||
for (Iterator iter = sessionsToDrop.iterator(); iter.hasNext();) {
|
||||
OutboundSession cur = (OutboundSession)_outboundSessions.remove(iter.next());
|
||||
if ( (cur != null) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("Removing session tags with " + cur.availableTags() + " available for "
|
||||
+ (cur.getLastExpirationDate()-_context.clock().now())
|
||||
+ "ms more", new Exception("Removed by"));
|
||||
}
|
||||
}
|
||||
for (Iterator iter = sessionsToDrop.iterator(); iter.hasNext();)
|
||||
_outboundSessions.remove(iter.next());
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
@@ -388,7 +416,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
private List _tagSets;
|
||||
|
||||
public OutboundSession(PublicKey target) {
|
||||
this(target, null, Clock.getInstance().now(), Clock.getInstance().now(), new ArrayList());
|
||||
this(target, null, _context.clock().now(), _context.clock().now(), new ArrayList());
|
||||
}
|
||||
|
||||
OutboundSession(PublicKey target, SessionKey curKey, long established, long lastUsed, List tagSets) {
|
||||
@@ -415,6 +443,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
}
|
||||
|
||||
public void setCurrentKey(SessionKey key) {
|
||||
_lastUsed = _context.clock().now();
|
||||
if (_currentKey != null) {
|
||||
if (!_currentKey.equals(key)) {
|
||||
int dropped = 0;
|
||||
@@ -445,22 +474,24 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
* Expire old tags, returning the number of tag sets removed
|
||||
*/
|
||||
public int expireTags() {
|
||||
long now = Clock.getInstance().now();
|
||||
Set toRemove = new HashSet(64);
|
||||
long now = _context.clock().now();
|
||||
int removed = 0;
|
||||
synchronized (_tagSets) {
|
||||
for (int i = 0; i < _tagSets.size(); i++) {
|
||||
TagSet set = (TagSet) _tagSets.get(i);
|
||||
if (set.getDate() + SESSION_TAG_DURATION_MS <= now) {
|
||||
toRemove.add(set);
|
||||
_tagSets.remove(i);
|
||||
i--;
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
_tagSets.removeAll(toRemove);
|
||||
}
|
||||
return toRemove.size();
|
||||
return removed;
|
||||
}
|
||||
|
||||
public SessionTag consumeNext() {
|
||||
long now = Clock.getInstance().now();
|
||||
long now = _context.clock().now();
|
||||
_lastUsed = now;
|
||||
synchronized (_tagSets) {
|
||||
while (_tagSets.size() > 0) {
|
||||
TagSet set = (TagSet) _tagSets.get(0);
|
||||
@@ -479,10 +510,12 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
|
||||
public int availableTags() {
|
||||
int tags = 0;
|
||||
long now = _context.clock().now();
|
||||
synchronized (_tagSets) {
|
||||
for (int i = 0; i < _tagSets.size(); i++) {
|
||||
TagSet set = (TagSet) _tagSets.get(i);
|
||||
tags += set.getTags().size();
|
||||
if (set.getDate() + SESSION_TAG_DURATION_MS > now)
|
||||
tags += set.getTags().size();
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
@@ -498,13 +531,18 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
synchronized (_tagSets) {
|
||||
for (Iterator iter = _tagSets.iterator(); iter.hasNext();) {
|
||||
TagSet set = (TagSet) iter.next();
|
||||
if (set.getDate() > last) last = set.getDate();
|
||||
if ( (set.getDate() > last) && (set.getTags().size() > 0) )
|
||||
last = set.getDate();
|
||||
}
|
||||
}
|
||||
return last + SESSION_TAG_DURATION_MS;
|
||||
if (last > 0)
|
||||
return last + SESSION_TAG_DURATION_MS;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void addTags(TagSet set) {
|
||||
_lastUsed = _context.clock().now();
|
||||
synchronized (_tagSets) {
|
||||
_tagSets.add(set);
|
||||
}
|
||||
@@ -516,12 +554,12 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
private SessionKey _key;
|
||||
private long _date;
|
||||
|
||||
public TagSet(Set tags, SessionKey key) {
|
||||
public TagSet(Set tags, SessionKey key, long date) {
|
||||
if (key == null) throw new IllegalArgumentException("Missing key");
|
||||
if (tags == null) throw new IllegalArgumentException("Missing tags");
|
||||
_sessionTags = tags;
|
||||
_key = key;
|
||||
_date = Clock.getInstance().now();
|
||||
_date = date;
|
||||
}
|
||||
|
||||
public long getDate() {
|
||||
|
@@ -34,6 +34,7 @@ import java.util.TreeMap;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
|
||||
/**
|
||||
@@ -153,6 +154,9 @@ public class DataHelper {
|
||||
while ( (line = in.readLine()) != null) {
|
||||
if (line.trim().length() <= 0) continue;
|
||||
if (line.charAt(0) == '#') continue;
|
||||
if (line.charAt(0) == ';') continue;
|
||||
if (line.indexOf('#') > 0) // trim off any end of line comment
|
||||
line = line.substring(0, line.indexOf('#')).trim();
|
||||
int split = line.indexOf('=');
|
||||
if (split <= 0) continue;
|
||||
String key = line.substring(0, split);
|
||||
@@ -310,7 +314,7 @@ public class DataHelper {
|
||||
throw new IllegalArgumentException("wtf, fromLong got a negative? " + rv + ": offset="+ offset +" numBytes="+numBytes);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String args[]) {
|
||||
for (int i = 0; i <= 0xFF; i++)
|
||||
testLong(1, i);
|
||||
@@ -737,13 +741,16 @@ public class DataHelper {
|
||||
if ((orig == null) || (orig.length <= 0)) return orig;
|
||||
GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(orig, offset, length), length);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(length * 2);
|
||||
byte buf[] = new byte[4 * 1024];
|
||||
ByteCache cache = ByteCache.getInstance(10, 4*1024);
|
||||
ByteArray ba = cache.acquire();
|
||||
byte buf[] = ba.getData(); // new byte[4 * 1024];
|
||||
while (true) {
|
||||
int read = in.read(buf);
|
||||
if (read == -1) break;
|
||||
baos.write(buf, 0, read);
|
||||
}
|
||||
byte rv[] = baos.toByteArray();
|
||||
cache.release(ba);
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Decompression of " + orig.length + " into " + rv.length + " (or " + 100.0d
|
||||
// * (((double) rv.length) / ((double) orig.length)) + "% savings)");
|
||||
|
108
core/java/src/net/i2p/util/ByteCache.java
Normal file
108
core/java/src/net/i2p/util/ByteCache.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package net.i2p.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
|
||||
/**
|
||||
* Cache the objects frequently used to reduce memory churn. The ByteArray
|
||||
* should be held onto as long as the data referenced in it is needed.
|
||||
*
|
||||
*/
|
||||
public final class ByteCache {
|
||||
private static Map _caches = new HashMap(16);
|
||||
/**
|
||||
* Get a cache responsible for objects of the given size
|
||||
*
|
||||
* @param cacheSize how large we want the cache to grow before using on
|
||||
* demand allocation
|
||||
* @param size how large should the objects cached be?
|
||||
*/
|
||||
public static ByteCache getInstance(int cacheSize, int size) {
|
||||
Integer sz = new Integer(size);
|
||||
synchronized (_caches) {
|
||||
if (!_caches.containsKey(sz))
|
||||
_caches.put(sz, new ByteCache(cacheSize, size));
|
||||
return (ByteCache)_caches.get(sz);
|
||||
}
|
||||
}
|
||||
private Log _log;
|
||||
/** list of available and available entries */
|
||||
private List _available;
|
||||
private int _maxCached;
|
||||
private int _entrySize;
|
||||
private long _lastOverflow;
|
||||
|
||||
/** do we actually want to cache? */
|
||||
private static final boolean _cache = true;
|
||||
|
||||
/** how often do we cleanup the cache */
|
||||
private static final int CLEANUP_FREQUENCY = 30*1000;
|
||||
/** if we haven't exceeded the cache size in 2 minutes, cut our cache in half */
|
||||
private static final long EXPIRE_PERIOD = 2*60*1000;
|
||||
|
||||
private ByteCache(int maxCachedEntries, int entrySize) {
|
||||
if (_cache)
|
||||
_available = new ArrayList(maxCachedEntries);
|
||||
_maxCached = maxCachedEntries;
|
||||
_entrySize = entrySize;
|
||||
_lastOverflow = -1;
|
||||
SimpleTimer.getInstance().addEvent(new Cleanup(), CLEANUP_FREQUENCY);
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(ByteCache.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next available structure, either from the cache or a brand new one
|
||||
*
|
||||
*/
|
||||
public final ByteArray acquire() {
|
||||
if (_cache) {
|
||||
synchronized (_available) {
|
||||
if (_available.size() > 0)
|
||||
return (ByteArray)_available.remove(0);
|
||||
}
|
||||
}
|
||||
_lastOverflow = System.currentTimeMillis();
|
||||
byte data[] = new byte[_entrySize];
|
||||
return new ByteArray(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put this structure back onto the available cache for reuse
|
||||
*
|
||||
*/
|
||||
public final void release(ByteArray entry) {
|
||||
if (_cache) {
|
||||
if ( (entry == null) || (entry.getData() == null) )
|
||||
return;
|
||||
|
||||
Arrays.fill(entry.getData(), (byte)0x0);
|
||||
synchronized (_available) {
|
||||
if (_available.size() < _maxCached)
|
||||
_available.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Cleanup implements SimpleTimer.TimedEvent {
|
||||
public void timeReached() {
|
||||
if (System.currentTimeMillis() - _lastOverflow > EXPIRE_PERIOD) {
|
||||
// we haven't exceeded the cache size in a few minutes, so lets
|
||||
// shrink the cache
|
||||
synchronized (_available) {
|
||||
int toRemove = _available.size() / 2;
|
||||
for (int i = 0; i < toRemove; i++)
|
||||
_available.remove(0);
|
||||
if ( (toRemove > 0) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("Removing " + toRemove + " cached entries of size " + _entrySize);
|
||||
}
|
||||
}
|
||||
SimpleTimer.getInstance().addEvent(Cleanup.this, CLEANUP_FREQUENCY);
|
||||
}
|
||||
}
|
||||
}
|
@@ -17,6 +17,7 @@ import net.i2p.time.Timestamper;
|
||||
public class Clock implements Timestamper.UpdateListener {
|
||||
private I2PAppContext _context;
|
||||
private Timestamper _timestamper;
|
||||
private long _startedOn;
|
||||
|
||||
public Clock(I2PAppContext context) {
|
||||
_context = context;
|
||||
@@ -24,6 +25,7 @@ public class Clock implements Timestamper.UpdateListener {
|
||||
_alreadyChanged = false;
|
||||
_listeners = new HashSet(64);
|
||||
_timestamper = new Timestamper(context, this);
|
||||
_startedOn = System.currentTimeMillis();
|
||||
}
|
||||
public static Clock getInstance() {
|
||||
return I2PAppContext.getGlobalContext().clock();
|
||||
@@ -40,6 +42,8 @@ public class Clock implements Timestamper.UpdateListener {
|
||||
|
||||
/** if the clock is skewed by 3+ days, fuck 'em */
|
||||
public final static long MAX_OFFSET = 3 * 24 * 60 * 60 * 1000;
|
||||
/** after we've started up and shifted the clock, don't allow shifts of more than a minute */
|
||||
public final static long MAX_LIVE_OFFSET = 60 * 1000;
|
||||
/** if the clock skewed changes by less than 1s, ignore the update (so we don't slide all over the place) */
|
||||
public final static long MIN_OFFSET_CHANGE = 10 * 1000;
|
||||
|
||||
@@ -60,6 +64,15 @@ public class Clock implements Timestamper.UpdateListener {
|
||||
return;
|
||||
}
|
||||
|
||||
// only allow substantial modifications before the first 10 minutes
|
||||
if (_alreadyChanged && (System.currentTimeMillis() - _startedOn > 10 * 60 * 1000)) {
|
||||
if ( (delta > MAX_LIVE_OFFSET) || (delta < 0 - MAX_LIVE_OFFSET) ) {
|
||||
getLog().log(Log.CRIT, "The clock has already been updated, but you want to change it by "
|
||||
+ delta + " to " + offsetMs + "? Did something break?");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((delta < MIN_OFFSET_CHANGE) && (delta > 0 - MIN_OFFSET_CHANGE)) {
|
||||
getLog().debug("Not changing offset since it is only " + delta + "ms");
|
||||
return;
|
||||
|
11
core/java/src/net/i2p/util/Copy.java
Normal file
11
core/java/src/net/i2p/util/Copy.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package net.i2p.util;
|
||||
|
||||
/**
|
||||
* Usage: Copy from to
|
||||
*
|
||||
*/
|
||||
public class Copy {
|
||||
public static void main(String args[]) {
|
||||
FileUtil.copy(args[0], args[1], true);
|
||||
}
|
||||
}
|
11
core/java/src/net/i2p/util/Delete.java
Normal file
11
core/java/src/net/i2p/util/Delete.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package net.i2p.util;
|
||||
|
||||
/**
|
||||
* Usage: Delete name
|
||||
*
|
||||
*/
|
||||
public class Delete {
|
||||
public static void main(String args[]) {
|
||||
FileUtil.rmdir(args[0], false);
|
||||
}
|
||||
}
|
25
core/java/src/net/i2p/util/Exec.java
Normal file
25
core/java/src/net/i2p/util/Exec.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package net.i2p.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Usage: Exec dir command [args ...]
|
||||
*
|
||||
*/
|
||||
public class Exec {
|
||||
public static void main(String args[]) {
|
||||
try {
|
||||
String cmd[] = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, cmd, 0, cmd.length);
|
||||
Process proc = Runtime.getRuntime().exec(cmd, (String[])null, new File(args[0]));
|
||||
|
||||
// ugly hack, because it seems we'll block otherwise!
|
||||
// http://cephas.net/blog/2004/03/23/external_applications_javas_runtimeexec.html
|
||||
try { proc.exitValue(); } catch (Throwable t) { }
|
||||
Runtime.getRuntime().halt(0);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@@ -127,10 +127,13 @@ public class FileUtil {
|
||||
|
||||
/**
|
||||
* Read in the last few lines of a (newline delimited) textfile, or null if
|
||||
* the file doesn't exist.
|
||||
* the file doesn't exist.
|
||||
*
|
||||
* @param startAtBeginning if true, read the first maxNumLines, otherwise read
|
||||
* the last maxNumLines
|
||||
*
|
||||
*/
|
||||
public static String readTextFile(String filename, int maxNumLines) {
|
||||
public static String readTextFile(String filename, int maxNumLines, boolean startAtBeginning) {
|
||||
File f = new File(filename);
|
||||
if (!f.exists()) return null;
|
||||
FileInputStream fis = null;
|
||||
@@ -141,8 +144,12 @@ public class FileUtil {
|
||||
String line = null;
|
||||
while ( (line = in.readLine()) != null) {
|
||||
lines.add(line);
|
||||
while (lines.size() > maxNumLines)
|
||||
lines.remove(0);
|
||||
if (lines.size() >= maxNumLines) {
|
||||
if (startAtBeginning)
|
||||
break;
|
||||
else
|
||||
lines.remove(0);
|
||||
}
|
||||
}
|
||||
StringBuffer buf = new StringBuffer(lines.size() * 80);
|
||||
for (int i = 0; i < lines.size(); i++)
|
||||
@@ -155,9 +162,53 @@ public class FileUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
testRmdir();
|
||||
/** return true if it was copied successfully */
|
||||
public static boolean copy(String source, String dest, boolean overwriteExisting) {
|
||||
File src = new File(source);
|
||||
File dst = new File(dest);
|
||||
|
||||
if (dst.exists() && dst.isDirectory())
|
||||
dst = new File(dst, src.getName());
|
||||
|
||||
if (!src.exists()) return false;
|
||||
if (dst.exists() && !overwriteExisting) return false;
|
||||
|
||||
byte buf[] = new byte[4096];
|
||||
try {
|
||||
FileInputStream in = new FileInputStream(src);
|
||||
FileOutputStream out = new FileOutputStream(dst);
|
||||
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1)
|
||||
out.write(buf, 0, read);
|
||||
|
||||
in.close();
|
||||
out.close();
|
||||
return true;
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage: FileUtil (delete path | copy source dest)
|
||||
*
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
if ( (args == null) || (args.length < 2) ) {
|
||||
testRmdir();
|
||||
} else if ("delete".equals(args[0])) {
|
||||
boolean deleted = FileUtil.rmdir(args[1], false);
|
||||
if (!deleted)
|
||||
System.err.println("Error deleting [" + args[1] + "]");
|
||||
} else if ("copy".equals(args[0])) {
|
||||
boolean copied = FileUtil.copy(args[1], args[2], false);
|
||||
if (!copied)
|
||||
System.err.println("Error copying [" + args[1] + "] to [" + args[2] + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void testRmdir() {
|
||||
File t = new File("rmdirTest/test/subdir/blah");
|
||||
boolean created = t.mkdirs();
|
||||
|
@@ -27,7 +27,7 @@ class LogRecordFormatter {
|
||||
private final static int MAX_PRIORITY_LENGTH = 5;
|
||||
|
||||
public static String formatRecord(LogManager manager, LogRecord rec) {
|
||||
int size = 64 + rec.getMessage().length();
|
||||
int size = 128 + rec.getMessage().length();
|
||||
if (rec.getThrowable() != null)
|
||||
size += 512;
|
||||
StringBuffer buf = new StringBuffer(size);
|
||||
|
@@ -349,7 +349,9 @@ public class ShellCommand {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
command.append("\"").append(args[i]).append("\" ");
|
||||
}
|
||||
cmd.execute(command.toString());
|
||||
try {
|
||||
cmd.executeSilent(command.toString());
|
||||
} catch (IOException ioe) { ioe.printStackTrace(); }
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -20,11 +20,12 @@ public class SimpleTimer {
|
||||
public static SimpleTimer getInstance() { return _instance; }
|
||||
private Log _log;
|
||||
/** event time (Long) to event (TimedEvent) mapping */
|
||||
private Map _events;
|
||||
private TreeMap _events;
|
||||
/** event (TimedEvent) to event time (Long) mapping */
|
||||
private Map _eventTimes;
|
||||
|
||||
private SimpleTimer() {
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(SimpleTimer.class);
|
||||
_events = new TreeMap();
|
||||
_eventTimes = new HashMap();
|
||||
I2PThread runner = new I2PThread(new SimpleTimerRunner());
|
||||
@@ -71,38 +72,46 @@ public class SimpleTimer {
|
||||
_log.log(Log.CRIT, msg, t);
|
||||
}
|
||||
|
||||
private long _occurredTime;
|
||||
private long _occurredEventCount;
|
||||
private TimedEvent _recentEvents[] = new TimedEvent[5];
|
||||
|
||||
private class SimpleTimerRunner implements Runnable {
|
||||
public void run() {
|
||||
List eventsToFire = new ArrayList(1);
|
||||
List timesToRemove = new ArrayList(1);
|
||||
while (true) {
|
||||
try {
|
||||
synchronized (_events) {
|
||||
if (_events.size() <= 0)
|
||||
_events.wait();
|
||||
//if (_events.size() > 100)
|
||||
// _log.warn("> 100 events! " + _events.values());
|
||||
long now = System.currentTimeMillis();
|
||||
long nextEventDelay = -1;
|
||||
for (Iterator iter = _events.keySet().iterator(); iter.hasNext(); ) {
|
||||
Long when = (Long)iter.next();
|
||||
Object nextEvent = null;
|
||||
while (true) {
|
||||
if (_events.size() <= 0) break;
|
||||
Long when = (Long)_events.firstKey();
|
||||
if (when.longValue() <= now) {
|
||||
TimedEvent evt = (TimedEvent)_events.get(when);
|
||||
eventsToFire.add(evt);
|
||||
timesToRemove.add(when);
|
||||
TimedEvent evt = (TimedEvent)_events.remove(when);
|
||||
if (evt != null) {
|
||||
_eventTimes.remove(evt);
|
||||
eventsToFire.add(evt);
|
||||
}
|
||||
} else {
|
||||
nextEventDelay = when.longValue() - now;
|
||||
nextEvent = _events.get(when);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (timesToRemove.size() > 0) {
|
||||
for (int i = 0; i < timesToRemove.size(); i++)
|
||||
_events.remove(timesToRemove.get(i));
|
||||
for (int i = 0; i < eventsToFire.size(); i++)
|
||||
_eventTimes.remove(eventsToFire.get(i));
|
||||
} else {
|
||||
if (nextEventDelay != -1)
|
||||
if (eventsToFire.size() <= 0) {
|
||||
if (nextEventDelay != -1) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Next event in " + nextEventDelay + ": " + nextEvent);
|
||||
_events.wait(nextEventDelay);
|
||||
else
|
||||
} else {
|
||||
_events.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
@@ -116,6 +125,9 @@ public class SimpleTimer {
|
||||
}
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
now = now - (now % 1000);
|
||||
|
||||
for (int i = 0; i < eventsToFire.size(); i++) {
|
||||
TimedEvent evt = (TimedEvent)eventsToFire.get(i);
|
||||
try {
|
||||
@@ -123,9 +135,31 @@ public class SimpleTimer {
|
||||
} catch (Throwable t) {
|
||||
log("wtf, event borked: " + evt, t);
|
||||
}
|
||||
_recentEvents[4] = _recentEvents[3];
|
||||
_recentEvents[3] = _recentEvents[2];
|
||||
_recentEvents[2] = _recentEvents[1];
|
||||
_recentEvents[1] = _recentEvents[0];
|
||||
_recentEvents[0] = evt;
|
||||
}
|
||||
|
||||
if (_occurredTime == now) {
|
||||
_occurredEventCount += eventsToFire.size();
|
||||
} else {
|
||||
_occurredTime = now;
|
||||
if (_occurredEventCount > 100) {
|
||||
StringBuffer buf = new StringBuffer(256);
|
||||
buf.append("Too many simpleTimerJobs (").append(_occurredEventCount);
|
||||
buf.append(") in a second! Last 5: \n");
|
||||
for (int i = 0; i < _recentEvents.length; i++) {
|
||||
if (_recentEvents[i] != null)
|
||||
buf.append(_recentEvents[i]).append('\n');
|
||||
}
|
||||
_log.log(Log.CRIT, buf.toString());
|
||||
}
|
||||
_occurredEventCount = 0;
|
||||
}
|
||||
|
||||
eventsToFire.clear();
|
||||
timesToRemove.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
187
history.txt
187
history.txt
@@ -1,4 +1,189 @@
|
||||
$Id: history.txt,v 1.65 2004/11/06 21:25:13 jrandom Exp $
|
||||
$Id: history.txt,v 1.91 2004/12/01 17:31:56 jrandom Exp $
|
||||
|
||||
* 2004-12-01 0.4.2.2 released
|
||||
|
||||
2004-12-01 jrandom
|
||||
* Fixed a stupid typo that inadvertantly allowed persistent HTTP
|
||||
connections to work (thanks duck!)
|
||||
* Make sure we override the inactivity timeout too
|
||||
|
||||
* 2004-12-01 0.4.2.1 released
|
||||
|
||||
2004-12-01 jrandom
|
||||
* Strip out any of the Accept-* HTTP header lines, and always make sure to
|
||||
include the forged User-agent header.
|
||||
* Adjust the default read timeout on the eepproxy to 60s, unless
|
||||
overridden.
|
||||
* Minor tweak on stream shutdown.
|
||||
|
||||
2004-11-30 jrandom
|
||||
* Render the burst rate fields on /config.jsp properly (thanks ugha!)
|
||||
* Build in a simple timeout to flush data queued into the I2PSocket but
|
||||
not yet flushed.
|
||||
* Don't explicitly flush after each SAM stream write, but leave it up to
|
||||
the [nonblocking] passive flush.
|
||||
* Don't whine about 10-99 connection events occurring in a second
|
||||
* Don't wait for completion of packets that will not be ACKed (duh)
|
||||
* Adjust the congestion window, even if the packet was resent (duh)
|
||||
* Make sure to wake up any blocking read()'s when the MessageInputStream
|
||||
is close()ed (duh)
|
||||
* Never wait more than the disconnect timeout for a write to complete
|
||||
|
||||
2004-11-29 jrandom
|
||||
* Minor fixes to avoid unnecessary errors on shutdown (thanks susi!)
|
||||
|
||||
2004-11-29 jrandom
|
||||
* Reduced contention for local client delivery
|
||||
* Drop the new code that munges the wrapper.config. Instead, updates that
|
||||
need to change it will include their own wrapper.config in the
|
||||
i2pupdate.zip, overwriting the existing file. If the file
|
||||
"wrapper.config.updated" is included, it is deleted at first opportunity
|
||||
and the router shut down, displaying a notice that the router must be
|
||||
started again cleanly to allow the changes to the wrapper.config to take
|
||||
effect.
|
||||
* Properly stop accept()ing I2PSocket connections if we close down the
|
||||
session (duh).
|
||||
* Make sure we cancel any outstanding Packets in flight when a connection
|
||||
is terminated (thanks susi!)
|
||||
* Split up the I2PTunnel closing a little further.
|
||||
|
||||
2004-11-28 jrandom
|
||||
* Accept IP address detection changes with a 2-out-of-3 minimum.
|
||||
* As long as the router is up, keep retrying to bind the I2CP listener.
|
||||
* Decrease the java service wrapper ping frequency to once every 10
|
||||
minutes, rather than once every 5 seconds.
|
||||
|
||||
2004-11-27 jrandom
|
||||
* Some cleanup and bugfixes for the IP address detection code where we
|
||||
only consider connections that have actually sent and received messages
|
||||
recently as active, rather than the mere presence of a TCP socket as
|
||||
activity.
|
||||
|
||||
2004-11-27 jrandom
|
||||
* Removed the I2PTunnel inactivity timeout thread, since the new streaming
|
||||
lib can do that (without an additional per-connection thread).
|
||||
* Close the I2PTunnel forwarder threads more aggressively
|
||||
|
||||
2004-11-27 jrandom
|
||||
* Fix for a fast loop caused by a race in the new streaming library (thanks
|
||||
DrWoo, frontier, pwk_, and thetower!)
|
||||
* Minor updates to the SimpleTimer and Connection to help track down a
|
||||
high CPU usage problem (dumping debug info to stdout/wrapper.log if too
|
||||
many events/tasks fire in a second)
|
||||
* Minor fixes for races on client disconnects (causing NPEs)
|
||||
|
||||
* 2004-11-26 0.4.2 released
|
||||
|
||||
2004-11-26 jrandom
|
||||
* Enable the new streaming lib as the default. That means, for any
|
||||
substantial definition, it is NOT BACKWARDS COMPATIBLE.
|
||||
|
||||
2004-11-25 jrandom
|
||||
* Revised the installer to include start menu and desktop shortcuts for
|
||||
windows platforms, including pretty icons (thanks DrWoo!)
|
||||
* Allow clients specified in clients.config to have an explicit startup
|
||||
delay.
|
||||
* Update the default install to launch a browser pointing at the console
|
||||
whenever I2P starts up, rather than only the first time it starts up
|
||||
(configurable on /configservice.jsp, or in clients.config)
|
||||
* Bugfix to the clock skew checking code to monitor the delta between
|
||||
offsets, not the offset itself (duh)
|
||||
* Router console html update
|
||||
* New (and uuuuugly) code to verify that the wrapper.config contains
|
||||
the necessary classpath entries on update. If it has to update the
|
||||
wrapper.config, it will stop the JVM and service completely, since the
|
||||
java service wrapper doesn't reread the wrapper.config on JVM restart -
|
||||
requiring the user to manually restart the service after an update.
|
||||
* Increase the TCP connection timeout to 30s (which is obscenely long)
|
||||
|
||||
2004-11-22 jrandom
|
||||
* Update to the SAM bridge to reduce some unnecessary memory allocation.
|
||||
* New stat to keep track of slow jobs (ones that take more than a second
|
||||
to excute). This is published in the netDb as jobQueue.jobRunSlow
|
||||
|
||||
2004-11-21 jrandom
|
||||
* Update the I2PTunnel web interface to include an option for the new
|
||||
streaming lib (which is ignored until the 0.4.2 release).
|
||||
* Revised the I2PTunnel web interface to keep the I2CP options of client
|
||||
and httpclient tunnels in sync, as they all share the same I2CP session.
|
||||
|
||||
2004-11-21 jrandom
|
||||
* Only allow small clock skews after the first 10 minutes of operation
|
||||
(to prevent later network lag bouncing us way off course - yes, we
|
||||
really need an NTP impl to balance out the network burps...)
|
||||
* Revamp the I2PTunnel web interface startup process so that everything
|
||||
is shown immediately, so that different pieces hanging don't hang
|
||||
the rest, and other minor bugfixes.
|
||||
* Take note of SAM startup error (in case you're already running a SAM
|
||||
bridge...)
|
||||
* Increase the bandwidth limiter burst values available to 10-60s (or
|
||||
whatever is placed in /configadvanced.jsp, of course)
|
||||
|
||||
2004-11-21 jrandom
|
||||
* Allow end of line comments in the hosts.txt and other config files,
|
||||
using '#' to begin the comments (thanks susi!)
|
||||
* Add support to I2PTunnel's 'client' feature for picking between multiple
|
||||
target destinations (e.g. 'client 6668 irc.duck.i2p,irc.baffled.i2p')
|
||||
* Add a quick link on the left hand nav to reseed if there aren't enough
|
||||
known peers, as well as link to the config page if there are no active
|
||||
peers. Revised config page accordingly.
|
||||
|
||||
2004-11-21 jrandom
|
||||
* Destroy ElGamal/AES+SessionTag keys after 15 minutes of inactivity
|
||||
rather that every 15 minutes, and increase the warning period in which
|
||||
we refresh tags from 30s to 2 minutes.
|
||||
* Bugfix for a rare problem closing an I2PTunnel stream where we'd fail
|
||||
to close the I2PSocket (leaving it to timeout).
|
||||
|
||||
2004-11-19 jrandom
|
||||
* Off-by-one fix to the tunnel pool management code, along side some
|
||||
explicit initialization. This can affect clients whose lengths are
|
||||
shorter than the router's default (thanks duck!)
|
||||
|
||||
2004-11-17 jrandom
|
||||
* Fix to propogate i2psocket options into the SAM bridge correctly (thanks
|
||||
Ragnarok!)
|
||||
|
||||
2004-11-17 jrandom
|
||||
* Minor logging update.
|
||||
|
||||
2004-11-16 jrandom
|
||||
* Clean up the propogation of i2psocket options so that various streaming
|
||||
libs can honor them more precisely
|
||||
|
||||
2004-11-16 jrandom
|
||||
* Minor logging update
|
||||
|
||||
2004-11-14 jrandom
|
||||
* Fix a long standing leak in I2PTunnel (hanging on to i2psocket objects)
|
||||
* Fix a leak injected into the SimpleTimer
|
||||
* Fix a race condition in the tunnel message handling
|
||||
|
||||
2004-11-13 jrandom
|
||||
* Added throttles on how many I2PTunnel client connections we open at once
|
||||
* Replaced some buffered streams in I2PTunnel with unbuffered streams, as
|
||||
the streaming library used should take care of any buffering.
|
||||
* Added a cache for some objects used in I2PTunnel, especially useful when
|
||||
there are many short lived connections.
|
||||
* Trimmed the SimpleTimer's processing a bit
|
||||
|
||||
2004-11-10 jrandom
|
||||
* Allow loading the (mini)streaming connection options from the
|
||||
environment.
|
||||
* More defensive programming in the DSA implementation.
|
||||
|
||||
2004-11-08 jrandom
|
||||
* Remove spurious flush calls from I2PTunnel, and work with the
|
||||
I2PSocket's output stream directly (as the various implementations
|
||||
do their own buffering).
|
||||
* Another pass at a long standing JobQueue bug - dramatically simplify
|
||||
the job management synchronization since we dont need to deal with
|
||||
high contention (unlike last year when we had dozens of queue runners
|
||||
going at once).
|
||||
* Logging
|
||||
|
||||
2004-11-08 jrandom
|
||||
* Make the SAM bridge more resiliant to bad handshakes (thanks duck!)
|
||||
|
||||
* 2004-11-06 0.4.1.4 released
|
||||
|
||||
|
20
hosts.txt
20
hosts.txt
@@ -1,6 +1,14 @@
|
||||
; TC's hosts.txt guaranteed freshness
|
||||
; $Id: hosts.txt,v 1.74 2004/11/05 06:22:56 jrandom Exp $
|
||||
; $Id: hosts.txt,v 1.82 2004/11/28 17:47:01 jrandom Exp $
|
||||
; changelog:
|
||||
; (1.105) added bdl.i2p
|
||||
; (1.104) added bacardi.i2p and guttersnipe.i2p
|
||||
; (1.103) added evil.i2p
|
||||
; (1.102) added bsdm.i2p
|
||||
; (1.101) added eschaton.i2p
|
||||
; (1.100) added blog.curiosity.i2p
|
||||
; (1.99) added slacker.i2p
|
||||
; (1.98) added ses.i2p and pdforge.i2p
|
||||
; (1.97) added orion.i2p and protokol.i2p
|
||||
; (1.96) added blog.polecat.i2p
|
||||
; (1.95) added ciaran.i2p
|
||||
@@ -217,4 +225,14 @@ ciaran.i2p=3Ot-weeZY96NbdDSOwJD6t0plEwIMUMyZPSO71AtkNEhO8aIKqAMyAU14EDH~F27OZqe~
|
||||
blog.polecat.i2p=sdnTAgmDs1zgSWw8P64avIzhBrtuzl2gKxoPTKz7kAr2oAWxaLTAvRHRs0I~KCi8CAhPGwOVk5qGmlgeoeLobojlJI5u2mxL08fslrvhUxEFbAPQVpyQoSo0VITXLSeYsdlwXHEVPh5ONpR78HKVP0~bF4LV9by9982UdgZvp9IW64VRZI1fQ7qCZftwZBKyziSIyV8lp6P9SF5EBlRlvOC9JWtYE7gPGYnDqDt6WcSNGP5eWcWuWzaiQutD~LsmfDPSVWt4fCtj8aSDy2qqLeoo-dF52OgcPVi32AfjlY4LpTnUfFJoFIxi0qE37nOsRY4e9QA~rULJVMRKinab~C24hpKJlM4IZTe46PZ1iVs40aRYtX9BkD6F8M5mCuDQTTbb5g9ib34ZsH-lHgiqwFQhtrL5R~Cx32uTVvfVFxcP9GRau1w5k4jYfIzSPpLUQRwek1pY35PCGf4tH8J4ZO16Cahfz~u2ahuHNeRrin1cgdtd~GT1SEJikUsvw9X9AAAA
|
||||
orion.i2p=gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA
|
||||
protokol.i2p=l8e~NR26yXd3C6Hi8XDRrn3Q6~FztVMb1Sg1JzV6yortHBs7aSYt8UgokX1XbhzIM1LCq~k-KzXScmwh-VOjBu-hEdacFO4MgyJEiUJdMd2qwwxrwmiUDVns~CGmywJ9K2dVRK1o5GwgTY1vuS11~AMobXbd~-xCE1a~V2xSW9HHrGgaRVA0KlVF9U-LHCzSK-37vxDTeCSoP5CW-q5VOqGDoTcCbbo0JgABaSZjpzbTa9bfwZDjTy26Wx1rHoEpP5C1T6iRO4lkAuy6VUau~S1Tovb5w4T-SLtbx~~B5brDQJG6m9jFMp~OKffnZ51fsqVke-~snct33PJptg8UDnBkpij3mlJELEi54KhpN-EGHH56Y7hfAq-sTkIqUFwiFCCcHpgyBtekwmwgBXoDqUsvtV22~Wq7qK6jRcO2A0LrcKZ20y-2yQPyPvqXR1cKnFOt7j6LALiHWvM70urysWcIEohwuVirq9eY2xikRHwJEI0LsKLyDLFCJHtlImUiAAAA
|
||||
ses.i2p=OqfisCUxgHRMyk2CJaOxGsBMv2Y2NkqSLY3lwNIYIMClybDtKGWq-xsydwV~uQkWoQAI0SYMeUKcOSg94~h8pu6q3TwlLSCRFrQDmrtqhFLIa8q7hxDkP5DAdfe2~gbg6JRyek~IoYsi1LVMd1KZh2PfP4T-VJKSw2EXB2n--HPwKzrNcBBLJZl7aYzZBKD7eixLD2tx5tYq0ONCJcKeWPeO2ZhFKU9Xi53VA0jM9L~J-NfnlBeGlYw-9w4Q49jBqlRVx9GMpQiYrAsaKqyLmKjM9ExAwkeXDoAz3fj5MTLdKZhQ78Bvznu9QTYfUtM~Qg8QCSzTLizeZqF3STDs9RW~4Zsq5CH05KP-cpwFKqC2HZpIj0VtGsD-IReDyyJVD8NycUjaWRqef4Bt045ApRsUkEFFcsEO0ahpMqLdjPWLqW5aelS0m-iZ6tb33PrAGb8Tzc5fIfyt0lQjCjp3X0dETuGr7LqIdIL5G9geqFI4k4c~OzTNRjn~DaWkGTkHAAAA
|
||||
pdforge.i2p=2pFasuY6uXebywQncY3IGxMiJ8GEyaTKARnU3W3fB0IAgQ-4HxAVmLRZ2HyCtkiCJrLQlKC8TIrK0xls~IhaYxWcSwkVsRVvVSskin6sdS9TzyqwL7OKoxLJC~4lvR8c6HyHC9RmY7-jh2eDDWJYz7w1uSwfkAWVp9Hj74bzjjEta8uqVChdtqldEy-a8TJ9CXLAD~KnRSs40kR3o4ZKS9XekA7Kvm95jI6ALaim7WWeRqPCn0cmsjBgGtyvswol4MbpkRAu4nn8gMHpX7~cCOllvE-ciV6W2ZGIaLZ6CC2n7chbBaC1EvC6Du0dlHWy420BImLLu9LttwT8ch23JnAzo~BRC0vCaUdS1PUYOiP7K3U3RWyC0U3WZ3pz8Lz2nZIIGKkgG43OrRA4mxIJE-57ExnBjJjNx0MTVA-8tkk~RQA6M81ECl5j0tP0~oSWAcNUJwoDKjL9kafj5CYW5xWJg33YUe7ExUvLYVCYjabdsuMlmc59WGP2pBkFFnsrAAAA
|
||||
slacker.i2p=BR~D1lQNF~NLjdrJILDBA4DaQZpDoQZhVFNAwBgUz3sEP2tHnuiS7-EO2jwXEPKHIVZNGSNNDcO-7iapYcPJU~40AirUep9IX7IIAh3blGsHdasPha2OEls0lH3IAQvpUKUyhH85acP5DLUcR3q6X4VY2uHfgJHfxf5hKh~8P6EAgSaxt2XcolU4l5k9pKibxjivz0mMlZgcmrSmeFJkXy9Pko-0cBaSniqESTkzVhsCk0PcjO2A6yu9Zs1T~dWn-AvxG1Fd79xL71Xmq~c2AjJHQoJ44Kz~Kre0wdKF6~LMUgr-zs-Wdz8Ay8bU4pjOt6ygNKLLP3NjBBvpMVpyJx9XfNVFcCfb0ty7z4cFcHD3bT0A~J3SD6XLImYQaMTUEOprmD3YiuWXiyqlue6bwXrzzi9L0S0YaPs1vmZYU0w5ra35v-uDxqJA1e9wM7OWDNN3oPNt5F~XsYGKVR0dvXZXHc0vWHa0pwuo8Dh6MrfNjGpzTwCPH9S5x96lHpdSAAAA
|
||||
blog.curiosity.i2p=GqgGNWvNcbEABjAxBNJmILDIVJq0GSpf3eyjQt5EQsJFPCA3fYFYUULh4WoPFEk3I4pOV0lPGlkFX6Q52nkIk~kW~YGsCjxDJJ~08m31KgPJ70-svh7Fj-D8Bs27VaG9nXEltCJfjzfHsNY6m5jCCf400-14jTEghAjguIwPbpCpJ351jrE36sSU861LhhKnCiTItIN6ig9LROIUOJnTvh4-vRHrHS~oQVesAUqZ0zMPRNYlC1GHgTvou8iKLXlPnq3u1ITfCQYjeC6lpNn3bnnJOfrzGsPW3Y70jG5fqTPRqCyef5j0gNa~ZewMAQvV~oKxpDFTixNx8pBRv913pRXTdZfxT8D-fpYbXP1O0GtaHTgPO7PYWFKHCAaxmleD7tzSpIyfl4LpO0Y1Be5eHAZh5Lh1nE~tqVJZUbcVqZzDPmrmT20oCDljsZ8kEIRb3mn3VpzN5wOikPTh8AHg1jfic9hGBv5Lqu5SBffSCQZi7znTB7peX3ZavGODotR2AAAA
|
||||
eschaton.i2p=YTlIyW42pdmE~D8fc3eUSdyN9x~SsddHcAXi3hISTTovLO-CphmfAqyvAZ1GV2-xExVlK7u~WBT8w9NBeig48NsAXCkNuWvZS~hzw9AERj5HVe5Jwzvd78x6rsFLaYbAFeSngtH~Bxi~ZLfLtXbcD7aZsXh2bCSV8OwKPRmPiKsV48Bwi9BSJA84VQLtpnbg3MtSDAciLVVH2Prvu939CciLAkdaST6JWBDY553c~4RlaHkxR~uHsskO0sNnCp2ASGRR8zoaDTP9eKhy9vXcCJZ6EYKDmRGcb~0hpuB67lP47PJaiNCWNk5jI~VNdCUE2O9D-XBPA88qvDZVK0wZWTL3Hxh5uvqr-rKZ2Txlh-qZ1vispjv47VC88ht~mQi~PGbqZxvPIOe9LxemIjlBedDjcS675cyVm-stwx-8G8au-hE7QNhZ3XN8kRlNcCTtMJxX2QRXT6juCZVNXfnO~sCexp7inl7~Q~yCeBbOoWhQoOOsm7kHMNEGxOkpkxjxAAAA
|
||||
bdsm.i2p=qOcqbBo4vxHbldI758R76ZJZu92zWefovF6IEEv~XKA-UkHNbY-y6v9w-9ts-tbZxpak8l1QyB6Mp1Rwy-sE1YxevjIPhV-UQZSsJHRJHVZasR3ULncY-g-dvzGLab9YGtkdfub2GMlCJzDm9F8sUvGnLxlZ28mj3~ZLC0vEt5BeCl5hDuT9CiAzWarcSC2M1PIOMxIUYz9K5ueanOZjmAkNet4igKM0XmfXL8c85VeiPO8jx9G0ZDARwObXxvNdPq5pDwOZSY-5f-crDWMV1KesdmqsKIgydZ5GSPzq5jKLJdY3bUsxViNoWzjIWKfbwm6Wgl3eb9wzLEXDP1m2WGWAetNCgwSbT30A2yhTEWFwEqSE0GvWKvlztyG3oGm8eKN-VymRwduORnpKYbALNZ2MG4ZtVhKklER4hrgYSLuuXIi7KhQ0WXrKjPFnnL2MmnpPsFYn0ZQz6ilYOSAYcV3rBsd83RAFPWodlCjt0TpnpvcFfYZqfKZtYQryzkdFAAAA
|
||||
evil.i2p=NGNaN9qrpk2dC~OQlo-1yHT~Tvb6SvU8VH9SiBkwNZxuNVRKN~2ZtU50nkTklevB5V08GMT5tkpyZCyVwVgZNNl50uz25t~xP3Pte6GUxzBfBJ8n2GoFdlhn5jGHhmM6QI3nlDgLO7AaNdIQu8LxaoO4lln~fReKvY3sNRMAOJlBO0mHH1PSsemJmqkhl02VX6-3KllgZ99E7uVT-ap2i7Pf3wvLtBtoHryZXvYtKL9zV17tBfhmIVT~VSuhsGRVoy43eoep~fPNCE3s0jr4GLEeWfx2LF0WsvN2avii43DFB23BaZAKd7myLV~pmN9L7pOILnsrktO2MQXy2q2OriZs8dxN3w0yboE5iAfTt8IWtHfNLp8aGx2HzmF1TacrPxuJmg~C9mawTGtZ2aKMBr7vHtN1VuZnOdVi5hcC7tQ0YuwPtAkOB1iO2Wbh3csYtfusPoIqezP1O-iUndNCRg86u3PZ71jXhSq4mMZaNZGYiiqB7nXVkBi15nUbQRb8AAAA
|
||||
bacardi.i2p=oZm0JRHiUFKwAzLz1wlNOK2h2fI8V6u1nUhgCpt1RcErs99QMPHqu4oR4cel5lsJbeg1X0GgHe72JYabsntjimjWs1zi0RzknddVvq0hMGnn9EA-9Atu9qViScXp42ddnYhIlMBNNswMp8AJ01jHkO3SUSDSVk-rF5wnrhpyN9BFyho8h18nHrr6S4jaGHsuLVage8ImwQRv~PYfr4hVULdiFn5HDRePdvgzKUD3o4Y7nFiQMXswP02ZhXXtE-rMGDs2TD15qYah6mkQWdZtRYFuKuCh7Myn35u2IHGyMs56zpnuVr~w~Uh7wydg26JShsAz4AyZhGy70eTFUC2dtEr7bUvQSE3V3xNKOeBuGYadipWB30xmGDX2kfaU5Efy45MLPBwTU-8f4owQBokgSD3jBEc4vV0DbJrGoNnA3zpAC0JwZ9rFLolQNzenRgkWYlO09PEkKPUyUMpUBGY~Wj7jcsGnt5uNskeHHpV4hsvdpboHnlPAcZKvN7rTCenoAAAA
|
||||
guttersnipe.i2p=yS6ECPvCobRLKMXJg-rLZ5PF6dRcXliR2e9KEX~aUZMEsznpzE-lCs~FgGd7w9-~GW8ObvKiyt47S7SuCunjndl874xPhrQ0dFRUNEsdovLBr6s9v2xi7kpWC1yKLbT-Ti3p8WhNPZkGsJl6YEIc357j4FjNVFx2-b~V16G0lhjhY1a55nsCoU7xXisdqOaKZL-4Y7vEVHqZNgM6cAZpY8vHtui30J3oGmV-RcJG9ahYkorqleKtb2G0fwbUMakxHv5uzlHyAU~r7OmS7NCIfoyBgU3MrSiwkYiW5elQ294XV-1Bciy9DFnJh4KA0rLIA7GanvkQ-NwqoGLsXz08mpi12vVssDhdavXooBeZLg7gipFYzle3-B1rgGKZ~51~9Tou~XSn6jGJGgtqKV7fTmys4~AXNy7vCWrMKQaWh6M1HI69yV466bJWv6nVhwNmOmtk48EIC3ik2IXC1wjA6Rgz2AcSiR-UEyaBkv251Vu6H92HZJTrsTzuzVmZcYt0AAAA
|
||||
bdl.i2p=IMzcrD4jQLBtyzV8TdL-jgbyDhRjMBS1BfI6xGuddMyJEoMnZ1ZWDUn3NwqdyEYRQD7zREgbsQgzpE7Gdmpp-vLrzba3W1mUR5Ddu13tgIiDrOWR38Omv7L~DTDimK9m0Y-HCJROVigRfxdZbsI6P37d77NArdpwLzFos6qyK2C40JQFIaNCPYGkf05DsXtHLPwTJXYVKVp0R2V7nGmI77BUdwDgt4zSotlyvmsX6U1mYqKwutwr~oxggdvgfoNbrGC0~xQCbfQtEOwFYwxc5oUeJlt4jjb1-C9HAb7r4LtJ3Daqr0bx1hXS5hADw68cfUHbEjbfrhJhBA0mMqgGMm~r~0II8R19EXprc91YY4d0QycR2Osdn9MVNXS01Ziy~JX-SfT8DTjj2ZZn7rpyjcq7rgVbspPLJOdNQNERiAm06yjosOrPjtl8mrWXxWcOcGlMO5ftoTvX2hFsfc-vmfN7S8IZEfUdJRpJxDytChNTUr~qMWuP0LicNv0xKtA3AAAA
|
||||
|
||||
|
@@ -1 +1,85 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1" standalone="yes" ?>
|
||||
<?xml version="1.0" encoding="iso-8859-1" standalone="yes" ?>
|
||||
|
||||
<installation version="1.0">
|
||||
|
||||
<info>
|
||||
<appname>i2p</appname>
|
||||
<appversion>0.4.2.2</appversion>
|
||||
<authors>
|
||||
<author name="I2P" email="support@i2p.net"/>
|
||||
</authors>
|
||||
<url>http://www.i2p.net</url>
|
||||
</info>
|
||||
|
||||
<guiprefs width="590" height="356" resizable="yes">
|
||||
<laf name="liquid">
|
||||
<os family="unix"/>
|
||||
</laf>
|
||||
</guiprefs>
|
||||
|
||||
<locale>
|
||||
<langpack iso3="eng"/>
|
||||
</locale>
|
||||
|
||||
<native type="izpack" name="ShellLink.dll" />
|
||||
|
||||
<resources>
|
||||
<res id="Installer.image" src="installer/resources/i2plogo.png" />
|
||||
<res id="InfoPanel.info" src="installer/resources/readme.license.txt"/>
|
||||
<!-- <res id="ProcessPanel.Spec.xml" src="installer/resources/ProcessPanel.Spec.xml"/> -->
|
||||
<res id="shortcutSpec.xml" src="installer/resources/shortcutSpec.xml" />
|
||||
</resources>
|
||||
|
||||
<panels>
|
||||
<panel classname="HelloPanel"/>
|
||||
<panel classname="InfoPanel"/>
|
||||
<panel classname="TargetPanel"/>
|
||||
<panel classname="InstallPanel"/>
|
||||
<panel classname="ShortcutPanel"><os family="windows" /></panel>
|
||||
<!-- <panel classname="ProcessPanel"><os family="windows" /></panel> -->
|
||||
<panel classname="SimpleFinishPanel"/>
|
||||
</panels>
|
||||
|
||||
<packs>
|
||||
<pack name="Base" required="yes">
|
||||
<description>Base installation files</description>
|
||||
<fileset dir="pkg-temp" includes="**/*" targetdir="$INSTALL_PATH"/>
|
||||
|
||||
<!-- postinstall stuff for windows -->
|
||||
<executable targetfile="$INSTALL_PATH/lib/copy.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH\lib\wrapper\win32\I2Psvc.exe" /><arg value="$INSTALL_PATH" /></args></executable>
|
||||
<executable targetfile="$INSTALL_PATH/lib/copy.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH\lib\wrapper\win32\wrapper.dll" /><arg value="$INSTALL_PATH\lib" /></args></executable>
|
||||
<executable targetfile="$INSTALL_PATH/lib/copy.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH\lib\wrapper\win32\wrapper.jar" /><arg value="$INSTALL_PATH\lib" /></args></executable>
|
||||
<executable targetfile="$INSTALL_PATH/lib/delete.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH\i2prouter" /></args></executable>
|
||||
<executable targetfile="$INSTALL_PATH/lib/delete.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH\install_i2p_service_unix" /></args></executable>
|
||||
<executable targetfile="$INSTALL_PATH/lib/delete.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH\install-headless.txt" /></args></executable>
|
||||
<executable targetfile="$INSTALL_PATH/lib/delete.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH\osid" /></args></executable>
|
||||
<executable targetfile="$INSTALL_PATH/lib/delete.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH\postinstall.sh" /></args></executable>
|
||||
<executable targetfile="$INSTALL_PATH/lib/delete.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH\postinstall.bat" /></args></executable>
|
||||
<executable targetfile="$INSTALL_PATH/lib/delete.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH\uninstall_i2p_service_unix" /></args></executable>
|
||||
<executable targetfile="$INSTALL_PATH/lib/delete.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH\lib\wrapper" /></args></executable>
|
||||
<!--
|
||||
<executable targetfile="$INSTALL_PATH/lib/exec.jar" type="jar" stage="postinstall" keep="true" failure="warn"> <os family="windows" />
|
||||
<args><arg value="$INSTALL_PATH" /><arg value="$INSTALL_PATH\I2Psvc.exe" /><arg value="-c" /><arg value="$INSTALL_PATH\wrapper.config" /></args></executable>
|
||||
-->
|
||||
|
||||
<!-- postinstall stuff for *nix -->
|
||||
<!-- stage=never means chmod a+x -->
|
||||
<executable targetfile="$INSTALL_PATH/postinstall.sh" type="bin" stage="never" keep="true" failure="warn"><os family="unix" /></executable>
|
||||
<executable targetfile="$INSTALL_PATH/postinstall.sh" type="bin" stage="postinstall" keep="true" failure="warn"><os family="unix" />
|
||||
<args><arg value="$INSTALL_PATH" /></args></executable>
|
||||
|
||||
</pack>
|
||||
</packs>
|
||||
|
||||
</installation>
|
||||
|
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1" standalone="yes" ?>
|
||||
|
||||
<processing>
|
||||
<job name="Launching I2P...">
|
||||
<os family="windows" />
|
||||
<executefile name="$INSTALL_PATH\postinstall.bat">
|
||||
<arg>$INSTALL_PATH</arg>
|
||||
</executefile>
|
||||
</job>
|
||||
<job name="Launching I2P...">
|
||||
<os family="unix" />
|
||||
<executefile name="/bin/sh">
|
||||
<arg>$INSTALL_PATH/postinstall.sh</arg><arg>$INSTALL_PATH</arg>
|
||||
</executefile>
|
||||
</job>
|
||||
</processing>
|
@@ -17,4 +17,11 @@ clientApp.2.args=i2ptunnel.config
|
||||
# run our own eepsite with a seperate jetty instance
|
||||
clientApp.3.main=org.mortbay.jetty.Server
|
||||
clientApp.3.name=eepsite
|
||||
clientApp.3.args=eepsite/jetty.xml
|
||||
clientApp.3.args=eepsite/jetty.xml
|
||||
clientApp.3.delay=30
|
||||
|
||||
# load a browser pointing at the web console whenever we start up
|
||||
clientApp.4.main=net.i2p.apps.systray.UrlLauncher
|
||||
clientApp.4.name=consoleBrowser
|
||||
clientApp.4.args=http://localhost:7657/
|
||||
clientApp.4.delay=5
|
||||
|
BIN
installer/resources/console.ico
Normal file
BIN
installer/resources/console.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
BIN
installer/resources/favicon.ico
Normal file
BIN
installer/resources/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
start /m I2Psvc.exe -c wrapper.config
|
||||
exit
|
@@ -9,6 +9,7 @@ tunnel.0.i2cpHost=localhost
|
||||
tunnel.0.i2cpPort=7654
|
||||
tunnel.0.option.tunnels.depthInbound=2
|
||||
tunnel.0.option.tunnels.numInbound=2
|
||||
tunnel.0.option.i2p.streaming.connectDelay=1000
|
||||
tunnel.0.startOnLoad=true
|
||||
|
||||
# irc
|
||||
@@ -17,11 +18,12 @@ tunnel.1.description=IRC proxy to access the anonymous irc net
|
||||
tunnel.1.type=client
|
||||
tunnel.1.interface=127.0.0.1
|
||||
tunnel.1.listenPort=6668
|
||||
tunnel.1.targetDestination=irc.duck.i2p
|
||||
tunnel.1.targetDestination=irc.duck.i2p,irc.baffled.i2p
|
||||
tunnel.1.i2cpHost=localhost
|
||||
tunnel.1.i2cpPort=7654
|
||||
tunnel.1.option.tunnels.depthInbound=2
|
||||
tunnel.1.option.tunnels.numInbound=2
|
||||
tunnel.1.option.i2p.streaming.connectDelay=1000
|
||||
tunnel.1.startOnLoad=true
|
||||
|
||||
# I2P's cvs server
|
||||
@@ -59,6 +61,7 @@ tunnel.4.listenPort=7659
|
||||
tunnel.4.name=smtp.postman.i2p
|
||||
tunnel.4.option.tunnels.depthInbound=2
|
||||
tunnel.4.option.tunnels.numInbound=2
|
||||
tunnel.4.option.i2p.streaming.connectDelay=1000
|
||||
tunnel.4.startOnLoad=false
|
||||
tunnel.4.targetDestination=smtp.postman.i2p
|
||||
tunnel.4.type=client
|
||||
@@ -72,19 +75,7 @@ tunnel.5.interface=127.0.0.1
|
||||
tunnel.5.listenPort=7660
|
||||
tunnel.5.option.tunnels.depthInbound=2
|
||||
tunnel.5.option.tunnels.numInbound=2
|
||||
tunnel.5.option.i2p.streaming.connectDelay=1000
|
||||
tunnel.5.startOnLoad=false
|
||||
tunnel.5.targetDestination=pop.postman.i2p
|
||||
tunnel.5.type=client
|
||||
|
||||
# another irc server, linked with irc.duck.i2p and IIP
|
||||
tunnel.6.description=irc.baffled.i2p
|
||||
tunnel.6.i2cpHost=localhost
|
||||
tunnel.6.i2cpPort=7654
|
||||
tunnel.6.interface=localhost
|
||||
tunnel.6.listenPort=7661
|
||||
tunnel.6.name=irc.baffled.i2p
|
||||
tunnel.6.option.tunnels.depthInbound=2
|
||||
tunnel.6.option.tunnels.numInbound=2
|
||||
tunnel.6.startOnLoad=false
|
||||
tunnel.6.targetDestination=irc.baffled.i2p
|
||||
tunnel.6.type=client
|
||||
tunnel.5.type=client
|
33
installer/resources/shortcutSpec.xml
Normal file
33
installer/resources/shortcutSpec.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shortcuts>
|
||||
<programGroup defaultName="I2P" location="startMenu" />
|
||||
<shortcut name="Start I2P"
|
||||
target="$INSTALL_PATH\I2Psvc.exe"
|
||||
commandLine="-c wrapper.config"
|
||||
workingDirectory="$INSTALL_PATH"
|
||||
iconFile="$INSTALL_PATH\docs\start.ico"
|
||||
initialState="noShow"
|
||||
programGroup="yes"
|
||||
startMenu="no"
|
||||
desktop="yes"
|
||||
startup="no" />
|
||||
<shortcut name="I2P router console"
|
||||
target="$INSTALL_PATH\docs\startconsole.html"
|
||||
commandLine=""
|
||||
workingDirectory="$INSTALL_PATH"
|
||||
iconFile="$INSTALL_PATH\docs\console.ico"
|
||||
initialState="noShow"
|
||||
programGroup="yes"
|
||||
startMenu="no"
|
||||
desktop="yes"
|
||||
startup="no" />
|
||||
<shortcut name="Uninstall I2P"
|
||||
target="$INSTALL_PATH\uninstaller\uninstaller.jar"
|
||||
commandLine=""
|
||||
workingDirectory="$INSTALL_PATH"
|
||||
iconFile="$INSTALL_PATH\docs\uninstall.ico"
|
||||
initialState="noShow"
|
||||
startMenu="no"
|
||||
programGroup="yes"
|
||||
startup="no" />
|
||||
</shortcuts>
|
BIN
installer/resources/start.ico
Normal file
BIN
installer/resources/start.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
6
installer/resources/startconsole.html
Normal file
6
installer/resources/startconsole.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<html><body>
|
||||
<meta http-equiv="Refresh" CONTENT="0;URL=http://localhost:7657/index.jsp" />
|
||||
Continue to your <a href="http://localhost:7657/index.jsp">I2P Router console</a>.
|
||||
If that page does not load, please make sure I2P is running, or review the file
|
||||
wrapper.log for critical errors.
|
||||
</body></html>
|
BIN
installer/resources/uninstall.ico
Normal file
BIN
installer/resources/uninstall.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
@@ -32,6 +32,7 @@ wrapper.java.classpath.17=lib/xml-apis.jar
|
||||
wrapper.java.classpath.18=lib/jbigi.jar
|
||||
wrapper.java.classpath.19=lib/systray.jar
|
||||
wrapper.java.classpath.20=lib/systray4j.jar
|
||||
wrapper.java.classpath.21=lib/streaming.jar
|
||||
|
||||
# Java Library Path (location of Wrapper.DLL or libwrapper.so)
|
||||
wrapper.java.library.path.1=.
|
||||
@@ -102,6 +103,9 @@ wrapper.jvm_exit.timeout=10
|
||||
# give the OS 60s to clear all the old sockets / etc before restarting
|
||||
wrapper.restart.delay=60
|
||||
|
||||
wrapper.ping.interval=600
|
||||
wrapper.ping.timeout=605
|
||||
|
||||
# use the wrapper's internal timer thread. otherwise this would
|
||||
# force a restart of the router during daylight savings time as well
|
||||
# as any time that the OS clock changes
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user