forked from I2P_Developers/i2p.i2p
merge of '398a24f487b61ef778a2e849660e953ef7e43b39'
and '598d00efae4c9b675b64fd626bc2eab2b921e0c5'
This commit is contained in:
@ -152,6 +152,9 @@ public class I2PSnarkUtil {
|
|||||||
*/
|
*/
|
||||||
synchronized public boolean connect() {
|
synchronized public boolean connect() {
|
||||||
if (_manager == null) {
|
if (_manager == null) {
|
||||||
|
// try to find why reconnecting after stop
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Connecting to I2P", new Exception("I did it"));
|
||||||
Properties opts = new Properties();
|
Properties opts = new Properties();
|
||||||
if (_opts != null) {
|
if (_opts != null) {
|
||||||
for (Iterator iter = _opts.keySet().iterator(); iter.hasNext(); ) {
|
for (Iterator iter = _opts.keySet().iterator(); iter.hasNext(); ) {
|
||||||
@ -163,6 +166,10 @@ public class I2PSnarkUtil {
|
|||||||
opts.setProperty("inbound.nickname", "I2PSnark");
|
opts.setProperty("inbound.nickname", "I2PSnark");
|
||||||
if (opts.getProperty("outbound.nickname") == null)
|
if (opts.getProperty("outbound.nickname") == null)
|
||||||
opts.setProperty("outbound.nickname", "I2PSnark");
|
opts.setProperty("outbound.nickname", "I2PSnark");
|
||||||
|
// Dont do this for now, it is set in I2PSocketEepGet for announces,
|
||||||
|
// we don't need fast handshake for peer connections.
|
||||||
|
//if (opts.getProperty("i2p.streaming.connectDelay") == null)
|
||||||
|
// opts.setProperty("i2p.streaming.connectDelay", "500");
|
||||||
if (opts.getProperty("i2p.streaming.inactivityTimeout") == null)
|
if (opts.getProperty("i2p.streaming.inactivityTimeout") == null)
|
||||||
opts.setProperty("i2p.streaming.inactivityTimeout", "240000");
|
opts.setProperty("i2p.streaming.inactivityTimeout", "240000");
|
||||||
if (opts.getProperty("i2p.streaming.inactivityAction") == null)
|
if (opts.getProperty("i2p.streaming.inactivityAction") == null)
|
||||||
@ -186,6 +193,7 @@ public class I2PSnarkUtil {
|
|||||||
*/
|
*/
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
I2PSocketManager mgr = _manager;
|
I2PSocketManager mgr = _manager;
|
||||||
|
// FIXME this can cause race NPEs elsewhere
|
||||||
_manager = null;
|
_manager = null;
|
||||||
_shitlist.clear();
|
_shitlist.clear();
|
||||||
mgr.destroySocketManager();
|
mgr.destroySocketManager();
|
||||||
@ -197,6 +205,9 @@ public class I2PSnarkUtil {
|
|||||||
|
|
||||||
/** connect to the given destination */
|
/** connect to the given destination */
|
||||||
I2PSocket connect(PeerID peer) throws IOException {
|
I2PSocket connect(PeerID peer) throws IOException {
|
||||||
|
I2PSocketManager mgr = _manager;
|
||||||
|
if (mgr == null)
|
||||||
|
throw new IOException("No socket manager");
|
||||||
Destination addr = peer.getAddress();
|
Destination addr = peer.getAddress();
|
||||||
if (addr == null)
|
if (addr == null)
|
||||||
throw new IOException("Null address");
|
throw new IOException("Null address");
|
||||||
|
@ -56,8 +56,8 @@ public class Peer implements Comparable
|
|||||||
private long _id;
|
private long _id;
|
||||||
final static long CHECK_PERIOD = PeerCoordinator.CHECK_PERIOD; // 40 seconds
|
final static long CHECK_PERIOD = PeerCoordinator.CHECK_PERIOD; // 40 seconds
|
||||||
final static int RATE_DEPTH = PeerCoordinator.RATE_DEPTH; // make following arrays RATE_DEPTH long
|
final static int RATE_DEPTH = PeerCoordinator.RATE_DEPTH; // make following arrays RATE_DEPTH long
|
||||||
private long uploaded_old[] = {-1,-1,-1,-1,-1,-1};
|
private long uploaded_old[] = {-1,-1,-1};
|
||||||
private long downloaded_old[] = {-1,-1,-1,-1,-1,-1};
|
private long downloaded_old[] = {-1,-1,-1};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a disconnected peer given a PeerID, your own id and the
|
* Creates a disconnected peer given a PeerID, your own id and the
|
||||||
@ -117,10 +117,15 @@ public class Peer implements Comparable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns socket (for debug printing)
|
* @return socket debug string (for debug printing)
|
||||||
*/
|
*/
|
||||||
public String getSocket()
|
public String getSocket()
|
||||||
{
|
{
|
||||||
|
if (state != null) {
|
||||||
|
String r = state.getRequests();
|
||||||
|
if (r != null)
|
||||||
|
return sock.toString() + "<br>Requests: " + r;
|
||||||
|
}
|
||||||
return sock.toString();
|
return sock.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,6 +392,37 @@ public class Peer implements Comparable
|
|||||||
s.havePiece(piece);
|
s.havePiece(piece);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the other side that we are no longer interested in any of
|
||||||
|
* the outstanding requests (if any) for this piece.
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
void cancel(int piece) {
|
||||||
|
PeerState s = state;
|
||||||
|
if (s != null)
|
||||||
|
s.cancelPiece(piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are we currently requesting the piece?
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
boolean isRequesting(int p) {
|
||||||
|
PeerState s = state;
|
||||||
|
return s != null && s.isRequesting(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the request queue.
|
||||||
|
* Call after adding wanted pieces.
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
void request() {
|
||||||
|
PeerState s = state;
|
||||||
|
if (s != null)
|
||||||
|
s.addRequest();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the peer is interested in pieces we have. Returns
|
* Whether or not the peer is interested in pieces we have. Returns
|
||||||
* false if not connected.
|
* false if not connected.
|
||||||
@ -545,17 +581,8 @@ public class Peer implements Comparable
|
|||||||
*/
|
*/
|
||||||
public void setRateHistory(long up, long down)
|
public void setRateHistory(long up, long down)
|
||||||
{
|
{
|
||||||
setRate(up, uploaded_old);
|
PeerCoordinator.setRate(up, uploaded_old);
|
||||||
setRate(down, downloaded_old);
|
PeerCoordinator.setRate(down, downloaded_old);
|
||||||
}
|
|
||||||
|
|
||||||
private void setRate(long val, long array[])
|
|
||||||
{
|
|
||||||
synchronized(array) {
|
|
||||||
for (int i = RATE_DEPTH-1; i > 0; i--)
|
|
||||||
array[i] = array[i-1];
|
|
||||||
array[0] = val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -563,28 +590,11 @@ public class Peer implements Comparable
|
|||||||
*/
|
*/
|
||||||
public long getUploadRate()
|
public long getUploadRate()
|
||||||
{
|
{
|
||||||
return getRate(uploaded_old);
|
return PeerCoordinator.getRate(uploaded_old);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getDownloadRate()
|
public long getDownloadRate()
|
||||||
{
|
{
|
||||||
return getRate(downloaded_old);
|
return PeerCoordinator.getRate(downloaded_old);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getRate(long array[])
|
|
||||||
{
|
|
||||||
long rate = 0;
|
|
||||||
int i = 0;
|
|
||||||
synchronized(array) {
|
|
||||||
for ( ; i < RATE_DEPTH; i++){
|
|
||||||
if (array[i] < 0)
|
|
||||||
break;
|
|
||||||
rate += array[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == 0)
|
|
||||||
return 0;
|
|
||||||
return rate / (i * CHECK_PERIOD / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -57,9 +57,9 @@ public class PeerCoordinator implements PeerListener
|
|||||||
|
|
||||||
private long uploaded;
|
private long uploaded;
|
||||||
private long downloaded;
|
private long downloaded;
|
||||||
final static int RATE_DEPTH = 6; // make following arrays RATE_DEPTH long
|
final static int RATE_DEPTH = 3; // make following arrays RATE_DEPTH long
|
||||||
private long uploaded_old[] = {-1,-1,-1,-1,-1,-1};
|
private long uploaded_old[] = {-1,-1,-1};
|
||||||
private long downloaded_old[] = {-1,-1,-1,-1,-1,-1};
|
private long downloaded_old[] = {-1,-1,-1};
|
||||||
|
|
||||||
// synchronize on this when changing peers or downloaders
|
// synchronize on this when changing peers or downloaders
|
||||||
final List<Peer> peers = new ArrayList();
|
final List<Peer> peers = new ArrayList();
|
||||||
@ -78,6 +78,7 @@ public class PeerCoordinator implements PeerListener
|
|||||||
|
|
||||||
private final CoordinatorListener listener;
|
private final CoordinatorListener listener;
|
||||||
public I2PSnarkUtil _util;
|
public I2PSnarkUtil _util;
|
||||||
|
private static final Random _random = I2PAppContext.getGlobalContext().random();
|
||||||
|
|
||||||
public String trackerProblems = null;
|
public String trackerProblems = null;
|
||||||
public int trackerSeenPeers = 0;
|
public int trackerSeenPeers = 0;
|
||||||
@ -97,20 +98,29 @@ public class PeerCoordinator implements PeerListener
|
|||||||
// Install a timer to check the uploaders.
|
// Install a timer to check the uploaders.
|
||||||
// Randomize the first start time so multiple tasks are spread out,
|
// Randomize the first start time so multiple tasks are spread out,
|
||||||
// this will help the behavior with global limits
|
// this will help the behavior with global limits
|
||||||
Random r = I2PAppContext.getGlobalContext().random();
|
timer.schedule(new PeerCheckerTask(_util, this), (CHECK_PERIOD / 2) + _random.nextInt((int) CHECK_PERIOD), CHECK_PERIOD);
|
||||||
timer.schedule(new PeerCheckerTask(_util, this), (CHECK_PERIOD / 2) + r.nextInt((int) CHECK_PERIOD), CHECK_PERIOD);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// only called externally from Storage after the double-check fails
|
// only called externally from Storage after the double-check fails
|
||||||
public void setWantedPieces()
|
public void setWantedPieces()
|
||||||
{
|
{
|
||||||
// Make a list of pieces
|
// Make a list of pieces
|
||||||
|
// FIXME synchronize, clear and re-add instead?
|
||||||
|
// Don't replace something we are synchronizing on.
|
||||||
wantedPieces = new ArrayList();
|
wantedPieces = new ArrayList();
|
||||||
BitField bitfield = storage.getBitField();
|
BitField bitfield = storage.getBitField();
|
||||||
for(int i = 0; i < metainfo.getPieces(); i++)
|
int[] pri = storage.getPiecePriorities();
|
||||||
if (!bitfield.get(i))
|
for(int i = 0; i < metainfo.getPieces(); i++) {
|
||||||
wantedPieces.add(new Piece(i));
|
// only add if we don't have and the priority is >= 0
|
||||||
Collections.shuffle(wantedPieces);
|
if ((!bitfield.get(i)) &&
|
||||||
|
(pri == null || pri[i] >= 0)) {
|
||||||
|
Piece p = new Piece(i);
|
||||||
|
if (pri != null)
|
||||||
|
p.setPriority(pri[i]);
|
||||||
|
wantedPieces.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.shuffle(wantedPieces, _random);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Storage getStorage() { return storage; }
|
public Storage getStorage() { return storage; }
|
||||||
@ -183,7 +193,7 @@ public class PeerCoordinator implements PeerListener
|
|||||||
setRate(down, downloaded_old);
|
setRate(down, downloaded_old);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setRate(long val, long array[])
|
static void setRate(long val, long array[])
|
||||||
{
|
{
|
||||||
synchronized(array) {
|
synchronized(array) {
|
||||||
for (int i = RATE_DEPTH-1; i > 0; i--)
|
for (int i = RATE_DEPTH-1; i > 0; i--)
|
||||||
@ -214,20 +224,23 @@ public class PeerCoordinator implements PeerListener
|
|||||||
return (r * 1000) / CHECK_PERIOD;
|
return (r * 1000) / CHECK_PERIOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getRate(long array[])
|
static long getRate(long array[])
|
||||||
{
|
{
|
||||||
long rate = 0;
|
long rate = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
int factor = 0;
|
||||||
synchronized(array) {
|
synchronized(array) {
|
||||||
for ( ; i < RATE_DEPTH; i++) {
|
for ( ; i < RATE_DEPTH; i++) {
|
||||||
if (array[i] < 0)
|
if (array[i] < 0)
|
||||||
break;
|
break;
|
||||||
rate += array[i];
|
int f = RATE_DEPTH - i;
|
||||||
|
rate += array[i] * f;
|
||||||
|
factor += f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
return 0;
|
return 0;
|
||||||
return rate / (i * CHECK_PERIOD / 1000);
|
return rate / (factor * CHECK_PERIOD / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MetaInfo getMetaInfo()
|
public MetaInfo getMetaInfo()
|
||||||
@ -454,7 +467,7 @@ public class PeerCoordinator implements PeerListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if we don't have the given piece yet.
|
* @return true if we still want the given piece
|
||||||
*/
|
*/
|
||||||
public boolean gotHave(Peer peer, int piece)
|
public boolean gotHave(Peer peer, int piece)
|
||||||
{
|
{
|
||||||
@ -499,6 +512,12 @@ public class PeerCoordinator implements PeerListener
|
|||||||
*/
|
*/
|
||||||
private static final int END_GAME_THRESHOLD = 8;
|
private static final int END_GAME_THRESHOLD = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max number of peers to get a piece from when in end game
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
private static final int MAX_PARALLEL_REQUESTS = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns one of pieces in the given BitField that is still wanted or
|
* Returns one of pieces in the given BitField that is still wanted or
|
||||||
* -1 if none of the given pieces are wanted.
|
* -1 if none of the given pieces are wanted.
|
||||||
@ -520,6 +539,9 @@ public class PeerCoordinator implements PeerListener
|
|||||||
while (piece == null && it.hasNext())
|
while (piece == null && it.hasNext())
|
||||||
{
|
{
|
||||||
Piece p = it.next();
|
Piece p = it.next();
|
||||||
|
// sorted by priority, so when we hit a disabled piece we are done
|
||||||
|
if (p.isDisabled())
|
||||||
|
break;
|
||||||
if (havePieces.get(p.getId()) && !p.isRequested())
|
if (havePieces.get(p.getId()) && !p.isRequested())
|
||||||
{
|
{
|
||||||
piece = p;
|
piece = p;
|
||||||
@ -538,15 +560,32 @@ public class PeerCoordinator implements PeerListener
|
|||||||
if (wantedPieces.size() > END_GAME_THRESHOLD)
|
if (wantedPieces.size() > END_GAME_THRESHOLD)
|
||||||
return -1; // nothing to request and not in end game
|
return -1; // nothing to request and not in end game
|
||||||
// let's not all get on the same piece
|
// let's not all get on the same piece
|
||||||
Collections.shuffle(requested);
|
// Even better would be to sort by number of requests
|
||||||
|
Collections.shuffle(requested, _random);
|
||||||
Iterator<Piece> it2 = requested.iterator();
|
Iterator<Piece> it2 = requested.iterator();
|
||||||
while (piece == null && it2.hasNext())
|
while (piece == null && it2.hasNext())
|
||||||
{
|
{
|
||||||
Piece p = it2.next();
|
Piece p = it2.next();
|
||||||
if (havePieces.get(p.getId()))
|
if (havePieces.get(p.getId())) {
|
||||||
{
|
// limit number of parallel requests
|
||||||
|
int requestedCount = 0;
|
||||||
|
synchronized(peers) {
|
||||||
|
for (Peer pr : peers) {
|
||||||
|
if (pr.isRequesting(p.getId())) {
|
||||||
|
if (pr.equals(peer)) {
|
||||||
|
// don't give it to him again
|
||||||
|
requestedCount = MAX_PARALLEL_REQUESTS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (++requestedCount >= MAX_PARALLEL_REQUESTS)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (requestedCount >= MAX_PARALLEL_REQUESTS)
|
||||||
|
continue;
|
||||||
piece = p;
|
piece = p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (piece == null) {
|
if (piece == null) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
@ -555,7 +594,7 @@ public class PeerCoordinator implements PeerListener
|
|||||||
// + " wanted = " + wantedPieces + " peerHas = " + havePieces);
|
// + " wanted = " + wantedPieces + " peerHas = " + havePieces);
|
||||||
return -1; //If we still can't find a piece we want, so be it.
|
return -1; //If we still can't find a piece we want, so be it.
|
||||||
} else {
|
} else {
|
||||||
// Should be a lot smarter here - limit # of parallel attempts and
|
// Should be a lot smarter here -
|
||||||
// share blocks rather than starting from 0 with each peer.
|
// share blocks rather than starting from 0 with each peer.
|
||||||
// This is where the flaws of the snark data model are really exposed.
|
// This is where the flaws of the snark data model are really exposed.
|
||||||
// Could also randomize within the duplicate set rather than strict rarest-first
|
// Could also randomize within the duplicate set rather than strict rarest-first
|
||||||
@ -563,11 +602,83 @@ public class PeerCoordinator implements PeerListener
|
|||||||
_log.debug("parallel request (end game?) for " + peer + ": piece = " + piece);
|
_log.debug("parallel request (end game?) for " + peer + ": piece = " + piece);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Now requesting: piece " + piece + " priority " + piece.getPriority());
|
||||||
piece.setRequested(true);
|
piece.setRequested(true);
|
||||||
return piece.getId();
|
return piece.getId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps file priorities to piece priorities.
|
||||||
|
* Call after updating file priorities Storage.setPriority()
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
public void updatePiecePriorities() {
|
||||||
|
int[] pri = storage.getPiecePriorities();
|
||||||
|
if (pri == null) {
|
||||||
|
_log.debug("Updated piece priorities called but no priorities to set?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized(wantedPieces) {
|
||||||
|
// Add incomplete and previously unwanted pieces to the list
|
||||||
|
// Temp to avoid O(n**2)
|
||||||
|
BitField want = new BitField(pri.length);
|
||||||
|
for (Piece p : wantedPieces) {
|
||||||
|
want.set(p.getId());
|
||||||
|
}
|
||||||
|
BitField bitfield = storage.getBitField();
|
||||||
|
for (int i = 0; i < pri.length; i++) {
|
||||||
|
if (pri[i] >= 0 && !bitfield.get(i)) {
|
||||||
|
if (!want.get(i)) {
|
||||||
|
Piece piece = new Piece(i);
|
||||||
|
wantedPieces.add(piece);
|
||||||
|
// As connections are already up, new Pieces will
|
||||||
|
// not have their PeerID list populated, so do that.
|
||||||
|
synchronized(peers) {
|
||||||
|
for (Peer p : peers) {
|
||||||
|
PeerState s = p.state;
|
||||||
|
if (s != null) {
|
||||||
|
BitField bf = s.bitfield;
|
||||||
|
if (bf != null && bf.get(i))
|
||||||
|
piece.addPeer(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now set the new priorities and remove newly unwanted pieces
|
||||||
|
for (Iterator<Piece> iter = wantedPieces.iterator(); iter.hasNext(); ) {
|
||||||
|
Piece p = iter.next();
|
||||||
|
int priority = pri[p.getId()];
|
||||||
|
if (priority >= 0) {
|
||||||
|
p.setPriority(priority);
|
||||||
|
} else {
|
||||||
|
iter.remove();
|
||||||
|
// cancel all peers
|
||||||
|
synchronized(peers) {
|
||||||
|
for (Peer peer : peers) {
|
||||||
|
peer.cancel(p.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Updated piece priorities, now wanted: " + wantedPieces);
|
||||||
|
// if we added pieces, they will be in-order unless we shuffle
|
||||||
|
Collections.shuffle(wantedPieces, _random);
|
||||||
|
|
||||||
|
// update request queues, in case we added wanted pieces
|
||||||
|
// and we were previously uninterested
|
||||||
|
synchronized(peers) {
|
||||||
|
for (Peer peer : peers) {
|
||||||
|
peer.request();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a byte array containing the requested piece or null of
|
* Returns a byte array containing the requested piece or null of
|
||||||
* the piece is unknown.
|
* the piece is unknown.
|
||||||
@ -632,14 +743,18 @@ public class PeerCoordinator implements PeerListener
|
|||||||
|
|
||||||
// No need to announce have piece to peers.
|
// No need to announce have piece to peers.
|
||||||
// Assume we got a good piece, we don't really care anymore.
|
// Assume we got a good piece, we don't really care anymore.
|
||||||
return true;
|
// Well, this could be caused by a change in priorities, so
|
||||||
|
// only return true if we already have it, otherwise might as well keep it.
|
||||||
|
if (storage.getBitField().get(piece))
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (storage.putPiece(piece, bs))
|
if (storage.putPiece(piece, bs))
|
||||||
{
|
{
|
||||||
_log.info("Got valid piece " + piece + "/" + metainfo.getPieces() +" from " + peer + " for " + metainfo.getName());
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Got valid piece " + piece + "/" + metainfo.getPieces() +" from " + peer + " for " + metainfo.getName());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -55,12 +55,10 @@ class PeerState
|
|||||||
final PeerConnectionOut out;
|
final PeerConnectionOut out;
|
||||||
|
|
||||||
// Outstanding request
|
// Outstanding request
|
||||||
private final List outstandingRequests = new ArrayList();
|
private final List<Request> outstandingRequests = new ArrayList();
|
||||||
|
/** the tail (NOT the head) of the request queue */
|
||||||
private Request lastRequest = null;
|
private Request lastRequest = null;
|
||||||
|
|
||||||
// If we have te resend outstanding requests (true after we got choked).
|
|
||||||
private boolean resend = false;
|
|
||||||
|
|
||||||
private final static int MAX_PIPELINE = 5; // this is for outbound requests
|
private final static int MAX_PIPELINE = 5; // this is for outbound requests
|
||||||
private final static int MAX_PIPELINE_BYTES = 128*1024; // this is for inbound requests
|
private final static int MAX_PIPELINE_BYTES = 128*1024; // this is for inbound requests
|
||||||
public final static int PARTSIZE = 16*1024; // outbound request
|
public final static int PARTSIZE = 16*1024; // outbound request
|
||||||
@ -91,14 +89,13 @@ class PeerState
|
|||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(peer + " rcv " + (choke ? "" : "un") + "choked");
|
_log.debug(peer + " rcv " + (choke ? "" : "un") + "choked");
|
||||||
|
|
||||||
|
boolean resend = choked && !choke;
|
||||||
choked = choke;
|
choked = choke;
|
||||||
if (choked)
|
|
||||||
resend = true;
|
|
||||||
|
|
||||||
listener.gotChoke(peer, choke);
|
listener.gotChoke(peer, choke);
|
||||||
|
|
||||||
if (!choked && interesting)
|
if (interesting && !choked)
|
||||||
request();
|
request(resend);
|
||||||
}
|
}
|
||||||
|
|
||||||
void interestedMessage(boolean interest)
|
void interestedMessage(boolean interest)
|
||||||
@ -278,7 +275,7 @@ class PeerState
|
|||||||
synchronized private int getFirstOutstandingRequest(int piece)
|
synchronized private int getFirstOutstandingRequest(int piece)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < outstandingRequests.size(); i++)
|
for (int i = 0; i < outstandingRequests.size(); i++)
|
||||||
if (((Request)outstandingRequests.get(i)).piece == piece)
|
if (outstandingRequests.get(i).piece == piece)
|
||||||
return i;
|
return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -313,12 +310,12 @@ class PeerState
|
|||||||
Request req;
|
Request req;
|
||||||
synchronized(this)
|
synchronized(this)
|
||||||
{
|
{
|
||||||
req = (Request)outstandingRequests.get(r);
|
req = outstandingRequests.get(r);
|
||||||
while (req.piece == piece && req.off != begin
|
while (req.piece == piece && req.off != begin
|
||||||
&& r < outstandingRequests.size() - 1)
|
&& r < outstandingRequests.size() - 1)
|
||||||
{
|
{
|
||||||
r++;
|
r++;
|
||||||
req = (Request)outstandingRequests.get(r);
|
req = outstandingRequests.get(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Something wrong?
|
// Something wrong?
|
||||||
@ -342,7 +339,7 @@ class PeerState
|
|||||||
+ ", wanted for peer: " + peer);
|
+ ", wanted for peer: " + peer);
|
||||||
for (int i = 0; i < r; i++)
|
for (int i = 0; i < r; i++)
|
||||||
{
|
{
|
||||||
Request dropReq = (Request)outstandingRequests.remove(0);
|
Request dropReq = outstandingRequests.remove(0);
|
||||||
outstandingRequests.add(dropReq);
|
outstandingRequests.add(dropReq);
|
||||||
if (!choked)
|
if (!choked)
|
||||||
out.sendRequest(dropReq);
|
out.sendRequest(dropReq);
|
||||||
@ -366,11 +363,11 @@ class PeerState
|
|||||||
{
|
{
|
||||||
Request req = null;
|
Request req = null;
|
||||||
for (int i = 0; i < outstandingRequests.size(); i++) {
|
for (int i = 0; i < outstandingRequests.size(); i++) {
|
||||||
Request r1 = (Request)outstandingRequests.get(i);
|
Request r1 = outstandingRequests.get(i);
|
||||||
int j = getFirstOutstandingRequest(r1.piece);
|
int j = getFirstOutstandingRequest(r1.piece);
|
||||||
if (j == -1)
|
if (j == -1)
|
||||||
continue;
|
continue;
|
||||||
Request r2 = (Request)outstandingRequests.get(j);
|
Request r2 = outstandingRequests.get(j);
|
||||||
if (r2.off > 0 && ((req == null) || (r2.off > req.off)))
|
if (r2.off > 0 && ((req == null) || (r2.off > req.off)))
|
||||||
req = r2;
|
req = r2;
|
||||||
}
|
}
|
||||||
@ -398,7 +395,7 @@ class PeerState
|
|||||||
}
|
}
|
||||||
Request req = null;
|
Request req = null;
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
Request r1 = (Request)outstandingRequests.get(i);
|
Request r1 = outstandingRequests.get(i);
|
||||||
if (pc != r1.piece) {
|
if (pc != r1.piece) {
|
||||||
pc = r1.piece;
|
pc = r1.piece;
|
||||||
arr[pos++] = pc;
|
arr[pos++] = pc;
|
||||||
@ -423,32 +420,19 @@ class PeerState
|
|||||||
+ " length: " + bs.length);
|
+ " length: " + bs.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We now have this piece.
|
||||||
|
* Tell the peer and cancel any requests for the piece.
|
||||||
|
*/
|
||||||
void havePiece(int piece)
|
void havePiece(int piece)
|
||||||
{
|
{
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Tell " + peer + " havePiece(" + piece + ")");
|
_log.debug("Tell " + peer + " havePiece(" + piece + ")");
|
||||||
|
|
||||||
synchronized(this)
|
|
||||||
{
|
|
||||||
// Tell the other side that we are no longer interested in any of
|
// Tell the other side that we are no longer interested in any of
|
||||||
// the outstanding requests for this piece.
|
// the outstanding requests for this piece.
|
||||||
if (lastRequest != null && lastRequest.piece == piece)
|
cancelPiece(piece);
|
||||||
lastRequest = null;
|
|
||||||
|
|
||||||
Iterator it = outstandingRequests.iterator();
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
Request req = (Request)it.next();
|
|
||||||
if (req.piece == piece)
|
|
||||||
{
|
|
||||||
it.remove();
|
|
||||||
// Send cancel even when we are choked to make sure that it is
|
|
||||||
// really never ever send.
|
|
||||||
out.sendCancel(req);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell the other side that we really have this piece.
|
// Tell the other side that we really have this piece.
|
||||||
out.sendHave(piece);
|
out.sendHave(piece);
|
||||||
|
|
||||||
@ -463,8 +447,46 @@ class PeerState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts or resumes requesting pieces.
|
/**
|
||||||
private void request()
|
* Tell the other side that we are no longer interested in any of
|
||||||
|
* the outstanding requests (if any) for this piece.
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
synchronized void cancelPiece(int piece) {
|
||||||
|
if (lastRequest != null && lastRequest.piece == piece)
|
||||||
|
lastRequest = null;
|
||||||
|
|
||||||
|
Iterator<Request> it = outstandingRequests.iterator();
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
Request req = it.next();
|
||||||
|
if (req.piece == piece)
|
||||||
|
{
|
||||||
|
it.remove();
|
||||||
|
// Send cancel even when we are choked to make sure that it is
|
||||||
|
// really never ever send.
|
||||||
|
out.sendCancel(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are we currently requesting the piece?
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
synchronized boolean isRequesting(int piece) {
|
||||||
|
for (Request req : outstandingRequests) {
|
||||||
|
if (req.piece == piece)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts or resumes requesting pieces.
|
||||||
|
* @param resend should we resend outstanding requests?
|
||||||
|
*/
|
||||||
|
private void request(boolean resend)
|
||||||
{
|
{
|
||||||
// Are there outstanding requests that have to be resend?
|
// Are there outstanding requests that have to be resend?
|
||||||
if (resend)
|
if (resend)
|
||||||
@ -472,7 +494,6 @@ class PeerState
|
|||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
out.sendRequests(outstandingRequests);
|
out.sendRequests(outstandingRequests);
|
||||||
}
|
}
|
||||||
resend = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add/Send some more requests if necessary.
|
// Add/Send some more requests if necessary.
|
||||||
@ -481,8 +502,11 @@ class PeerState
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new request to the outstanding requests list.
|
* Adds a new request to the outstanding requests list.
|
||||||
|
* Then send interested if we weren't.
|
||||||
|
* Then send new requests if not choked.
|
||||||
|
* If nothing to request, send not interested if we were.
|
||||||
*/
|
*/
|
||||||
synchronized private void addRequest()
|
synchronized void addRequest()
|
||||||
{
|
{
|
||||||
boolean more_pieces = true;
|
boolean more_pieces = true;
|
||||||
while (more_pieces)
|
while (more_pieces)
|
||||||
@ -526,6 +550,7 @@ class PeerState
|
|||||||
/**
|
/**
|
||||||
* Starts requesting first chunk of next piece. Returns true if
|
* Starts requesting first chunk of next piece. Returns true if
|
||||||
* something has been added to the requests, false otherwise.
|
* something has been added to the requests, false otherwise.
|
||||||
|
* Caller should synchronize.
|
||||||
*/
|
*/
|
||||||
private boolean requestNextPiece()
|
private boolean requestNextPiece()
|
||||||
{
|
{
|
||||||
@ -553,11 +578,10 @@ class PeerState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
int nextPiece = listener.wantPiece(peer, bitfield);
|
int nextPiece = listener.wantPiece(peer, bitfield);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (nextPiece != -1
|
||||||
_log.debug(peer + " want piece " + nextPiece);
|
&& (lastRequest == null || lastRequest.piece != nextPiece)) {
|
||||||
if (nextPiece != -1
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
&& (lastRequest == null || lastRequest.piece != nextPiece))
|
_log.debug(peer + " want piece " + nextPiece);
|
||||||
{
|
|
||||||
// Fail safe to make sure we are interested
|
// Fail safe to make sure we are interested
|
||||||
// When we transition into the end game we may not be interested...
|
// When we transition into the end game we may not be interested...
|
||||||
if (!interesting) {
|
if (!interesting) {
|
||||||
@ -584,9 +608,25 @@ class PeerState
|
|||||||
out.sendRequest(req);
|
out.sendRequest(req);
|
||||||
lastRequest = req;
|
lastRequest = req;
|
||||||
return true;
|
return true;
|
||||||
}
|
} else {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug(peer + " no more pieces to request");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// failsafe
|
||||||
|
if (outstandingRequests.isEmpty())
|
||||||
|
lastRequest = null;
|
||||||
|
|
||||||
|
// If we are not in the end game, we may run out of things to request
|
||||||
|
// because we are asking other peers. Set not-interesting now rather than
|
||||||
|
// wait for those other requests to be satisfied via havePiece()
|
||||||
|
if (interesting && lastRequest == null) {
|
||||||
|
interesting = false;
|
||||||
|
out.sendInterest(false);
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug(peer + " nothing more to request, now uninteresting");
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,7 +641,7 @@ class PeerState
|
|||||||
out.sendInterest(interest);
|
out.sendInterest(interest);
|
||||||
|
|
||||||
if (interesting && !choked)
|
if (interesting && !choked)
|
||||||
request();
|
request(true); // we shouldnt have any pending requests, but if we do, resend them
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -627,4 +667,16 @@ class PeerState
|
|||||||
if (interesting && !choked)
|
if (interesting && !choked)
|
||||||
out.retransmitRequests(outstandingRequests);
|
out.retransmitRequests(outstandingRequests);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* debug
|
||||||
|
* @return string or null
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
synchronized String getRequests() {
|
||||||
|
if (outstandingRequests.isEmpty())
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return outstandingRequests.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,31 @@
|
|||||||
package org.klomp.snark;
|
package org.klomp.snark;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.i2p.util.ConcurrentHashSet;
|
||||||
|
|
||||||
public class Piece implements Comparable {
|
public class Piece implements Comparable {
|
||||||
|
|
||||||
private int id;
|
private int id;
|
||||||
private Set peers;
|
private Set<PeerID> peers;
|
||||||
private boolean requested;
|
private boolean requested;
|
||||||
|
/** @since 0.8.1 */
|
||||||
|
private int priority;
|
||||||
|
|
||||||
public Piece(int id) {
|
public Piece(int id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.peers = Collections.synchronizedSet(new HashSet());
|
this.peers = new ConcurrentHashSet();
|
||||||
this.requested = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highest priority first,
|
||||||
|
* then rarest first
|
||||||
|
*/
|
||||||
public int compareTo(Object o) throws ClassCastException {
|
public int compareTo(Object o) throws ClassCastException {
|
||||||
|
int pdiff = ((Piece)o).priority - this.priority; // reverse
|
||||||
|
if (pdiff != 0)
|
||||||
|
return pdiff;
|
||||||
return this.peers.size() - ((Piece)o).peers.size();
|
return this.peers.size() - ((Piece)o).peers.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,12 +46,25 @@ public class Piece implements Comparable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getId() { return this.id; }
|
public int getId() { return this.id; }
|
||||||
public Set getPeers() { return this.peers; }
|
/** @deprecated unused */
|
||||||
|
public Set<PeerID> getPeers() { return this.peers; }
|
||||||
public boolean addPeer(Peer peer) { return this.peers.add(peer.getPeerID()); }
|
public boolean addPeer(Peer peer) { return this.peers.add(peer.getPeerID()); }
|
||||||
public boolean removePeer(Peer peer) { return this.peers.remove(peer.getPeerID()); }
|
public boolean removePeer(Peer peer) { return this.peers.remove(peer.getPeerID()); }
|
||||||
public boolean isRequested() { return this.requested; }
|
public boolean isRequested() { return this.requested; }
|
||||||
public void setRequested(boolean requested) { this.requested = requested; }
|
public void setRequested(boolean requested) { this.requested = requested; }
|
||||||
|
|
||||||
|
/** @return default 0 @since 0.8.1 */
|
||||||
|
public int getPriority() { return this.priority; }
|
||||||
|
|
||||||
|
/** @since 0.8.1 */
|
||||||
|
public void setPriority(int p) { this.priority = p; }
|
||||||
|
|
||||||
|
/** @since 0.8.1 */
|
||||||
|
public boolean isDisabled() { return this.priority < 0; }
|
||||||
|
|
||||||
|
/** @since 0.8.1 */
|
||||||
|
public void setDisabled() { this.priority = -1; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.valueOf(id);
|
return String.valueOf(id);
|
||||||
|
@ -54,6 +54,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
public static final String PROP_DIR = "i2psnark.dir";
|
public static final String PROP_DIR = "i2psnark.dir";
|
||||||
public static final String PROP_META_PREFIX = "i2psnark.zmeta.";
|
public static final String PROP_META_PREFIX = "i2psnark.zmeta.";
|
||||||
public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
|
public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
|
||||||
|
public static final String PROP_META_PRIORITY_SUFFIX = ".priority";
|
||||||
|
|
||||||
private static final String CONFIG_FILE = "i2psnark.config";
|
private static final String CONFIG_FILE = "i2psnark.config";
|
||||||
public static final String PROP_AUTO_START = "i2snark.autoStart"; // oops
|
public static final String PROP_AUTO_START = "i2snark.autoStart"; // oops
|
||||||
@ -510,6 +511,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
torrent = new Snark(_util, filename, null, -1, null, null, this,
|
torrent = new Snark(_util, filename, null, -1, null, null, this,
|
||||||
_peerCoordinatorSet, _connectionAcceptor,
|
_peerCoordinatorSet, _connectionAcceptor,
|
||||||
false, dataDir.getPath());
|
false, dataDir.getPath());
|
||||||
|
loadSavedFilePriorities(torrent);
|
||||||
torrent.completeListener = this;
|
torrent.completeListener = this;
|
||||||
synchronized (_snarks) {
|
synchronized (_snarks) {
|
||||||
_snarks.put(filename, torrent);
|
_snarks.put(filename, torrent);
|
||||||
@ -587,6 +589,33 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
return new BitField(bitfield, len);
|
return new BitField(bitfield, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the saved priorities for a torrent from the config file.
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
public void loadSavedFilePriorities(Snark snark) {
|
||||||
|
MetaInfo metainfo = snark.meta;
|
||||||
|
if (metainfo.getFiles() == null)
|
||||||
|
return;
|
||||||
|
byte[] ih = metainfo.getInfoHash();
|
||||||
|
String infohash = Base64.encode(ih);
|
||||||
|
infohash = infohash.replace('=', '$');
|
||||||
|
String pri = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX);
|
||||||
|
if (pri == null)
|
||||||
|
return;
|
||||||
|
int filecount = metainfo.getFiles().size();
|
||||||
|
int[] rv = new int[filecount];
|
||||||
|
String[] arr = pri.split(",");
|
||||||
|
for (int i = 0; i < filecount && i < arr.length; i++) {
|
||||||
|
if (arr[i].length() > 0) {
|
||||||
|
try {
|
||||||
|
rv[i] = Integer.parseInt(arr[i]);
|
||||||
|
} catch (Throwable t) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snark.storage.setFilePriorities(rv);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the completion status of a torrent and the current time in the config file
|
* Save the completion status of a torrent and the current time in the config file
|
||||||
* in the form "i2psnark.zmeta.$base64infohash=$time,$base64bitfield".
|
* in the form "i2psnark.zmeta.$base64infohash=$time,$base64bitfield".
|
||||||
@ -595,8 +624,9 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
* The time is a standard long converted to string.
|
* The time is a standard long converted to string.
|
||||||
* The status is either a bitfield converted to Base64 or "." for a completed
|
* The status is either a bitfield converted to Base64 or "." for a completed
|
||||||
* torrent to save space in the config file and in memory.
|
* torrent to save space in the config file and in memory.
|
||||||
|
* @param priorities may be null
|
||||||
*/
|
*/
|
||||||
public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield) {
|
public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities) {
|
||||||
byte[] ih = metainfo.getInfoHash();
|
byte[] ih = metainfo.getInfoHash();
|
||||||
String infohash = Base64.encode(ih);
|
String infohash = Base64.encode(ih);
|
||||||
infohash = infohash.replace('=', '$');
|
infohash = infohash.replace('=', '$');
|
||||||
@ -609,6 +639,34 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
bfs = Base64.encode(bf);
|
bfs = Base64.encode(bf);
|
||||||
}
|
}
|
||||||
_config.setProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX, now + "," + bfs);
|
_config.setProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX, now + "," + bfs);
|
||||||
|
|
||||||
|
// now the file priorities
|
||||||
|
String prop = PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX;
|
||||||
|
if (priorities != null) {
|
||||||
|
boolean nonzero = false;
|
||||||
|
for (int i = 0; i < priorities.length; i++) {
|
||||||
|
if (priorities[i] != 0) {
|
||||||
|
nonzero = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nonzero) {
|
||||||
|
// generate string like -5,,4,3,,,,,,-2 where no number is zero.
|
||||||
|
StringBuilder buf = new StringBuilder(2 * priorities.length);
|
||||||
|
for (int i = 0; i < priorities.length; i++) {
|
||||||
|
if (priorities[i] != 0)
|
||||||
|
buf.append(Integer.toString(priorities[i]));
|
||||||
|
if (i != priorities.length - 1)
|
||||||
|
buf.append(',');
|
||||||
|
}
|
||||||
|
_config.setProperty(prop, buf.toString());
|
||||||
|
} else {
|
||||||
|
_config.remove(prop);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_config.remove(prop);
|
||||||
|
}
|
||||||
|
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -621,6 +679,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
String infohash = Base64.encode(ih);
|
String infohash = Base64.encode(ih);
|
||||||
infohash = infohash.replace('=', '$');
|
infohash = infohash.replace('=', '$');
|
||||||
_config.remove(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
|
_config.remove(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
|
||||||
|
_config.remove(PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX);
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -742,7 +801,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateStatus(Snark snark) {
|
public void updateStatus(Snark snark) {
|
||||||
saveTorrentStatus(snark.meta, snark.storage.getBitField());
|
saveTorrentStatus(snark.meta, snark.storage.getBitField(), snark.storage.getFilePriorities());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void monitorTorrents(File dir) {
|
private void monitorTorrents(File dir) {
|
||||||
|
@ -42,6 +42,8 @@ public class Storage
|
|||||||
private Object[] RAFlock; // lock on RAF access
|
private Object[] RAFlock; // lock on RAF access
|
||||||
private long[] RAFtime; // when was RAF last accessed, or 0 if closed
|
private long[] RAFtime; // when was RAF last accessed, or 0 if closed
|
||||||
private File[] RAFfile; // File to make it easier to reopen
|
private File[] RAFfile; // File to make it easier to reopen
|
||||||
|
/** priorities by file; default 0; may be null. @since 0.8.1 */
|
||||||
|
private int[] priorities;
|
||||||
|
|
||||||
private final StorageListener listener;
|
private final StorageListener listener;
|
||||||
private I2PSnarkUtil _util;
|
private I2PSnarkUtil _util;
|
||||||
@ -228,6 +230,8 @@ public class Storage
|
|||||||
RAFlock = new Object[size];
|
RAFlock = new Object[size];
|
||||||
RAFtime = new long[size];
|
RAFtime = new long[size];
|
||||||
RAFfile = new File[size];
|
RAFfile = new File[size];
|
||||||
|
priorities = new int[size];
|
||||||
|
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
Iterator it = files.iterator();
|
Iterator it = files.iterator();
|
||||||
@ -330,6 +334,102 @@ public class Storage
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param file canonical path (non-directory)
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
public int getPriority(String file) {
|
||||||
|
if (complete() || metainfo.getFiles() == null || priorities == null)
|
||||||
|
return 0;
|
||||||
|
for (int i = 0; i < rafs.length; i++) {
|
||||||
|
File f = RAFfile[i];
|
||||||
|
// use canonical in case snark dir or sub dirs are symlinked
|
||||||
|
if (f != null) {
|
||||||
|
try {
|
||||||
|
String canonical = f.getCanonicalPath();
|
||||||
|
if (canonical.equals(file))
|
||||||
|
return priorities[i];
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must call setPiecePriorities() after calling this
|
||||||
|
* @param file canonical path (non-directory)
|
||||||
|
* @param priority default 0; <0 to disable
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
public void setPriority(String file, int pri) {
|
||||||
|
if (complete() || metainfo.getFiles() == null || priorities == null)
|
||||||
|
return;
|
||||||
|
for (int i = 0; i < rafs.length; i++) {
|
||||||
|
File f = RAFfile[i];
|
||||||
|
// use canonical in case snark dir or sub dirs are symlinked
|
||||||
|
if (f != null) {
|
||||||
|
try {
|
||||||
|
String canonical = f.getCanonicalPath();
|
||||||
|
if (canonical.equals(file)) {
|
||||||
|
priorities[i] = pri;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file priorities array.
|
||||||
|
* @return null on error, if complete, or if only one file
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
public int[] getFilePriorities() {
|
||||||
|
return priorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the file priorities array.
|
||||||
|
* Only call this when stopped, but after check()
|
||||||
|
* @param p may be null
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
void setFilePriorities(int[] p) {
|
||||||
|
priorities = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call setPriority() for all changed files first,
|
||||||
|
* then call this.
|
||||||
|
* Set the piece priority to the highest priority
|
||||||
|
* of all files spanning the piece.
|
||||||
|
* Caller must pass array to the PeerCoordinator.
|
||||||
|
* @return null on error, if complete, or if only one file
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
public int[] getPiecePriorities() {
|
||||||
|
if (complete() || metainfo.getFiles() == null || priorities == null)
|
||||||
|
return null;
|
||||||
|
int[] rv = new int[metainfo.getPieces()];
|
||||||
|
int file = 0;
|
||||||
|
long pcEnd = -1;
|
||||||
|
long fileEnd = lengths[0] - 1;
|
||||||
|
int psz = metainfo.getPieceLength(0);
|
||||||
|
for (int i = 0; i < rv.length; i++) {
|
||||||
|
pcEnd += psz;
|
||||||
|
int pri = priorities[file];
|
||||||
|
while (fileEnd <= pcEnd && file < lengths.length - 1) {
|
||||||
|
file++;
|
||||||
|
long oldFileEnd = fileEnd;
|
||||||
|
fileEnd += lengths[file];
|
||||||
|
if (priorities[file] > pri && oldFileEnd < pcEnd)
|
||||||
|
pri = priorities[file];
|
||||||
|
}
|
||||||
|
rv[i] = pri;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The BitField that tells which pieces this storage contains.
|
* The BitField that tells which pieces this storage contains.
|
||||||
* Do not change this since this is the current state of the storage.
|
* Do not change this since this is the current state of the storage.
|
||||||
@ -436,10 +536,14 @@ public class Storage
|
|||||||
changed = true;
|
changed = true;
|
||||||
checkCreateFiles();
|
checkCreateFiles();
|
||||||
}
|
}
|
||||||
if (complete())
|
if (complete()) {
|
||||||
_util.debug("Torrent is complete", Snark.NOTICE);
|
_util.debug("Torrent is complete", Snark.NOTICE);
|
||||||
else
|
} else {
|
||||||
|
// fixme saved priorities
|
||||||
|
if (files != null)
|
||||||
|
priorities = new int[files.size()];
|
||||||
_util.debug("Still need " + needed + " out of " + metainfo.getPieces() + " pieces", Snark.NOTICE);
|
_util.debug("Still need " + needed + " out of " + metainfo.getPieces() + " pieces", Snark.NOTICE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -565,6 +669,10 @@ public class Storage
|
|||||||
changed = true;
|
changed = true;
|
||||||
synchronized(RAFlock[i]) {
|
synchronized(RAFlock[i]) {
|
||||||
allocateFile(i);
|
allocateFile(i);
|
||||||
|
// close as we go so we don't run out of file descriptors
|
||||||
|
try {
|
||||||
|
closeRAF(i);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_util.debug("File '" + names[i] + "' exists, but has wrong length - repairing corruption", Snark.ERROR);
|
_util.debug("File '" + names[i] + "' exists, but has wrong length - repairing corruption", Snark.ERROR);
|
||||||
@ -573,8 +681,10 @@ public class Storage
|
|||||||
synchronized(RAFlock[i]) {
|
synchronized(RAFlock[i]) {
|
||||||
checkRAF(i);
|
checkRAF(i);
|
||||||
rafs[i].setLength(lengths[i]);
|
rafs[i].setLength(lengths[i]);
|
||||||
|
try {
|
||||||
|
closeRAF(i);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
}
|
}
|
||||||
// will be closed below
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,10 +693,25 @@ public class Storage
|
|||||||
{
|
{
|
||||||
pieces = metainfo.getPieces();
|
pieces = metainfo.getPieces();
|
||||||
byte[] piece = new byte[metainfo.getPieceLength(0)];
|
byte[] piece = new byte[metainfo.getPieceLength(0)];
|
||||||
|
int file = 0;
|
||||||
|
long fileEnd = lengths[0];
|
||||||
|
long pieceEnd = 0;
|
||||||
for (int i = 0; i < pieces; i++)
|
for (int i = 0; i < pieces; i++)
|
||||||
{
|
{
|
||||||
int length = getUncheckedPiece(i, piece);
|
int length = getUncheckedPiece(i, piece);
|
||||||
boolean correctHash = metainfo.checkPiece(i, piece, 0, length);
|
boolean correctHash = metainfo.checkPiece(i, piece, 0, length);
|
||||||
|
// close as we go so we don't run out of file descriptors
|
||||||
|
pieceEnd += length;
|
||||||
|
while (fileEnd <= pieceEnd) {
|
||||||
|
synchronized(RAFlock[file]) {
|
||||||
|
try {
|
||||||
|
closeRAF(file);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
if (++file >= rafs.length)
|
||||||
|
break;
|
||||||
|
fileEnd += lengths[file];
|
||||||
|
}
|
||||||
if (correctHash)
|
if (correctHash)
|
||||||
{
|
{
|
||||||
bitfield.set(i);
|
bitfield.set(i);
|
||||||
@ -601,13 +726,14 @@ public class Storage
|
|||||||
_probablyComplete = complete();
|
_probablyComplete = complete();
|
||||||
// close all the files so we don't end up with a zillion open ones;
|
// close all the files so we don't end up with a zillion open ones;
|
||||||
// we will reopen as needed
|
// we will reopen as needed
|
||||||
for (int i = 0; i < rafs.length; i++) {
|
// Now closed above to avoid running out of file descriptors
|
||||||
synchronized(RAFlock[i]) {
|
//for (int i = 0; i < rafs.length; i++) {
|
||||||
try {
|
// synchronized(RAFlock[i]) {
|
||||||
closeRAF(i);
|
// try {
|
||||||
} catch (IOException ioe) {}
|
// closeRAF(i);
|
||||||
}
|
// } catch (IOException ioe) {}
|
||||||
}
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.storageAllChecked(this);
|
listener.storageAllChecked(this);
|
||||||
@ -616,6 +742,7 @@ public class Storage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** this calls openRAF(); caller must synnchronize and call closeRAF() */
|
||||||
private void allocateFile(int nr) throws IOException
|
private void allocateFile(int nr) throws IOException
|
||||||
{
|
{
|
||||||
// caller synchronized
|
// caller synchronized
|
||||||
@ -624,7 +751,12 @@ public class Storage
|
|||||||
// the whole file?
|
// the whole file?
|
||||||
listener.storageCreateFile(this, names[nr], lengths[nr]);
|
listener.storageCreateFile(this, names[nr], lengths[nr]);
|
||||||
final int ZEROBLOCKSIZE = metainfo.getPieceLength(0);
|
final int ZEROBLOCKSIZE = metainfo.getPieceLength(0);
|
||||||
byte[] zeros = new byte[ZEROBLOCKSIZE];
|
byte[] zeros;
|
||||||
|
try {
|
||||||
|
zeros = new byte[ZEROBLOCKSIZE];
|
||||||
|
} catch (OutOfMemoryError oom) {
|
||||||
|
throw new IOException(oom.toString());
|
||||||
|
}
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < lengths[nr]/ZEROBLOCKSIZE; i++)
|
for (i = 0; i < lengths[nr]/ZEROBLOCKSIZE; i++)
|
||||||
{
|
{
|
||||||
|
@ -266,9 +266,9 @@ public class TrackerClient extends I2PAppThread
|
|||||||
// we only want to talk to new people if we need things
|
// we only want to talk to new people if we need things
|
||||||
// from them (duh)
|
// from them (duh)
|
||||||
List ordered = new ArrayList(peers);
|
List ordered = new ArrayList(peers);
|
||||||
Collections.shuffle(ordered);
|
Collections.shuffle(ordered, r);
|
||||||
Iterator it = ordered.iterator();
|
Iterator it = ordered.iterator();
|
||||||
while (it.hasNext()) {
|
while ((!stop) && it.hasNext()) {
|
||||||
Peer cur = (Peer)it.next();
|
Peer cur = (Peer)it.next();
|
||||||
// FIXME if id == us || dest == us continue;
|
// FIXME if id == us || dest == us continue;
|
||||||
// only delay if we actually make an attempt to add peer
|
// only delay if we actually make an attempt to add peer
|
||||||
@ -357,7 +357,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
+ "&uploaded=" + uploaded
|
+ "&uploaded=" + uploaded
|
||||||
+ "&downloaded=" + downloaded
|
+ "&downloaded=" + downloaded
|
||||||
+ "&left=" + left
|
+ "&left=" + left
|
||||||
+ "&compact"
|
+ "&compact=1" // NOTE: opentracker will return 400 for &compact alone
|
||||||
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
|
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
|
||||||
if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
|
if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
|
||||||
s += "&numwant=0";
|
s += "&numwant=0";
|
||||||
|
@ -133,6 +133,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
// bypass the horrid Resource.getListHTML()
|
// bypass the horrid Resource.getListHTML()
|
||||||
String pathInfo = req.getPathInfo();
|
String pathInfo = req.getPathInfo();
|
||||||
String pathInContext = URI.addPaths(path, pathInfo);
|
String pathInContext = URI.addPaths(path, pathInfo);
|
||||||
|
req.setCharacterEncoding("UTF-8");
|
||||||
resp.setCharacterEncoding("UTF-8");
|
resp.setCharacterEncoding("UTF-8");
|
||||||
resp.setContentType("text/html; charset=UTF-8");
|
resp.setContentType("text/html; charset=UTF-8");
|
||||||
Resource resource = getResource(pathInContext);
|
Resource resource = getResource(pathInContext);
|
||||||
@ -140,7 +141,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
resp.sendError(HttpResponse.__404_Not_Found);
|
resp.sendError(HttpResponse.__404_Not_Found);
|
||||||
} else {
|
} else {
|
||||||
String base = URI.addPaths(req.getRequestURI(), "/");
|
String base = URI.addPaths(req.getRequestURI(), "/");
|
||||||
String listing = getListHTML(resource, base, true);
|
String listing = getListHTML(resource, base, true, method.equals("POST") ? req.getParameterMap() : null);
|
||||||
if (listing != null)
|
if (listing != null)
|
||||||
resp.getWriter().write(listing);
|
resp.getWriter().write(listing);
|
||||||
else // shouldn't happen
|
else // shouldn't happen
|
||||||
@ -241,7 +242,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(TABLE_HEADER);
|
out.write(TABLE_HEADER);
|
||||||
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\"");
|
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\"");
|
||||||
out.write(" title=\"");
|
out.write(" title=\"");
|
||||||
out.write(_("Torrent Status"));
|
out.write(_("Status"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
out.write(_("Status"));
|
out.write(_("Status"));
|
||||||
if (_manager.util().connected() && !snarks.isEmpty()) {
|
if (_manager.util().connected() && !snarks.isEmpty()) {
|
||||||
@ -249,24 +250,24 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(req.getRequestURI());
|
out.write(req.getRequestURI());
|
||||||
if (peerParam != null) {
|
if (peerParam != null) {
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/showpeers.png\" title=\"");
|
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/showpeers.png\" title=\"");
|
||||||
out.write(_("Toggle Peer Visibility"));
|
out.write(_("Hide Peers"));
|
||||||
out.write("\" alt=\"");
|
out.write("\" alt=\"");
|
||||||
out.write(_("Hide Peers"));
|
out.write(_("Hide Peers"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
} else {
|
} else {
|
||||||
out.write("?p=1\">");
|
out.write("?p=1\">");
|
||||||
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/hidepeers.png\" title=\"");
|
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/hidepeers.png\" title=\"");
|
||||||
out.write(_("Toggle Peer Visibility"));
|
out.write(_("Show Peers"));
|
||||||
out.write("\" alt=\"");
|
out.write("\" alt=\"");
|
||||||
out.write(_("Show Peers"));
|
out.write(_("Show Peers"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
}
|
}
|
||||||
out.write("</a><br>\n");
|
out.write("</a><br>\n");
|
||||||
}
|
}
|
||||||
out.write("</th>\n<th align=\"left\">");
|
out.write("</th>\n<th align=\"left\">");
|
||||||
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/torrent.png\" title=\"");
|
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/torrent.png\" title=\"");
|
||||||
out.write(_("Loaded Torrents"));
|
out.write(_("Torrent"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
out.write(_("Torrent"));
|
out.write(_("Torrent"));
|
||||||
out.write("</th>\n<th align=\"center\">");
|
out.write("</th>\n<th align=\"center\">");
|
||||||
@ -276,21 +277,21 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(_("ETA"));
|
out.write(_("ETA"));
|
||||||
out.write("</th>\n<th align=\"center\">");
|
out.write("</th>\n<th align=\"center\">");
|
||||||
out.write("<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"");
|
out.write("<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"");
|
||||||
out.write(_("Data Downloaded"));
|
out.write(_("Downloaded"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
out.write(_("RX"));
|
out.write(_("RX"));
|
||||||
out.write("</th>\n<th align=\"center\">");
|
out.write("</th>\n<th align=\"center\">");
|
||||||
out.write("<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"");
|
out.write("<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"");
|
||||||
out.write(_("Data Uploaded"));
|
out.write(_("Uploaded"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
out.write(_("TX"));
|
out.write(_("TX"));
|
||||||
out.write("</th>\n<th align=\"center\">");
|
out.write("</th>\n<th align=\"center\">");
|
||||||
out.write("<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"");
|
out.write("<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"");
|
||||||
out.write(_("Download Speed"));
|
out.write(_("Down Rate"));
|
||||||
out.write("\">Rate");
|
out.write("\">Rate");
|
||||||
out.write("</th>\n<th align=\"center\">");
|
out.write("</th>\n<th align=\"center\">");
|
||||||
out.write("<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"");
|
out.write("<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"");
|
||||||
out.write(_("Upload Speed"));
|
out.write(_("Up Rate"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
out.write(_("Rate"));
|
out.write(_("Rate"));
|
||||||
out.write("</th>\n");
|
out.write("</th>\n");
|
||||||
@ -301,7 +302,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(_("Stop all torrents and the I2P tunnel"));
|
out.write(_("Stop all torrents and the I2P tunnel"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
out.write("<img src=\"/themes/snark/ubergine/images/stop_all.png\" title=\"");
|
out.write("<img src=\"/themes/snark/ubergine/images/stop_all.png\" title=\"");
|
||||||
out.write(_("Stop All Torrents"));
|
out.write(_("Stop all torrents and the I2P tunnel"));
|
||||||
out.write("\" alt=\"");
|
out.write("\" alt=\"");
|
||||||
out.write(_("Stop All"));
|
out.write(_("Stop All"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
@ -312,7 +313,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(_("Start all torrents and the I2P tunnel"));
|
out.write(_("Start all torrents and the I2P tunnel"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
out.write("<img src=\"/themes/snark/ubergine/images/start_all.png\" title=\"");
|
out.write("<img src=\"/themes/snark/ubergine/images/start_all.png\" title=\"");
|
||||||
out.write(_("Start All Torrents"));
|
out.write(_("Start all torrents and the I2P tunnel"));
|
||||||
out.write("\" alt=\"Start All\">");
|
out.write("\" alt=\"Start All\">");
|
||||||
out.write("</a>");
|
out.write("</a>");
|
||||||
} else {
|
} else {
|
||||||
@ -537,7 +538,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
File torrentFile = new File(baseFile.getParent(), baseFile.getName() + ".torrent");
|
File torrentFile = new File(baseFile.getParent(), baseFile.getName() + ".torrent");
|
||||||
if (torrentFile.exists())
|
if (torrentFile.exists())
|
||||||
throw new IOException("Cannot overwrite an existing .torrent file: " + torrentFile.getPath());
|
throw new IOException("Cannot overwrite an existing .torrent file: " + torrentFile.getPath());
|
||||||
_manager.saveTorrentStatus(info, s.getBitField()); // so addTorrent won't recheck
|
_manager.saveTorrentStatus(info, s.getBitField(), null); // so addTorrent won't recheck
|
||||||
// DirMonitor could grab this first, maybe hold _snarks lock?
|
// DirMonitor could grab this first, maybe hold _snarks lock?
|
||||||
FileOutputStream out = new FileOutputStream(torrentFile);
|
FileOutputStream out = new FileOutputStream(torrentFile);
|
||||||
out.write(info.getTorrentData());
|
out.write(info.getTorrentData());
|
||||||
@ -564,6 +565,8 @@ public class I2PSnarkServlet extends Default {
|
|||||||
_manager.stopTorrent(snark.torrent, false);
|
_manager.stopTorrent(snark.torrent, false);
|
||||||
}
|
}
|
||||||
if (_manager.util().connected()) {
|
if (_manager.util().connected()) {
|
||||||
|
// Give the stopped announces time to get out
|
||||||
|
try { Thread.sleep(2000); } catch (InterruptedException ie) {}
|
||||||
_manager.util().disconnect();
|
_manager.util().disconnect();
|
||||||
_manager.addMessage(_("I2P tunnel closed."));
|
_manager.addMessage(_("I2P tunnel closed."));
|
||||||
}
|
}
|
||||||
@ -718,7 +721,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
curPeers + "/" +
|
curPeers + "/" +
|
||||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
||||||
else
|
else
|
||||||
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/complete.png\" title=\"" + _("Complete") + "\">" + _("Not Seeding");
|
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/complete.png\" title=\"" + _("Complete") + "\">" + _("Complete");
|
||||||
} else {
|
} else {
|
||||||
if (isRunning && curPeers > 0 && downBps > 0 && !showPeers)
|
if (isRunning && curPeers > 0 && downBps > 0 && !showPeers)
|
||||||
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/downloading.png\" title=\"" + _("Downloading") + "\">" +
|
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/downloading.png\" title=\"" + _("Downloading") + "\">" +
|
||||||
@ -727,7 +730,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
||||||
else if (isRunning && curPeers > 0 && downBps > 0)
|
else if (isRunning && curPeers > 0 && downBps > 0)
|
||||||
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/downloading.png\" title=\"" + _("Downloading") + "\">" +
|
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/downloading.png\" title=\"" + _("Downloading") + "\">" +
|
||||||
" (" + curPeers + "/" +
|
curPeers + "/" +
|
||||||
ngettext("1 peer", "{0} peers", knownPeers);
|
ngettext("1 peer", "{0} peers", knownPeers);
|
||||||
else if (isRunning && curPeers > 0 && !showPeers)
|
else if (isRunning && curPeers > 0 && !showPeers)
|
||||||
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/stalled.png\" title=\"" + _("Stalled") + "\">" +
|
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/stalled.png\" title=\"" + _("Stalled") + "\">" +
|
||||||
@ -790,7 +793,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
baseURL = baseURL.substring(e + 1);
|
baseURL = baseURL.substring(e + 1);
|
||||||
out.write(" <a href=\"" + baseURL + "details.php?dllist=1&filelist=1&info_hash=");
|
out.write(" <a href=\"" + baseURL + "details.php?dllist=1&filelist=1&info_hash=");
|
||||||
out.write(TrackerClient.urlencode(snark.meta.getInfoHash()));
|
out.write(TrackerClient.urlencode(snark.meta.getInfoHash()));
|
||||||
out.write("\" title=\"" + name + _("Tracker") + "\" target=\"_blank\">");
|
out.write("\" title=\"" + name + ' ' + _("Tracker") + "\" target=\"_blank\">");
|
||||||
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/details.png\">");
|
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/details.png\">");
|
||||||
out.write("</a>");
|
out.write("</a>");
|
||||||
break;
|
break;
|
||||||
@ -804,7 +807,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write("</td>\n\t");
|
out.write("</td>\n\t");
|
||||||
out.write("<td align=\"right\" class=\"snarkTorrentDownloaded " + rowClass + "\">");
|
out.write("<td align=\"right\" class=\"snarkTorrentDownloaded " + rowClass + "\">");
|
||||||
if (remaining > 0)
|
if (remaining > 0)
|
||||||
out.write(formatSize(total-remaining) + "/" + formatSize(total)); // 18MB/3GB
|
out.write(formatSize(total-remaining) + " / " + formatSize(total)); // 18MB/3GB; thin space so it will line break well
|
||||||
else
|
else
|
||||||
out.write(formatSize(total)); // 3GB
|
out.write(formatSize(total)); // 3GB
|
||||||
out.write("</td>\n\t");
|
out.write("</td>\n\t");
|
||||||
@ -828,7 +831,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(_("Stop the torrent"));
|
out.write(_("Stop the torrent"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
out.write("<img src=\"/themes/snark/ubergine/images/stop.png\" title=\"");
|
out.write("<img src=\"/themes/snark/ubergine/images/stop.png\" title=\"");
|
||||||
out.write(_("Stop Torrent"));
|
out.write(_("Stop"));
|
||||||
out.write("\" alt=\"");
|
out.write("\" alt=\"");
|
||||||
out.write(_("Stop"));
|
out.write(_("Stop"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
@ -840,7 +843,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(_("Start the torrent"));
|
out.write(_("Start the torrent"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
out.write("<img src=\"/themes/snark/ubergine/images/start.png\" title=\"");
|
out.write("<img src=\"/themes/snark/ubergine/images/start.png\" title=\"");
|
||||||
out.write(_("Start Torrent"));
|
out.write(_("Start the torrent"));
|
||||||
out.write("\" alt=\"");
|
out.write("\" alt=\"");
|
||||||
out.write(_("Start"));
|
out.write(_("Start"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
@ -856,7 +859,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(_("Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?", fullFilename));
|
out.write(_("Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?", fullFilename));
|
||||||
out.write("')) { return false; }\">");
|
out.write("')) { return false; }\">");
|
||||||
out.write("<img src=\"/themes/snark/ubergine/images/remove.png\" title=\"");
|
out.write("<img src=\"/themes/snark/ubergine/images/remove.png\" title=\"");
|
||||||
out.write(_("Remove Torrent"));
|
out.write(_("Remove"));
|
||||||
out.write("\" alt=\"");
|
out.write("\" alt=\"");
|
||||||
out.write(_("Remove"));
|
out.write(_("Remove"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
@ -871,7 +874,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullFilename));
|
out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullFilename));
|
||||||
out.write("')) { return false; }\">");
|
out.write("')) { return false; }\">");
|
||||||
out.write("<img src=\"/themes/snark/ubergine/images/delete.png\" title=\"");
|
out.write("<img src=\"/themes/snark/ubergine/images/delete.png\" title=\"");
|
||||||
out.write(_("Delete Torrent + Data"));
|
out.write(_("Delete"));
|
||||||
out.write("\" alt=\"");
|
out.write("\" alt=\"");
|
||||||
out.write(_("Delete"));
|
out.write(_("Delete"));
|
||||||
out.write("\">");
|
out.write("\">");
|
||||||
@ -1007,7 +1010,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(_("Add torrent"));
|
out.write(_("Add torrent"));
|
||||||
out.write("\" name=\"foo\" ><br>\n");
|
out.write("\" name=\"foo\" ><br>\n");
|
||||||
out.write("<tr><td> <td><span class=\"snarkAddInfo\">");
|
out.write("<tr><td> <td><span class=\"snarkAddInfo\">");
|
||||||
out.write(_("You can also copy .torrent files to: {0}.", "<code>" + _manager.getDataDir().getAbsolutePath ())) + "</code>";
|
out.write(_("You can also copy .torrent files to: {0}.", "<code>" + _manager.getDataDir().getAbsolutePath () + "</code>"));
|
||||||
out.write("\n");
|
out.write("\n");
|
||||||
out.write(_("Removing a .torrent will cause it to stop."));
|
out.write(_("Removing a .torrent will cause it to stop."));
|
||||||
out.write("<br></span></table>\n");
|
out.write("<br></span></table>\n");
|
||||||
@ -1262,19 +1265,21 @@ public class I2PSnarkServlet extends Default {
|
|||||||
// rounding makes us look faster :)
|
// rounding makes us look faster :)
|
||||||
private static String formatSize(long bytes) {
|
private static String formatSize(long bytes) {
|
||||||
if (bytes < 5*1024)
|
if (bytes < 5*1024)
|
||||||
return bytes + " B";
|
return bytes + " B";
|
||||||
else if (bytes < 5*1024*1024)
|
else if (bytes < 5*1024*1024)
|
||||||
return ((bytes + 512)/1024) + " KB";
|
return ((bytes + 512)/1024) + " KB";
|
||||||
else if (bytes < 10*1024*1024*1024l)
|
else if (bytes < 10*1024*1024*1024l)
|
||||||
return ((bytes + 512*1024)/(1024*1024)) + " MB";
|
return ((bytes + 512*1024)/(1024*1024)) + " MB";
|
||||||
else
|
else
|
||||||
return ((bytes + 512*1024*1024)/(1024*1024*1024)) + " GB";
|
return ((bytes + 512*1024*1024)/(1024*1024*1024)) + " GB";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 0.7.14 */
|
/** @since 0.7.14 */
|
||||||
private static String urlify(String s) {
|
private static String urlify(String s) {
|
||||||
StringBuilder buf = new StringBuilder(256);
|
StringBuilder buf = new StringBuilder(256);
|
||||||
buf.append("<a href=\"").append(s).append("\">").append(s).append("</a>");
|
// browsers seem to work without doing this but let's be strict
|
||||||
|
String link = s.replace("&", "&");
|
||||||
|
buf.append("<a href=\"").append(link).append("\">").append(link).append("</a>");
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1313,10 +1318,11 @@ public class I2PSnarkServlet extends Default {
|
|||||||
* @param r The Resource
|
* @param r The Resource
|
||||||
* @param base The base URL
|
* @param base The base URL
|
||||||
* @param parent True if the parent directory should be included
|
* @param parent True if the parent directory should be included
|
||||||
|
* @param postParams map of POST parameters or null if not a POST
|
||||||
* @return String of HTML
|
* @return String of HTML
|
||||||
* @since 0.7.14
|
* @since 0.7.14
|
||||||
*/
|
*/
|
||||||
private String getListHTML(Resource r, String base, boolean parent)
|
private String getListHTML(Resource r, String base, boolean parent, Map postParams)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
if (!r.isDirectory())
|
if (!r.isDirectory())
|
||||||
@ -1341,19 +1347,40 @@ public class I2PSnarkServlet extends Default {
|
|||||||
else
|
else
|
||||||
torrentName = title;
|
torrentName = title;
|
||||||
Snark snark = _manager.getTorrentByBaseName(torrentName);
|
Snark snark = _manager.getTorrentByBaseName(torrentName);
|
||||||
|
|
||||||
|
if (snark != null && postParams != null)
|
||||||
|
savePriorities(snark, postParams);
|
||||||
|
|
||||||
if (title.endsWith("/"))
|
if (title.endsWith("/"))
|
||||||
title = title.substring(0, title.length() - 1);
|
title = title.substring(0, title.length() - 1);
|
||||||
title = _("Torrent") + ": " + title;
|
title = _("Torrent") + ": " + title;
|
||||||
buf.append(title);
|
buf.append(title);
|
||||||
buf.append("</TITLE>").append(HEADER).append("<link rel=\"shortcut icon\" href=\"/themes/snark/ubergine/favicon.ico\"></HEAD><BODY>\n<center><div class=\"snarknavbar\"> <a href=\"/i2psnark/\" title=\"Torrents\"");
|
buf.append("</TITLE>").append(HEADER).append("<link rel=\"shortcut icon\" href=\"/themes/snark/ubergine/favicon.ico\"></HEAD><BODY>\n<center><div class=\"snarknavbar\"> <a href=\"/i2psnark/\" title=\"Torrents\"");
|
||||||
buf.append(" class=\"snarkRefresh\">I2PSnark</a>").append("</div>");
|
buf.append(" class=\"snarkRefresh\">I2PSnark</a>").append("</div>");
|
||||||
|
|
||||||
buf.append("<div class=\"page\"><div class=\"mainsection\">" +
|
if (parent)
|
||||||
"<TABLE BORDER=0 class=\"snarkTorrents\" cellpadding=\"5px 10px\">" +
|
{
|
||||||
|
buf.append("\n<br><A HREF=\"");
|
||||||
|
// corrupts utf-8
|
||||||
|
//buf.append(URI.encodePath(URI.addPaths(base,"../")));
|
||||||
|
buf.append(URI.addPaths(base,"../"));
|
||||||
|
buf.append("\"><img border=\"0\" src=\"/themes/console/images/outbound.png\"> ")
|
||||||
|
.append(_("Up to higher level directory")).append("</A>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.append("</div><div class=\"page\"><div class=\"mainsection\">");
|
||||||
|
boolean showPriority = snark != null && !snark.storage.complete();
|
||||||
|
if (showPriority)
|
||||||
|
buf.append("<form action=\"").append(base).append("\" method=\"POST\">\n");
|
||||||
|
buf.append("<TABLE BORDER=0 class=\"snarkTorrents\" cellpadding=\"5px 10px\">" +
|
||||||
"<thead><tr><th>").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/file.png\" title=\"").append(_("File")).append("\" alt=\"").append(_("File")).append("\"> ").append(title).append("</th><th align=\"right\">").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/size.png\" title=\"").append(_("FileSize")).append("\" alt=\"").append(_("FileSize")).append("\">").append(_("Size"));
|
"<thead><tr><th>").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/file.png\" title=\"").append(_("File")).append("\" alt=\"").append(_("File")).append("\"> ").append(title).append("</th><th align=\"right\">").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/size.png\" title=\"").append(_("FileSize")).append("\" alt=\"").append(_("FileSize")).append("\">").append(_("Size"));
|
||||||
buf.append("</th><th>").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\" title=\"").append(_("Download Status")).append("\">").append(_("Status")).append("</th></tr></thead>");
|
buf.append("</th><th>").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\" title=\"").append(_("Download Status")).append("\">").append(_("Status")).append("</th>");
|
||||||
|
if (showPriority)
|
||||||
|
buf.append("<th>").append(_("Priority")).append("</th>");
|
||||||
|
buf.append("</tr></thead>\n");
|
||||||
//DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
|
//DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
|
||||||
// DateFormat.MEDIUM);
|
// DateFormat.MEDIUM);
|
||||||
|
boolean showSaveButton = false;
|
||||||
for (int i=0 ; i< ls.length ; i++)
|
for (int i=0 ; i< ls.length ; i++)
|
||||||
{
|
{
|
||||||
String encoded=URI.encodePath(ls[i]);
|
String encoded=URI.encodePath(ls[i]);
|
||||||
@ -1391,7 +1418,8 @@ public class I2PSnarkServlet extends Default {
|
|||||||
complete = true;
|
complete = true;
|
||||||
status = toImg("tick") + _("Complete");
|
status = toImg("tick") + _("Complete");
|
||||||
} else {
|
} else {
|
||||||
status = toImg("clock") +
|
status =
|
||||||
|
(snark.storage.getPriority(f.getCanonicalPath()) < 0 ? toImg("cancel") : toImg("clock")) +
|
||||||
(100 * (length - remaining) / length) + "% " + _("complete") +
|
(100 * (length - remaining) / length) + "% " + _("complete") +
|
||||||
" (" + DataHelper.formatSize2(remaining) + _("bytes remaining") + ")";
|
" (" + DataHelper.formatSize2(remaining) + _("bytes remaining") + ")";
|
||||||
}
|
}
|
||||||
@ -1435,22 +1463,41 @@ public class I2PSnarkServlet extends Default {
|
|||||||
buf.append("</TD><TD class=\"").append(rowClass).append(" snarkFileStatus\">");
|
buf.append("</TD><TD class=\"").append(rowClass).append(" snarkFileStatus\">");
|
||||||
//buf.append(dfmt.format(new Date(item.lastModified())));
|
//buf.append(dfmt.format(new Date(item.lastModified())));
|
||||||
buf.append(status);
|
buf.append(status);
|
||||||
buf.append("</TD></TR>\n");
|
buf.append("</TD>");
|
||||||
|
if (showPriority) {
|
||||||
|
buf.append("<td>");
|
||||||
|
File f = item.getFile();
|
||||||
|
if ((!complete) && (!item.isDirectory()) && f != null) {
|
||||||
|
int pri = snark.storage.getPriority(f.getCanonicalPath());
|
||||||
|
buf.append("<input type=\"radio\" value=\"5\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
|
||||||
|
if (pri > 0)
|
||||||
|
buf.append("checked=\"true\"");
|
||||||
|
buf.append('>').append(_("High"));
|
||||||
|
|
||||||
|
buf.append("<input type=\"radio\" value=\"0\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
|
||||||
|
if (pri == 0)
|
||||||
|
buf.append("checked=\"true\"");
|
||||||
|
buf.append('>').append(_("Normal"));
|
||||||
|
|
||||||
|
buf.append("<input type=\"radio\" value=\"-9\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
|
||||||
|
if (pri < 0)
|
||||||
|
buf.append("checked=\"true\"");
|
||||||
|
buf.append('>').append(_("Do not download"));
|
||||||
|
showSaveButton = true;
|
||||||
|
}
|
||||||
|
buf.append("</td>");
|
||||||
|
}
|
||||||
|
buf.append("</TR>\n");
|
||||||
}
|
}
|
||||||
|
if (showSaveButton) {
|
||||||
if (parent)
|
buf.append("<thead><tr><th colspan=\"3\"> </th><th align=\"center\"><input type=\"submit\" value=\"");
|
||||||
{
|
buf.append(_("Save priorities"));
|
||||||
buf.append("<tfoot align=\"left\"><tr><td colspan=\"3\"><A HREF=\"");
|
buf.append("\" name=\"foo\" ></th></tr></thead>\n");
|
||||||
// corrupts utf-8
|
|
||||||
//buf.append(URI.encodePath(URI.addPaths(base,"../")));
|
|
||||||
buf.append(URI.addPaths(base,"../"));
|
|
||||||
buf.append("\"><img border=\"0\" src=\"/themes/snark/ubergine/images/up.png\"> ")
|
|
||||||
.append(_("Up to higher level directory")).append("</A></td></tr></thead>\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
buf.append("</TABLE>\n");
|
buf.append("</TABLE>\n");
|
||||||
buf.append("</div></div></center></BODY></HTML>\n");
|
if (showPriority)
|
||||||
|
buf.append("</form>");
|
||||||
|
buf.append("</div></div></BODY></HTML>\n");
|
||||||
|
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
@ -1515,6 +1562,26 @@ buf.append("</div></div></center></BODY></HTML>\n");
|
|||||||
return "<img alt=\"\" height=\"16\" width=\"16\" src=\"/i2psnark/_icons/" + icon + ".png\"> ";
|
return "<img alt=\"\" height=\"16\" width=\"16\" src=\"/i2psnark/_icons/" + icon + ".png\"> ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 0.8.1 */
|
||||||
|
private void savePriorities(Snark snark, Map postParams) {
|
||||||
|
Set<Map.Entry> entries = postParams.entrySet();
|
||||||
|
for (Map.Entry entry : entries) {
|
||||||
|
String key = (String)entry.getKey();
|
||||||
|
if (key.startsWith("pri.")) {
|
||||||
|
try {
|
||||||
|
String file = key.substring(4);
|
||||||
|
String val = ((String[])entry.getValue())[0]; // jetty arrays
|
||||||
|
int pri = Integer.parseInt(val);
|
||||||
|
snark.storage.setPriority(file, pri);
|
||||||
|
//System.err.println("Priority now " + pri + " for " + file);
|
||||||
|
} catch (Throwable t) { t.printStackTrace(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (snark.coordinator != null)
|
||||||
|
snark.coordinator.updatePiecePriorities();
|
||||||
|
_manager.saveTorrentStatus(snark.storage.getMetaInfo(), snark.storage.getBitField(), snark.storage.getFilePriorities());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** inner class, don't bother reindenting */
|
/** inner class, don't bother reindenting */
|
||||||
private static class FetchAndAdd implements Runnable {
|
private static class FetchAndAdd implements Runnable {
|
||||||
|
@ -175,7 +175,7 @@ public class EditBean extends IndexBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getAccessList(int tunnel) {
|
public String getAccessList(int tunnel) {
|
||||||
return getProperty(tunnel, "i2cp.accessList", "").replaceAll(",", "\n");
|
return getProperty(tunnel, "i2cp.accessList", "").replace(",", "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getClose(int tunnel) {
|
public boolean getClose(int tunnel) {
|
||||||
|
@ -666,7 +666,7 @@ public class IndexBean {
|
|||||||
}
|
}
|
||||||
public void setAccessList(String val) {
|
public void setAccessList(String val) {
|
||||||
if (val != null)
|
if (val != null)
|
||||||
_otherOptions.put("i2cp.accessList", val.trim().replaceAll("\r\n", ",").replaceAll("\n", ",").replaceAll(" ", ","));
|
_otherOptions.put("i2cp.accessList", val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
|
||||||
}
|
}
|
||||||
public void setCloseTime(String val) {
|
public void setCloseTime(String val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
|
@ -159,7 +159,7 @@
|
|||||||
%>
|
%>
|
||||||
</label>
|
</label>
|
||||||
<input type="text" size="30" id="targetDestination" name="targetDestination" title="Destination of the Tunnel" value="<%=editBean.getClientDestination(curTunnel)%>" class="freetext" />
|
<input type="text" size="30" id="targetDestination" name="targetDestination" title="Destination of the Tunnel" value="<%=editBean.getClientDestination(curTunnel)%>" class="freetext" />
|
||||||
<span class="comment">(<%=intl._("name or destination")%>)</span>
|
<span class="comment">(<%=intl._("name or destination")%>; <%=intl._("b32 not recommended")%>)</span>
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
<% } %>
|
||||||
<% if (!"streamrclient".equals(tunnelType)) { %>
|
<% if (!"streamrclient".equals(tunnelType)) { %>
|
||||||
|
@ -45,6 +45,10 @@ public class I2PSocketEepGet extends EepGet {
|
|||||||
/** this replaces _proxy in the superclass. Sadly, I2PSocket does not extend Socket. */
|
/** this replaces _proxy in the superclass. Sadly, I2PSocket does not extend Socket. */
|
||||||
private I2PSocket _socket;
|
private I2PSocket _socket;
|
||||||
|
|
||||||
|
/** from ConnectionOptions */
|
||||||
|
private static final String PROP_CONNECT_DELAY = "i2p.streaming.connectDelay";
|
||||||
|
private static final String CONNECT_DELAY = "500";
|
||||||
|
|
||||||
public I2PSocketEepGet(I2PAppContext ctx, I2PSocketManager mgr, int numRetries, String outputFile, String url) {
|
public I2PSocketEepGet(I2PAppContext ctx, I2PSocketManager mgr, int numRetries, String outputFile, String url) {
|
||||||
this(ctx, mgr, numRetries, -1, -1, outputFile, null, url);
|
this(ctx, mgr, numRetries, -1, -1, outputFile, null, url);
|
||||||
}
|
}
|
||||||
@ -123,6 +127,10 @@ public class I2PSocketEepGet extends EepGet {
|
|||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
props.setProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT, "" + CONNECT_TIMEOUT);
|
props.setProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT, "" + CONNECT_TIMEOUT);
|
||||||
props.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, "" + INACTIVITY_TIMEOUT);
|
props.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, "" + INACTIVITY_TIMEOUT);
|
||||||
|
// This is important - even if the underlying socket doesn't have a connect delay,
|
||||||
|
// we want to set it for this connection, so the request headers will go out
|
||||||
|
// in the SYN packet, saving one RTT.
|
||||||
|
props.setProperty(PROP_CONNECT_DELAY, CONNECT_DELAY);
|
||||||
I2PSocketOptions opts = _socketManager.buildOptions(props);
|
I2PSocketOptions opts = _socketManager.buildOptions(props);
|
||||||
_socket = _socketManager.connect(dest, opts);
|
_socket = _socketManager.connect(dest, opts);
|
||||||
} else {
|
} else {
|
||||||
|
@ -24,7 +24,10 @@ public class ConfigLoggingHelper extends HelperBase {
|
|||||||
public String getMaxFileSize() {
|
public String getMaxFileSize() {
|
||||||
int bytes = _context.logManager().getFileSize();
|
int bytes = _context.logManager().getFileSize();
|
||||||
if (bytes <= 0) return "1.00 MB";
|
if (bytes <= 0) return "1.00 MB";
|
||||||
return DataHelper.formatSize2(bytes) + 'B';
|
// " " comes back in the POST as 0xc2 0xa0
|
||||||
|
// non-breaking space is U+00A0 which is 0xc2 0xa0 in UTF-8.
|
||||||
|
// we could figure out where the UTF-8 problem is but why bother.
|
||||||
|
return DataHelper.formatSize2(bytes).replace(" ", " ") + 'B';
|
||||||
}
|
}
|
||||||
public String getLogLevelTable() {
|
public String getLogLevelTable() {
|
||||||
StringBuilder buf = new StringBuilder(32*1024);
|
StringBuilder buf = new StringBuilder(32*1024);
|
||||||
|
@ -142,7 +142,7 @@ public class ConfigUpdateHandler extends FormHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( (_updateURL != null) && (_updateURL.length() > 0) ) {
|
if ( (_updateURL != null) && (_updateURL.length() > 0) ) {
|
||||||
_updateURL = _updateURL.replaceAll("\r\n", ",").replaceAll("\n", ",");
|
_updateURL = _updateURL.replace("\r\n", ",").replace("\n", ",");
|
||||||
String oldURL = _context.router().getConfigSetting(PROP_UPDATE_URL);
|
String oldURL = _context.router().getConfigSetting(PROP_UPDATE_URL);
|
||||||
if ( (oldURL == null) || (!_updateURL.equals(oldURL)) ) {
|
if ( (oldURL == null) || (!_updateURL.equals(oldURL)) ) {
|
||||||
_context.router().setConfigSetting(PROP_UPDATE_URL, _updateURL);
|
_context.router().setConfigSetting(PROP_UPDATE_URL, _updateURL);
|
||||||
@ -151,7 +151,7 @@ public class ConfigUpdateHandler extends FormHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( (_trustedKeys != null) && (_trustedKeys.length() > 0) ) {
|
if ( (_trustedKeys != null) && (_trustedKeys.length() > 0) ) {
|
||||||
_trustedKeys = _trustedKeys.replaceAll("\r\n", ",").replaceAll("\n", ",");
|
_trustedKeys = _trustedKeys.replace("\r\n", ",").replace("\n", ",");
|
||||||
String oldKeys = new TrustedUpdate(_context).getTrustedKeysString();
|
String oldKeys = new TrustedUpdate(_context).getTrustedKeysString();
|
||||||
if ( (oldKeys == null) || (!_trustedKeys.equals(oldKeys)) ) {
|
if ( (oldKeys == null) || (!_trustedKeys.equals(oldKeys)) ) {
|
||||||
_context.router().setConfigSetting(PROP_TRUSTED_KEYS, _trustedKeys);
|
_context.router().setConfigSetting(PROP_TRUSTED_KEYS, _trustedKeys);
|
||||||
|
@ -40,7 +40,7 @@ public class ConfigUpdateHelper extends HelperBase {
|
|||||||
public String getUpdateURL() {
|
public String getUpdateURL() {
|
||||||
String url = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL);
|
String url = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL);
|
||||||
if (url != null)
|
if (url != null)
|
||||||
return url.replaceAll(",", "\n");
|
return url.replace(",", "\n");
|
||||||
else
|
else
|
||||||
return ConfigUpdateHandler.DEFAULT_UPDATE_URL;
|
return ConfigUpdateHandler.DEFAULT_UPDATE_URL;
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,9 @@ public class LogsHelper extends HelperBase {
|
|||||||
}
|
}
|
||||||
String str = FileUtil.readTextFile(f.getAbsolutePath(), 250, false);
|
String str = FileUtil.readTextFile(f.getAbsolutePath(), 250, false);
|
||||||
if (str == null)
|
if (str == null)
|
||||||
return "";
|
return _("File not found") + ": <b><code>" + f.getAbsolutePath() + "</code></b>";
|
||||||
else {
|
else {
|
||||||
str = str.replaceAll("<", "<").replaceAll(">", ">");
|
str = str.replace("&", "&").replace("<", "<").replace(">", ">");
|
||||||
return _("File location") + ": <b><code>" + f.getAbsolutePath() + "</code></b> <pre>" + str + "</pre>";
|
return _("File location") + ": <b><code>" + f.getAbsolutePath() + "</code></b> <pre>" + str + "</pre>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,12 +54,14 @@ public class LogsHelper extends HelperBase {
|
|||||||
buf.append("<code>\n");
|
buf.append("<code>\n");
|
||||||
for (int i = msgs.size(); i > 0; i--) {
|
for (int i = msgs.size(); i > 0; i--) {
|
||||||
String msg = msgs.get(i - 1);
|
String msg = msgs.get(i - 1);
|
||||||
|
msg = msg.replace("&", "&").replace("<", "<").replace(">", ">");
|
||||||
buf.append("<li>");
|
buf.append("<li>");
|
||||||
if (colorize) {
|
if (colorize) {
|
||||||
String color;
|
String color;
|
||||||
// Homeland Security Advisory System
|
// Homeland Security Advisory System
|
||||||
// http://www.dhs.gov/xinfoshare/programs/Copy_of_press_release_0046.shtm
|
// http://www.dhs.gov/xinfoshare/programs/Copy_of_press_release_0046.shtm
|
||||||
// but pink instead of yellow for WARN
|
// but pink instead of yellow for WARN
|
||||||
|
// FIXME doesnt work for translated levels
|
||||||
if (msg.contains("CRIT"))
|
if (msg.contains("CRIT"))
|
||||||
color = "#cc0000";
|
color = "#cc0000";
|
||||||
else if (msg.contains("ERROR"))
|
else if (msg.contains("ERROR"))
|
||||||
@ -71,7 +73,7 @@ public class LogsHelper extends HelperBase {
|
|||||||
else
|
else
|
||||||
color = "#006600";
|
color = "#006600";
|
||||||
buf.append("<font color=\"").append(color).append("\">");
|
buf.append("<font color=\"").append(color).append("\">");
|
||||||
buf.append(msg.replaceAll("<", "<").replaceAll(">", ">"));
|
buf.append(msg);
|
||||||
buf.append("</font>");
|
buf.append("</font>");
|
||||||
} else {
|
} else {
|
||||||
buf.append(msg);
|
buf.append(msg);
|
||||||
|
@ -112,7 +112,7 @@ public class TunnelRenderer {
|
|||||||
if (lifetime > 10*60)
|
if (lifetime > 10*60)
|
||||||
lifetime = 10*60;
|
lifetime = 10*60;
|
||||||
int bps = 1024 * (int) cfg.getProcessedMessagesCount() / lifetime;
|
int bps = 1024 * (int) cfg.getProcessedMessagesCount() / lifetime;
|
||||||
out.write("<td class=\"cells\" align=\"center\">" + bps + "Bps</td>");
|
out.write("<td class=\"cells\" align=\"center\">" + bps + " Bps</td>");
|
||||||
if (cfg.getSendTo() == null)
|
if (cfg.getSendTo() == null)
|
||||||
out.write("<td class=\"cells\" align=\"center\">" + _("Outbound Endpoint") + "</td>");
|
out.write("<td class=\"cells\" align=\"center\">" + _("Outbound Endpoint") + "</td>");
|
||||||
else if (cfg.getReceiveFrom() == null)
|
else if (cfg.getReceiveFrom() == null)
|
||||||
|
@ -15,6 +15,7 @@ msgstr ""
|
|||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||||
"X-Poedit-Language: German\n"
|
"X-Poedit-Language: German\n"
|
||||||
|
|
||||||
#: ../../../router/java/src/net/i2p/router/Blocklist.java:126
|
#: ../../../router/java/src/net/i2p/router/Blocklist.java:126
|
||||||
|
@ -15,8 +15,8 @@ msgstr ""
|
|||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Poedit-Language: German\n"
|
"X-Poedit-Language: French\n"
|
||||||
"X-Poedit-Basepath: /home/lee/work/i2p/monotone/i2p.i2p/apps/routerconsole/java\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||||
|
|
||||||
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:106
|
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:106
|
||||||
msgid "config networking"
|
msgid "config networking"
|
||||||
|
@ -15,6 +15,7 @@ msgstr ""
|
|||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||||
"X-Poedit-Language: Dutch\n"
|
"X-Poedit-Language: Dutch\n"
|
||||||
|
|
||||||
#: ../../../router/java/src/net/i2p/router/Blocklist.java:126
|
#: ../../../router/java/src/net/i2p/router/Blocklist.java:126
|
||||||
|
@ -47,6 +47,9 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
|||||||
private int _maxTotalConnsPerHour;
|
private int _maxTotalConnsPerHour;
|
||||||
private int _maxTotalConnsPerDay;
|
private int _maxTotalConnsPerDay;
|
||||||
|
|
||||||
|
// NOTE - almost all the options are below, but see
|
||||||
|
// I2PSocketOptions in ministreaming for a few more
|
||||||
|
|
||||||
public static final int PROFILE_BULK = 1;
|
public static final int PROFILE_BULK = 1;
|
||||||
public static final int PROFILE_INTERACTIVE = 2;
|
public static final int PROFILE_INTERACTIVE = 2;
|
||||||
|
|
||||||
|
21
apps/systray/doc/README.txt
Normal file
21
apps/systray/doc/README.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Bundled in ../java/lib/ are the binaries for systray4j version 2.4.1 2004-03-28,
|
||||||
|
which is still the latest.
|
||||||
|
|
||||||
|
Files are from systray4j-2.4.1-win32.zip.
|
||||||
|
|
||||||
|
SHA1Sums:
|
||||||
|
28acaea97816f53d188d01fd88b72e670e67286b systray4j-2.4.1-win32.zip
|
||||||
|
a7f5e02c3652f3f1a72559e54ee69226b8b97859 systray4j.dll
|
||||||
|
947bd91c483494256cf48ad87c211e8701b4f85b systray4j.jar
|
||||||
|
|
||||||
|
|
||||||
|
systray4j is GPLv2, see LICENSE.systray4j.txt.
|
||||||
|
I2P systray code in ../java/src is public domain.
|
||||||
|
|
||||||
|
SysTray is really obsolete. It supports Windows and kde3 only.
|
||||||
|
We only instantiate it on Windows.
|
||||||
|
|
||||||
|
The java.awt.SystemTray classes added in Java 6
|
||||||
|
(and used by apps/desktopgui) are the way to go now.
|
||||||
|
|
||||||
|
We could either rewrite this to use SystemTray, or switch to desktopgui.
|
@ -184,4 +184,20 @@ public class SysTray implements SysTrayMenuListener {
|
|||||||
_sysTrayMenu.addItem(_itemOpenConsole);
|
_sysTrayMenu.addItem(_itemOpenConsole);
|
||||||
refreshDisplay();
|
refreshDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts SysTray, even on linux (but requires kde3 libsystray4j.so to do anything)
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
public static void main(String args[]) {
|
||||||
|
System.err.println("SysTray4j version " + SysTrayMenu.VERSION);
|
||||||
|
System.err.println("Hit ^C to exit");
|
||||||
|
new SysTray();
|
||||||
|
Thread t = Thread.currentThread();
|
||||||
|
synchronized(t) {
|
||||||
|
try {
|
||||||
|
t.wait();
|
||||||
|
} catch (InterruptedException ie) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,14 @@
|
|||||||
<!--
|
<!--
|
||||||
<property name="javac.compilerargs" value="-warn:-unchecked,raw,unused,serial" />
|
<property name="javac.compilerargs" value="-warn:-unchecked,raw,unused,serial" />
|
||||||
-->
|
-->
|
||||||
|
<!-- Add Apache Harmony's Pack200 library if you don't have java.util.jar.Pack200
|
||||||
|
See core/java/src/net/i2p/util/FileUtil.java for code changes required
|
||||||
|
to use this library instead of Sun's version.
|
||||||
|
Or to comment it all out if you don't have either.
|
||||||
|
-->
|
||||||
|
<!--
|
||||||
|
<property name="javac.classpath" value="/PATH/TO/pack200.jar" />
|
||||||
|
-->
|
||||||
|
|
||||||
<!-- You probably don't want to change anything from here down -->
|
<!-- You probably don't want to change anything from here down -->
|
||||||
<target name="help" depends="all" />
|
<target name="help" depends="all" />
|
||||||
|
@ -17,10 +17,11 @@
|
|||||||
</target>
|
</target>
|
||||||
<!-- only used if not set by a higher build.xml -->
|
<!-- only used if not set by a higher build.xml -->
|
||||||
<property name="javac.compilerargs" value="" />
|
<property name="javac.compilerargs" value="" />
|
||||||
|
<property name="javac.classpath" value="" />
|
||||||
<target name="compile" depends="depend">
|
<target name="compile" depends="depend">
|
||||||
<mkdir dir="./build" />
|
<mkdir dir="./build" />
|
||||||
<mkdir dir="./build/obj" />
|
<mkdir dir="./build/obj" />
|
||||||
<javac srcdir="./src" debug="true" source="1.5" target="1.5" deprecation="on" destdir="./build/obj" >
|
<javac srcdir="./src" debug="true" source="1.5" target="1.5" deprecation="on" destdir="./build/obj" classpath="${javac.classpath}" >
|
||||||
<compilerarg line="${javac.compilerargs}" />
|
<compilerarg line="${javac.compilerargs}" />
|
||||||
</javac>
|
</javac>
|
||||||
</target>
|
</target>
|
||||||
|
@ -1076,7 +1076,8 @@ public class DataHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like formatSize but with a space after the number
|
* Like formatSize but with a non-breaking space after the number
|
||||||
|
* Use only in HTML
|
||||||
* @since 0.7.14
|
* @since 0.7.14
|
||||||
*/
|
*/
|
||||||
public static String formatSize2(long bytes) {
|
public static String formatSize2(long bytes) {
|
||||||
@ -1091,11 +1092,11 @@ public class DataHelper {
|
|||||||
|
|
||||||
String str = fmt.format(val);
|
String str = fmt.format(val);
|
||||||
switch (scale) {
|
switch (scale) {
|
||||||
case 1: return str + " K";
|
case 1: return str + " K";
|
||||||
case 2: return str + " M";
|
case 2: return str + " M";
|
||||||
case 3: return str + " G";
|
case 3: return str + " G";
|
||||||
case 4: return str + " T";
|
case 4: return str + " T";
|
||||||
default: return bytes + " ";
|
default: return bytes + " ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,10 +13,18 @@ import java.util.ArrayList;
|
|||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.jar.JarOutputStream;
|
import java.util.jar.JarOutputStream;
|
||||||
import java.util.jar.Pack200;
|
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
// Pack200 import
|
||||||
|
// you must also uncomment the correct line in unpack() below
|
||||||
|
// For gcj, gij, etc., comment both out
|
||||||
|
//
|
||||||
|
// For Sun, OpenJDK, IcedTea, etc, use this
|
||||||
|
import java.util.jar.Pack200;
|
||||||
|
|
||||||
|
// For Apache Harmony or if you put its pack200.jar in your library directory use this
|
||||||
|
//import org.apache.harmony.unpack200.Archive;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General helper methods for messing with files
|
* General helper methods for messing with files
|
||||||
@ -119,7 +127,7 @@ public class FileUtil {
|
|||||||
if (entry.getName().endsWith(".jar.pack") || entry.getName().endsWith(".war.pack")) {
|
if (entry.getName().endsWith(".jar.pack") || entry.getName().endsWith(".war.pack")) {
|
||||||
target = new File(targetDir, entry.getName().substring(0, entry.getName().length() - ".pack".length()));
|
target = new File(targetDir, entry.getName().substring(0, entry.getName().length() - ".pack".length()));
|
||||||
JarOutputStream fos = new JarOutputStream(new FileOutputStream(target));
|
JarOutputStream fos = new JarOutputStream(new FileOutputStream(target));
|
||||||
Pack200.newUnpacker().unpack(in, fos);
|
unpack(in, fos);
|
||||||
fos.close();
|
fos.close();
|
||||||
System.err.println("INFO: File [" + entry.getName() + "] extracted and unpacked");
|
System.err.println("INFO: File [" + entry.getName() + "] extracted and unpacked");
|
||||||
} else {
|
} else {
|
||||||
@ -189,9 +197,7 @@ public class FileUtil {
|
|||||||
} else {
|
} else {
|
||||||
if (p200TestRequired &&
|
if (p200TestRequired &&
|
||||||
(entry.getName().endsWith(".jar.pack") || entry.getName().endsWith(".war.pack"))) {
|
(entry.getName().endsWith(".jar.pack") || entry.getName().endsWith(".war.pack"))) {
|
||||||
try {
|
if (!isPack200Supported()) {
|
||||||
Class.forName("java.util.jar.Pack200", false, ClassLoader.getSystemClassLoader());
|
|
||||||
} catch (Exception e) { // ClassNotFoundException but compiler not happy with that
|
|
||||||
System.err.println("ERROR: Zip verify failed, your JVM does not support unpack200");
|
System.err.println("ERROR: Zip verify failed, your JVM does not support unpack200");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -224,6 +230,40 @@ public class FileUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This won't work right if one of the two options in unpack() is commented out.
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
private static boolean isPack200Supported() {
|
||||||
|
try {
|
||||||
|
Class.forName("java.util.jar.Pack200", false, ClassLoader.getSystemClassLoader());
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {}
|
||||||
|
try {
|
||||||
|
Class.forName("org.apache.harmony.pack200.Archive", false, ClassLoader.getSystemClassLoader());
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caller must close streams
|
||||||
|
* @since 0.8.1
|
||||||
|
*/
|
||||||
|
private static void unpack(InputStream in, JarOutputStream out) throws Exception {
|
||||||
|
// For Sun, OpenJDK, IcedTea, etc, use this
|
||||||
|
Pack200.newUnpacker().unpack(in, out);
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
// For Apache Harmony or if you put its pack200.jar in your library directory use this
|
||||||
|
//(new Archive(in, out)).unpack();
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
// For gcj, gij, etc., use this
|
||||||
|
//throw new IOException("Pack200 not supported");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read in the last few lines of a (newline delimited) textfile, or null if
|
* Read in the last few lines of a (newline delimited) textfile, or null if
|
||||||
* the file doesn't exist.
|
* the file doesn't exist.
|
||||||
@ -352,6 +392,18 @@ public class FileUtil {
|
|||||||
boolean copied = FileUtil.copy(args[1], args[2], false);
|
boolean copied = FileUtil.copy(args[1], args[2], false);
|
||||||
if (!copied)
|
if (!copied)
|
||||||
System.err.println("Error copying [" + args[1] + "] to [" + args[2] + "]");
|
System.err.println("Error copying [" + args[1] + "] to [" + args[2] + "]");
|
||||||
|
} else if ("unzip".equals(args[0])) {
|
||||||
|
File f = new File(args[1]);
|
||||||
|
File to = new File("tmp");
|
||||||
|
to.mkdir();
|
||||||
|
boolean copied = verifyZip(f);
|
||||||
|
if (!copied)
|
||||||
|
System.err.println("Error verifying " + args[1]);
|
||||||
|
copied = extractZip(f, to);
|
||||||
|
if (copied)
|
||||||
|
System.err.println("Unzipped [" + args[1] + "] to [" + to + "]");
|
||||||
|
else
|
||||||
|
System.err.println("Error unzipping [" + args[1] + "] to [" + to + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -429,7 +430,8 @@ public class LogManager {
|
|||||||
v = v.substring(0, v.length() - 1);
|
v = v.substring(0, v.length() - 1);
|
||||||
char mod = v.charAt(v.length() - 1);
|
char mod = v.charAt(v.length() - 1);
|
||||||
if (!Character.isDigit(mod)) v = v.substring(0, v.length() - 1);
|
if (!Character.isDigit(mod)) v = v.substring(0, v.length() - 1);
|
||||||
double val = Double.parseDouble(v);
|
// output to form was in current locale, so have to parse it back that way
|
||||||
|
double val = (new DecimalFormat()).parse(v.trim()).doubleValue();
|
||||||
switch (mod) {
|
switch (mod) {
|
||||||
case 'K':
|
case 'K':
|
||||||
val *= 1024;
|
val *= 1024;
|
||||||
|
@ -56,7 +56,7 @@ public class SecureFileOutputStream extends FileOutputStream {
|
|||||||
* Tries to set the permissions to 600,
|
* Tries to set the permissions to 600,
|
||||||
* ignores errors
|
* ignores errors
|
||||||
*/
|
*/
|
||||||
private static void setPerms(File f) {
|
public static void setPerms(File f) {
|
||||||
if (!canSetPerms)
|
if (!canSetPerms)
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
|
154
history.txt
154
history.txt
@ -1,3 +1,157 @@
|
|||||||
|
2010-11-03 zzz
|
||||||
|
* Merge and snark fixups
|
||||||
|
|
||||||
|
2010-11-01 zzz
|
||||||
|
* ClientConnectionRunner: Add synch to fix race causing AIOOBE
|
||||||
|
(http://forum.i2p/viewtopic.php?t=5061)
|
||||||
|
* configlogging.jsp: Parse log limit with current locale
|
||||||
|
(ticket #118)
|
||||||
|
* i2psnark:
|
||||||
|
- Limit number of parallel requests of a single piece when in the end game
|
||||||
|
- Shorten and weight the speed tracker so the display is more
|
||||||
|
reflective of current speed
|
||||||
|
* logs.jsp: Add message if wrapper log not found
|
||||||
|
(ticket #103)
|
||||||
|
|
||||||
|
2010-10-30 zzz
|
||||||
|
* i2psnark:
|
||||||
|
- Priority mapping bugfix
|
||||||
|
- Close files as we go when creating/checking
|
||||||
|
so we don't run out of file descriptors
|
||||||
|
- Update request queues after priority change
|
||||||
|
- Only add wanted pieces to wanted list at startup
|
||||||
|
- Make sure lastRequest is null when it should be
|
||||||
|
- Delay during StopAll so we don't close the tunnel before the
|
||||||
|
stopped announces go out and reopen it
|
||||||
|
- Logging tweaks
|
||||||
|
|
||||||
|
2010-10-27 zzz
|
||||||
|
* i2psnark:
|
||||||
|
- Don't stay interested if we run out of pieces
|
||||||
|
to request (thanks sponge)
|
||||||
|
- Enhance debug mode to show requests
|
||||||
|
- Priority mapping bugfix
|
||||||
|
* Transport: Avoid rare NPE at startup
|
||||||
|
|
||||||
|
2010-10-24 zzz
|
||||||
|
* FileUtil: Make it easier to compile without Pack200, or with
|
||||||
|
Apache Harmony's Pack200, add unzip to main()
|
||||||
|
* i2psnark: Catch a race after disconnect()
|
||||||
|
* NTCP: Catch a race after stop()
|
||||||
|
* Router: Set permissions on wrapper.log when not called by RouterLaunch
|
||||||
|
* Systray: New doc and main()
|
||||||
|
|
||||||
|
2010-10-19 zzz
|
||||||
|
* Escape & in logs and i2psnark (much more to do)
|
||||||
|
* JobImpl: Deprecate two debugging methods
|
||||||
|
* replaceAll() -> replace() when we don't need regex
|
||||||
|
|
||||||
|
2010-10-15 zzz
|
||||||
|
* i2psnark: Add file priority feature
|
||||||
|
* I2PSocketEepGet: Set connect delay to save a RTT, will
|
||||||
|
speed announces in i2psnark
|
||||||
|
|
||||||
|
2010-10-12 zzz
|
||||||
|
*** 1.6 or higher JDK now required to build
|
||||||
|
* configlogging.jsp:
|
||||||
|
- Add easy way to add an override
|
||||||
|
- Make file size specifier more flexible
|
||||||
|
* Console:
|
||||||
|
- Sort RouterAddress options on netdb.jsp and peers.jsp
|
||||||
|
- Remove unused web-*.xml file from war
|
||||||
|
* Crypto:
|
||||||
|
- Convert all ArrayList caching to LBQs in YKGenerator,
|
||||||
|
HMACGenerator, and AESKeyCache.
|
||||||
|
- Change DSAEngine params from Hash to new SHA1Hash, since
|
||||||
|
these were really 20 byte hashes, not 32 byte Hashes.
|
||||||
|
- Add stats to track YKGenerator caching success
|
||||||
|
- Fix YKGenerator precalculation to be much more useful by
|
||||||
|
increasing the cache size and dramatically shortening the delay
|
||||||
|
- Option cleanups
|
||||||
|
- YKGenerator cleanups
|
||||||
|
- Mark HMAC256Generator unused
|
||||||
|
* EepGet: Reset length variable on redirect
|
||||||
|
* Files: Change permissions to 600/700 for all written files/directories.
|
||||||
|
Now requires Java 1.6 to build, but only 1.5+ to run.
|
||||||
|
(requires 1.6 to set permissiomns)
|
||||||
|
* GeoIP: Fix locking bug causing lookups to stop
|
||||||
|
* Hash: Throw IAE if data length is not 32 bytes,
|
||||||
|
now that DSAEngine abuse is gone
|
||||||
|
* HTTPResponseOutputStream:
|
||||||
|
- More caching
|
||||||
|
- Stats cleanup
|
||||||
|
- Max header length check
|
||||||
|
- Catch OOM
|
||||||
|
- Initializer cleanup
|
||||||
|
- Javadoc
|
||||||
|
* I2CP:
|
||||||
|
- Add new option i2cp.messageReliability=none, which prevents the
|
||||||
|
router from sending MessageStatusMessages back in reply to an
|
||||||
|
outbound SendMessageMessage. Since the streaming lib always ignored
|
||||||
|
the MSMs anyway, make it the default for streaming.
|
||||||
|
This will reduce the I2CP traffic significantly.
|
||||||
|
MSM handling now avoided, but it is still fairly broken, see
|
||||||
|
comments in I2PSessionImpl2.
|
||||||
|
- Cleanups to replace method calls with fields
|
||||||
|
- More cleanups, javadoc, rate reduction
|
||||||
|
* i2psnark:
|
||||||
|
- Compact response format
|
||||||
|
- Add link to finished torrent in message box
|
||||||
|
- Don't let one bad torrent prevent others from
|
||||||
|
starting or stopping
|
||||||
|
- Sort peers by completion %
|
||||||
|
- Add some missing mime types to web.xml
|
||||||
|
- shouldLog() cleanup
|
||||||
|
* i2ptunnel:
|
||||||
|
- Now that streaming flush() is fixed, use it in IRCClient, and
|
||||||
|
for initial data in I2PTunnel runner, to avoid the 250 ms
|
||||||
|
passive flush delay
|
||||||
|
- Add hostname DSA signature field, to be used for addkey forms.
|
||||||
|
Experimental, may be commented out later.
|
||||||
|
- More header blocking (thanks telecomix!)
|
||||||
|
- Remove unused web-*.xml file from war
|
||||||
|
* Installer: Add startup hint for non-x86
|
||||||
|
* Javadoc updates all over the place
|
||||||
|
* LogConsoleBuffer: Java 5
|
||||||
|
* Naming:
|
||||||
|
- Increase cache size and expiration time
|
||||||
|
- Add clearCache() method
|
||||||
|
- Don't use EepGet or Exec for b32
|
||||||
|
- Javadoc updates
|
||||||
|
* NetDB:
|
||||||
|
- Expire unreachable routers quickly, even if they don't have introducers,
|
||||||
|
so we don't have old data on routers that ran out of introducers.
|
||||||
|
- Fix rare NPEs at shutdown
|
||||||
|
* NTCP:
|
||||||
|
- Cleanups
|
||||||
|
* Streaming:
|
||||||
|
- Make flush() block less, by waiting only for "accept" into the
|
||||||
|
streaming queue rather than "completion" (i.e. ACK from the far end).
|
||||||
|
This prevents complete stalls when flushing, and should help performance
|
||||||
|
of apps that use flush(), like i2psnark (and SAM?).
|
||||||
|
close() still does a flush that waits for completion, as i2ptunnel
|
||||||
|
doesn't like a fast return from close().
|
||||||
|
- cleanups
|
||||||
|
* SusiDNS:
|
||||||
|
- Remove unused web-*.xml file from war
|
||||||
|
* TransportManager: Convert _transports from a List to a CHM
|
||||||
|
to prevent a rare concurrent exception
|
||||||
|
* Tunnels:
|
||||||
|
- Don't use peers < 0.7.9 for tunnels due to the old
|
||||||
|
message corruption bugs
|
||||||
|
- Javadoc
|
||||||
|
- Cleanups
|
||||||
|
* UDP:
|
||||||
|
- Beginnings of destroy message support
|
||||||
|
- Try to avoid running out of introducers by relaxing selection criteria
|
||||||
|
and increasing minimum number of potential introducers
|
||||||
|
- Avoid rare AIOOBE
|
||||||
|
- PacketBuilder refactor
|
||||||
|
- Make most classes package private
|
||||||
|
- Comments
|
||||||
|
- Logging cleanup
|
||||||
|
- Comment out a main()
|
||||||
|
|
||||||
2010-10-22 sponge
|
2010-10-22 sponge
|
||||||
* Sanity and some fixs for slackware package
|
* Sanity and some fixs for slackware package
|
||||||
|
|
||||||
|
@ -39,11 +39,21 @@ public abstract class JobImpl implements Job {
|
|||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* As of 0.8.1, this is a noop, as it just adds classes to the log manager
|
||||||
|
* class list for no good reason. Logging in jobs is almost always
|
||||||
|
* set explicitly rather than by class name.
|
||||||
|
*/
|
||||||
void addedToQueue() {
|
void addedToQueue() {
|
||||||
if (_context.logManager().getLog(getClass()).shouldLog(Log.DEBUG))
|
//if (_context.logManager().getLog(getClass()).shouldLog(Log.DEBUG))
|
||||||
_addedBy = new Exception();
|
// _addedBy = new Exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @return null always
|
||||||
|
*/
|
||||||
public Exception getAddedBy() { return _addedBy; }
|
public Exception getAddedBy() { return _addedBy; }
|
||||||
public long getMadeReadyOn() { return _madeReadyOn; }
|
public long getMadeReadyOn() { return _madeReadyOn; }
|
||||||
public void madeReady() { _madeReadyOn = _context.clock().now(); }
|
public void madeReady() { _madeReadyOn = _context.clock().now(); }
|
||||||
|
@ -142,8 +142,9 @@ public class JobQueue {
|
|||||||
public void addJob(Job job) {
|
public void addJob(Job job) {
|
||||||
if (job == null || !_alive) return;
|
if (job == null || !_alive) return;
|
||||||
|
|
||||||
if (job instanceof JobImpl)
|
// This does nothing
|
||||||
((JobImpl)job).addedToQueue();
|
//if (job instanceof JobImpl)
|
||||||
|
// ((JobImpl)job).addedToQueue();
|
||||||
|
|
||||||
long numReady = 0;
|
long numReady = 0;
|
||||||
boolean alreadyExists = false;
|
boolean alreadyExists = false;
|
||||||
|
@ -218,6 +218,19 @@ public class Router {
|
|||||||
// NOW we start all the activity
|
// NOW we start all the activity
|
||||||
_context.initAll();
|
_context.initAll();
|
||||||
|
|
||||||
|
// Set wrapper.log permissions.
|
||||||
|
// Just hope this is the right location, we don't know for sure,
|
||||||
|
// but this is the same method used in LogsHelper and we have no complaints.
|
||||||
|
// (we could look for the wrapper.config file and parse it I guess...)
|
||||||
|
// If we don't have a wrapper, RouterLaunch does this for us.
|
||||||
|
if (System.getProperty("wrapper.version") != null) {
|
||||||
|
File f = new File(System.getProperty("java.io.tmpdir"), "wrapper.log");
|
||||||
|
if (!f.exists())
|
||||||
|
f = new File(_context.getBaseDir(), "wrapper.log");
|
||||||
|
if (f.exists())
|
||||||
|
SecureFileOutputStream.setPerms(f);
|
||||||
|
}
|
||||||
|
|
||||||
_routerInfo = null;
|
_routerInfo = null;
|
||||||
_higherVersionSeen = false;
|
_higherVersionSeen = false;
|
||||||
_log = _context.logManager().getLog(Router.class);
|
_log = _context.logManager().getLog(Router.class);
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 05;
|
public final static long BUILD = 9;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
@ -361,18 +361,21 @@ public class ClientConnectionRunner {
|
|||||||
// TunnelPool.locked_buildNewLeaseSet() ensures that leases are sorted,
|
// TunnelPool.locked_buildNewLeaseSet() ensures that leases are sorted,
|
||||||
// so the comparison will always work.
|
// so the comparison will always work.
|
||||||
int leases = set.getLeaseCount();
|
int leases = set.getLeaseCount();
|
||||||
if (_currentLeaseSet != null && _currentLeaseSet.getLeaseCount() == leases) {
|
// synch so _currentLeaseSet isn't changed out from under us
|
||||||
for (int i = 0; i < leases; i++) {
|
synchronized (this) {
|
||||||
if (! _currentLeaseSet.getLease(i).getTunnelId().equals(set.getLease(i).getTunnelId()))
|
if (_currentLeaseSet != null && _currentLeaseSet.getLeaseCount() == leases) {
|
||||||
break;
|
for (int i = 0; i < leases; i++) {
|
||||||
if (! _currentLeaseSet.getLease(i).getGateway().equals(set.getLease(i).getGateway()))
|
if (! _currentLeaseSet.getLease(i).getTunnelId().equals(set.getLease(i).getTunnelId()))
|
||||||
break;
|
break;
|
||||||
if (i == leases - 1) {
|
if (! _currentLeaseSet.getLease(i).getGateway().equals(set.getLease(i).getGateway()))
|
||||||
if (_log.shouldLog(Log.INFO))
|
break;
|
||||||
_log.info("Requested leaseSet hasn't changed");
|
if (i == leases - 1) {
|
||||||
if (onCreateJob != null)
|
if (_log.shouldLog(Log.INFO))
|
||||||
_context.jobQueue().addJob(onCreateJob);
|
_log.info("Requested leaseSet hasn't changed");
|
||||||
return; // no change
|
if (onCreateJob != null)
|
||||||
|
_context.jobQueue().addJob(onCreateJob);
|
||||||
|
return; // no change
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -590,7 +593,7 @@ public class ClientConnectionRunner {
|
|||||||
+ " for session [" + _sessionId.getSessionId()
|
+ " for session [" + _sessionId.getSessionId()
|
||||||
+ "] (with nonce=2), retrying after ["
|
+ "] (with nonce=2), retrying after ["
|
||||||
+ (_context.clock().now() - _lastTried)
|
+ (_context.clock().now() - _lastTried)
|
||||||
+ "]", getAddedBy());
|
+ "]");
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Updating message status for message " + _messageId + " to "
|
_log.debug("Updating message status for message " + _messageId + " to "
|
||||||
|
@ -76,7 +76,7 @@ public class SendMessageDirectJob extends JobImpl {
|
|||||||
if (_expiration < now) {
|
if (_expiration < now) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Timed out sending message " + _message + " directly (expiration = "
|
_log.warn("Timed out sending message " + _message + " directly (expiration = "
|
||||||
+ new Date(_expiration) + ") to " + _targetHash.toBase64(), getAddedBy());
|
+ new Date(_expiration) + ") to " + _targetHash.toBase64());
|
||||||
if (_onFail != null)
|
if (_onFail != null)
|
||||||
getContext().jobQueue().addJob(_onFail);
|
getContext().jobQueue().addJob(_onFail);
|
||||||
return;
|
return;
|
||||||
@ -104,7 +104,7 @@ public class SendMessageDirectJob extends JobImpl {
|
|||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Unable to find the router to send to: " + _targetHash
|
_log.warn("Unable to find the router to send to: " + _targetHash
|
||||||
+ " after searching for " + (getContext().clock().now()-_searchOn)
|
+ " after searching for " + (getContext().clock().now()-_searchOn)
|
||||||
+ "ms, message: " + _message, getAddedBy());
|
+ "ms, message: " + _message);
|
||||||
if (_onFail != null)
|
if (_onFail != null)
|
||||||
getContext().jobQueue().addJob(_onFail);
|
getContext().jobQueue().addJob(_onFail);
|
||||||
}
|
}
|
||||||
|
@ -136,17 +136,17 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isBacklogged(Hash dest) {
|
public boolean isBacklogged(Hash dest) {
|
||||||
return _manager.isBacklogged(dest);
|
return _manager != null && _manager.isBacklogged(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEstablished(Hash dest) {
|
public boolean isEstablished(Hash dest) {
|
||||||
return _manager.isEstablished(dest);
|
return _manager != null && _manager.isEstablished(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean wasUnreachable(Hash dest) {
|
public boolean wasUnreachable(Hash dest) {
|
||||||
return _manager.wasUnreachable(dest);
|
return _manager != null && _manager.wasUnreachable(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -615,8 +615,8 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Type " + msg.getMessage().getType() + " pri " + msg.getPriority() + " slot " + slot);
|
_log.info("Type " + msg.getMessage().getType() + " pri " + msg.getPriority() + " slot " + slot);
|
||||||
boolean removed = _outbound.remove(msg);
|
boolean removed = _outbound.remove(msg);
|
||||||
if ((!removed) && _log.shouldLog(Log.ERROR))
|
if ((!removed) && _log.shouldLog(Log.WARN))
|
||||||
_log.info("Already removed??? " + msg.getMessage().getType());
|
_log.warn("Already removed??? " + msg.getMessage().getType());
|
||||||
}
|
}
|
||||||
_currentOutbound = msg;
|
_currentOutbound = msg;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package net.i2p.router.transport.ntcp;
|
|||||||
|
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
@ -24,9 +25,9 @@ import net.i2p.util.Log;
|
|||||||
*/
|
*/
|
||||||
public class NTCPSendFinisher {
|
public class NTCPSendFinisher {
|
||||||
private static final int THREADS = 4;
|
private static final int THREADS = 4;
|
||||||
private I2PAppContext _context;
|
private final I2PAppContext _context;
|
||||||
private NTCPTransport _transport;
|
private final NTCPTransport _transport;
|
||||||
private Log _log;
|
private final Log _log;
|
||||||
private int _count;
|
private int _count;
|
||||||
private ThreadPoolExecutor _executor;
|
private ThreadPoolExecutor _executor;
|
||||||
|
|
||||||
@ -47,7 +48,12 @@ public class NTCPSendFinisher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void add(OutNetMessage msg) {
|
public void add(OutNetMessage msg) {
|
||||||
_executor.execute(new RunnableEvent(msg));
|
try {
|
||||||
|
_executor.execute(new RunnableEvent(msg));
|
||||||
|
} catch (RejectedExecutionException ree) {
|
||||||
|
// race with stop()
|
||||||
|
_log.warn("NTCP send finisher stopped, discarding msg.afterSend()");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// not really needed for now but in case we want to add some hooks like afterExecute()
|
// not really needed for now but in case we want to add some hooks like afterExecute()
|
||||||
|
@ -317,7 +317,7 @@ class TestJob extends JobImpl {
|
|||||||
public String getName() { return "Tunnel test timeout"; }
|
public String getName() { return "Tunnel test timeout"; }
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Timeout: found? " + _found, getAddedBy());
|
_log.warn("Timeout: found? " + _found);
|
||||||
if (!_found) {
|
if (!_found) {
|
||||||
// don't clog up the SKM with old one-tag tagsets
|
// don't clog up the SKM with old one-tag tagsets
|
||||||
if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) {
|
if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) {
|
||||||
|
@ -81,7 +81,7 @@ public class SendGarlicJob extends JobImpl {
|
|||||||
long after = getContext().clock().now();
|
long after = getContext().clock().now();
|
||||||
if ( (after - before) > 1000) {
|
if ( (after - before) > 1000) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Building the garlic took too long [" + (after-before)+" ms]", getAddedBy());
|
_log.warn("Building the garlic took too long [" + (after-before)+" ms]");
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Building the garlic was fast! " + (after - before) + " ms");
|
_log.debug("Building the garlic was fast! " + (after - before) + " ms");
|
||||||
|
Reference in New Issue
Block a user