propagate from branch 'i2p.i2p.zzz.test4' (head 592b7d2b980e8cba19167fa064f25251296ed8bb)

to branch 'i2p.i2p' (head 0ba672eaca7076092389d2277dba231fdd34423b)
This commit is contained in:
zzz
2011-01-31 13:42:36 +00:00
164 changed files with 2480 additions and 1303 deletions

View File

@@ -65,7 +65,7 @@ import net.i2p.util.I2PProperties.I2PPropertyCallback;
*/
public class I2PAppContext {
/** the context that components without explicit root are bound */
protected static I2PAppContext _globalAppContext;
protected static volatile I2PAppContext _globalAppContext;
protected I2PProperties _overrideProps;
@@ -119,7 +119,8 @@ public class I2PAppContext {
*
*/
public static I2PAppContext getGlobalContext() {
// skip the global lock
// skip the global lock - _gAC must be volatile
// http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
I2PAppContext rv = _globalAppContext;
if (rv != null)
return rv;
@@ -476,6 +477,9 @@ public class I2PAppContext {
* provided during the context construction, as well as the ones included in
* System.getProperties.
*
* WARNING - not overridden in RouterContext, doesn't contain router config settings,
* use getProperties() instead.
*
* @return set of Strings containing the names of defined system properties
*/
public Set getPropertyNames() {
@@ -485,6 +489,21 @@ public class I2PAppContext {
return names;
}
/**
* Access the configuration attributes of this context, listing the properties
* provided during the context construction, as well as the ones included in
* System.getProperties.
*
* @return new Properties with system and context properties
* @since 0.8.4
*/
public Properties getProperties() {
Properties rv = new Properties();
rv.putAll(System.getProperties());
rv.putAll(_overrideProps);
return rv;
}
/**
* Add a callback, which will fire upon changes in the property
* given in the specific callback.
@@ -767,7 +786,7 @@ public class I2PAppContext {
* enable simulators to play with clock skew among different instances.
*
*/
public Clock clock() { // overridden in RouterContext
public Clock clock() {
if (!_clockInitialized)
initializeClock();
return _clock;

View File

@@ -12,6 +12,8 @@ package net.i2p.client;
import java.util.Date;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
@@ -41,22 +43,51 @@ import net.i2p.util.Log;
* @author jrandom
*/
class I2CPMessageProducer {
private final static Log _log = new Log(I2CPMessageProducer.class);
private final Log _log;
private final I2PAppContext _context;
private int _sendBps;
private long _sendPeriodBytes;
private long _sendPeriodBeginTime;
private int _maxBytesPerSecond;
private volatile int _sendPeriodBytes;
private volatile long _sendPeriodBeginTime;
private final ReentrantLock _lock;
private static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
/** see ConnectionOptions in streaming - MTU + streaming overhead + gzip overhead */
private static final int TYP_SIZE = 1730 + 28 + 23;
private static final int MIN_RATE = 2 * TYP_SIZE;
public I2CPMessageProducer(I2PAppContext context) {
_context = context;
context.statManager().createRateStat("client.sendBpsRaw", "How fast we pump out I2CP data messages", "ClientMessages", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
_log = context.logManager().getLog(I2CPMessageProducer.class);
_lock = new ReentrantLock(true);
context.statManager().createRateStat("client.sendThrottled", "Times waited for bandwidth", "ClientMessages", new long[] { 60*1000 });
context.statManager().createRateStat("client.sendDropped", "Length of msg dropped waiting for bandwidth", "ClientMessages", new long[] { 60*1000 });
}
/**
* Update the bandwidth setting
* @since 0.8.4
*/
public void updateBandwidth(I2PSessionImpl session) {
String max = session.getOptions().getProperty(PROP_MAX_BW);
if (max != null) {
try {
int iMax = Integer.parseInt(max);
if (iMax > 0)
// round up to next higher TYP_SIZE for efficiency, then add some fudge for small messages
_maxBytesPerSecond = 256 + Math.max(MIN_RATE, TYP_SIZE * ((iMax + TYP_SIZE - 1) / TYP_SIZE));
else
_maxBytesPerSecond = 0;
} catch (NumberFormatException nfe) {}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Setting " + _maxBytesPerSecond + " BPS max");
}
/**
* Send all the messages that a client needs to send to a router to establish
* a new session.
*/
public void connect(I2PSessionImpl session) throws I2PSessionException {
updateBandwidth(session);
CreateSessionMessage msg = new CreateSessionMessage();
SessionConfig cfg = new SessionConfig(session.getMyDestination());
cfg.setOptions(session.getOptions());
@@ -99,32 +130,135 @@ class I2CPMessageProducer {
*/
public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag,
SessionKey key, Set tags, SessionKey newKey, long expires) throws I2PSessionException {
sendMessage(session, dest, nonce, payload, expires, 0);
}
/**
* Package up and send the payload to the router for delivery
* @since 0.8.4
*/
public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload,
long expires, int flags) throws I2PSessionException {
if (!updateBps(payload.length, expires))
// drop the message... send fail notification?
return;
SendMessageMessage msg;
if (expires > 0) {
msg = new SendMessageExpiresMessage();
((SendMessageExpiresMessage)msg).setExpiration(new Date(expires));
if (expires > 0 || flags > 0) {
SendMessageExpiresMessage smsg = new SendMessageExpiresMessage();
smsg.setExpiration(expires);
smsg.setFlags(flags);
msg = smsg;
} else
msg = new SendMessageMessage();
msg.setDestination(dest);
msg.setSessionId(session.getSessionId());
msg.setNonce(nonce);
Payload data = createPayload(dest, payload, tag, key, tags, newKey);
Payload data = createPayload(dest, payload, null, null, null, null);
msg.setPayload(data);
session.sendMessage(msg);
updateBps(payload.length);
}
private void updateBps(int len) {
long now = _context.clock().now();
float period = ((float)now-_sendPeriodBeginTime)/1000f;
if (period >= 1f) {
// first term decays on slow transmission
_sendBps = (int)(((float)0.9f * (float)_sendBps) + ((float)0.1f*((float)_sendPeriodBytes)/period));
_sendPeriodBytes = len;
_sendPeriodBeginTime = now;
_context.statManager().addRateData("client.sendBpsRaw", _sendBps, 0);
} else {
_sendPeriodBytes += len;
/**
* Super-simple bandwidth throttler.
* We only calculate on a one-second basis, so large messages
* (compared to the one-second limit) may exceed the limits.
* Tuned for streaming, may not work well for large datagrams.
*
* This does poorly with low rate limits since it doesn't credit
* bandwidth across two periods. So the limit is rounded up,
* and the min limit is set to 2x the typ size, above.
*
* Blocking so this could be very bad for retransmissions,
* as it could clog StreamingTimer.
* Waits are somewhat "fair" using ReentrantLock.
* While out-of-order transmission is acceptable, fairness
* reduces the chance of starvation. ReentrantLock does not
* guarantee in-order execution due to thread priority issues,
* so out-of-order may still occur. But shouldn't happen within
* the same thread anyway... Also note that small messages may
* go ahead of large ones that are waiting for the next window.
* Also, threads waiting a second time go to the back of the line.
*
* Since this is at the I2CP layer, it includes streaming overhead,
* streaming acks and retransmissions,
* gzip overhead (or "underhead" for compression),
* repliable datagram overhead, etc.
* However, it does not, of course, include the substantial overhead
* imposed by the router for the leaseset, tags, encryption,
* and fixed-size tunnel messages.
*
* @param expires if > 0, an expiration date
* @return true if we should send the message, false to drop it
*/
private boolean updateBps(int len, long expires) {
if (_maxBytesPerSecond <= 0)
return true;
//synchronized(this) {
_lock.lock();
try {
int waitCount = 0;
while (true) {
long now = _context.clock().now();
if (waitCount > 0 && expires > 0 && expires < now) {
// just say no to bufferbloat... drop the message right here
_context.statManager().addRateData("client.sendDropped", len, 0);
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping " + len + " byte msg expired in queue");
return false;
}
long period = now - _sendPeriodBeginTime;
if (period >= 2000) {
// start new period, always let it through no matter how big
_sendPeriodBytes = len;
_sendPeriodBeginTime = now;
if (_log.shouldLog(Log.DEBUG))
_log.debug("New period after idle, " + len + " bytes");
return true;
}
if (period >= 1000) {
// start new period
// Allow burst within 2 sec, only advance window by 1 sec, and
// every other second give credit for unused bytes in previous period
if (_sendPeriodBytes > 0 && ((_sendPeriodBeginTime / 1000) & 0x01) == 0)
_sendPeriodBytes += len - _maxBytesPerSecond;
else
_sendPeriodBytes = len;
_sendPeriodBeginTime += 1000;
if (_log.shouldLog(Log.DEBUG))
_log.debug("New period, " + len + " bytes");
return true;
}
if (_sendPeriodBytes + len <= _maxBytesPerSecond) {
// still bytes available in this period
_sendPeriodBytes += len;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending " + len + ", Elapsed " + period + "ms, total " + _sendPeriodBytes + " bytes");
return true;
}
if (waitCount >= 2) {
// just say no to bufferbloat... drop the message right here
_context.statManager().addRateData("client.sendDropped", len, 0);
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping " + len + " byte msg after waiting " + waitCount + " times");
return false;
}
// wait until next period
_context.statManager().addRateData("client.sendThrottled", ++waitCount, 0);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Throttled " + len + " bytes, wait #" + waitCount + ' ' + (1000 - period) + "ms" /*, new Exception()*/);
try {
//this.wait(1000 - period);
_lock.newCondition().await(1000 - period, TimeUnit.MILLISECONDS);
} catch (InterruptedException ie) {}
}
} finally {
_lock.unlock();
}
}

View File

@@ -9,6 +9,7 @@ package net.i2p.client;
*
*/
import java.util.Properties;
import java.util.Set;
import net.i2p.data.Destination;
@@ -20,17 +21,20 @@ import net.i2p.data.SigningPrivateKey;
/**
* <p>Define the standard means of sending and receiving messages on the
* I2P network by using the I2CP (the client protocol). This is done over a
* bidirectional TCP socket and never sends any private keys - all end to end
* encryption is done transparently within the client's I2PSession
* itself. Periodically the router will ask the client to authorize a new set of
* bidirectional TCP socket and never sends any private keys.
*
* End to end encryption in I2PSession was disabled in release 0.6.
*
* Periodically the router will ask the client to authorize a new set of
* tunnels to be allocated to the client, which the client can accept by sending a
* {@link net.i2p.data.LeaseSet} signed by the {@link net.i2p.data.Destination}.
* In addition, the router may on occation provide the client with an updated
* In addition, the router may on occasion provide the client with an updated
* clock offset so that the client can stay in sync with the network (even if
* the host computer's clock is off).</p>
*
*/
public interface I2PSession {
/** Send a new message to the given destination, containing the specified
* payload, returning true if the router feels confident that the message
* was delivered.
@@ -39,11 +43,18 @@ public interface I2PSession {
* @return whether it was accepted by the router for delivery or not
*/
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size) throws I2PSessionException;
/** See I2PSessionMuxedImpl for details */
/**
* See I2PSessionMuxedImpl for proto/port details.
* @since 0.7.1
*/
public boolean sendMessage(Destination dest, byte[] payload, int proto, int fromport, int toport) throws I2PSessionException;
/**
* End-to-End Crypto is disabled, tags and keys are ignored!
*
* Like sendMessage above, except the key used and the tags sent are exposed to the
* application. <p />
*
@@ -61,25 +72,62 @@ public interface I2PSession {
*
* @param dest location to send the message
* @param payload body of the message to be sent (unencrypted)
* @param keyUsed session key delivered to the destination for association with the tags sent. This is essentially
* @param keyUsed UNUSED, IGNORED. Session key delivered to the destination for association with the tags sent. This is essentially
* an output parameter - keyUsed.getData() is ignored during this call, but after the call completes,
* it will be filled with the bytes of the session key delivered. Typically the key delivered is the
* same one as the key encrypted with, but not always. If this is null then the key data will not be
* exposed.
* @param tagsSent set of tags delivered to the peer and associated with the keyUsed. This is also an output parameter -
* @param tagsSent UNUSED, IGNORED. Set of tags delivered to the peer and associated with the keyUsed. This is also an output parameter -
* the contents of the set is ignored during the call, but afterwards it contains a set of SessionTag
* objects that were sent along side the given keyUsed.
*/
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
/**
* End-to-End Crypto is disabled, tags and keys are ignored.
* @param keyUsed UNUSED, IGNORED.
* @param tagsSent UNUSED, IGNORED.
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
/**
* End-to-End Crypto is disabled, tags and keys are ignored.
* @param keyUsed UNUSED, IGNORED.
* @param tagsSent UNUSED, IGNORED.
* @since 0.7.1
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire) throws I2PSessionException;
/** See I2PSessionMuxedImpl for details */
/**
* See I2PSessionMuxedImpl for proto/port details.
* End-to-End Crypto is disabled, tags and keys are ignored.
* @param keyUsed UNUSED, IGNORED.
* @param tagsSent UNUSED, IGNORED.
* @since 0.7.1
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent,
int proto, int fromport, int toport) throws I2PSessionException;
/** See I2PSessionMuxedImpl for details */
/**
* See I2PSessionMuxedImpl for proto/port details.
* End-to-End Crypto is disabled, tags and keys are ignored.
* @param keyUsed UNUSED, IGNORED.
* @param tagsSent UNUSED, IGNORED.
* @since 0.7.1
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire,
int proto, int fromport, int toport) throws I2PSessionException;
/**
* See I2PSessionMuxedImpl for proto/port details.
* End-to-End Crypto is disabled, tags and keys are ignored.
* @param keyUsed UNUSED, IGNORED.
* @param tagsSent UNUSED, IGNORED.
* @since 0.8.4
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire,
int proto, int fromport, int toport, int flags) throws I2PSessionException;
/** Receive a message that the router has notified the client about, returning
* the payload.
* @param msgId message to fetch
@@ -151,8 +199,16 @@ public interface I2PSession {
*/
public Destination lookupDest(Hash h, long maxWait) throws I2PSessionException;
/**
* Does not remove properties previously present but missing from this options parameter.
* @param options non-null
* @since 0.8.4
*/
public void updateOptions(Properties options);
/**
* Get the current bandwidth limits. Blocking.
* @since 0.8.3
*/
public int[] bandwidthLimits() throws I2PSessionException;

View File

@@ -221,20 +221,32 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
}
/** save some memory, don't pass along the pointless properties */
private Properties filter(Properties options) {
Properties rv = new Properties();
for (Iterator iter = options.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
String val = options.getProperty(key);
if (key.startsWith("java") ||
key.startsWith("user") ||
key.startsWith("os") ||
key.startsWith("sun") ||
key.startsWith("file") ||
key.startsWith("line") ||
key.startsWith("wrapper")) {
if (key.startsWith("java.") ||
key.startsWith("user.") ||
key.startsWith("os.") ||
key.startsWith("sun.") ||
key.startsWith("file.") ||
key.equals("line.separator") ||
key.equals("path.separator") ||
key.equals("prng.buffers") ||
key.equals("router.trustedUpdateKeys") ||
key.startsWith("router.update") ||
key.startsWith("routerconsole.") ||
key.startsWith("time.") ||
key.startsWith("stat.") ||
key.startsWith("gnu.") || // gnu JVM
key.startsWith("net.i2p.router.web.") || // console nonces
key.startsWith("wrapper.")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping property: " + key);
} else if ((key.length() > 255) || (val.length() > 255)) {
continue;
}
String val = options.getProperty(key);
if ((key.length() > 255) || (val.length() > 255)) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "Not passing on property ["
+ key
@@ -247,6 +259,18 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
return rv;
}
/**
* Update the tunnel and bandwidth settings
* @since 0.8.4
*/
public void updateOptions(Properties options) {
_options.putAll(filter(options));
_producer.updateBandwidth(this);
try {
_producer.updateTunnels(this, 0);
} catch (I2PSessionException ise) {}
}
void setLeaseSet(LeaseSet ls) {
_leaseSet = ls;
if (ls != null) {
@@ -397,7 +421,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
*
*/
public byte[] receiveMessage(int msgId) throws I2PSessionException {
MessagePayloadMessage msg = _availableMessages.remove(new Long(msgId));
MessagePayloadMessage msg = _availableMessages.remove(Long.valueOf(msgId));
if (msg == null) {
_log.error("Receive message " + msgId + " had no matches");
return null;
@@ -414,21 +438,6 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_producer.reportAbuse(this, msgId, severity);
}
/**
* Send the data to the destination.
* TODO: this currently always returns true, regardless of whether the message was
* delivered successfully. make this wait for at least ACCEPTED
*
*/
public abstract boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException;
/**
* @param keyUsed unused - no end-to-end crypto
* @param tagsSent unused - no end-to-end crypto
*/
public abstract boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed,
Set tagsSent) throws I2PSessionException;
public abstract void receiveStatus(int msgId, long nonce, int status);
/****** no end-to-end crypto
@@ -444,7 +453,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* Recieve a payload message and let the app know its available
*/
public void addNewMessage(MessagePayloadMessage msg) {
Long mid = new Long(msg.getMessageId());
Long mid = Long.valueOf(msg.getMessageId());
_availableMessages.put(mid, msg);
long id = msg.getMessageId();
byte data[] = msg.getPayload().getUnencryptedData();
@@ -494,7 +503,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
public void available(long msgId, int size) {
synchronized (AvailabilityNotifier.this) {
_pendingIds.add(new Long(msgId));
_pendingIds.add(Long.valueOf(msgId));
_pendingSizes.add(Integer.valueOf(size));
AvailabilityNotifier.this.notifyAll();
}

View File

@@ -130,6 +130,10 @@ class I2PSessionImpl2 extends I2PSessionImpl {
int proto, int fromport, int toport) throws I2PSessionException {
throw new IllegalArgumentException("Use MuxedImpl");
}
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire,
int proto, int fromport, int toport, int flags) throws I2PSessionException {
throw new IllegalArgumentException("Use MuxedImpl");
}
@Override
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException {
@@ -222,14 +226,23 @@ class I2PSessionImpl2 extends I2PSessionImpl {
private static final int NUM_TAGS = 50;
/**
* TODO - Don't need to save MessageState since actuallyWait is false...
* But for now just use sendNoEffort() instead.
*
* @param keyUsed unused - no end-to-end crypto
* @param tagsSent unused - no end-to-end crypto
*/
protected boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent, long expires)
throws I2PSessionException {
return sendBestEffort(dest, payload, expires, 0);
}
/**
* TODO - Don't need to save MessageState since actuallyWait is false...
* But for now just use sendNoEffort() instead.
*
* @param flags to be passed to the router
* @since 0.8.4
*/
protected boolean sendBestEffort(Destination dest, byte payload[], long expires, int flags)
throws I2PSessionException {
//SessionKey key = null;
//SessionKey newKey = null;
//SessionTag tag = null;
@@ -324,7 +337,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
+ " sync took " + (inSendingSync-beforeSendingSync)
+ " add took " + (afterSendingSync-inSendingSync));
//_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey, expires);
_producer.sendMessage(this, dest, nonce, payload, null, null, null, null, expires);
_producer.sendMessage(this, dest, nonce, payload, expires, flags);
// since this is 'best effort', all we're waiting for is a status update
// saying that the router received it - in theory, that should come back

View File

@@ -162,12 +162,34 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
* 255 disallowed
* @param fromPort 1-65535 or 0 for unset
* @param toPort 1-65535 or 0 for unset
* @since 0.7.1
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size,
SessionKey keyUsed, Set tagsSent, long expires,
int proto, int fromPort, int toPort)
throws I2PSessionException {
return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, proto, fromPort, toPort, 0);
}
/**
* @param keyUsed unused - no end-to-end crypto
* @param tagsSent unused - no end-to-end crypto
* @param proto 1-254 or 0 for unset; recommended:
* I2PSession.PROTO_UNSPECIFIED
* I2PSession.PROTO_STREAMING
* I2PSession.PROTO_DATAGRAM
* 255 disallowed
* @param fromPort 1-65535 or 0 for unset
* @param toPort 1-65535 or 0 for unset
* @param flags to be passed to the router
* @since 0.8.4
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size,
SessionKey keyUsed, Set tagsSent, long expires,
int proto, int fromPort, int toPort, int flags)
throws I2PSessionException {
if (isClosed()) throw new I2PSessionException("Already closed");
updateActivity();
@@ -183,7 +205,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
_context.statManager().addRateData("i2cp.tx.msgCompressed", payload.length, 0);
_context.statManager().addRateData("i2cp.tx.msgExpanded", size, 0);
return sendBestEffort(dest, payload, keyUsed, tagsSent, expires);
return sendBestEffort(dest, payload, expires, flags);
}
/**
@@ -191,7 +213,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
*/
@Override
public void addNewMessage(MessagePayloadMessage msg) {
Long mid = new Long(msg.getMessageId());
Long mid = Long.valueOf(msg.getMessageId());
_availableMessages.put(mid, msg);
long id = msg.getMessageId();
byte data[] = msg.getPayload().getUnencryptedData();

View File

@@ -98,10 +98,17 @@ class I2PSimpleSession extends I2PSessionImpl2 {
}
}
/**
* Ignore, does nothing
* @since 0.8.4
*/
@Override
public void updateOptions(Properties options) {}
/**
* Only map message handlers that we will use
*/
class SimpleMessageHandlerMap extends I2PClientMessageHandlerMap {
private static class SimpleMessageHandlerMap extends I2PClientMessageHandlerMap {
public SimpleMessageHandlerMap(I2PAppContext context) {
int highest = Math.max(DestReplyMessage.MESSAGE_TYPE, BandwidthLimitsMessage.MESSAGE_TYPE);
_handlers = new I2CPMessageHandler[highest+1];

View File

@@ -149,7 +149,7 @@ public class CryptixAESEngine extends AESEngine {
@Override
public final void decryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte rv[], int outIndex) {
if ( (payload == null) || (rv == null) )
throw new IllegalArgumentException("null block args [payload=" + payload + " rv="+rv);
throw new IllegalArgumentException("null block args");
if (payload.length - inIndex > rv.length - outIndex)
throw new IllegalArgumentException("bad block args [payload.len=" + payload.length
+ " inIndex=" + inIndex + " rv.len=" + rv.length

View File

@@ -10,6 +10,7 @@ package net.i2p.crypto;
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -527,8 +528,6 @@ public class ElGamalAESEngine {
return aesEncr;
}
private final static Set EMPTY_SET = new HashSet();
/**
* For both scenarios, this method encrypts the AES area using the given key, iv
* and making sure the resulting data is at least as long as the paddedSize and
@@ -552,7 +551,7 @@ public class ElGamalAESEngine {
long paddedSize, int prefixBytes) {
//_log.debug("iv for encryption: " + DataHelper.toString(iv, 16));
//_log.debug("Encrypting AES");
if (tagsForDelivery == null) tagsForDelivery = EMPTY_SET;
if (tagsForDelivery == null) tagsForDelivery = Collections.EMPTY_SET;
int size = 2 // sizeof(tags)
+ tagsForDelivery.size()
+ SessionTag.BYTE_LENGTH*tagsForDelivery.size()

View File

@@ -72,13 +72,13 @@ public class Base32 {
}
private static void runApp(String args[]) {
if ("encodestring".equalsIgnoreCase(args[0])) {
System.out.println(encode(args[1].getBytes()));
return;
}
InputStream in = System.in;
OutputStream out = System.out;
try {
if ("encodestring".equalsIgnoreCase(args[0])) {
System.out.println(encode(args[1].getBytes()));
return;
}
InputStream in = System.in;
OutputStream out = System.out;
if (args.length >= 3) {
out = new FileOutputStream(args[2]);
}
@@ -95,6 +95,9 @@ public class Base32 {
}
} catch (IOException ioe) {
ioe.printStackTrace(System.err);
} finally {
try { in.close(); } catch (IOException e) {}
try { out.close(); } catch (IOException e) {}
}
}

View File

@@ -178,13 +178,13 @@ public class Base64 {
}
private static void runApp(String args[]) {
if ("encodestring".equalsIgnoreCase(args[0])) {
System.out.println(encode(args[1].getBytes()));
return;
}
InputStream in = System.in;
OutputStream out = System.out;
try {
if ("encodestring".equalsIgnoreCase(args[0])) {
System.out.println(encode(args[1].getBytes()));
return;
}
InputStream in = System.in;
OutputStream out = System.out;
if (args.length >= 3) {
out = new FileOutputStream(args[2]);
}
@@ -201,6 +201,9 @@ public class Base64 {
}
} catch (IOException ioe) {
ioe.printStackTrace(System.err);
} finally {
try { in.close(); } catch (IOException e) {}
try { out.close(); } catch (IOException e) {}
}
}

View File

@@ -845,7 +845,7 @@ public class DataHelper {
*/
public final static void xor(byte lhs[], int startLeft, byte rhs[], int startRight, byte out[], int startOut, int len) {
if ( (lhs == null) || (rhs == null) || (out == null) )
throw new NullPointerException("Invalid params to xor (" + lhs + ", " + rhs + ", " + out + ")");
throw new NullPointerException("Null params to xor");
if (lhs.length < startLeft + len)
throw new IllegalArgumentException("Left hand side is too short");
if (rhs.length < startRight + len)

View File

@@ -0,0 +1,139 @@
package net.i2p.data;
/*
* free (adj.): unencumbered; not under the control of others
* Released into the public domain
* with no warranty of any kind, either expressed or implied.
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
/**
* A six-byte Date and 2 bytes of flags, since a Date won't encroach
* on the top two bytes until the year 10889.
*
* The flag format is not specified here. The bits may be used in
* an application-specific manner. The application should
* be designed so that a flags value of 0 is the default, for
* compatibility with an 8-byte Date.
*
* If we really need some more bits we could use the first few bits
* of the third byte.
*
* @author zzz
* @since 0.8.4
*/
public class DateAndFlags extends DataStructureImpl {
private int _flags;
private long _date;
public DateAndFlags() {}
/**
* @param flags 0 - 65535
*/
public DateAndFlags(int flags, long date) {
_flags = flags;
_date = date;
}
/**
* @param flags 0 - 65535
*/
public DateAndFlags(int flags, Date date) {
_flags = flags;
_date = date.getTime();
}
public int getFlags() {
return _flags;
}
/**
* @param flags 0 - 65535
*/
public void setFlags(int flags) {
_flags = flags;
}
/**
* The Date object is created here, it is not cached.
* Use getTime() if you only need the long value.
*/
public Date getDate() {
return new Date(_date);
}
public long getTime() {
return (_date);
}
public void setDate(long date) {
_date = date;
}
public void setDate(Date date) {
_date = date.getTime();
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_flags = (int) DataHelper.readLong(in, 2);
_date = DataHelper.readLong(in, 6);
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
DataHelper.writeLong(out, 2, _flags);
DataHelper.writeLong(out, 6, _date);
}
/**
* Overridden for efficiency.
*/
@Override
public byte[] toByteArray() {
byte[] rv = DataHelper.toLong(8, _date);
rv[0] = (byte) ((_flags >> 8) & 0xff);
rv[1] = (byte) (_flags & 0xff);
return rv;
}
/**
* Overridden for efficiency.
* @param data non-null
* @throws DataFormatException if null or wrong length
*/
@Override
public void fromByteArray(byte data[]) throws DataFormatException {
if (data == null) throw new DataFormatException("Null data passed in");
if (data.length != 8) throw new DataFormatException("Bad data length");
_flags = (int) DataHelper.fromLong(data, 0, 2);
_date = DataHelper.fromLong(data, 2, 6);
}
@Override
public boolean equals(Object object) {
if ((object == null) || !(object instanceof DateAndFlags)) return false;
DateAndFlags daf = (DateAndFlags) object;
return _date == daf._date && _flags == daf._flags;
}
@Override
public int hashCode() {
return _flags + (int) _date;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("[DateAndFlags: ");
buf.append("\n\tDate: ").append((new Date(_date)).toString());
buf.append("\n\tFlags: 0x").append(Integer.toHexString(_flags));
buf.append("]");
return buf.toString();
}
}

View File

@@ -133,9 +133,15 @@ public class PrivateKeyFile {
*/
public Destination createIfAbsent() throws I2PException, IOException, DataFormatException {
if(!this.file.exists()) {
FileOutputStream out = new FileOutputStream(this.file);
this.client.createDestination(out);
out.close();
FileOutputStream out = null;
try {
out = new FileOutputStream(this.file);
this.client.createDestination(out);
} finally {
if (out != null) {
try { out.close(); } catch (IOException ioe) {}
}
}
}
return getDestination();
}
@@ -243,29 +249,36 @@ public class PrivateKeyFile {
public I2PSession open() throws I2PSessionException, IOException {
return this.open(new Properties());
}
public I2PSession open(Properties opts) throws I2PSessionException, IOException {
// open input file
FileInputStream in = new FileInputStream(this.file);
// create sesssion
I2PSession s = this.client.createSession(in, opts);
// close file
in.close();
return s;
FileInputStream in = null;
try {
in = new FileInputStream(this.file);
I2PSession s = this.client.createSession(in, opts);
return s;
} finally {
if (in != null) {
try { in.close(); } catch (IOException ioe) {}
}
}
}
/**
* Copied from I2PClientImpl.createDestination()
*/
public void write() throws IOException, DataFormatException {
FileOutputStream out = new FileOutputStream(this.file);
this.dest.writeBytes(out);
this.privKey.writeBytes(out);
this.signingPrivKey.writeBytes(out);
out.flush();
out.close();
FileOutputStream out = null;
try {
out = new FileOutputStream(this.file);
this.dest.writeBytes(out);
this.privKey.writeBytes(out);
this.signingPrivKey.writeBytes(out);
out.flush();
} finally {
if (out != null) {
try { out.close(); } catch (IOException ioe) {}
}
}
}
@Override
@@ -377,7 +390,8 @@ public class PrivateKeyFile {
}
}
}
} catch (Exception ioe) {
} catch (DataFormatException dfe) {
} catch (IOException ioe) {
}
// not found, continue to the next file
}

View File

@@ -50,6 +50,26 @@ public class TunnelId extends DataStructureImpl {
DataHelper.writeLong(out, 4, _tunnelId);
}
/**
* Overridden for efficiency.
*/
@Override
public byte[] toByteArray() {
return DataHelper.toLong(4, _tunnelId);
}
/**
* Overridden for efficiency.
* @param data non-null
* @throws DataFormatException if null or wrong length
*/
@Override
public void fromByteArray(byte data[]) throws DataFormatException {
if (data == null) throw new DataFormatException("Null data passed in");
if (data.length != 4) throw new DataFormatException("Bad data length");
_tunnelId = (int) DataHelper.fromLong(data, 0, 4);
}
@Override
public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof TunnelId))

View File

@@ -74,10 +74,11 @@ public class DestReplyMessage extends I2CPMessageImpl {
}
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if (_dest == null && _hash == null)
return new byte[0]; // null response allowed
if (_dest == null && _hash != null)
if (_dest == null) {
if (_hash == null)
return new byte[0]; // null response allowed
return _hash.getData();
}
ByteArrayOutputStream os = new ByteArrayOutputStream(_dest.size());
try {
_dest.writeBytes(os);

View File

@@ -16,32 +16,66 @@ import java.util.Date;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.DateAndFlags;
import net.i2p.data.Destination;
import net.i2p.data.Payload;
/**
* Same as SendMessageMessage, but with an expiration to be passed to the router
*
* As of 0.8.4, retrofitted to use DateAndFlags. Backwards compatible.
*
* @author zzz
*/
public class SendMessageExpiresMessage extends SendMessageMessage {
/* FIXME hides another field FIXME */
public final static int MESSAGE_TYPE = 36;
private SessionId _sessionId;
private Destination _destination;
private Payload _payload;
private Date _expiration;
private final DateAndFlags _daf;
public SendMessageExpiresMessage() {
super();
_daf = new DateAndFlags();
}
/**
* The Date object is created here, it is not cached.
* Use getExpirationTime() if you only need the long value.
*/
public Date getExpiration() {
return _expiration;
return _daf.getDate();
}
/**
* Use this instead of getExpiration().getTime()
* @since 0.8.4
*/
public long getExpirationTime() {
return _daf.getTime();
}
public void setExpiration(Date d) {
_expiration = d;
_daf.setDate(d);
}
/**
* @since 0.8.4
*/
public void setExpiration(long d) {
_daf.setDate(d);
}
/**
* @since 0.8.4
*/
public int getFlags() {
return _daf.getFlags();
}
/**
* @since 0.8.4
*/
public void setFlags(int f) {
_daf.setFlags(f);
}
/**
@@ -54,7 +88,7 @@ public class SendMessageExpiresMessage extends SendMessageMessage {
super.readMessage(in, length, type);
try {
_expiration = DataHelper.readDate(in);
_daf.readBytes(in);
} catch (DataFormatException dfe) {
throw new I2CPMessageException("Unable to load the message data", dfe);
}
@@ -68,7 +102,7 @@ public class SendMessageExpiresMessage extends SendMessageMessage {
*/
@Override
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException {
if ((getSessionId() == null) || (getDestination() == null) || (getPayload() == null) || (getNonce() <= 0) || (_expiration == null))
if ((getSessionId() == null) || (getDestination() == null) || (getPayload() == null) || (getNonce() <= 0))
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
int len = 2 + getDestination().size() + getPayload().getSize() + 4 + 4 + DataHelper.DATE_LENGTH;
@@ -79,7 +113,7 @@ public class SendMessageExpiresMessage extends SendMessageMessage {
getDestination().writeBytes(out);
getPayload().writeBytes(out);
DataHelper.writeLong(out, 4, getNonce());
DataHelper.writeDate(out, _expiration);
_daf.writeBytes(out);
} catch (DataFormatException dfe) {
throw new I2CPMessageException("Error writing the msg", dfe);
}
@@ -96,7 +130,7 @@ public class SendMessageExpiresMessage extends SendMessageMessage {
if ((object != null) && (object instanceof SendMessageExpiresMessage)) {
SendMessageExpiresMessage msg = (SendMessageExpiresMessage) object;
return super.equals(object)
&& DataHelper.eq(getExpiration(), msg.getExpiration());
&& _daf.equals(msg._daf);
}
return false;

View File

@@ -89,7 +89,7 @@ public class FrequencyStat {
/** @since 0.8.2 */
@Override
public boolean equals(Object obj) {
if ((obj == null) || (obj.getClass() != FrequencyStat.class)) return false;
if ((obj == null) || !(obj instanceof FrequencyStat)) return false;
return _statName.equals(((FrequencyStat)obj)._statName);
}

View File

@@ -473,7 +473,7 @@ public class Rate {
@Override
public boolean equals(Object obj) {
if ((obj == null) || (obj.getClass() != Rate.class)) return false;
if ((obj == null) || !(obj instanceof Rate)) return false;
if (obj == this) return true;
Rate r = (Rate) obj;
return _period == r.getPeriod() && _creationDate == r.getCreationDate() &&

View File

@@ -108,7 +108,7 @@ public class RateStat {
@Override
public boolean equals(Object obj) {
if ((obj == null) || (obj.getClass() != RateStat.class)) return false;
if ((obj == null) || !(obj instanceof RateStat)) return false;
RateStat rs = (RateStat) obj;
if (DataHelper.eq(getGroupName(), rs.getGroupName()) && DataHelper.eq(getDescription(), rs.getDescription())
&& DataHelper.eq(getName(), rs.getName())) {

View File

@@ -1,8 +1,8 @@
package net.i2p.util;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import net.i2p.I2PAppContext;
import net.i2p.time.Timestamper;
@@ -19,19 +19,19 @@ import net.i2p.time.Timestamper;
*
*/
public class Clock implements Timestamper.UpdateListener {
protected I2PAppContext _context;
private Timestamper _timestamper;
protected long _startedOn;
protected final I2PAppContext _context;
private final Timestamper _timestamper;
protected final long _startedOn;
protected boolean _statCreated;
protected volatile long _offset;
protected boolean _alreadyChanged;
private final Set _listeners;
public Clock(I2PAppContext context) {
_context = context;
_offset = 0;
_alreadyChanged = false;
_listeners = new HashSet(1);
_listeners = new CopyOnWriteArraySet();
_timestamper = new Timestamper(context, this);
_startedOn = System.currentTimeMillis();
_statCreated = false;
}
public static Clock getInstance() {
return I2PAppContext.getGlobalContext().clock();
@@ -41,10 +41,6 @@ public class Clock implements Timestamper.UpdateListener {
/** we fetch it on demand to avoid circular dependencies (logging uses the clock) */
protected Log getLog() { return _context.logManager().getLog(Clock.class); }
protected volatile long _offset;
protected boolean _alreadyChanged;
private final Set _listeners;
/** if the clock is skewed by 3+ days, fuck 'em */
public final static long MAX_OFFSET = 3 * 24 * 60 * 60 * 1000;
@@ -136,24 +132,18 @@ public class Clock implements Timestamper.UpdateListener {
}
public void addUpdateListener(ClockUpdateListener lsnr) {
synchronized (_listeners) {
_listeners.add(lsnr);
}
}
public void removeUpdateListener(ClockUpdateListener lsnr) {
synchronized (_listeners) {
_listeners.remove(lsnr);
}
}
protected void fireOffsetChanged(long delta) {
synchronized (_listeners) {
for (Iterator iter = _listeners.iterator(); iter.hasNext();) {
ClockUpdateListener lsnr = (ClockUpdateListener) iter.next();
lsnr.offsetChanged(delta);
}
}
}
public static interface ClockUpdateListener {

View File

@@ -14,7 +14,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public class ConcurrentHashSet<E> extends AbstractSet<E> implements Set<E> {
private static final Object DUMMY = new Object();
private Map<E, Object> _map;
private final Map<E, Object> _map;
public ConcurrentHashSet() {
_map = new ConcurrentHashMap();

View File

@@ -20,28 +20,34 @@ import org.xlattice.crypto.filters.BloomSHA1;
* Further analysis and tweaking for the tunnel IVV may be required.
*/
public class DecayingBloomFilter {
private I2PAppContext _context;
private Log _log;
protected final I2PAppContext _context;
protected final Log _log;
private BloomSHA1 _current;
private BloomSHA1 _previous;
private int _durationMs;
private int _entryBytes;
protected final int _durationMs;
protected final int _entryBytes;
private byte _extenders[][];
private byte _extended[];
private byte _longToEntry[];
private long _longToEntryMask;
protected long _currentDuplicates;
private boolean _keepDecaying;
private DecayEvent _decayEvent;
protected volatile boolean _keepDecaying;
protected SimpleTimer.TimedEvent _decayEvent;
/** just for logging */
private String _name;
protected final String _name;
private static final int DEFAULT_M = 23;
private static final int DEFAULT_K = 11;
private static final boolean ALWAYS_MISS = false;
/** noop for DHS */
public DecayingBloomFilter() {}
/** only for extension by DHS */
protected DecayingBloomFilter(int durationMs, int entryBytes, String name, I2PAppContext context) {
_context = context;
_log = context.logManager().getLog(getClass());
_entryBytes = entryBytes;
_name = name;
_durationMs = durationMs;
}
/**
* Create a bloom filter that will decay its entries over time.
@@ -87,7 +93,6 @@ public class DecayingBloomFilter {
_longToEntry = new byte[_entryBytes];
_longToEntryMask = (1l << (_entryBytes * 8l)) -1;
}
_currentDuplicates = 0;
_decayEvent = new DecayEvent();
_keepDecaying = true;
SimpleTimer.getInstance().addEvent(_decayEvent, _durationMs);
@@ -105,11 +110,13 @@ public class DecayingBloomFilter {
}
public long getCurrentDuplicateCount() { return _currentDuplicates; }
public int getInsertedCount() {
synchronized (this) {
return _current.size() + _previous.size();
}
}
public double getFalsePositiveRate() {
synchronized (this) {
return _current.falsePositives();
@@ -117,12 +124,15 @@ public class DecayingBloomFilter {
}
/**
* return true if the entry added is a duplicate
*
* @return true if the entry added is a duplicate
*/
public boolean add(byte entry[]) {
return add(entry, 0, entry.length);
}
/**
* @return true if the entry added is a duplicate
*/
public boolean add(byte entry[], int off, int len) {
if (ALWAYS_MISS) return false;
if (entry == null)
@@ -131,55 +141,52 @@ public class DecayingBloomFilter {
throw new IllegalArgumentException("Bad entry [" + len + ", expected "
+ _entryBytes + "]");
synchronized (this) {
return locked_add(entry, off, len);
return locked_add(entry, off, len, true);
}
}
/**
* return true if the entry added is a duplicate. the number of low order
* @return true if the entry added is a duplicate. the number of low order
* bits used is determined by the entryBytes parameter used on creation of the
* filter.
*
*/
public boolean add(long entry) {
if (ALWAYS_MISS) return false;
if (_entryBytes <= 7)
entry = ((entry ^ _longToEntryMask) & ((1 << 31)-1)) | (entry ^ _longToEntryMask);
//entry &= _longToEntryMask;
if (entry < 0) {
DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry);
_longToEntry[0] |= (1 << 7);
} else {
DataHelper.toLong(_longToEntry, 0, _entryBytes, entry);
}
synchronized (this) {
if (_entryBytes <= 7)
entry = ((entry ^ _longToEntryMask) & ((1 << 31)-1)) | (entry ^ _longToEntryMask);
//entry &= _longToEntryMask;
if (entry < 0) {
DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry);
_longToEntry[0] |= (1 << 7);
} else {
DataHelper.toLong(_longToEntry, 0, _entryBytes, entry);
}
return locked_add(_longToEntry, 0, _longToEntry.length);
return locked_add(_longToEntry, 0, _longToEntry.length, true);
}
}
/**
* return true if the entry is already known. this does NOT add the
* @return true if the entry is already known. this does NOT add the
* entry however.
*
*/
public boolean isKnown(long entry) {
if (ALWAYS_MISS) return false;
if (_entryBytes <= 7)
entry = ((entry ^ _longToEntryMask) & ((1 << 31)-1)) | (entry ^ _longToEntryMask);
if (entry < 0) {
DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry);
_longToEntry[0] |= (1 << 7);
} else {
DataHelper.toLong(_longToEntry, 0, _entryBytes, entry);
}
synchronized (this) {
if (_entryBytes <= 7)
entry = ((entry ^ _longToEntryMask) & ((1 << 31)-1)) | (entry ^ _longToEntryMask);
if (entry < 0) {
DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry);
_longToEntry[0] |= (1 << 7);
} else {
DataHelper.toLong(_longToEntry, 0, _entryBytes, entry);
}
return locked_add(_longToEntry, 0, _longToEntry.length, false);
}
}
private boolean locked_add(byte entry[], int offset, int len) {
return locked_add(entry, offset, len, true);
}
private boolean locked_add(byte entry[], int offset, int len, boolean addIfNew) {
if (_extended != null) {
// extend the entry to 32 bytes
@@ -195,7 +202,6 @@ public class DecayingBloomFilter {
} else {
if (addIfNew) {
_current.locked_insert(_extended);
_previous.locked_insert(_extended);
}
return false;
}
@@ -208,7 +214,6 @@ public class DecayingBloomFilter {
} else {
if (addIfNew) {
_current.locked_insert(entry, offset, len);
_previous.locked_insert(entry, offset, len);
}
return false;
}

View File

@@ -17,12 +17,15 @@ import net.i2p.data.DataHelper;
*
* ./router/java/src/net/i2p/router/tunnel/BuildMessageProcessor.java:
* 32 bytes, peak 10 entries in 1m
* (320 peak entries seen on fast router)
*
* ./router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java:
* 4 bytes, peak 150 entries in 10s
* (1600 peak entries seen on fast router)
*
* ./router/java/src/net/i2p/router/MessageValidator.java:
* 8 bytes, peak 1K entries in 2m
* (36K peak entries seen on fast router)
*
* ./router/java/src/net/i2p/router/tunnel/BloomFilterIVValidator.java:
* 16 bytes, peak 15K entries in 10m
@@ -57,19 +60,10 @@ import net.i2p.data.DataHelper;
* @author zzz
*/
public class DecayingHashSet extends DecayingBloomFilter {
private final I2PAppContext _context;
private final Log _log;
private ConcurrentHashSet<ArrayWrapper> _current;
private ConcurrentHashSet<ArrayWrapper> _previous;
private int _durationMs;
private int _entryBytes;
private volatile boolean _keepDecaying;
private final DecayEvent _decayEvent;
/** just for logging */
private final String _name;
/** synchronize against this lock when switching double buffers */
private final ReentrantReadWriteLock _reorganizeLock = new ReentrantReadWriteLock(true);
/**
* Create a double-buffered hash set that will decay its entries over time.
@@ -83,16 +77,11 @@ public class DecayingHashSet extends DecayingBloomFilter {
/** @param name just for logging / debugging / stats */
public DecayingHashSet(I2PAppContext context, int durationMs, int entryBytes, String name) {
super(durationMs, entryBytes, name, context);
if (entryBytes <= 0 || entryBytes > 32)
throw new IllegalArgumentException("Bad size");
_context = context;
_log = context.logManager().getLog(DecayingHashSet.class);
_entryBytes = entryBytes;
_name = name;
_current = new ConcurrentHashSet(128);
_previous = new ConcurrentHashSet(128);
_durationMs = durationMs;
_currentDuplicates = 0;
_decayEvent = new DecayEvent();
_keepDecaying = true;
SimpleScheduler.getInstance().addEvent(_decayEvent, _durationMs);
@@ -111,6 +100,7 @@ public class DecayingHashSet extends DecayingBloomFilter {
public int getInsertedCount() {
return _current.size() + _previous.size();
}
/** pointless, only used for logging elsewhere */
@Override
public double getFalsePositiveRate() {
@@ -121,7 +111,6 @@ public class DecayingHashSet extends DecayingBloomFilter {
/**
* @return true if the entry added is a duplicate
*
*/
@Override
public boolean add(byte entry[], int off, int len) {
@@ -130,9 +119,10 @@ public class DecayingHashSet extends DecayingBloomFilter {
if (len != _entryBytes)
throw new IllegalArgumentException("Bad entry [" + len + ", expected "
+ _entryBytes + "]");
ArrayWrapper w = new ArrayWrapper(entry, off, len);
getReadLock();
try {
return locked_add(entry, off, len, true);
return locked_add(w, true);
} finally { releaseReadLock(); }
}
@@ -158,35 +148,30 @@ public class DecayingHashSet extends DecayingBloomFilter {
}
private boolean add(long entry, boolean addIfNew) {
int len = Math.min(8, _entryBytes);
byte[] b = toLong(len, entry);
ArrayWrapper w = new ArrayWrapper(entry);
getReadLock();
try {
return locked_add(b, 0, len, addIfNew);
return locked_add(w, addIfNew);
} finally { releaseReadLock(); }
}
/** from DataHelper, except negative values ok */
private static byte[] toLong(int numBytes, long value) {
byte target[] = new byte[numBytes];
for (int i = 0; i < numBytes; i++)
target[numBytes-i-1] = (byte)(value >>> (i*8));
return target;
}
/** so many questions... */
private boolean locked_add(byte entry[], int offset, int len, boolean addIfNew) {
ArrayWrapper w = new ArrayWrapper(entry, offset, len);
boolean seen = _current.contains(w);
seen = seen || _previous.contains(w);
/**
* @param addIfNew if true, add the element to current if it is not already there;
* if false, only check
* @return if the element is in either the current or previous set
*/
private boolean locked_add(ArrayWrapper w, boolean addIfNew) {
boolean seen;
// only access _current once. This adds to _current even if seen in _previous.
if (addIfNew)
seen = !_current.add(w);
else
seen = _current.contains(w);
if (!seen)
seen = _previous.contains(w);
if (seen) {
// why increment if addIfNew == false?
// why not add to current if only in previous?
// why increment if addIfNew == false? Only used for stats...
_currentDuplicates++;
} else if (addIfNew) {
_current.add(w);
// why add to previous?
_previous.add(w);
}
return seen;
}
@@ -270,14 +255,22 @@ public class DecayingHashSet extends DecayingBloomFilter {
* the maximum entropy given the length of the data.
*/
private static class ArrayWrapper {
private long _longhashcode;
private final long _longhashcode;
public ArrayWrapper(byte[] b, int offset, int len) {
int idx = offset;
int shift = Math.min(8, 64 / len);
long lhc = 0;
for (int i = 0; i < len; i++) {
// xor better than + in tests
_longhashcode ^= (((long) b[idx++]) << (i * shift));
lhc ^= (((long) b[idx++]) << (i * shift));
}
_longhashcode = lhc;
}
/** faster version for when storing <= 8 bytes */
public ArrayWrapper(long b) {
_longhashcode = b;
}
public int hashCode() {

View File

@@ -430,29 +430,33 @@ public class EepGet {
_log.debug("Fetching (proxied? " + _shouldProxy + ") url=" + _actualURL);
while (_keepFetching) {
SocketTimeout timeout = null;
if (_fetchHeaderTimeout > 0)
if (_fetchHeaderTimeout > 0) {
timeout = new SocketTimeout(_fetchHeaderTimeout);
final SocketTimeout stimeout = timeout; // ugly - why not use sotimeout?
timeout.setTimeoutCommand(new Runnable() {
public void run() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("timeout reached on " + _url + ": " + stimeout);
_aborted = true;
}
});
timeout.setTotalTimeoutPeriod(_fetchEndTime);
final SocketTimeout stimeout = timeout; // ugly - why not use sotimeout?
timeout.setTimeoutCommand(new Runnable() {
public void run() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("timeout reached on " + _url + ": " + stimeout);
_aborted = true;
}
});
timeout.setTotalTimeoutPeriod(_fetchEndTime);
}
try {
for (int i = 0; i < _listeners.size(); i++)
_listeners.get(i).attempting(_url);
sendRequest(timeout);
timeout.resetTimer();
if (timeout != null)
timeout.resetTimer();
doFetch(timeout);
timeout.cancel();
if (timeout != null)
timeout.cancel();
if (!_transferFailed)
return true;
break;
} catch (IOException ioe) {
timeout.cancel();
if (timeout != null)
timeout.cancel();
for (int i = 0; i < _listeners.size(); i++)
_listeners.get(i).attemptFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt, _numRetries, ioe);
if (_log.shouldLog(Log.WARN))
@@ -492,7 +496,10 @@ public class EepGet {
return false;
}
/** single fetch */
/**
* single fetch
* @param timeout may be null
*/
protected void doFetch(SocketTimeout timeout) throws IOException {
_headersRead = false;
_aborted = false;
@@ -504,11 +511,13 @@ public class EepGet {
if (_aborted)
throw new IOException("Timed out reading the HTTP headers");
timeout.resetTimer();
if (_fetchInactivityTimeout > 0)
timeout.setInactivityTimeout(_fetchInactivityTimeout);
else
timeout.setInactivityTimeout(INACTIVITY_TIMEOUT);
if (timeout != null) {
timeout.resetTimer();
if (_fetchInactivityTimeout > 0)
timeout.setInactivityTimeout(_fetchInactivityTimeout);
else
timeout.setInactivityTimeout(INACTIVITY_TIMEOUT);
}
if (_redirectLocation != null) {
//try {
@@ -571,7 +580,8 @@ public class EepGet {
int read = _proxyIn.read(buf, 0, toRead);
if (read == -1)
break;
timeout.resetTimer();
if (timeout != null)
timeout.resetTimer();
_out.write(buf, 0, read);
_bytesTransferred += read;
if ((_maxSize > -1) && (_alreadyTransferred + read > _maxSize)) // could transfer a little over maxSize
@@ -597,7 +607,8 @@ public class EepGet {
read++;
}
}
timeout.resetTimer();
if (timeout != null)
timeout.resetTimer();
if (_bytesRemaining >= read) // else chunked?
_bytesRemaining -= read;
if (read > 0) {
@@ -622,7 +633,8 @@ public class EepGet {
if (_aborted)
throw new IOException("Timed out reading the HTTP data");
timeout.cancel();
if (timeout != null)
timeout.cancel();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Done transferring " + _bytesTransferred + " (ok? " + !_transferFailed + ")");
@@ -867,6 +879,9 @@ public class EepGet {
private static final byte NL = '\n';
private static boolean isNL(byte b) { return (b == NL); }
/**
* @param timeout may be null
*/
protected void sendRequest(SocketTimeout timeout) throws IOException {
if (_outputStream != null) {
// We are reading into a stream supplied by a caller,
@@ -907,7 +922,8 @@ public class EepGet {
_proxyIn = _proxy.getInputStream();
_proxyOut = _proxy.getOutputStream();
timeout.setSocket(_proxy);
if (timeout != null)
timeout.setSocket(_proxy);
_proxyOut.write(DataHelper.getUTF8(req));
_proxyOut.flush();

View File

@@ -31,6 +31,7 @@ public class EepPost {
_log = ctx.logManager().getLog(EepPost.class);
}
/*****
public static void main(String args[]) {
EepPost e = new EepPost();
Map fields = new HashMap();
@@ -47,6 +48,8 @@ public class EepPost {
//e.postFiles("http://localhost/cgi-bin/read.pl", null, -1, fields, null);
//e.postFiles("http://localhost:2001/import.jsp", null, -1, fields, null);
}
*****/
/**
* Submit an HTTP POST to the given URL (using the proxy if specified),
* uploading the given fields. If the field's value is a File object, then
@@ -117,7 +120,7 @@ public class EepPost {
}
}
out.close();
} catch (Exception e) {
} catch (IOException e) {
e.printStackTrace();
} finally {
if (s != null) try { s.close(); } catch (IOException ioe) {}

View File

@@ -122,24 +122,24 @@ public class FileUtil {
}
}
} else {
InputStream in = null;
FileOutputStream fos = null;
JarOutputStream jos = null;
try {
InputStream in = zip.getInputStream(entry);
in = zip.getInputStream(entry);
if (entry.getName().endsWith(".jar.pack") || entry.getName().endsWith(".war.pack")) {
target = new File(targetDir, entry.getName().substring(0, entry.getName().length() - ".pack".length()));
JarOutputStream fos = new JarOutputStream(new FileOutputStream(target));
unpack(in, fos);
fos.close();
jos = new JarOutputStream(new FileOutputStream(target));
unpack(in, jos);
System.err.println("INFO: File [" + entry.getName() + "] extracted and unpacked");
} else {
FileOutputStream fos = new FileOutputStream(target);
fos = new FileOutputStream(target);
int read = 0;
while ( (read = in.read(buf)) != -1) {
fos.write(buf, 0, read);
}
fos.close();
System.err.println("INFO: File [" + entry.getName() + "] extracted");
}
in.close();
} catch (IOException ioe) {
System.err.println("ERROR: Error extracting the zip entry (" + entry.getName() + ')');
if (ioe.getMessage() != null && ioe.getMessage().indexOf("CAFED00D") >= 0)
@@ -151,6 +151,10 @@ public class FileUtil {
System.err.println("ERROR: Error unpacking the zip entry (" + entry.getName() +
"), your JVM does not support unpack200");
return false;
} finally {
try { if (in != null) in.close(); } catch (IOException ioe) {}
try { if (fos != null) fos.close(); } catch (IOException ioe) {}
try { if (jos != null) jos.close(); } catch (IOException ioe) {}
}
}
}
@@ -401,21 +405,24 @@ public class FileUtil {
if (dst.exists() && !overwriteExisting) return false;
byte buf[] = new byte[4096];
InputStream in = null;
OutputStream out = null;
try {
FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst);
in = new FileInputStream(src);
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) {
if (!quiet)
ioe.printStackTrace();
return false;
} finally {
try { if (in != null) in.close(); } catch (IOException ioe) {}
try { if (out != null) out.close(); } catch (IOException ioe) {}
}
}

View File

@@ -10,9 +10,9 @@ package net.i2p.util;
*/
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Like I2PThread but with per-thread OOM listeners,
@@ -22,7 +22,7 @@ import java.util.Set;
*/
public class I2PAppThread extends I2PThread {
private Set _threadListeners = new HashSet(0);
private final Set _threadListeners = new CopyOnWriteArraySet();
public I2PAppThread() {
super();

View File

@@ -10,9 +10,9 @@ package net.i2p.util;
*/
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* In case its useful later...
@@ -21,7 +21,7 @@ import java.util.Set;
*/
public class I2PThread extends Thread {
private static volatile Log _log;
private static Set _listeners = new HashSet(4);
private static final Set _listeners = new CopyOnWriteArraySet();
private String _name;
private Exception _createdBy;

View File

@@ -205,7 +205,8 @@ public class Log {
}
@Override
public boolean equals(Object obj) {
if (obj == null) throw new NullPointerException("Null object scope?");
if (obj == null)
return false;
if (obj instanceof LogScope) {
LogScope s = (LogScope)obj;
return s._scopeCache.equals(_scopeCache);

View File

@@ -166,8 +166,10 @@ public class LogManager {
Log rv = _logs.get(scope);
if (rv == null) {
rv = new Log(this, cls, name);
_logs.putIfAbsent(scope, rv);
isNew = true;
Log old = _logs.putIfAbsent(scope, rv);
isNew = old == null;
if (!isNew)
rv = old;
}
if (isNew)
updateLimit(rv);
@@ -180,8 +182,9 @@ public class LogManager {
}
void addLog(Log log) {
_logs.putIfAbsent(log.getScope(), log);
updateLimit(log);
Log old = _logs.putIfAbsent(log.getScope(), log);
if (old == null)
updateLimit(log);
}
public LogConsoleBuffer getBuffer() { return _consoleBuffer; }
@@ -636,6 +639,7 @@ public class LogManager {
return _dateFormatPattern;
}
/*****
public static void main(String args[]) {
I2PAppContext ctx = new I2PAppContext();
Log l1 = ctx.logManager().getLog("test.1");
@@ -656,6 +660,7 @@ public class LogManager {
}
System.exit(0);
}
*****/
public void shutdown() {
if (_writer != null) {

View File

@@ -92,10 +92,13 @@ class LogRecordFormatter {
}
/** don't translate */
/****
private static String getPriority(LogRecord rec) {
return toString(Log.toLevelString(rec.getPriority()), MAX_PRIORITY_LENGTH);
}
****/
/** */
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
/** translate @since 0.7.14 */

View File

@@ -78,6 +78,7 @@ public class LookaheadInputStream extends FilterInputStream {
/** grab the lookahead footer */
public byte[] getFooter() { return _footerLookahead; }
/*******
public static void main(String args[]) {
byte buf[] = new byte[32];
for (int i = 0; i < 32; i++)
@@ -128,4 +129,5 @@ public class LookaheadInputStream extends FilterInputStream {
return false;
}
}
******/
}

View File

@@ -482,12 +482,14 @@ public class SSLEepGet extends EepGet {
if (_aborted)
throw new IOException("Timed out reading the HTTP headers");
timeout.resetTimer();
if (_fetchInactivityTimeout > 0)
timeout.setInactivityTimeout(_fetchInactivityTimeout);
else
timeout.setInactivityTimeout(60*1000);
if (timeout != null) {
timeout.resetTimer();
if (_fetchInactivityTimeout > 0)
timeout.setInactivityTimeout(_fetchInactivityTimeout);
else
timeout.setInactivityTimeout(60*1000);
}
if (_redirectLocation != null) {
throw new IOException("Server redirect to " + _redirectLocation + " not allowed");
}
@@ -506,7 +508,8 @@ public class SSLEepGet extends EepGet {
int read = _proxyIn.read(buf, 0, toRead);
if (read == -1)
break;
timeout.resetTimer();
if (timeout != null)
timeout.resetTimer();
_out.write(buf, 0, read);
_bytesTransferred += read;
@@ -531,7 +534,8 @@ public class SSLEepGet extends EepGet {
read++;
}
}
timeout.resetTimer();
if (timeout != null)
timeout.resetTimer();
if (_bytesRemaining >= read) // else chunked?
_bytesRemaining -= read;
if (read > 0) {
@@ -556,7 +560,8 @@ public class SSLEepGet extends EepGet {
if (_aborted)
throw new IOException("Timed out reading the HTTP data");
timeout.cancel();
if (timeout != null)
timeout.cancel();
if (_transferFailed) {
// 404, etc - transferFailed is called after all attempts fail, by fetch() above

View File

@@ -89,7 +89,7 @@ public class ShellCommand {
*
* @author hypercubus
*/
private class StreamConsumer extends Thread {
private static class StreamConsumer extends Thread {
private BufferedReader bufferedReader;
private InputStreamReader inputStreamReader;
@@ -123,7 +123,7 @@ public class ShellCommand {
*
* @author hypercubus
*/
private class StreamReader extends Thread {
private static class StreamReader extends Thread {
private BufferedReader bufferedReader;
private InputStreamReader inputStreamReader;
@@ -159,7 +159,7 @@ public class ShellCommand {
*
* @author hypercubus
*/
private class StreamWriter extends Thread {
private static class StreamWriter extends Thread {
private BufferedWriter bufferedWriter;
private BufferedReader in;
@@ -183,7 +183,7 @@ public class ShellCommand {
bufferedWriter.write(input, 0, input.length());
bufferedWriter.flush();
}
} catch (Exception e) {
} catch (IOException e) {
try {
bufferedWriter.flush();
} catch (IOException e1) {

View File

@@ -90,7 +90,7 @@ public class SimpleTimer {
int totalEvents = 0;
long now = System.currentTimeMillis();
long eventTime = now + timeoutMs;
Long time = new Long(eventTime);
Long time = Long.valueOf(eventTime);
synchronized (_events) {
// remove the old scheduled position, then reinsert it
Long oldTime = (Long)_eventTimes.get(event);

View File

@@ -55,7 +55,7 @@ public class SimpleTimer2 {
_executor.shutdownNow();
}
private class CustomScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
private static class CustomScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
public CustomScheduledThreadPoolExecutor(int threads, ThreadFactory factory) {
super(threads, factory);
}