From 82fb65193f3f685e9eb7e52f5cad0832e2f32b71 Mon Sep 17 00:00:00 2001 From: Dmitry Panin Date: Wed, 10 Apr 2013 12:37:53 +0400 Subject: [PATCH 01/68] Fix read when message size wasn't read in one step --- .../com/turn/ttorrent/client/peer/PeerExchange.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index a7448d00c..2561e7c7c 100644 --- a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -210,21 +210,18 @@ public void run() { buffer.rewind(); buffer.limit(PeerMessage.MESSAGE_LENGTH_FIELD_SIZE); - if (channel.read(buffer) < 0) { - throw new EOFException( - "Reached end-of-stream while reading size header"); - } - // Keep reading bytes until the length field has been read // entirely. - if (buffer.hasRemaining()) { + while (!stop && buffer.hasRemaining()) { + if (channel.read(buffer) < 0) { + throw new EOFException( + "Reached end-of-stream while reading size header"); + } try { Thread.sleep(1); } catch (InterruptedException ie) { // Ignore and move along. } - - continue; } int pstrlen = buffer.getInt(0); From 728edf83a3918e9c0bc0b71066a2376c7315b668 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Thu, 18 Jul 2013 12:11:31 +0200 Subject: [PATCH 02/68] Throw AnnounceException when current tracker client isn't available. --- .../ttorrent/client/announce/Announce.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/announce/Announce.java b/src/main/java/com/turn/ttorrent/client/announce/Announce.java index 4c0a1b223..558c44fd7 100644 --- a/src/main/java/com/turn/ttorrent/client/announce/Announce.java +++ b/src/main/java/com/turn/ttorrent/client/announce/Announce.java @@ -226,7 +226,12 @@ public void run() { event = AnnounceRequestMessage.RequestEvent.NONE; } catch (AnnounceException ae) { logger.warn(ae.getMessage()); - this.moveToNextTrackerClient(); + + try { + this.moveToNextTrackerClient(); + } catch (AnnounceException e) { + logger.error("Unable to move to the next tracker client: {}", e.getMessage()); + } } try { @@ -281,8 +286,14 @@ private TrackerClient createTrackerClient(SharedTorrent torrent, Peer peer, /** * Returns the current tracker client used for announces. + * @throws AnnounceException */ - public TrackerClient getCurrentTrackerClient() { + public TrackerClient getCurrentTrackerClient() throws AnnounceException { + if (!this.clients.contains(this.currentTier) + || !this.clients.get(this.currentTier).contains(this.currentClient)) { + throw new AnnounceException("Current tier or client isn't available"); + } + return this.clients .get(this.currentTier) .get(this.currentClient); @@ -300,8 +311,9 @@ public TrackerClient getCurrentTrackerClient() { * The index of the currently used {@link TrackerClient} is reset to 0 to * reflect this change. *

+ * @throws AnnounceException */ - private void promoteCurrentTrackerClient() { + private void promoteCurrentTrackerClient() throws AnnounceException { logger.trace("Promoting current tracker client for {} " + "(tier {}, position {} -> 0).", new Object[] { @@ -327,8 +339,9 @@ private void promoteCurrentTrackerClient() { * By design no empty tier can be in the tracker list structure so we don't * need to check for empty tiers here. *

+ * @throws AnnounceException */ - private void moveToNextTrackerClient() { + private void moveToNextTrackerClient() throws AnnounceException { int tier = this.currentTier; int client = this.currentClient + 1; From 9370d86986a68ce9893c4e6083e6811a9a56924c Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Mon, 22 Jul 2013 05:08:28 -0400 Subject: [PATCH 03/68] Fix order of uploaded/downloaded parameters in HTTPAnnounceRequestMessage.parse Order of the parameters to the HTTPAnnounceRequestMessage constructor was incorrect in the parse() method. Thanks to Dan Oxlade for reporting the problem. Closes #48. Signed-off-by: Maxime Petazzoni --- .../common/protocol/http/HTTPAnnounceRequestMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java b/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java index 0362ba72f..3645573d0 100644 --- a/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java +++ b/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java @@ -254,7 +254,7 @@ public static HTTPAnnounceRequestMessage parse(ByteBuffer data) return new HTTPAnnounceRequestMessage(data, infoHash, new Peer(ip, port, ByteBuffer.wrap(peerId)), - downloaded, uploaded, left, compact, noPeerId, + uploaded, downloaded, left, compact, noPeerId, event, numWant); } catch (InvalidBEncodingException ibee) { throw new MessageValidationException( From 2e643f4d463ffccfcb9671010f2a1e5492f35413 Mon Sep 17 00:00:00 2001 From: "P. Taylor Goetz" Date: Fri, 19 Jul 2013 11:40:24 -0400 Subject: [PATCH 04/68] add the ability to set upload/download rate-limits (in kB/sec.) on shared torrents --- .../java/com/turn/ttorrent/client/Client.java | 29 +++++++++-- .../turn/ttorrent/client/SharedTorrent.java | 21 +++++++- .../ttorrent/client/peer/PeerExchange.java | 52 +++++++++++++++++-- 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/Client.java b/src/main/java/com/turn/ttorrent/client/Client.java index 480657dbf..18c7da987 100644 --- a/src/main/java/com/turn/ttorrent/client/Client.java +++ b/src/main/java/com/turn/ttorrent/client/Client.java @@ -171,6 +171,14 @@ public Client(InetAddress address, SharedTorrent torrent) this.connected = new ConcurrentHashMap(); this.random = new Random(System.currentTimeMillis()); } + + public void setMaxDownloadRate(double rate){ + this.torrent.setMaxDownloadRate(rate); + } + + public void setMaxUploadRate(double rate){ + this.torrent.setMaxUploadRate(rate); + } /** * Get this client's peer specification. @@ -991,10 +999,12 @@ private static void usage(PrintStream s) { s.println("usage: Client [options] "); s.println(); s.println("Available options:"); - s.println(" -h,--help Show this help and exit."); - s.println(" -o,--output DIR Read/write data to directory DIR."); - s.println(" -i,--iface IFACE Bind to interface IFACE."); - s.println(" -s,--seed SECONDS Time to seed after downloading (default: infinitely)."); + s.println(" -h,--help Show this help and exit."); + s.println(" -o,--output DIR Read/write data to directory DIR."); + s.println(" -i,--iface IFACE Bind to interface IFACE."); + s.println(" -s,--seed SECONDS Time to seed after downloading (default: infinitely)."); + s.println(" -d,--max-download KB/SEC Max download rate (default: infinitely)."); + s.println(" -u,--max-upload KB/SEC Max upload rate (default: infinitely)."); s.println(); } @@ -1052,6 +1062,11 @@ public static void main(String[] args) { CmdLineParser.Option output = parser.addStringOption('o', "output"); CmdLineParser.Option iface = parser.addStringOption('i', "iface"); CmdLineParser.Option seedTime = parser.addIntegerOption('s', "seed"); + CmdLineParser.Option maxUpload = parser.addDoubleOption('u', "max-upload"); + CmdLineParser.Option maxDownload = parser.addDoubleOption('d', "max-download"); + + logger.debug("Max Download: {}", parser.getOptionValue(maxDownload, 0.0)); + try { parser.parse(args); @@ -1071,6 +1086,9 @@ public static void main(String[] args) { DEFAULT_OUTPUT_DIRECTORY); String ifaceValue = (String)parser.getOptionValue(iface); int seedTimeValue = (Integer)parser.getOptionValue(seedTime, -1); + + double maxDownloadRate = (Double)parser.getOptionValue(maxDownload, 0.0); + double maxUploadRate = (Double)parser.getOptionValue(maxUpload, 0.0); String[] otherArgs = parser.getRemainingArgs(); if (otherArgs.length != 1) { @@ -1084,6 +1102,9 @@ public static void main(String[] args) { SharedTorrent.fromFile( new File(otherArgs[0]), new File(outputValue))); + + c.setMaxDownloadRate(maxDownloadRate); + c.setMaxUploadRate(maxUploadRate); // Set a shutdown hook that will stop the sharing/seeding and send // a STOPPED announce request. diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index 85f4ac310..96a1c9c9b 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -18,6 +18,7 @@ import com.turn.ttorrent.bcodec.InvalidBEncodingException; import com.turn.ttorrent.common.Torrent; import com.turn.ttorrent.client.peer.PeerActivityListener; +import com.turn.ttorrent.client.peer.Rate; import com.turn.ttorrent.client.peer.SharingPeer; import com.turn.ttorrent.client.storage.TorrentByteStorage; import com.turn.ttorrent.client.storage.FileStorage; @@ -99,7 +100,9 @@ public class SharedTorrent extends Torrent implements PeerActivityListener { private SortedSet rarest; private BitSet completedPieces; private BitSet requestedPieces; - + + private double maxUploadRate = 0.0; + private double maxDownloadRate = 0.0; /** * Create a new shared torrent from a base Torrent object. * @@ -245,6 +248,22 @@ public static SharedTorrent fromFile(File source, File parent) fis.close(); return new SharedTorrent(data, parent); } + + public double getMaxUploadRate(){ + return this.maxUploadRate; + } + + public void setMaxUploadRate(double rate){ + this.maxUploadRate = rate; + } + + public double getMaxDownloadRate(){ + return this.maxDownloadRate; + } + + public void setMaxDownloadRate(double rate){ + this.maxDownloadRate = rate; + } /** * Get the number of bytes uploaded for this torrent. diff --git a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index a7448d00c..8a475f0e3 100644 --- a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -17,6 +17,7 @@ import com.turn.ttorrent.client.SharedTorrent; import com.turn.ttorrent.common.protocol.PeerMessage; +import com.turn.ttorrent.common.protocol.PeerMessage.Type; import java.io.EOFException; import java.io.IOException; @@ -200,6 +201,8 @@ public void close() { * @author mpetazzoni */ private class IncomingThread extends Thread { + private Rate rate = new Rate(); + private long sleep = 1000; @Override public void run() { @@ -230,8 +233,11 @@ public void run() { int pstrlen = buffer.getInt(0); buffer.limit(PeerMessage.MESSAGE_LENGTH_FIELD_SIZE + pstrlen); + long size = 0; while (!stop && buffer.hasRemaining()) { - if (channel.read(buffer) < 0) { + int read = channel.read(buffer); + size += read; + if (read < 0) { throw new EOFException( "Reached end-of-stream while reading message"); } @@ -242,6 +248,24 @@ public void run() { try { PeerMessage message = PeerMessage.parse(buffer, torrent); logger.trace("Received {} from {}", message, peer); + + // throttling + if(message.getType() == Type.PIECE && PeerExchange.this.torrent.getMaxDownloadRate() > 0){ + try { + rate.add(size); + if(rate.get() > (PeerExchange.this.torrent.getMaxDownloadRate() * 1024)){ + Thread.sleep(this.sleep); + this.sleep += 50; + } else { + this.sleep -= 50; + } + if(this.sleep < 0){ + this.sleep = 0; + } + } catch (InterruptedException e) { + // not critical + } + } for (MessageListener listener : listeners) { listener.handleMessage(message); @@ -278,7 +302,9 @@ public void run() { * @author mpetazzoni */ private class OutgoingThread extends Thread { - + private Rate rate = new Rate(); + private long sleep = 1000; + @Override public void run() { try { @@ -302,12 +328,32 @@ public void run() { logger.trace("Sending {} to {}", message, peer); ByteBuffer data = message.getData(); + long size = 0; while (!stop && data.hasRemaining()) { - if (channel.write(data) < 0) { + int written = channel.write(data); + size += written; + if (written < 0) { throw new EOFException( "Reached end of stream while writing"); } } + + if(message.getType() == Type.PIECE && PeerExchange.this.torrent.getMaxUploadRate() > 0){ + try { + rate.add(size); + if(rate.get() > (PeerExchange.this.torrent.getMaxUploadRate() * 1024)){ + Thread.sleep(this.sleep); + this.sleep += 50; + } else { + this.sleep -= 50; + } + if(this.sleep < 0){ + this.sleep = 0; + } + } catch (InterruptedException e) { + // not critical + } + } } catch (InterruptedException ie) { // Ignore and potentially terminate } From b15ea5e23b6cd3870a0c9ccee393cd53761f077b Mon Sep 17 00:00:00 2001 From: "P. Taylor Goetz" Date: Mon, 22 Jul 2013 12:05:04 -0400 Subject: [PATCH 05/68] add javadoc documentation for upload/download rate-limit functionality --- src/main/java/com/turn/ttorrent/client/Client.java | 12 ++++++++++++ .../java/com/turn/ttorrent/client/SharedTorrent.java | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/main/java/com/turn/ttorrent/client/Client.java b/src/main/java/com/turn/ttorrent/client/Client.java index 18c7da987..e754a0ab0 100644 --- a/src/main/java/com/turn/ttorrent/client/Client.java +++ b/src/main/java/com/turn/ttorrent/client/Client.java @@ -172,10 +172,22 @@ public Client(InetAddress address, SharedTorrent torrent) this.random = new Random(System.currentTimeMillis()); } + /** + * Set the maximum download rate (in kb/second) for this + * torrent. A setting of <= 0.0 disables rate limiting. + * + * @param rate The maximum download rate + */ public void setMaxDownloadRate(double rate){ this.torrent.setMaxDownloadRate(rate); } + /** + * Set the maximum upload rate (in kb/second) for this + * torrent. A setting of <= 0.0 disables rate limiting. + * + * @param rate The maximum upload rate + */ public void setMaxUploadRate(double rate){ this.torrent.setMaxUploadRate(rate); } diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index 96a1c9c9b..6fa207043 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -253,6 +253,12 @@ public double getMaxUploadRate(){ return this.maxUploadRate; } + /** + * Set the maximum upload rate (in kb/second) for this + * torrent. A setting of <= 0.0 disables rate limiting. + * + * @param rate The maximum upload rate + */ public void setMaxUploadRate(double rate){ this.maxUploadRate = rate; } @@ -261,6 +267,12 @@ public double getMaxDownloadRate(){ return this.maxDownloadRate; } + /** + * Set the maximum download rate (in kb/second) for this + * torrent. A setting of <= 0.0 disables rate limiting. + * + * @param rate The maximum download rate + */ public void setMaxDownloadRate(double rate){ this.maxDownloadRate = rate; } From 73b78f203080ff15fc4cb87fffcf8fdb02569f7f Mon Sep 17 00:00:00 2001 From: "P. Taylor Goetz" Date: Mon, 22 Jul 2013 12:41:14 -0400 Subject: [PATCH 06/68] reduce rate limit code duplication by refactoring code present in both InboundThread and OutboundThread to a common ExchangeThread superclass. --- .../ttorrent/client/peer/PeerExchange.java | 82 ++++++++++--------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index 8a475f0e3..46bd8cc0a 100644 --- a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -187,6 +187,45 @@ public void close() { logger.debug("Peer exchange with {} closed.", this.peer); } + + /** + * Abstract Thread subclass that allows conditional rate limiting + * for Type.PIECE messages. + * + *

+ * To impose rate limits, we only want to throttle when + * processing PIECE messages. All other peer messages + * should be exchanged as quickly as possible. + *

+ * + * @author ptgoetz + * + */ + private abstract class RateLimitThread extends Thread{ + protected Rate rate = new Rate(); + protected long sleep = 1000; + + protected void rateLimit(double maxRate, long messageSize, PeerMessage message){ + if(message.getType() == Type.PIECE && maxRate > 0){ + try { + this.rate.add(messageSize); + // continuously adjust the sleep time to try to hit our + // target rate limit + if(rate.get() > (maxRate * 1024)){ + Thread.sleep(this.sleep); + this.sleep += 50; + } else { + this.sleep -= 50; + } + if(this.sleep < 0){ + this.sleep = 0; + } + } catch (InterruptedException e) { + // not critical + } + } + } + } /** * Incoming messages thread. @@ -200,9 +239,7 @@ public void close() { * * @author mpetazzoni */ - private class IncomingThread extends Thread { - private Rate rate = new Rate(); - private long sleep = 1000; + private class IncomingThread extends RateLimitThread { @Override public void run() { @@ -249,23 +286,7 @@ public void run() { PeerMessage message = PeerMessage.parse(buffer, torrent); logger.trace("Received {} from {}", message, peer); - // throttling - if(message.getType() == Type.PIECE && PeerExchange.this.torrent.getMaxDownloadRate() > 0){ - try { - rate.add(size); - if(rate.get() > (PeerExchange.this.torrent.getMaxDownloadRate() * 1024)){ - Thread.sleep(this.sleep); - this.sleep += 50; - } else { - this.sleep -= 50; - } - if(this.sleep < 0){ - this.sleep = 0; - } - } catch (InterruptedException e) { - // not critical - } - } + rateLimit(PeerExchange.this.torrent.getMaxDownloadRate(), size, message); for (MessageListener listener : listeners) { listener.handleMessage(message); @@ -301,9 +322,7 @@ public void run() { * * @author mpetazzoni */ - private class OutgoingThread extends Thread { - private Rate rate = new Rate(); - private long sleep = 1000; + private class OutgoingThread extends RateLimitThread { @Override public void run() { @@ -338,22 +357,7 @@ public void run() { } } - if(message.getType() == Type.PIECE && PeerExchange.this.torrent.getMaxUploadRate() > 0){ - try { - rate.add(size); - if(rate.get() > (PeerExchange.this.torrent.getMaxUploadRate() * 1024)){ - Thread.sleep(this.sleep); - this.sleep += 50; - } else { - this.sleep -= 50; - } - if(this.sleep < 0){ - this.sleep = 0; - } - } catch (InterruptedException e) { - // not critical - } - } + rateLimit(PeerExchange.this.torrent.getMaxUploadRate(), size, message); } catch (InterruptedException ie) { // Ignore and potentially terminate } From 3ef9e9b1545cdc96135e2bfa72dda6ca96368d0a Mon Sep 17 00:00:00 2001 From: "P. Taylor Goetz" Date: Mon, 22 Jul 2013 13:02:18 -0400 Subject: [PATCH 07/68] add example of upload/download rate limiting to the client usage sample. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index e014586bb..9af87198e 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,13 @@ Client client = new Client( new File("/path/to/your.torrent"), new File("/path/to/output/directory"))); + // You can optionally set download/upload rate limits + // in kb/second. Setting a limit to 0.0 disables rate + // limits. + client.setMaxDownloadRate(50.0); + client.setMaxUploadRate(50.0); + + // At this point, can you either call download() to download the torrent and // stop immediately after... client.download(); From 13db7ab546f9f0d09696dfa842fb91c8bec1709d Mon Sep 17 00:00:00 2001 From: "P. Taylor Goetz" Date: Mon, 22 Jul 2013 17:25:39 -0400 Subject: [PATCH 08/68] add comments documenting the drawbacks/potential improvements for the download/upload rate limiting algorithm --- .../ttorrent/client/peer/PeerExchange.java | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index 46bd8cc0a..d16a1d24e 100644 --- a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -201,23 +201,45 @@ public void close() { * @author ptgoetz * */ - private abstract class RateLimitThread extends Thread{ + private abstract class RateLimitThread extends Thread { protected Rate rate = new Rate(); protected long sleep = 1000; - protected void rateLimit(double maxRate, long messageSize, PeerMessage message){ - if(message.getType() == Type.PIECE && maxRate > 0){ + /** + * Dynamically determines an amount of time to sleep, based + * on the average read/write throughput. + * + *

+ * The algorithm is functional, but could certainly be + * improved upon. One obvious drawback is that with large + * changes in maxRate, it will take a while + * for the sleep time to adjust and the throttled rate + * to "smooth out." + *

+ * + *

+ * Ideally, it would calculate the optimal sleep time + * necessary to hit a desired throughput rather than + * continuously adjust toward a goal. + *

+ * + * @param maxRate the target rate in kB/second + * @param messageSize the size, in bytes, of the last message read/written + * @param message the last PeerMessage read/written + */ + protected void rateLimit(double maxRate, long messageSize, PeerMessage message) { + if(message.getType() == Type.PIECE && maxRate > 0) { try { this.rate.add(messageSize); // continuously adjust the sleep time to try to hit our // target rate limit - if(rate.get() > (maxRate * 1024)){ + if(rate.get() > (maxRate * 1024)) { Thread.sleep(this.sleep); this.sleep += 50; } else { this.sleep -= 50; } - if(this.sleep < 0){ + if(this.sleep < 0) { this.sleep = 0; } } catch (InterruptedException e) { From 00bc8f287aa361850c7b8638ce243bd182747025 Mon Sep 17 00:00:00 2001 From: "P. Taylor Goetz" Date: Mon, 22 Jul 2013 17:27:37 -0400 Subject: [PATCH 09/68] minor formatting/style modifications based on feedback from pull request #49 --- README.md | 2 +- .../java/com/turn/ttorrent/client/Client.java | 15 ++++++--------- .../com/turn/ttorrent/client/SharedTorrent.java | 12 ++++++------ 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 9af87198e..b51ddb18e 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Client client = new Client( new File("/path/to/output/directory"))); // You can optionally set download/upload rate limits - // in kb/second. Setting a limit to 0.0 disables rate + // in kB/second. Setting a limit to 0.0 disables rate // limits. client.setMaxDownloadRate(50.0); client.setMaxUploadRate(50.0); diff --git a/src/main/java/com/turn/ttorrent/client/Client.java b/src/main/java/com/turn/ttorrent/client/Client.java index e754a0ab0..3b9c4cbd5 100644 --- a/src/main/java/com/turn/ttorrent/client/Client.java +++ b/src/main/java/com/turn/ttorrent/client/Client.java @@ -173,22 +173,22 @@ public Client(InetAddress address, SharedTorrent torrent) } /** - * Set the maximum download rate (in kb/second) for this + * Set the maximum download rate (in kB/second) for this * torrent. A setting of <= 0.0 disables rate limiting. * * @param rate The maximum download rate */ - public void setMaxDownloadRate(double rate){ + public void setMaxDownloadRate(double rate) { this.torrent.setMaxDownloadRate(rate); } /** - * Set the maximum upload rate (in kb/second) for this + * Set the maximum upload rate (in kB/second) for this * torrent. A setting of <= 0.0 disables rate limiting. * * @param rate The maximum upload rate */ - public void setMaxUploadRate(double rate){ + public void setMaxUploadRate(double rate) { this.torrent.setMaxUploadRate(rate); } @@ -1015,8 +1015,8 @@ private static void usage(PrintStream s) { s.println(" -o,--output DIR Read/write data to directory DIR."); s.println(" -i,--iface IFACE Bind to interface IFACE."); s.println(" -s,--seed SECONDS Time to seed after downloading (default: infinitely)."); - s.println(" -d,--max-download KB/SEC Max download rate (default: infinitely)."); - s.println(" -u,--max-upload KB/SEC Max upload rate (default: infinitely)."); + s.println(" -d,--max-download KB/SEC Max download rate (default: unlimited)."); + s.println(" -u,--max-upload KB/SEC Max upload rate (default: unlimited)."); s.println(); } @@ -1076,9 +1076,6 @@ public static void main(String[] args) { CmdLineParser.Option seedTime = parser.addIntegerOption('s', "seed"); CmdLineParser.Option maxUpload = parser.addDoubleOption('u', "max-upload"); CmdLineParser.Option maxDownload = parser.addDoubleOption('d', "max-download"); - - logger.debug("Max Download: {}", parser.getOptionValue(maxDownload, 0.0)); - try { parser.parse(args); diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index 6fa207043..b4788defd 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -249,31 +249,31 @@ public static SharedTorrent fromFile(File source, File parent) return new SharedTorrent(data, parent); } - public double getMaxUploadRate(){ + public double getMaxUploadRate() { return this.maxUploadRate; } /** - * Set the maximum upload rate (in kb/second) for this + * Set the maximum upload rate (in kB/second) for this * torrent. A setting of <= 0.0 disables rate limiting. * * @param rate The maximum upload rate */ - public void setMaxUploadRate(double rate){ + public void setMaxUploadRate(double rate) { this.maxUploadRate = rate; } - public double getMaxDownloadRate(){ + public double getMaxDownloadRate() { return this.maxDownloadRate; } /** - * Set the maximum download rate (in kb/second) for this + * Set the maximum download rate (in kB/second) for this * torrent. A setting of <= 0.0 disables rate limiting. * * @param rate The maximum download rate */ - public void setMaxDownloadRate(double rate){ + public void setMaxDownloadRate(double rate) { this.maxDownloadRate = rate; } From 59a2ec1757c790d433f4d99499137f453b6a4a45 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Tue, 23 Jul 2013 15:41:42 +0200 Subject: [PATCH 10/68] Fix a few styling issues from recent contributions Signed-off-by: Maxime Petazzoni --- README.md | 11 ++- .../java/com/turn/ttorrent/client/Client.java | 12 +-- .../turn/ttorrent/client/SharedTorrent.java | 16 ++-- .../ttorrent/client/announce/Announce.java | 17 ++-- .../ttorrent/client/peer/PeerExchange.java | 88 ++++++++++--------- 5 files changed, 75 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index b51ddb18e..121d98360 100644 --- a/README.md +++ b/README.md @@ -82,12 +82,11 @@ Client client = new Client( new File("/path/to/your.torrent"), new File("/path/to/output/directory"))); - // You can optionally set download/upload rate limits - // in kB/second. Setting a limit to 0.0 disables rate - // limits. - client.setMaxDownloadRate(50.0); - client.setMaxUploadRate(50.0); - +// You can optionally set download/upload rate limits +// in kB/second. Setting a limit to 0.0 disables rate +// limits. +client.setMaxDownloadRate(50.0); +client.setMaxUploadRate(50.0); // At this point, can you either call download() to download the torrent and // stop immediately after... diff --git a/src/main/java/com/turn/ttorrent/client/Client.java b/src/main/java/com/turn/ttorrent/client/Client.java index 3b9c4cbd5..06c9fd6da 100644 --- a/src/main/java/com/turn/ttorrent/client/Client.java +++ b/src/main/java/com/turn/ttorrent/client/Client.java @@ -171,21 +171,21 @@ public Client(InetAddress address, SharedTorrent torrent) this.connected = new ConcurrentHashMap(); this.random = new Random(System.currentTimeMillis()); } - + /** - * Set the maximum download rate (in kB/second) for this + * Set the maximum download rate (in kB/second) for this * torrent. A setting of <= 0.0 disables rate limiting. - * + * * @param rate The maximum download rate */ public void setMaxDownloadRate(double rate) { this.torrent.setMaxDownloadRate(rate); } - + /** - * Set the maximum upload rate (in kB/second) for this + * Set the maximum upload rate (in kB/second) for this * torrent. A setting of <= 0.0 disables rate limiting. - * + * * @param rate The maximum upload rate */ public void setMaxUploadRate(double rate) { diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index b4788defd..c41df41b5 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -248,29 +248,29 @@ public static SharedTorrent fromFile(File source, File parent) fis.close(); return new SharedTorrent(data, parent); } - + public double getMaxUploadRate() { return this.maxUploadRate; } - + /** - * Set the maximum upload rate (in kB/second) for this + * Set the maximum upload rate (in kB/second) for this * torrent. A setting of <= 0.0 disables rate limiting. - * + * * @param rate The maximum upload rate */ public void setMaxUploadRate(double rate) { this.maxUploadRate = rate; } - + public double getMaxDownloadRate() { return this.maxDownloadRate; } - + /** - * Set the maximum download rate (in kB/second) for this + * Set the maximum download rate (in kB/second) for this * torrent. A setting of <= 0.0 disables rate limiting. - * + * * @param rate The maximum download rate */ public void setMaxDownloadRate(double rate) { diff --git a/src/main/java/com/turn/ttorrent/client/announce/Announce.java b/src/main/java/com/turn/ttorrent/client/announce/Announce.java index 558c44fd7..f6d3d7d3d 100644 --- a/src/main/java/com/turn/ttorrent/client/announce/Announce.java +++ b/src/main/java/com/turn/ttorrent/client/announce/Announce.java @@ -226,7 +226,7 @@ public void run() { event = AnnounceRequestMessage.RequestEvent.NONE; } catch (AnnounceException ae) { logger.warn(ae.getMessage()); - + try { this.moveToNextTrackerClient(); } catch (AnnounceException e) { @@ -286,14 +286,15 @@ private TrackerClient createTrackerClient(SharedTorrent torrent, Peer peer, /** * Returns the current tracker client used for announces. - * @throws AnnounceException + * @throws AnnounceException When the current announce tier isn't defined + * in the torrent. */ public TrackerClient getCurrentTrackerClient() throws AnnounceException { - if (!this.clients.contains(this.currentTier) - || !this.clients.get(this.currentTier).contains(this.currentClient)) { + if (!this.clients.contains(this.currentTier) || + !this.clients.get(this.currentTier).contains(this.currentClient)) { throw new AnnounceException("Current tier or client isn't available"); } - + return this.clients .get(this.currentTier) .get(this.currentClient); @@ -311,7 +312,8 @@ public TrackerClient getCurrentTrackerClient() throws AnnounceException { * The index of the currently used {@link TrackerClient} is reset to 0 to * reflect this change. *

- * @throws AnnounceException + * + * @throws AnnounceException */ private void promoteCurrentTrackerClient() throws AnnounceException { logger.trace("Promoting current tracker client for {} " + @@ -339,7 +341,8 @@ private void promoteCurrentTrackerClient() throws AnnounceException { * By design no empty tier can be in the tracker list structure so we don't * need to check for empty tiers here. *

- * @throws AnnounceException + * + * @throws AnnounceException */ private void moveToNextTrackerClient() throws AnnounceException { int tier = this.currentTier; diff --git a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index d16a1d24e..7fbf98849 100644 --- a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -187,64 +187,64 @@ public void close() { logger.debug("Peer exchange with {} closed.", this.peer); } - + /** * Abstract Thread subclass that allows conditional rate limiting - * for Type.PIECE messages. + * for PIECE messages. * *

- * To impose rate limits, we only want to throttle when - * processing PIECE messages. All other peer messages - * should be exchanged as quickly as possible. + * To impose rate limits, we only want to throttle when processing PIECE + * messages. All other peer messages should be exchanged as quickly as + * possible. *

* * @author ptgoetz - * */ private abstract class RateLimitThread extends Thread { - protected Rate rate = new Rate(); + + protected final Rate rate = new Rate(); protected long sleep = 1000; - + /** - * Dynamically determines an amount of time to sleep, based - * on the average read/write throughput. + * Dynamically determines an amount of time to sleep, based on the + * average read/write throughput. * *

- * The algorithm is functional, but could certainly be - * improved upon. One obvious drawback is that with large - * changes in maxRate, it will take a while - * for the sleep time to adjust and the throttled rate - * to "smooth out." + * The algorithm is functional, but could certainly be improved upon. + * One obvious drawback is that with large changes in + * maxRate, it will take a while for the sleep time to + * adjust and the throttled rate to "smooth out." *

* *

- * Ideally, it would calculate the optimal sleep time - * necessary to hit a desired throughput rather than - * continuously adjust toward a goal. + * Ideally, it would calculate the optimal sleep time necessary to hit + * a desired throughput rather than continuously adjust toward a goal. *

* - * @param maxRate the target rate in kB/second - * @param messageSize the size, in bytes, of the last message read/written - * @param message the last PeerMessage read/written + * @param maxRate the target rate in kB/second. + * @param messageSize the size, in bytes, of the last message read/written. + * @param message the last PeerMessage read/written. */ protected void rateLimit(double maxRate, long messageSize, PeerMessage message) { - if(message.getType() == Type.PIECE && maxRate > 0) { - try { - this.rate.add(messageSize); - // continuously adjust the sleep time to try to hit our - // target rate limit - if(rate.get() > (maxRate * 1024)) { - Thread.sleep(this.sleep); - this.sleep += 50; - } else { - this.sleep -= 50; - } - if(this.sleep < 0) { - this.sleep = 0; - } - } catch (InterruptedException e) { - // not critical + if (message.getType() != Type.PIECE || maxRate <= 0) { + return; + } + + try { + this.rate.add(messageSize); + + // Continuously adjust the sleep time to try to hit our target + // rate limit. + if (rate.get() > (maxRate * 1024)) { + Thread.sleep(this.sleep); + this.sleep += 50; + } else { + this.sleep = this.sleep > 50 + ? this.sleep - 50 + : 0; } + } catch (InterruptedException e) { + // Not critical, eat it. } } } @@ -307,8 +307,10 @@ public void run() { try { PeerMessage message = PeerMessage.parse(buffer, torrent); logger.trace("Received {} from {}", message, peer); - - rateLimit(PeerExchange.this.torrent.getMaxDownloadRate(), size, message); + + // Wait if needed to reach configured download rate. + this.rateLimit(PeerExchange.this.torrent.getMaxDownloadRate(), + size, message); for (MessageListener listener : listeners) { listener.handleMessage(message); @@ -345,7 +347,7 @@ public void run() { * @author mpetazzoni */ private class OutgoingThread extends RateLimitThread { - + @Override public void run() { try { @@ -378,8 +380,10 @@ public void run() { "Reached end of stream while writing"); } } - - rateLimit(PeerExchange.this.torrent.getMaxUploadRate(), size, message); + + // Wait if needed to reach configured upload rate. + this.rateLimit(PeerExchange.this.torrent.getMaxUploadRate(), + size, message); } catch (InterruptedException ie) { // Ignore and potentially terminate } From 64b886552678578d48edee5a860c32387d132246 Mon Sep 17 00:00:00 2001 From: Dan Oxlade Date: Mon, 22 Jul 2013 21:24:05 +0100 Subject: [PATCH 11/68] fixes Issue #47 --- src/main/java/com/turn/ttorrent/bcodec/BDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/turn/ttorrent/bcodec/BDecoder.java b/src/main/java/com/turn/ttorrent/bcodec/BDecoder.java index 305c56170..29a941dc9 100644 --- a/src/main/java/com/turn/ttorrent/bcodec/BDecoder.java +++ b/src/main/java/com/turn/ttorrent/bcodec/BDecoder.java @@ -206,7 +206,7 @@ public BEValue bdecodeNumber() throws IOException { c = this.read(); if (c == '0') throw new InvalidBEncodingException("Negative zero not allowed"); - chars[off] = (char)c; + chars[off] = '-'; off++; } From a47a2757e8e82b4cad570c6dabd5ed293172f1eb Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Tue, 23 Jul 2013 23:01:48 -0700 Subject: [PATCH 12/68] fixes turn/ttorrent#51 --- src/main/java/com/turn/ttorrent/client/announce/Announce.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/announce/Announce.java b/src/main/java/com/turn/ttorrent/client/announce/Announce.java index f6d3d7d3d..e1cca71eb 100644 --- a/src/main/java/com/turn/ttorrent/client/announce/Announce.java +++ b/src/main/java/com/turn/ttorrent/client/announce/Announce.java @@ -290,8 +290,8 @@ private TrackerClient createTrackerClient(SharedTorrent torrent, Peer peer, * in the torrent. */ public TrackerClient getCurrentTrackerClient() throws AnnounceException { - if (!this.clients.contains(this.currentTier) || - !this.clients.get(this.currentTier).contains(this.currentClient)) { + if ((this.currentTier >= this.clients.size()) || + (this.currentClient >= this.clients.get(this.currentTier).size())) { throw new AnnounceException("Current tier or client isn't available"); } From c1dbcd92d7a6f2c7fcdf8145551021dc3ef33c79 Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Mon, 29 Jul 2013 22:40:34 -0700 Subject: [PATCH 13/68] Update pom for compatibility with the maven-release-plugin --- pom.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 33d6c98ca..58392a482 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ http://turn.github.com/ttorrent/ com.turn ttorrent - 1.2 + 1.3-SNAPSHOT jar @@ -27,7 +27,9 @@ scm:git:git://github.com/turn/ttorrent.git + scm:git:ssh://git@github.com/turn/ttorrent.git http://github.com/turn/ttorrent + HEAD From 9e8abedaa003a43e18fcb289968bf2154e1c74f8 Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Mon, 29 Jul 2013 23:01:55 -0700 Subject: [PATCH 14/68] Use maven-shade-plugin to produce executable jar --- bin/client | 2 +- bin/torrent | 2 +- bin/tracker | 2 +- pom.xml | 35 ++++++++++++++++++----------------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/bin/client b/bin/client index cd77b761a..f6ffac863 100755 --- a/bin/client +++ b/bin/client @@ -15,4 +15,4 @@ # limitations under the License. base=$(dirname $(readlink -f $0)) -exec java -jar $(find ${base}/../build -name "ttorrent-*.jar" | tail -n 1) $* +exec java -jar $(find ${base}/../build -name "ttorrent-*-shaded.jar" | tail -n 1) $* diff --git a/bin/torrent b/bin/torrent index 99d46d258..70ed5b76d 100755 --- a/bin/torrent +++ b/bin/torrent @@ -15,4 +15,4 @@ # limitations under the License. base=$(dirname $(readlink -f $0)) -exec java -cp $(find ${base}/../build -name "ttorrent-*.jar" | tail -n 1) com.turn.ttorrent.common.Torrent $* +exec java -cp $(find ${base}/../build -name "ttorrent-*-shaded.jar" | tail -n 1) com.turn.ttorrent.common.Torrent $* diff --git a/bin/tracker b/bin/tracker index 95aca596b..16db448eb 100755 --- a/bin/tracker +++ b/bin/tracker @@ -15,4 +15,4 @@ # limitations under the License. base=$(dirname $(readlink -f $0)) -exec java -cp $(find ${base}/../build -name "ttorrent-*.jar" | tail -n 1) com.turn.ttorrent.tracker.Tracker $* +exec java -cp $(find ${base}/../build -name "ttorrent-*-shaded.jar" | tail -n 1) com.turn.ttorrent.tracker.Tracker $* diff --git a/pom.xml b/pom.xml index 33d6c98ca..fa6a1d34d 100644 --- a/pom.xml +++ b/pom.xml @@ -127,6 +127,11 @@ maven-jar-plugin 2.4 + + + true + + ** @@ -144,28 +149,24 @@ - maven-assembly-plugin - - - jar-with-dependencies - - false - - - false - true - com.turn.ttorrent.client.Client - - - + maven-shade-plugin + 2.1 - make-my-jar-with-dependencies package - - assembly + shade + + ${project.build.directory}/${project.artifactId}-${project.version}-shaded.jar + + + + com.turn.ttorrent.client.Client + + + + From 5a499cefe0f38e702f944d6f6c9b92756344c266 Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Tue, 30 Jul 2013 11:24:56 -0700 Subject: [PATCH 15/68] Clean up javadoc problems --- src/main/java/com/turn/ttorrent/client/Client.java | 6 +++--- src/main/java/com/turn/ttorrent/client/SharedTorrent.java | 2 -- .../java/com/turn/ttorrent/client/announce/Announce.java | 2 -- .../com/turn/ttorrent/client/announce/TrackerClient.java | 2 +- .../com/turn/ttorrent/client/announce/UDPTrackerClient.java | 4 ++-- src/main/java/com/turn/ttorrent/common/Torrent.java | 3 +-- src/main/java/com/turn/ttorrent/tracker/TrackerService.java | 4 ++-- 7 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/Client.java b/src/main/java/com/turn/ttorrent/client/Client.java index 06c9fd6da..f16d23c71 100644 --- a/src/main/java/com/turn/ttorrent/client/Client.java +++ b/src/main/java/com/turn/ttorrent/client/Client.java @@ -941,11 +941,11 @@ public void handleIOException(SharingPeer peer, IOException ioe) { *

* When the download is complete, the client switches to seeding mode for * as long as requested in the share() call, if seeding was - * requested. If not, the StopSeedingTask will execute immediately to stop - * the client's main loop. + * requested. If not, the {@link ClientShutdown} will execute + * immediately to stop the client's main loop. *

* - * @see StopSeedingTask + * @see ClientShutdown */ private synchronized void seed() { // Silently ignore if we're already seeding. diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index c41df41b5..d1b02c736 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -173,8 +173,6 @@ public SharedTorrent(byte[] torrent, File destDir) * destination directory does not exist and can't be created. * @throws IOException If the torrent file cannot be read or decoded. * @throws NoSuchAlgorithmException - * @throws URISyntaxException When one of the defined tracker addresses is - * invalid. */ public SharedTorrent(byte[] torrent, File parent, boolean seeder) throws FileNotFoundException, IOException, NoSuchAlgorithmException { diff --git a/src/main/java/com/turn/ttorrent/client/announce/Announce.java b/src/main/java/com/turn/ttorrent/client/announce/Announce.java index e1cca71eb..0146b011f 100644 --- a/src/main/java/com/turn/ttorrent/client/announce/Announce.java +++ b/src/main/java/com/turn/ttorrent/client/announce/Announce.java @@ -75,8 +75,6 @@ public class Announce implements Runnable { * * @param torrent The torrent we're announcing about. * @param peer Our peer specification. - * @param type A string representing the announce type (used in the thread - * name). */ public Announce(SharedTorrent torrent, Peer peer) { this.peer = peer; diff --git a/src/main/java/com/turn/ttorrent/client/announce/TrackerClient.java b/src/main/java/com/turn/ttorrent/client/announce/TrackerClient.java index b757e0e17..7cfa0ece4 100644 --- a/src/main/java/com/turn/ttorrent/client/announce/TrackerClient.java +++ b/src/main/java/com/turn/ttorrent/client/announce/TrackerClient.java @@ -83,7 +83,7 @@ public abstract void announce(AnnounceRequestMessage.RequestEvent event, * Close any opened announce connection. * *

- * This method is called by {@link #stop()} to make sure all connections + * This method is called by {@link Announce#stop()} to make sure all connections * are correctly closed when the announce thread is asked to stop. *

*/ diff --git a/src/main/java/com/turn/ttorrent/client/announce/UDPTrackerClient.java b/src/main/java/com/turn/ttorrent/client/announce/UDPTrackerClient.java index 01b1c0420..5df3229b0 100644 --- a/src/main/java/com/turn/ttorrent/client/announce/UDPTrackerClient.java +++ b/src/main/java/com/turn/ttorrent/client/announce/UDPTrackerClient.java @@ -239,7 +239,7 @@ public void announce(AnnounceRequestMessage.RequestEvent event, * *

* Verifies the transaction ID of the message before passing it over to - * {@link Announce#handleTrackerAnnounceResponse()}. + * any registered {@link AnnounceResponseListener}. *

* * @param message The message received from the tracker in response to the @@ -352,7 +352,7 @@ private void send(ByteBuffer data) { * * @param attempt The attempt number, used to calculate the timeout for the * receive operation. - * @retun Returns a {@link ByteBuffer} containing the packet data. + * @return Returns a {@link ByteBuffer} containing the packet data. */ private ByteBuffer recv(int attempt) throws IOException, SocketException, SocketTimeoutException { diff --git a/src/main/java/com/turn/ttorrent/common/Torrent.java b/src/main/java/com/turn/ttorrent/common/Torrent.java index e468dc560..de346067c 100644 --- a/src/main/java/com/turn/ttorrent/common/Torrent.java +++ b/src/main/java/com/turn/ttorrent/common/Torrent.java @@ -133,7 +133,6 @@ public TorrentFile(File file, long size) { * BitTorrent specification) and create a Torrent object from it. * * @param torrent The meta-info byte data. - * @param parent The parent directory or location of the torrent files. * @param seeder Whether we'll be seeding for this torrent or not. * @throws IOException When the info dictionary can't be read or * encoded and hashed back to create the torrent's SHA-1 hash. @@ -592,7 +591,7 @@ public static Torrent create(File source, List> announceList, * considering we'll be a full initial seeder for it. *

* - * @param parent The parent directory or location of the torrent files, + * @param source The parent directory or location of the torrent files, * also used as the torrent's name. * @param files The files to add into this torrent. * @param announceList The announce URIs organized as tiers that will diff --git a/src/main/java/com/turn/ttorrent/tracker/TrackerService.java b/src/main/java/com/turn/ttorrent/tracker/TrackerService.java index 014ec5da6..53c2821ad 100644 --- a/src/main/java/com/turn/ttorrent/tracker/TrackerService.java +++ b/src/main/java/com/turn/ttorrent/tracker/TrackerService.java @@ -252,7 +252,7 @@ private void process(Request request, Response response, * Tracker HTTP protocol. *

* - * @param uri The request's full URI, including query parameters. + * @param request The request's full URI, including query parameters. * @return The {@link AnnounceRequestMessage} representing the client's * announce request. */ @@ -350,7 +350,7 @@ private void serveError(Response response, OutputStream body, * @param response The HTTP response object. * @param body The response output stream to write to. * @param status The HTTP status code to return. - * @param error The failure reason reported by the tracker. + * @param reason The failure reason reported by the tracker. */ private void serveError(Response response, OutputStream body, Status status, ErrorMessage.FailureReason reason) throws IOException { From 00802966f7444e12122ae35bb71243d8e60d24bc Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Wed, 14 Aug 2013 12:21:09 -0700 Subject: [PATCH 16/68] Fix pom parent relative path --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index cf2d70db3..35e7e3843 100644 --- a/pom.xml +++ b/pom.xml @@ -6,6 +6,7 @@ org.sonatype.oss oss-parent 7 + Java BitTorrent library From 9e09ee5b0b21e68a0cbce76a88317a61efff51c1 Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Thu, 15 Aug 2013 12:18:58 -0700 Subject: [PATCH 17/68] Adding more platform agnostic shell scripts --- bin/ttorrent | 73 ++++++++++++++++++++++++++++++++++++++++++++ bin/ttorrent-torrent | 18 +++++++++++ bin/ttorrent-tracker | 18 +++++++++++ 3 files changed, 109 insertions(+) create mode 100755 bin/ttorrent create mode 100755 bin/ttorrent-torrent create mode 100755 bin/ttorrent-tracker diff --git a/bin/ttorrent b/bin/ttorrent new file mode 100755 index 000000000..fc21deaf6 --- /dev/null +++ b/bin/ttorrent @@ -0,0 +1,73 @@ +#!/bin/sh + +# Copyright (C) 2012 Turn, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +EXECJAR="ttorrent-*-shaded.jar" + +real_path() { + case $1 in + /*) + SCRIPT="$1" + ;; + *) + PWD=`pwd` + SCRIPT="$PWD/$1" + ;; + esac + CHANGED=true + while [ "X$CHANGED" != "X" ] ; do + # Change spaces to ":" so the tokens can be parsed. + SAFESCRIPT=`echo $SCRIPT | sed -e 's; ;:;g'` + # Get the real path to this script, resolving any symbolic links + TOKENS=`echo $SAFESCRIPT | sed -e 's;/; ;g'` + REALPATH= + for C in $TOKENS; do + # Change any ":" in the token back to a space. + C=`echo $C | sed -e 's;:; ;g'` + REALPATH="$REALPATH/$C" + # If REALPATH is a sym link, resolve it. Loop for nested links. + while [ -h "$REALPATH" ] ; do + LS="`ls -ld "$REALPATH"`" + LINK="`expr "$LS" : '.*-> \(.*\)$'`" + if expr "$LINK" : '/.*' > /dev/null; then + # LINK is absolute. + REALPATH="$LINK" + else + # LINK is relative. + REALPATH="`dirname "$REALPATH"`""/$LINK" + fi + done + done + if [ "$REALPATH" = "$SCRIPT" ] ; then + CHANGED="" + else + SCRIPT="$REALPATH" + fi + done + echo "$REALPATH" +} + +base=$(dirname $(real_path $0)) +CPARG=$(find ${base}/../build -name "$EXECJAR" | tail -n 1) +if [ -z "$CPARG" ] ; then + echo "Unable to find $EXECJAR" + exit 1 +fi +if [ -z "$MAINCLASS" ] ; then + CPARG="-jar $CPARG" +else + CPARG="-cp $CPARG $MAINCLASS" +fi +exec java $CPARG "$@" diff --git a/bin/ttorrent-torrent b/bin/ttorrent-torrent new file mode 100755 index 000000000..30e387695 --- /dev/null +++ b/bin/ttorrent-torrent @@ -0,0 +1,18 @@ +#!/bin/sh + +# Copyright (C) 2012 Turn, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +EXEFILE="${0%-torrent}" +MAINCLASS="com.turn.ttorrent.common.Torrent" "${EXEFILE}" "$@" diff --git a/bin/ttorrent-tracker b/bin/ttorrent-tracker new file mode 100755 index 000000000..1af41aa3c --- /dev/null +++ b/bin/ttorrent-tracker @@ -0,0 +1,18 @@ +#!/bin/sh + +# Copyright (C) 2012 Turn, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +EXEFILE="${0%-tracker}" +MAINCLASS="com.turn.ttorrent.tracker.Tracker" "${EXEFILE}" "$@" From d50eb9957bea1b771336710c7bdcabb180db1490 Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Thu, 15 Aug 2013 12:20:11 -0700 Subject: [PATCH 18/68] Removing old shell scripts --- bin/client | 18 ------------------ bin/torrent | 18 ------------------ bin/tracker | 18 ------------------ 3 files changed, 54 deletions(-) delete mode 100755 bin/client delete mode 100755 bin/torrent delete mode 100755 bin/tracker diff --git a/bin/client b/bin/client deleted file mode 100755 index f6ffac863..000000000 --- a/bin/client +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2012 Turn, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -base=$(dirname $(readlink -f $0)) -exec java -jar $(find ${base}/../build -name "ttorrent-*-shaded.jar" | tail -n 1) $* diff --git a/bin/torrent b/bin/torrent deleted file mode 100755 index 70ed5b76d..000000000 --- a/bin/torrent +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2012 Turn, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -base=$(dirname $(readlink -f $0)) -exec java -cp $(find ${base}/../build -name "ttorrent-*-shaded.jar" | tail -n 1) com.turn.ttorrent.common.Torrent $* diff --git a/bin/tracker b/bin/tracker deleted file mode 100755 index 16db448eb..000000000 --- a/bin/tracker +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2012 Turn, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -base=$(dirname $(readlink -f $0)) -exec java -cp $(find ${base}/../build -name "ttorrent-*-shaded.jar" | tail -n 1) com.turn.ttorrent.tracker.Tracker $* From 1c8dbfceb70978b8b41f465aa93f3dad9e90efb8 Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Mon, 26 Aug 2013 11:28:35 -0700 Subject: [PATCH 19/68] Move entry-point main methods to separate package --- bin/ttorrent-torrent | 2 +- bin/ttorrent-tracker | 2 +- pom.xml | 2 +- .../com/turn/ttorrent/cli/ClientMain.java | 176 ++++++++++++++++++ .../com/turn/ttorrent/cli/TorrentMain.java | 169 +++++++++++++++++ .../com/turn/ttorrent/cli/TrackerMain.java | 118 ++++++++++++ .../java/com/turn/ttorrent/client/Client.java | 147 +-------------- .../com/turn/ttorrent/common/Torrent.java | 135 -------------- .../com/turn/ttorrent/tracker/Tracker.java | 100 +--------- 9 files changed, 479 insertions(+), 372 deletions(-) create mode 100644 src/main/java/com/turn/ttorrent/cli/ClientMain.java create mode 100644 src/main/java/com/turn/ttorrent/cli/TorrentMain.java create mode 100644 src/main/java/com/turn/ttorrent/cli/TrackerMain.java diff --git a/bin/ttorrent-torrent b/bin/ttorrent-torrent index 30e387695..4193c4e2c 100755 --- a/bin/ttorrent-torrent +++ b/bin/ttorrent-torrent @@ -15,4 +15,4 @@ # limitations under the License. EXEFILE="${0%-torrent}" -MAINCLASS="com.turn.ttorrent.common.Torrent" "${EXEFILE}" "$@" +MAINCLASS="com.turn.ttorrent.cli.TorrentMain" "${EXEFILE}" "$@" diff --git a/bin/ttorrent-tracker b/bin/ttorrent-tracker index 1af41aa3c..e6bacd5d9 100755 --- a/bin/ttorrent-tracker +++ b/bin/ttorrent-tracker @@ -15,4 +15,4 @@ # limitations under the License. EXEFILE="${0%-tracker}" -MAINCLASS="com.turn.ttorrent.tracker.Tracker" "${EXEFILE}" "$@" +MAINCLASS="com.turn.ttorrent.cli.TrackerMain" "${EXEFILE}" "$@" diff --git a/pom.xml b/pom.xml index 35e7e3843..c0e33c1eb 100644 --- a/pom.xml +++ b/pom.xml @@ -165,7 +165,7 @@ - com.turn.ttorrent.client.Client + com.turn.ttorrent.cli.ClientMain diff --git a/src/main/java/com/turn/ttorrent/cli/ClientMain.java b/src/main/java/com/turn/ttorrent/cli/ClientMain.java new file mode 100644 index 000000000..ae56e5f75 --- /dev/null +++ b/src/main/java/com/turn/ttorrent/cli/ClientMain.java @@ -0,0 +1,176 @@ +/** + * Copyright (C) 2011-2013 Turn, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.turn.ttorrent.cli; + +import com.turn.ttorrent.client.Client; +import com.turn.ttorrent.client.SharedTorrent; + +import java.io.File; +import java.io.PrintStream; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.channels.UnsupportedAddressTypeException; +import java.util.Enumeration; + +import jargs.gnu.CmdLineParser; +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.PatternLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Command-line entry-point for starting a {@link Client} + */ +public class ClientMain { + + private static final Logger logger = + LoggerFactory.getLogger(ClientMain.class); + + /** + * Default data output directory. + */ + private static final String DEFAULT_OUTPUT_DIRECTORY = "/tmp"; + + /** + * Returns a usable {@link Inet4Address} for the given interface name. + * + *

+ * If an interface name is given, return the first usable IPv4 address for + * that interface. If no interface name is given or if that interface + * doesn't have an IPv4 address, return's localhost address (if IPv4). + *

+ * + *

+ * It is understood this makes the client IPv4 only, but it is important to + * remember that most BitTorrent extensions (like compact peer lists from + * trackers and UDP tracker support) are IPv4-only anyway. + *

+ * + * @param iface The network interface name. + * @return A usable IPv4 address as a {@link Inet4Address}. + * @throws UnsupportedAddressTypeException If no IPv4 address was available + * to bind on. + */ + private static Inet4Address getIPv4Address(String iface) + throws SocketException, UnsupportedAddressTypeException, + UnknownHostException { + if (iface != null) { + Enumeration addresses = + NetworkInterface.getByName(iface).getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress addr = addresses.nextElement(); + if (addr instanceof Inet4Address) { + return (Inet4Address)addr; + } + } + } + + InetAddress localhost = InetAddress.getLocalHost(); + if (localhost instanceof Inet4Address) { + return (Inet4Address)localhost; + } + + throw new UnsupportedAddressTypeException(); + } + + /** + * Display program usage on the given {@link PrintStream}. + */ + private static void usage(PrintStream s) { + s.println("usage: Client [options] "); + s.println(); + s.println("Available options:"); + s.println(" -h,--help Show this help and exit."); + s.println(" -o,--output DIR Read/write data to directory DIR."); + s.println(" -i,--iface IFACE Bind to interface IFACE."); + s.println(" -s,--seed SECONDS Time to seed after downloading (default: infinitely)."); + s.println(" -d,--max-download KB/SEC Max download rate (default: unlimited)."); + s.println(" -u,--max-upload KB/SEC Max upload rate (default: unlimited)."); + s.println(); + } + + /** + * Main client entry point for stand-alone operation. + */ + public static void main(String[] args) { + BasicConfigurator.configure(new ConsoleAppender( + new PatternLayout("%d [%-25t] %-5p: %m%n"))); + + CmdLineParser parser = new CmdLineParser(); + CmdLineParser.Option help = parser.addBooleanOption('h', "help"); + CmdLineParser.Option output = parser.addStringOption('o', "output"); + CmdLineParser.Option iface = parser.addStringOption('i', "iface"); + CmdLineParser.Option seedTime = parser.addIntegerOption('s', "seed"); + CmdLineParser.Option maxUpload = parser.addDoubleOption('u', "max-upload"); + CmdLineParser.Option maxDownload = parser.addDoubleOption('d', "max-download"); + + try { + parser.parse(args); + } catch (CmdLineParser.OptionException oe) { + System.err.println(oe.getMessage()); + usage(System.err); + System.exit(1); + } + + // Display help and exit if requested + if (Boolean.TRUE.equals((Boolean)parser.getOptionValue(help))) { + usage(System.out); + System.exit(0); + } + + String outputValue = (String)parser.getOptionValue(output, + DEFAULT_OUTPUT_DIRECTORY); + String ifaceValue = (String)parser.getOptionValue(iface); + int seedTimeValue = (Integer)parser.getOptionValue(seedTime, -1); + + double maxDownloadRate = (Double)parser.getOptionValue(maxDownload, 0.0); + double maxUploadRate = (Double)parser.getOptionValue(maxUpload, 0.0); + + String[] otherArgs = parser.getRemainingArgs(); + if (otherArgs.length != 1) { + usage(System.err); + System.exit(1); + } + + try { + Client c = new Client( + getIPv4Address(ifaceValue), + SharedTorrent.fromFile( + new File(otherArgs[0]), + new File(outputValue))); + + c.setMaxDownloadRate(maxDownloadRate); + c.setMaxUploadRate(maxUploadRate); + + // Set a shutdown hook that will stop the sharing/seeding and send + // a STOPPED announce request. + Runtime.getRuntime().addShutdownHook( + new Thread(new Client.ClientShutdown(c, null))); + + c.share(seedTimeValue); + if (Client.ClientState.ERROR.equals(c.getState())) { + System.exit(1); + } + } catch (Exception e) { + logger.error("Fatal error: {}", e.getMessage(), e); + System.exit(2); + } + } +} diff --git a/src/main/java/com/turn/ttorrent/cli/TorrentMain.java b/src/main/java/com/turn/ttorrent/cli/TorrentMain.java new file mode 100644 index 000000000..bdcb5587c --- /dev/null +++ b/src/main/java/com/turn/ttorrent/cli/TorrentMain.java @@ -0,0 +1,169 @@ +/** + * Copyright (C) 2011-2013 Turn, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.turn.ttorrent.cli; + +import com.turn.ttorrent.common.Torrent; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.URI; +import java.util.Arrays; + +import jargs.gnu.CmdLineParser; +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.PatternLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Command-line entry-point for reading and writing {@link Torrent} files. + */ +public class TorrentMain { + + private static final Logger logger = + LoggerFactory.getLogger(TorrentMain.class); + + /** + * Display program usage on the given {@link PrintStream}. + */ + private static void usage(PrintStream s) { + usage(s, null); + } + + /** + * Display a message and program usage on the given {@link PrintStream}. + */ + private static void usage(PrintStream s, String msg) { + if (msg != null) { + s.println(msg); + s.println(); + } + + s.println("usage: Torrent [options] [file|directory]"); + s.println(); + s.println("Available options:"); + s.println(" -h,--help Show this help and exit."); + s.println(" -t,--torrent FILE Use FILE to read/write torrent file."); + s.println(); + s.println(" -c,--create Create a new torrent file using " + + "the given announce URL and data."); + s.println(" -a,--announce Tracker URL (can be repeated)."); + s.println(); + } + + /** + * Torrent reader and creator. + * + *

+ * You can use the {@code main()} function of this class to read or create + * torrent files. See usage for details. + *

+ * + * TODO: support multiple announce URLs. + */ + public static void main(String[] args) { + BasicConfigurator.configure(new ConsoleAppender( + new PatternLayout("%-5p: %m%n"))); + + CmdLineParser parser = new CmdLineParser(); + CmdLineParser.Option help = parser.addBooleanOption('h', "help"); + CmdLineParser.Option filename = parser.addStringOption('t', "torrent"); + CmdLineParser.Option create = parser.addBooleanOption('c', "create"); + CmdLineParser.Option announce = parser.addStringOption('a', "announce"); + + try { + parser.parse(args); + } catch (CmdLineParser.OptionException oe) { + System.err.println(oe.getMessage()); + usage(System.err); + System.exit(1); + } + + // Display help and exit if requested + if (Boolean.TRUE.equals((Boolean)parser.getOptionValue(help))) { + usage(System.out); + System.exit(0); + } + + String filenameValue = (String)parser.getOptionValue(filename); + if (filenameValue == null) { + usage(System.err, "Torrent file must be provided!"); + System.exit(1); + } + + Boolean createFlag = (Boolean)parser.getOptionValue(create); + String announceURL = (String)parser.getOptionValue(announce); + + String[] otherArgs = parser.getRemainingArgs(); + + if (Boolean.TRUE.equals(createFlag) && + (otherArgs.length != 1 || announceURL == null)) { + usage(System.err, "Announce URL and a file or directory must be " + + "provided to create a torrent file!"); + System.exit(1); + } + + OutputStream fos = null; + try { + if (Boolean.TRUE.equals(createFlag)) { + if (filenameValue != null) { + fos = new FileOutputStream(filenameValue); + } else { + fos = System.out; + } + + URI announceURI = new URI(announceURL); + File source = new File(otherArgs[0]); + if (!source.exists() || !source.canRead()) { + throw new IllegalArgumentException( + "Cannot access source file or directory " + + source.getName()); + } + + String creator = String.format("%s (ttorrent)", + System.getProperty("user.name")); + + Torrent torrent = null; + if (source.isDirectory()) { + File[] files = source.listFiles(); + Arrays.sort(files); + torrent = Torrent.create(source, Arrays.asList(files), + announceURI, creator); + } else { + torrent = Torrent.create(source, announceURI, creator); + } + + torrent.save(fos); + } else { + Torrent.load(new File(filenameValue), true); + } + } catch (Exception e) { + logger.error("{}", e.getMessage(), e); + System.exit(2); + } finally { + if (fos != null && fos != System.out) { + try { + fos.close(); + } catch (IOException ioe) { + } + } + } + } +} diff --git a/src/main/java/com/turn/ttorrent/cli/TrackerMain.java b/src/main/java/com/turn/ttorrent/cli/TrackerMain.java new file mode 100644 index 000000000..bd3ffa473 --- /dev/null +++ b/src/main/java/com/turn/ttorrent/cli/TrackerMain.java @@ -0,0 +1,118 @@ +/** + * Copyright (C) 2011-2013 Turn, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.turn.ttorrent.cli; + +import com.turn.ttorrent.tracker.TrackedTorrent; +import com.turn.ttorrent.tracker.Tracker; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.PrintStream; +import java.net.InetSocketAddress; + +import jargs.gnu.CmdLineParser; +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.PatternLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Command-line entry-point for starting a {@link Tracker} + */ +public class TrackerMain { + + private static final Logger logger = + LoggerFactory.getLogger(TrackerMain.class); + + /** + * Display program usage on the given {@link PrintStream}. + */ + private static void usage(PrintStream s) { + s.println("usage: Tracker [options] [directory]"); + s.println(); + s.println("Available options:"); + s.println(" -h,--help Show this help and exit."); + s.println(" -p,--port PORT Bind to port PORT."); + s.println(); + } + + /** + * Main function to start a tracker. + */ + public static void main(String[] args) { + BasicConfigurator.configure(new ConsoleAppender( + new PatternLayout("%d [%-25t] %-5p: %m%n"))); + + CmdLineParser parser = new CmdLineParser(); + CmdLineParser.Option help = parser.addBooleanOption('h', "help"); + CmdLineParser.Option port = parser.addIntegerOption('p', "port"); + + try { + parser.parse(args); + } catch (CmdLineParser.OptionException oe) { + System.err.println(oe.getMessage()); + usage(System.err); + System.exit(1); + } + + // Display help and exit if requested + if (Boolean.TRUE.equals((Boolean)parser.getOptionValue(help))) { + usage(System.out); + System.exit(0); + } + + Integer portValue = (Integer)parser.getOptionValue(port, + Integer.valueOf(Tracker.DEFAULT_TRACKER_PORT)); + + String[] otherArgs = parser.getRemainingArgs(); + + if (otherArgs.length > 1) { + usage(System.err); + System.exit(1); + } + + // Get directory from command-line argument or default to current + // directory + String directory = otherArgs.length > 0 + ? otherArgs[0] + : "."; + + FilenameFilter filter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".torrent"); + } + }; + + try { + Tracker t = new Tracker(new InetSocketAddress(portValue.intValue())); + + File parent = new File(directory); + for (File f : parent.listFiles(filter)) { + logger.info("Loading torrent from " + f.getName()); + t.announce(TrackedTorrent.load(f)); + } + + logger.info("Starting tracker with {} announced torrents...", + t.getTrackedTorrents().size()); + t.start(); + } catch (Exception e) { + logger.error("{}", e.getMessage(), e); + System.exit(2); + } + } +} diff --git a/src/main/java/com/turn/ttorrent/client/Client.java b/src/main/java/com/turn/ttorrent/client/Client.java index f16d23c71..00325f078 100644 --- a/src/main/java/com/turn/ttorrent/client/Client.java +++ b/src/main/java/com/turn/ttorrent/client/Client.java @@ -19,26 +19,19 @@ import com.turn.ttorrent.client.announce.AnnounceException; import com.turn.ttorrent.client.announce.AnnounceResponseListener; import com.turn.ttorrent.client.peer.PeerActivityListener; +import com.turn.ttorrent.client.peer.SharingPeer; import com.turn.ttorrent.common.Peer; import com.turn.ttorrent.common.Torrent; import com.turn.ttorrent.common.protocol.PeerMessage; import com.turn.ttorrent.common.protocol.TrackerMessage; -import com.turn.ttorrent.client.peer.SharingPeer; -import java.io.File; import java.io.IOException; -import java.io.PrintStream; -import java.net.Inet4Address; import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; -import java.nio.channels.UnsupportedAddressTypeException; import java.util.BitSet; import java.util.Comparator; -import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Observable; @@ -51,11 +44,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import jargs.gnu.CmdLineParser; - -import org.apache.log4j.BasicConfigurator; -import org.apache.log4j.ConsoleAppender; -import org.apache.log4j.PatternLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,9 +87,6 @@ public class Client extends Observable implements Runnable, private static final int RATE_COMPUTATION_ITERATIONS = 2; private static final int MAX_DOWNLOADERS_UNCHOKE = 4; - /** Default data output directory. */ - private static final String DEFAULT_OUTPUT_DIRECTORY = "/tmp"; - public enum ClientState { WAITING, VALIDATING, @@ -985,12 +970,12 @@ private synchronized void seed() { * * @author mpetazzoni */ - private static class ClientShutdown extends TimerTask { + public static class ClientShutdown extends TimerTask { private final Client client; private final Timer timer; - ClientShutdown(Client client, Timer timer) { + public ClientShutdown(Client client, Timer timer) { this.client = client; this.timer = timer; } @@ -1003,130 +988,4 @@ public void run() { } } }; - - /** - * Display program usage on the given {@link PrintStream}. - */ - private static void usage(PrintStream s) { - s.println("usage: Client [options] "); - s.println(); - s.println("Available options:"); - s.println(" -h,--help Show this help and exit."); - s.println(" -o,--output DIR Read/write data to directory DIR."); - s.println(" -i,--iface IFACE Bind to interface IFACE."); - s.println(" -s,--seed SECONDS Time to seed after downloading (default: infinitely)."); - s.println(" -d,--max-download KB/SEC Max download rate (default: unlimited)."); - s.println(" -u,--max-upload KB/SEC Max upload rate (default: unlimited)."); - s.println(); - } - - /** - * Returns a usable {@link Inet4Address} for the given interface name. - * - *

- * If an interface name is given, return the first usable IPv4 address for - * that interface. If no interface name is given or if that interface - * doesn't have an IPv4 address, return's localhost address (if IPv4). - *

- * - *

- * It is understood this makes the client IPv4 only, but it is important to - * remember that most BitTorrent extensions (like compact peer lists from - * trackers and UDP tracker support) are IPv4-only anyway. - *

- * - * @param iface The network interface name. - * @return A usable IPv4 address as a {@link Inet4Address}. - * @throws UnsupportedAddressTypeException If no IPv4 address was available - * to bind on. - */ - private static Inet4Address getIPv4Address(String iface) - throws SocketException, UnsupportedAddressTypeException, - UnknownHostException { - if (iface != null) { - Enumeration addresses = - NetworkInterface.getByName(iface).getInetAddresses(); - while (addresses.hasMoreElements()) { - InetAddress addr = addresses.nextElement(); - if (addr instanceof Inet4Address) { - return (Inet4Address)addr; - } - } - } - - InetAddress localhost = InetAddress.getLocalHost(); - if (localhost instanceof Inet4Address) { - return (Inet4Address)localhost; - } - - throw new UnsupportedAddressTypeException(); - } - - /** - * Main client entry point for stand-alone operation. - */ - public static void main(String[] args) { - BasicConfigurator.configure(new ConsoleAppender( - new PatternLayout("%d [%-25t] %-5p: %m%n"))); - - CmdLineParser parser = new CmdLineParser(); - CmdLineParser.Option help = parser.addBooleanOption('h', "help"); - CmdLineParser.Option output = parser.addStringOption('o', "output"); - CmdLineParser.Option iface = parser.addStringOption('i', "iface"); - CmdLineParser.Option seedTime = parser.addIntegerOption('s', "seed"); - CmdLineParser.Option maxUpload = parser.addDoubleOption('u', "max-upload"); - CmdLineParser.Option maxDownload = parser.addDoubleOption('d', "max-download"); - - try { - parser.parse(args); - } catch (CmdLineParser.OptionException oe) { - System.err.println(oe.getMessage()); - usage(System.err); - System.exit(1); - } - - // Display help and exit if requested - if (Boolean.TRUE.equals((Boolean)parser.getOptionValue(help))) { - usage(System.out); - System.exit(0); - } - - String outputValue = (String)parser.getOptionValue(output, - DEFAULT_OUTPUT_DIRECTORY); - String ifaceValue = (String)parser.getOptionValue(iface); - int seedTimeValue = (Integer)parser.getOptionValue(seedTime, -1); - - double maxDownloadRate = (Double)parser.getOptionValue(maxDownload, 0.0); - double maxUploadRate = (Double)parser.getOptionValue(maxUpload, 0.0); - - String[] otherArgs = parser.getRemainingArgs(); - if (otherArgs.length != 1) { - usage(System.err); - System.exit(1); - } - - try { - Client c = new Client( - getIPv4Address(ifaceValue), - SharedTorrent.fromFile( - new File(otherArgs[0]), - new File(outputValue))); - - c.setMaxDownloadRate(maxDownloadRate); - c.setMaxUploadRate(maxUploadRate); - - // Set a shutdown hook that will stop the sharing/seeding and send - // a STOPPED announce request. - Runtime.getRuntime().addShutdownHook( - new Thread(new ClientShutdown(c, null))); - - c.share(seedTimeValue); - if (ClientState.ERROR.equals(c.getState())) { - System.exit(1); - } - } catch (Exception e) { - logger.error("Fatal error: {}", e.getMessage(), e); - System.exit(2); - } - } } diff --git a/src/main/java/com/turn/ttorrent/common/Torrent.java b/src/main/java/com/turn/ttorrent/common/Torrent.java index de346067c..5132d4ced 100644 --- a/src/main/java/com/turn/ttorrent/common/Torrent.java +++ b/src/main/java/com/turn/ttorrent/common/Torrent.java @@ -23,10 +23,8 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.net.URI; @@ -51,12 +49,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; -import jargs.gnu.CmdLineParser; - -import org.apache.log4j.BasicConfigurator; -import org.apache.log4j.ConsoleAppender; -import org.apache.log4j.PatternLayout; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -842,131 +834,4 @@ private static int accumulateHashes(StringBuilder hashes, throw new IOException("Error while hashing the torrent data!", ee); } } - - /** - * Display program usage on the given {@link PrintStream}. - */ - private static void usage(PrintStream s) { - usage(s, null); - } - - /** - * Display a message and program usage on the given {@link PrintStream}. - */ - private static void usage(PrintStream s, String msg) { - if (msg != null) { - s.println(msg); - s.println(); - } - - s.println("usage: Torrent [options] [file|directory]"); - s.println(); - s.println("Available options:"); - s.println(" -h,--help Show this help and exit."); - s.println(" -t,--torrent FILE Use FILE to read/write torrent file."); - s.println(); - s.println(" -c,--create Create a new torrent file using " + - "the given announce URL and data."); - s.println(" -a,--announce Tracker URL (can be repeated)."); - s.println(); - } - - /** - * Torrent reader and creator. - * - *

- * You can use the {@code main()} function of this {@link Torrent} class to - * read or create torrent files. See usage for details. - *

- * - * TODO: support multiple announce URLs. - */ - public static void main(String[] args) { - BasicConfigurator.configure(new ConsoleAppender( - new PatternLayout("%-5p: %m%n"))); - - CmdLineParser parser = new CmdLineParser(); - CmdLineParser.Option help = parser.addBooleanOption('h', "help"); - CmdLineParser.Option filename = parser.addStringOption('t', "torrent"); - CmdLineParser.Option create = parser.addBooleanOption('c', "create"); - CmdLineParser.Option announce = parser.addStringOption('a', "announce"); - - try { - parser.parse(args); - } catch (CmdLineParser.OptionException oe) { - System.err.println(oe.getMessage()); - usage(System.err); - System.exit(1); - } - - // Display help and exit if requested - if (Boolean.TRUE.equals((Boolean)parser.getOptionValue(help))) { - usage(System.out); - System.exit(0); - } - - String filenameValue = (String)parser.getOptionValue(filename); - if (filenameValue == null) { - usage(System.err, "Torrent file must be provided!"); - System.exit(1); - } - - Boolean createFlag = (Boolean)parser.getOptionValue(create); - String announceURL = (String)parser.getOptionValue(announce); - - String[] otherArgs = parser.getRemainingArgs(); - - if (Boolean.TRUE.equals(createFlag) && - (otherArgs.length != 1 || announceURL == null)) { - usage(System.err, "Announce URL and a file or directory must be " + - "provided to create a torrent file!"); - System.exit(1); - } - - OutputStream fos = null; - try { - if (Boolean.TRUE.equals(createFlag)) { - if (filenameValue != null) { - fos = new FileOutputStream(filenameValue); - } else { - fos = System.out; - } - - URI announceURI = new URI(announceURL); - File source = new File(otherArgs[0]); - if (!source.exists() || !source.canRead()) { - throw new IllegalArgumentException( - "Cannot access source file or directory " + - source.getName()); - } - - String creator = String.format("%s (ttorrent)", - System.getProperty("user.name")); - - Torrent torrent = null; - if (source.isDirectory()) { - File[] files = source.listFiles(); - Arrays.sort(files); - torrent = Torrent.create(source, Arrays.asList(files), - announceURI, creator); - } else { - torrent = Torrent.create(source, announceURI, creator); - } - - torrent.save(fos); - } else { - Torrent.load(new File(filenameValue), true); - } - } catch (Exception e) { - logger.error("{}", e.getMessage(), e); - System.exit(2); - } finally { - if (fos != null && fos != System.out) { - try { - fos.close(); - } catch (IOException ioe) { - } - } - } - } } diff --git a/src/main/java/com/turn/ttorrent/tracker/Tracker.java b/src/main/java/com/turn/ttorrent/tracker/Tracker.java index 6ba05d8b1..638fae259 100644 --- a/src/main/java/com/turn/ttorrent/tracker/Tracker.java +++ b/src/main/java/com/turn/ttorrent/tracker/Tracker.java @@ -17,30 +17,21 @@ import com.turn.ttorrent.common.Torrent; -import java.io.File; -import java.io.FilenameFilter; import java.io.IOException; -import java.io.PrintStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.URL; +import java.util.Collection; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import jargs.gnu.CmdLineParser; - -import org.apache.log4j.BasicConfigurator; -import org.apache.log4j.ConsoleAppender; -import org.apache.log4j.PatternLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import org.simpleframework.transport.connect.Connection; import org.simpleframework.transport.connect.SocketConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * BitTorrent tracker. @@ -180,6 +171,13 @@ public void stop() { } } + /** + * Returns the list of tracker's torrents + */ + public Collection getTrackedTorrents() { + return torrents.values(); + } + /** * Announce a new torrent on this tracker. * @@ -318,82 +316,4 @@ public void run() { } } } - - /** - * Display program usage on the given {@link PrintStream}. - */ - private static void usage(PrintStream s) { - s.println("usage: Tracker [options] [directory]"); - s.println(); - s.println("Available options:"); - s.println(" -h,--help Show this help and exit."); - s.println(" -p,--port PORT Bind to port PORT."); - s.println(); - } - - /** - * Main function to start a tracker. - */ - public static void main(String[] args) { - BasicConfigurator.configure(new ConsoleAppender( - new PatternLayout("%d [%-25t] %-5p: %m%n"))); - - CmdLineParser parser = new CmdLineParser(); - CmdLineParser.Option help = parser.addBooleanOption('h', "help"); - CmdLineParser.Option port = parser.addIntegerOption('p', "port"); - - try { - parser.parse(args); - } catch (CmdLineParser.OptionException oe) { - System.err.println(oe.getMessage()); - usage(System.err); - System.exit(1); - } - - // Display help and exit if requested - if (Boolean.TRUE.equals((Boolean)parser.getOptionValue(help))) { - usage(System.out); - System.exit(0); - } - - Integer portValue = (Integer)parser.getOptionValue(port, - Integer.valueOf(DEFAULT_TRACKER_PORT)); - - String[] otherArgs = parser.getRemainingArgs(); - - if (otherArgs.length > 1) { - usage(System.err); - System.exit(1); - } - - // Get directory from command-line argument or default to current - // directory - String directory = otherArgs.length > 0 - ? otherArgs[0] - : "."; - - FilenameFilter filter = new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith(".torrent"); - } - }; - - try { - Tracker t = new Tracker(new InetSocketAddress(portValue.intValue())); - - File parent = new File(directory); - for (File f : parent.listFiles(filter)) { - logger.info("Loading torrent from " + f.getName()); - t.announce(TrackedTorrent.load(f)); - } - - logger.info("Starting tracker with {} announced torrents...", - t.torrents.size()); - t.start(); - } catch (Exception e) { - logger.error("{}", e.getMessage(), e); - System.exit(2); - } - } } From dbfd0048aefa075a5830b3a72f317639e0375d8d Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Tue, 27 Aug 2013 17:31:45 -0700 Subject: [PATCH 20/68] Fixes turn/ttorrent#42 --- .../protocol/http/HTTPAnnounceResponseMessage.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceResponseMessage.java b/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceResponseMessage.java index efb75fce3..01d27cd7f 100644 --- a/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceResponseMessage.java +++ b/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceResponseMessage.java @@ -87,6 +87,11 @@ public static HTTPAnnounceResponseMessage parse(ByteBuffer data) Map params = decoded.getMap(); + if (params.get("interval") == null) { + throw new MessageValidationException( + "Tracker message missing mandatory field 'interval'!"); + } + try { List peers; @@ -102,8 +107,8 @@ public static HTTPAnnounceResponseMessage parse(ByteBuffer data) return new HTTPAnnounceResponseMessage(data, params.get("interval").getInt(), - params.get("complete").getInt(), - params.get("incomplete").getInt(), + params.get("complete") != null ? params.get("complete").getInt() : 0, + params.get("incomplete") != null ? params.get("incomplete").getInt() : 0, peers); } catch (InvalidBEncodingException ibee) { throw new MessageValidationException("Invalid response " + From dd8e7c99c25103b74365125a4187b6b5688b17ac Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Wed, 11 Sep 2013 13:05:17 -0700 Subject: [PATCH 21/68] Use commons-io throughout --- pom.xml | 2 +- .../com/turn/ttorrent/cli/TorrentMain.java | 9 +++----- .../ttorrent/client/ConnectionHandler.java | 23 +++++++------------ .../turn/ttorrent/client/SharedTorrent.java | 7 ++---- .../ttorrent/client/peer/PeerExchange.java | 7 ++---- .../com/turn/ttorrent/common/Torrent.java | 16 ++++--------- .../turn/ttorrent/tracker/TrackedTorrent.java | 15 +++--------- .../turn/ttorrent/tracker/TrackerService.java | 9 ++------ 8 files changed, 25 insertions(+), 63 deletions(-) diff --git a/pom.xml b/pom.xml index c0e33c1eb..7d27b5241 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ commons-io commons-io - 2.1 + 2.4 diff --git a/src/main/java/com/turn/ttorrent/cli/TorrentMain.java b/src/main/java/com/turn/ttorrent/cli/TorrentMain.java index bdcb5587c..62b87bbc1 100644 --- a/src/main/java/com/turn/ttorrent/cli/TorrentMain.java +++ b/src/main/java/com/turn/ttorrent/cli/TorrentMain.java @@ -19,13 +19,13 @@ import java.io.File; import java.io.FileOutputStream; -import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.net.URI; import java.util.Arrays; import jargs.gnu.CmdLineParser; +import org.apache.commons.io.IOUtils; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.PatternLayout; @@ -158,11 +158,8 @@ public static void main(String[] args) { logger.error("{}", e.getMessage(), e); System.exit(2); } finally { - if (fos != null && fos != System.out) { - try { - fos.close(); - } catch (IOException ioe) { - } + if (fos != System.out) { + IOUtils.closeQuietly(fos); } } } diff --git a/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java b/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java index 8b56336c9..4653f9466 100644 --- a/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java +++ b/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java @@ -36,6 +36,7 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -309,16 +310,12 @@ private void accept(SocketChannel client) } catch (ParseException pe) { logger.info("Invalid handshake from {}: {}", this.socketRepr(client), pe.getMessage()); - try { client.close(); } catch (IOException e) { } + IOUtils.closeQuietly(client); } catch (IOException ioe) { logger.warn("An error occured while reading an incoming " + "handshake: {}", ioe.getMessage()); - try { - if (client.isConnected()) { - client.close(); - } - } catch (IOException e) { - // Ignore + if (client.isConnected()) { + IOUtils.closeQuietly(client); } } } @@ -456,7 +453,7 @@ public Thread newThread(Runnable r) { t.setName("bt-connect-" + ++this.number); return t; } - }; + } /** @@ -510,15 +507,11 @@ public void run() { channel.configureBlocking(false); this.handler.fireNewPeerConnection(channel, hs.getPeerId()); } catch (Exception e) { - try { - if (channel != null && channel.isConnected()) { - channel.close(); - } - } catch (IOException ioe) { - // Ignore + if (channel != null && channel.isConnected()) { + IOUtils.closeQuietly(channel); } this.handler.fireFailedConnection(this.peer, e); } } - }; + } } diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index d1b02c736..ae9213d49 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -25,7 +25,6 @@ import com.turn.ttorrent.client.storage.FileCollectionStorage; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; @@ -44,6 +43,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; +import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -240,10 +240,7 @@ public SharedTorrent(byte[] torrent, File parent, boolean seeder) */ public static SharedTorrent fromFile(File source, File parent) throws IOException, NoSuchAlgorithmException { - FileInputStream fis = new FileInputStream(source); - byte[] data = new byte[(int)source.length()]; - fis.read(data); - fis.close(); + byte[] data = FileUtils.readFileToByteArray(source); return new SharedTorrent(data, parent); } diff --git a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index e782381e1..b76012e25 100644 --- a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -33,6 +33,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -178,11 +179,7 @@ public void close() { this.stop = true; if (this.channel.isConnected()) { - try { - this.channel.close(); - } catch (IOException ioe) { - // Ignore - } + IOUtils.closeQuietly(this.channel); } logger.debug("Peer exchange with {} closed.", this.peer); diff --git a/src/main/java/com/turn/ttorrent/common/Torrent.java b/src/main/java/com/turn/ttorrent/common/Torrent.java index 5132d4ced..30127a0c8 100644 --- a/src/main/java/com/turn/ttorrent/common/Torrent.java +++ b/src/main/java/com/turn/ttorrent/common/Torrent.java @@ -49,6 +49,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; +import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -96,7 +97,7 @@ public TorrentFile(File file, long size) { this.file = file; this.size = size; } - }; + } protected final byte[] encoded; @@ -495,17 +496,8 @@ public static Torrent load(File torrent) */ public static Torrent load(File torrent, boolean seeder) throws IOException, NoSuchAlgorithmException { - FileInputStream fis = null; - try { - fis = new FileInputStream(torrent); - byte[] data = new byte[(int)torrent.length()]; - fis.read(data); - return new Torrent(data, seeder); - } finally { - if (fis != null) { - fis.close(); - } - } + byte[] data = FileUtils.readFileToByteArray(torrent); + return new Torrent(data, seeder); } /** Torrent creation --------------------------------------------------- */ diff --git a/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java b/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java index 98a2734b5..4589f621e 100644 --- a/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java +++ b/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java @@ -20,7 +20,6 @@ import com.turn.ttorrent.common.protocol.TrackerMessage.AnnounceRequestMessage.RequestEvent; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; @@ -33,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -297,16 +297,7 @@ public List getSomePeers(TrackedPeer peer) { */ public static TrackedTorrent load(File torrent) throws IOException, NoSuchAlgorithmException { - FileInputStream fis = null; - try { - fis = new FileInputStream(torrent); - byte[] data = new byte[(int)torrent.length()]; - fis.read(data); - return new TrackedTorrent(data); - } finally { - if (fis != null) { - fis.close(); - } - } + byte[] data = FileUtils.readFileToByteArray(torrent); + return new TrackedTorrent(data); } } diff --git a/src/main/java/com/turn/ttorrent/tracker/TrackerService.java b/src/main/java/com/turn/ttorrent/tracker/TrackerService.java index 53c2821ad..ae24cabd3 100644 --- a/src/main/java/com/turn/ttorrent/tracker/TrackerService.java +++ b/src/main/java/com/turn/ttorrent/tracker/TrackerService.java @@ -31,6 +31,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentMap; +import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -117,13 +118,7 @@ public void handle(Request request, Response response) { } catch (IOException ioe) { logger.warn("Error while writing response: {}!", ioe.getMessage()); } finally { - if (body != null) { - try { - body.close(); - } catch (IOException ioe) { - // Ignore - } - } + IOUtils.closeQuietly(body); } } From d243274ad77c098b2c5f2028b25f706313e3e479 Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Tue, 27 Aug 2013 20:36:22 -0700 Subject: [PATCH 22/68] Use commons-codec for hex and digest encoding --- pom.xml | 6 +++ .../java/com/turn/ttorrent/client/Piece.java | 15 ++---- .../turn/ttorrent/client/SharedTorrent.java | 15 ++---- .../com/turn/ttorrent/common/Torrent.java | 46 +++++++------------ .../turn/ttorrent/tracker/TrackedTorrent.java | 13 ++---- 5 files changed, 35 insertions(+), 60 deletions(-) diff --git a/pom.xml b/pom.xml index 7d27b5241..5d255ea57 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,12 @@ + + commons-codec + commons-codec + 1.8 + + commons-io commons-io diff --git a/src/main/java/com/turn/ttorrent/client/Piece.java b/src/main/java/com/turn/ttorrent/client/Piece.java index 77d60640b..57070b2b7 100644 --- a/src/main/java/com/turn/ttorrent/client/Piece.java +++ b/src/main/java/com/turn/ttorrent/client/Piece.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.nio.ByteBuffer; -import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.concurrent.Callable; @@ -159,16 +158,10 @@ public synchronized boolean validate() throws IOException { logger.trace("Validating {}...", this); this.valid = false; - try { - // TODO: remove cast to int when large ByteBuffer support is - // implemented in Java. - ByteBuffer buffer = this._read(0, this.length); - byte[] data = new byte[(int)this.length]; - buffer.get(data); - this.valid = Arrays.equals(Torrent.hash(data), this.hash); - } catch (NoSuchAlgorithmException nsae) { - logger.error("{}", nsae); - } + ByteBuffer buffer = this._read(0, this.length); + byte[] data = new byte[(int)this.length]; + buffer.get(data); + this.valid = Arrays.equals(Torrent.hash(data), this.hash); return this.isValid(); } diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index ae9213d49..88144c477 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -29,7 +29,6 @@ import java.io.IOException; import java.nio.ByteBuffer; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; @@ -117,10 +116,9 @@ public class SharedTorrent extends Torrent implements PeerActivityListener { * @throws FileNotFoundException If the torrent file location or * destination directory does not exist and can't be created. * @throws IOException If the torrent file cannot be read or decoded. - * @throws NoSuchAlgorithmException */ public SharedTorrent(Torrent torrent, File destDir) - throws FileNotFoundException, IOException, NoSuchAlgorithmException { + throws FileNotFoundException, IOException { this(torrent, destDir, false); } @@ -140,10 +138,9 @@ public SharedTorrent(Torrent torrent, File destDir) * @throws FileNotFoundException If the torrent file location or * destination directory does not exist and can't be created. * @throws IOException If the torrent file cannot be read or decoded. - * @throws NoSuchAlgorithmException */ public SharedTorrent(Torrent torrent, File destDir, boolean seeder) - throws FileNotFoundException, IOException, NoSuchAlgorithmException { + throws FileNotFoundException, IOException { this(torrent.getEncoded(), destDir, seeder); } @@ -158,7 +155,7 @@ public SharedTorrent(Torrent torrent, File destDir, boolean seeder) * @throws IOException If the torrent file cannot be read or decoded. */ public SharedTorrent(byte[] torrent, File destDir) - throws FileNotFoundException, IOException, NoSuchAlgorithmException { + throws FileNotFoundException, IOException { this(torrent, destDir, false); } @@ -172,10 +169,9 @@ public SharedTorrent(byte[] torrent, File destDir) * @throws FileNotFoundException If the torrent file location or * destination directory does not exist and can't be created. * @throws IOException If the torrent file cannot be read or decoded. - * @throws NoSuchAlgorithmException */ public SharedTorrent(byte[] torrent, File parent, boolean seeder) - throws FileNotFoundException, IOException, NoSuchAlgorithmException { + throws FileNotFoundException, IOException { super(torrent, seeder); if (parent == null || !parent.isDirectory()) { @@ -236,10 +232,9 @@ public SharedTorrent(byte[] torrent, File parent, boolean seeder) * meta-info from. * @param parent The parent directory or location of the torrent files. * @throws IOException When the torrent file cannot be read or decoded. - * @throws NoSuchAlgorithmException */ public static SharedTorrent fromFile(File source, File parent) - throws IOException, NoSuchAlgorithmException { + throws IOException { byte[] data = FileUtils.readFileToByteArray(source); return new SharedTorrent(data, parent); } diff --git a/src/main/java/com/turn/ttorrent/common/Torrent.java b/src/main/java/com/turn/ttorrent/common/Torrent.java index 30127a0c8..5ee4f471c 100644 --- a/src/main/java/com/turn/ttorrent/common/Torrent.java +++ b/src/main/java/com/turn/ttorrent/common/Torrent.java @@ -26,13 +26,11 @@ import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; -import java.math.BigInteger; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -49,6 +47,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -129,11 +129,8 @@ public TorrentFile(File file, long size) { * @param seeder Whether we'll be seeding for this torrent or not. * @throws IOException When the info dictionary can't be read or * encoded and hashed back to create the torrent's SHA-1 hash. - * @throws NoSuchAlgorithmException If the SHA-1 algorithm is not - * available. */ - public Torrent(byte[] torrent, boolean seeder) - throws IOException, NoSuchAlgorithmException { + public Torrent(byte[] torrent, boolean seeder) throws IOException { this.encoded = torrent; this.seeder = seeder; @@ -404,10 +401,8 @@ public void save(OutputStream output) throws IOException { output.write(this.getEncoded()); } - public static byte[] hash(byte[] data) throws NoSuchAlgorithmException { - MessageDigest md = MessageDigest.getInstance("SHA-1"); - md.update(data); - return md.digest(); + public static byte[] hash(byte[] data) { + return DigestUtils.sha1(data); } /** @@ -417,8 +412,7 @@ public static byte[] hash(byte[] data) throws NoSuchAlgorithmException { * @param bytes The byte array to convert. */ public static String byteArrayToHexString(byte[] bytes) { - BigInteger bi = new BigInteger(1, bytes); - return String.format("%0" + (bytes.length << 1) + "X", bi); + return new String(Hex.encodeHex(bytes, false)); } /** @@ -477,10 +471,8 @@ protected static int getHashingThreadsCount() { * @param torrent The abstract {@link File} object representing the * .torrent file to load. * @throws IOException When the torrent file cannot be read. - * @throws NoSuchAlgorithmException */ - public static Torrent load(File torrent) - throws IOException, NoSuchAlgorithmException { + public static Torrent load(File torrent) throws IOException { return Torrent.load(torrent, false); } @@ -492,10 +484,9 @@ public static Torrent load(File torrent) * @param seeder Whether we are a seeder for this torrent or not (disables * local data validation). * @throws IOException When the torrent file cannot be read. - * @throws NoSuchAlgorithmException */ public static Torrent load(File torrent, boolean seeder) - throws IOException, NoSuchAlgorithmException { + throws IOException { byte[] data = FileUtils.readFileToByteArray(torrent); return new Torrent(data, seeder); } @@ -517,7 +508,7 @@ public static Torrent load(File torrent, boolean seeder) * torrent's creator. */ public static Torrent create(File source, URI announce, String createdBy) - throws NoSuchAlgorithmException, InterruptedException, IOException { + throws InterruptedException, IOException { return Torrent.create(source, null, announce, null, createdBy); } @@ -539,8 +530,7 @@ public static Torrent create(File source, URI announce, String createdBy) * torrent's creator. */ public static Torrent create(File parent, List files, URI announce, - String createdBy) throws NoSuchAlgorithmException, - InterruptedException, IOException { + String createdBy) throws InterruptedException, IOException { return Torrent.create(parent, files, announce, null, createdBy); } @@ -560,8 +550,7 @@ public static Torrent create(File parent, List files, URI announce, * torrent's creator. */ public static Torrent create(File source, List> announceList, - String createdBy) throws NoSuchAlgorithmException, - InterruptedException, IOException { + String createdBy) throws InterruptedException, IOException { return Torrent.create(source, null, null, announceList, createdBy); } @@ -585,7 +574,7 @@ public static Torrent create(File source, List> announceList, */ public static Torrent create(File source, List files, List> announceList, String createdBy) - throws NoSuchAlgorithmException, InterruptedException, IOException { + throws InterruptedException, IOException { return Torrent.create(source, files, null, announceList, createdBy); } @@ -610,7 +599,7 @@ public static Torrent create(File source, List files, */ private static Torrent create(File parent, List files, URI announce, List> announceList, String createdBy) - throws NoSuchAlgorithmException, InterruptedException, IOException { + throws InterruptedException, IOException { if (files == null || files.isEmpty()) { logger.info("Creating single-file torrent for {}...", parent.getName()); @@ -687,9 +676,8 @@ private static class CallableChunkHasher implements Callable { private final MessageDigest md; private final ByteBuffer data; - CallableChunkHasher(ByteBuffer buffer) - throws NoSuchAlgorithmException { - this.md = MessageDigest.getInstance("SHA-1"); + CallableChunkHasher(ByteBuffer buffer) { + this.md = DigestUtils.getSha1Digest(); this.data = ByteBuffer.allocate(buffer.remaining()); buffer.mark(); @@ -722,12 +710,12 @@ public String call() throws UnsupportedEncodingException { * @param file The file to hash. */ private static String hashFile(File file) - throws NoSuchAlgorithmException, InterruptedException, IOException { + throws InterruptedException, IOException { return Torrent.hashFiles(Arrays.asList(new File[] { file })); } private static String hashFiles(List files) - throws NoSuchAlgorithmException, InterruptedException, IOException { + throws InterruptedException, IOException { int threads = getHashingThreadsCount(); ExecutorService executor = Executors.newFixedThreadPool(threads); ByteBuffer buffer = ByteBuffer.allocate(Torrent.PIECE_LENGTH); diff --git a/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java b/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java index 4589f621e..3e8e500d6 100644 --- a/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java +++ b/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java @@ -24,7 +24,6 @@ import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; -import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -77,11 +76,8 @@ public class TrackedTorrent extends Torrent { * @param torrent The meta-info byte data. * @throws IOException When the info dictionary can't be * encoded and hashed back to create the torrent's SHA-1 hash. - * @throws NoSuchAlgorithmException If the SHA-1 algorithm is not - * available. */ - public TrackedTorrent(byte[] torrent) - throws IOException, NoSuchAlgorithmException { + public TrackedTorrent(byte[] torrent) throws IOException { super(torrent, false); this.peers = new ConcurrentHashMap(); @@ -89,8 +85,7 @@ public TrackedTorrent(byte[] torrent) this.announceInterval = TrackedTorrent.DEFAULT_ANNOUNCE_INTERVAL_SECONDS; } - public TrackedTorrent(Torrent torrent) - throws IOException, NoSuchAlgorithmException { + public TrackedTorrent(Torrent torrent) throws IOException { this(torrent.getEncoded()); } @@ -293,10 +288,8 @@ public List getSomePeers(TrackedPeer peer) { * @param torrent The abstract {@link File} object representing the * .torrent file to load. * @throws IOException When the torrent file cannot be read. - * @throws NoSuchAlgorithmException */ - public static TrackedTorrent load(File torrent) throws IOException, - NoSuchAlgorithmException { + public static TrackedTorrent load(File torrent) throws IOException { byte[] data = FileUtils.readFileToByteArray(torrent); return new TrackedTorrent(data); } From 869f664874057857cb0c19b2494326d5639fc501 Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Thu, 19 Sep 2013 22:33:40 -0700 Subject: [PATCH 23/68] Fix Piece compareTo ordering --- src/main/java/com/turn/ttorrent/client/Piece.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/Piece.java b/src/main/java/com/turn/ttorrent/client/Piece.java index 57070b2b7..bb023270e 100644 --- a/src/main/java/com/turn/ttorrent/client/Piece.java +++ b/src/main/java/com/turn/ttorrent/client/Piece.java @@ -276,15 +276,11 @@ public String toString() { * @param other The piece to compare with, should not be null. */ public int compareTo(Piece other) { - if (this == other) { - return 0; - } - - if (this.seen < other.seen) { - return -1; - } else { - return 1; + int retval = Integer.compare(this.seen, other.seen); + if (retval == 0) { + retval = Integer.compare(this.index, other.index); } + return retval; } /** From 32c66e19db5f33c8037081da9489179490b03b07 Mon Sep 17 00:00:00 2001 From: Patrick Woodworth Date: Thu, 26 Sep 2013 12:17:07 -0700 Subject: [PATCH 24/68] Correcting improper use of Java 7 API --- src/main/java/com/turn/ttorrent/client/Piece.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/Piece.java b/src/main/java/com/turn/ttorrent/client/Piece.java index bb023270e..5cd8534ac 100644 --- a/src/main/java/com/turn/ttorrent/client/Piece.java +++ b/src/main/java/com/turn/ttorrent/client/Piece.java @@ -276,11 +276,11 @@ public String toString() { * @param other The piece to compare with, should not be null. */ public int compareTo(Piece other) { - int retval = Integer.compare(this.seen, other.seen); - if (retval == 0) { - retval = Integer.compare(this.index, other.index); + if (this.seen != other.seen) { + return this.seen < other.seen ? -1 : 1; } - return retval; + return this.index == other.index ? 0 : + (this.index < other.index ? -1 : 1); } /** From cce0c0254159f3275b4e5d2ef8f56938f9b95846 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Thu, 24 Oct 2013 08:37:05 -0700 Subject: [PATCH 25/68] Developer contact info updates Signed-off-by: Maxime Petazzoni --- README.md | 2 +- pom.xml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 121d98360..fd1028b48 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ License version 2.0. See COPYING file for more details. Authors and contributors ------------------------ -* Maxime Petazzoni <> (Platform Engineer at Turn, Inc) +* Maxime Petazzoni <> (Software Engineer at SignalFuse, Inc) Original author, main developer and maintainer * David Giffin <> Contributed parallel hashing and multi-file torrent support. diff --git a/pom.xml b/pom.xml index 5d255ea57..389d5b930 100644 --- a/pom.xml +++ b/pom.xml @@ -49,10 +49,10 @@ mpetazzoni Maxime Petazzoni - mpetazzoni@turn.com + maxime.petazzoni@bulix.org http://www.bulix.org - Turn, Inc - http://www.turn.com + SignalFuse, Inc + http://www.signalfuse.com maintainer architect From b540ebda609ca5bb4f0e806b888eaad851bfa079 Mon Sep 17 00:00:00 2001 From: a86c6f7964 Date: Sun, 24 Nov 2013 19:47:45 -0600 Subject: [PATCH 26/68] Change FileCollectionStorage to not write 0 bytes to the next file at the end --- .../client/storage/FileCollectionStorage.java | 2 +- .../storage/FileCollectionStorageTest.java | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 test/main/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java diff --git a/src/main/java/com/turn/ttorrent/client/storage/FileCollectionStorage.java b/src/main/java/com/turn/ttorrent/client/storage/FileCollectionStorage.java index c753132a6..6bb50d3b6 100644 --- a/src/main/java/com/turn/ttorrent/client/storage/FileCollectionStorage.java +++ b/src/main/java/com/turn/ttorrent/client/storage/FileCollectionStorage.java @@ -182,7 +182,7 @@ private List select(long offset, long length) { long bytes = 0; for (FileStorage file : this.files) { - if (file.offset() > offset + length) { + if (file.offset() >= offset + length) { break; } diff --git a/test/main/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java b/test/main/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java new file mode 100644 index 000000000..005013f2c --- /dev/null +++ b/test/main/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java @@ -0,0 +1,61 @@ +package com.turn.ttorrent.client.storage; + +import org.testng.annotations.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * User: loyd + * Date: 11/24/13 + */ +public class FileCollectionStorageTest { + @Test + public void testSelect() throws Exception { + final File file1 = File.createTempFile("testng", "fcst"); + file1.deleteOnExit(); + final File file2 = File.createTempFile("testng", "fcst"); + file2.deleteOnExit(); + + final List files = new ArrayList(); + files.add(new FileStorage(file1, 0, 2)); + files.add(new FileStorage(file2, 2, 2)); + final FileCollectionStorage storage = new FileCollectionStorage(files, 4); + // since all of these files already exist, we are considered finished + assertTrue(storage.isFinished()); + + // write to first file works + write(new byte[]{1, 2}, 0, storage); + check(new byte[]{1, 2}, file1); + + // write to second file works + write(new byte[]{5, 6}, 2, storage); + check(new byte[]{5, 6}, file2); + + // write to two files works + write(new byte[]{8,9,10,11}, 0, storage); + check(new byte[]{8,9}, file1); + check(new byte[]{10,11}, file2); + + // make sure partial write into next file works + write(new byte[]{100,101,102}, 0, storage); + check(new byte[]{102,11}, file2); + } + + private void write(byte[] bytes, int offset, FileCollectionStorage storage) throws IOException { + storage.write(ByteBuffer.wrap(bytes), offset); + storage.finish(); + } + private void check(byte[] bytes, File f) throws IOException { + final byte[] temp = new byte[bytes.length]; + assertEquals(new FileInputStream(f).read(temp), temp.length); + assertEquals(temp, bytes); + } +} From 053b13d9d489aa1e3f4d1e2a697f1673b86defcd Mon Sep 17 00:00:00 2001 From: Max Afonov Date: Sun, 27 Oct 2013 12:09:56 -0400 Subject: [PATCH 27/68] Instead of abusing SocketChannel.read in a tight loop, use a Selector to tell us when a peer incoming channel is ready to read. This fixes excessive CPU usage when running a peer over a slow link. In that case, an IncomingThread would spend >90% of its time repeatedly calling SocketChannel.read. Observed CPU use was in excess of 350% on a quad-core MacBook Air. --- .../ttorrent/client/peer/PeerExchange.java | 71 +++++++++++-------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index b76012e25..e43fd894b 100644 --- a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -25,10 +25,13 @@ import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; +import java.nio.channels.Selector; +import java.nio.channels.SelectionKey; import java.text.ParseException; import java.util.BitSet; import java.util.HashSet; import java.util.Set; +import java.util.Iterator; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -259,42 +262,53 @@ protected void rateLimit(double maxRate, long messageSize, PeerMessage message) * @author mpetazzoni */ private class IncomingThread extends RateLimitThread { + private long read(Selector selector, ByteBuffer buffer) throws IOException { + long size = 0; + if (selector.select() == 0 || !buffer.hasRemaining()) return size; + Iterator it = selector.selectedKeys().iterator(); + while (it.hasNext()) { + SelectionKey key = (SelectionKey) it.next(); + if (key.isReadable()) { + int read = ((SocketChannel) key.channel()).read(buffer); + size += read; + if (read < 0) throw new IOException("Unexpected end-of-stream while reading"); + } + it.remove(); + } + return size; + } + + private void handleIOE(IOException ioe) { + logger.debug("Could not read message from {}: {}", + peer, + ioe.getMessage() != null + ? ioe.getMessage() + : ioe.getClass().getName()); + peer.unbind(true); + } @Override public void run() { ByteBuffer buffer = ByteBuffer.allocateDirect(1*1024*1024); + Selector selector = null; try { + selector = Selector.open(); + channel.register(selector, SelectionKey.OP_READ); + while (!stop) { buffer.rewind(); buffer.limit(PeerMessage.MESSAGE_LENGTH_FIELD_SIZE); // Keep reading bytes until the length field has been read // entirely. - while (!stop && buffer.hasRemaining()) { - if (channel.read(buffer) < 0) { - throw new EOFException( - "Reached end-of-stream while reading size header"); - } - try { - Thread.sleep(1); - } catch (InterruptedException ie) { - // Ignore and move along. - } - } + while (!stop && buffer.hasRemaining()) read(selector, buffer); int pstrlen = buffer.getInt(0); buffer.limit(PeerMessage.MESSAGE_LENGTH_FIELD_SIZE + pstrlen); long size = 0; - while (!stop && buffer.hasRemaining()) { - int read = channel.read(buffer); - size += read; - if (read < 0) { - throw new EOFException( - "Reached end-of-stream while reading message"); - } - } + while (!stop && buffer.hasRemaining()) size += read(selector, buffer); buffer.rewind(); @@ -303,23 +317,22 @@ public void run() { logger.trace("Received {} from {}", message, peer); // Wait if needed to reach configured download rate. - this.rateLimit(PeerExchange.this.torrent.getMaxDownloadRate(), - size, message); + this.rateLimit(PeerExchange.this.torrent.getMaxDownloadRate(), size, message); - for (MessageListener listener : listeners) { + for (MessageListener listener : listeners) listener.handleMessage(message); - } } catch (ParseException pe) { logger.warn("{}", pe.getMessage()); } } } catch (IOException ioe) { - logger.debug("Could not read message from {}: {}", - peer, - ioe.getMessage() != null - ? ioe.getMessage() - : ioe.getClass().getName()); - peer.unbind(true); + handleIOE(ioe); + } finally { + try { + if (selector != null) selector.close(); + } catch (IOException ioe) { + handleIOE(ioe); + } } } } From 6ff991d843c9623da25a6a8293e39df9155b6b22 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Tue, 26 Nov 2013 11:54:42 -0800 Subject: [PATCH 28/68] Fixing email address of contributor in README Signed-off-by: Maxime Petazzoni --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd1028b48..dd80aee62 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ Authors and contributors * Thomas Zink <> Fixed a piece length computation issue when the total torrent size is an exact multiple of the piece size. -* Johan Parent <> +* Johan Parent <> Fixed a bug in unfresh peer collection and issues on download completion on Windows platforms. * Dmitriy Dumanskiy From 37bff2acb8f5eb793cb01905bd8f0c6d9090c073 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Tue, 26 Nov 2013 11:54:59 -0800 Subject: [PATCH 29/68] Code style fixes Signed-off-by: Maxime Petazzoni --- .../ttorrent/client/peer/PeerExchange.java | 49 ++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index e43fd894b..359bd2fed 100644 --- a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -262,28 +262,44 @@ protected void rateLimit(double maxRate, long messageSize, PeerMessage message) * @author mpetazzoni */ private class IncomingThread extends RateLimitThread { + + /** + * Read data from the incoming channel of the socket using a {@link + * Selector}. + * + * @param selector The socket selector into which the peer socket has + * been inserted. + * @param buffer A {@link ByteBuffer} to put the read data into. + * @return The number of bytes read. + */ private long read(Selector selector, ByteBuffer buffer) throws IOException { + if (selector.select() == 0 || !buffer.hasRemaining()) { + return 0; + } + long size = 0; - if (selector.select() == 0 || !buffer.hasRemaining()) return size; Iterator it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey key = (SelectionKey) it.next(); if (key.isReadable()) { int read = ((SocketChannel) key.channel()).read(buffer); + if (read < 0) { + throw new IOException("Unexpected end-of-stream while reading"); + } size += read; - if (read < 0) throw new IOException("Unexpected end-of-stream while reading"); } it.remove(); } + return size; } private void handleIOE(IOException ioe) { logger.debug("Could not read message from {}: {}", - peer, - ioe.getMessage() != null - ? ioe.getMessage() - : ioe.getClass().getName()); + peer, + ioe.getMessage() != null + ? ioe.getMessage() + : ioe.getClass().getName()); peer.unbind(true); } @@ -302,13 +318,18 @@ public void run() { // Keep reading bytes until the length field has been read // entirely. - while (!stop && buffer.hasRemaining()) read(selector, buffer); + while (!stop && buffer.hasRemaining()) { + this.read(selector, buffer); + } + // Reset the buffer limit to the expected message size. int pstrlen = buffer.getInt(0); buffer.limit(PeerMessage.MESSAGE_LENGTH_FIELD_SIZE + pstrlen); long size = 0; - while (!stop && buffer.hasRemaining()) size += read(selector, buffer); + while (!stop && buffer.hasRemaining()) { + size += this.read(selector, buffer); + } buffer.rewind(); @@ -317,7 +338,9 @@ public void run() { logger.trace("Received {} from {}", message, peer); // Wait if needed to reach configured download rate. - this.rateLimit(PeerExchange.this.torrent.getMaxDownloadRate(), size, message); + this.rateLimit( + PeerExchange.this.torrent.getMaxDownloadRate(), + size, message); for (MessageListener listener : listeners) listener.handleMessage(message); @@ -326,12 +349,14 @@ public void run() { } } } catch (IOException ioe) { - handleIOE(ioe); + this.handleIOE(ioe); } finally { try { - if (selector != null) selector.close(); + if (selector != null) { + selector.close(); + } } catch (IOException ioe) { - handleIOE(ioe); + this.handleIOE(ioe); } } } From d403b7a9e1c8016250387866335110e05f0b1cf2 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Fri, 6 Dec 2013 10:50:33 -0800 Subject: [PATCH 30/68] Create Travis-CI configuration Signed-off-by: Maxime Petazzoni --- .travis.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..dff5f3a5d --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +language: java From 9dac22a82b1f494d34502b40d7bef2896c3ba566 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Fri, 6 Dec 2013 10:54:57 -0800 Subject: [PATCH 31/68] Add Travis-CI status image Signed-off-by: Maxime Petazzoni --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dd80aee62..89074c148 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ Ttorrent, a Java implementation of the BitTorrent protocol ========================================================== +[![Build Status](https://travis-ci.org/mpetazzoni/ttorrent.png)](https://travis-ci.org/mpetazzoni/ttorrent) + Description ----------- From 5c9f3cf6c78fbf3cc6a0cdfd7afda5ca8d61b787 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Fri, 6 Dec 2013 11:40:49 -0800 Subject: [PATCH 32/68] Bump to version 1.4; first release to Maven Central Signed-off-by: Maxime Petazzoni --- .gitignore | 1 + pom.xml | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 9fc5403e2..72fd2a12d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # Ignore build output /build/* +/release.properties # Ignore Javadoc output /doc/* diff --git a/pom.xml b/pom.xml index 389d5b930..c2ad7a450 100644 --- a/pom.xml +++ b/pom.xml @@ -15,22 +15,17 @@ including support for several BEPs. It also provides a standalone client, a tracker and a torrent manipulation utility. - http://turn.github.com/ttorrent/ + http://mpetazzoni.github.io/ttorrent/ com.turn ttorrent - 1.3-SNAPSHOT + 1.4-SNAPSHOT jar - - Turn, Inc. - http://www.turn.com - - - scm:git:git://github.com/turn/ttorrent.git - scm:git:ssh://git@github.com/turn/ttorrent.git - http://github.com/turn/ttorrent - HEAD + scm:git:git://github.com/mpetazzoni/ttorrent.git + scm:git:ssh://git@github.com/mpetazzoni/ttorrent.git + http://github.com/mpetazzoni/ttorrent + master @@ -42,7 +37,7 @@ GitHub - https://github.com/turn/ttorrent/issues + https://github.com/mpetazzoni/ttorrent/issues @@ -157,6 +152,12 @@
+ + org.apache.maven.plugins + maven-release-plugin + 2.4.2 + + maven-shade-plugin 2.1 From 51f0676c1965dbae7109e7033bbbbd0d54047022 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Fri, 6 Dec 2013 11:58:56 -0800 Subject: [PATCH 33/68] [maven-release-plugin] prepare release ttorrent-1.4 --- pom.xml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index c2ad7a450..c8730f944 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,11 @@ - + 4.0.0 org.sonatype.oss oss-parent 7 - + Java BitTorrent library @@ -18,14 +17,14 @@ http://mpetazzoni.github.io/ttorrent/ com.turn ttorrent - 1.4-SNAPSHOT + 1.4 jar scm:git:git://github.com/mpetazzoni/ttorrent.git scm:git:ssh://git@github.com/mpetazzoni/ttorrent.git http://github.com/mpetazzoni/ttorrent - master + ttorrent-1.4 From efe3a26d3f704ce77b3df4931459b55da761cc80 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Fri, 6 Dec 2013 11:59:03 -0800 Subject: [PATCH 34/68] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c8730f944..7e6c7d4d8 100644 --- a/pom.xml +++ b/pom.xml @@ -17,14 +17,14 @@ http://mpetazzoni.github.io/ttorrent/ com.turn ttorrent - 1.4 + 1.5-SNAPSHOT jar scm:git:git://github.com/mpetazzoni/ttorrent.git scm:git:ssh://git@github.com/mpetazzoni/ttorrent.git http://github.com/mpetazzoni/ttorrent - ttorrent-1.4 + master From 9550b79aafd3f1843b25c0598726f1c238ab500c Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Sat, 7 Dec 2013 17:03:19 -0800 Subject: [PATCH 35/68] Add documentation about declaring ttorrent as a POM dependency With ttorrent now being available on Maven Central, this closes #12. Signed-off-by: Maxime Petazzoni --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 89074c148..262e9a52d 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,22 @@ usage message on the console when invoked with the ``-h`` command-line flag. ### As a library +To use ``ttorrent`` is a library in your project, all you need is to +declare the dependency on the latest version of ``ttorrent``. For +example, if you use Maven, add the following in your POM's dependencies +section: + +```xml + + ... + + com.turn + ttorrent + 1.4 + + +``` + *Thanks to Anatoli Vladev for the code examples in #16.* #### Client code From fd9d585f1c9c0a0b0a60207736bfc9108842bb68 Mon Sep 17 00:00:00 2001 From: Ralph Schaer Date: Sat, 12 Apr 2014 12:34:26 +0200 Subject: [PATCH 36/68] Update README.md Fix dependency section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 262e9a52d..8319e1894 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ section: ... com.turn - ttorrent + ttorrent 1.4 From 755483bb66ea74246f1978e5100434a88cf17a71 Mon Sep 17 00:00:00 2001 From: Chet Thakar Date: Mon, 21 Jul 2014 02:46:01 -0700 Subject: [PATCH 37/68] Added support for multiple announce URLs to CLI Completed a TODO in the CLI for Torrent creation: added support for multiple announce URLs. Now a number of announcement URLs can be listed with -a and will be added via Torrent.create (multiple announcement list constructor existed previously). --- .../com/turn/ttorrent/cli/TorrentMain.java | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/cli/TorrentMain.java b/src/main/java/com/turn/ttorrent/cli/TorrentMain.java index 62b87bbc1..b7094fe84 100644 --- a/src/main/java/com/turn/ttorrent/cli/TorrentMain.java +++ b/src/main/java/com/turn/ttorrent/cli/TorrentMain.java @@ -22,7 +22,10 @@ import java.io.OutputStream; import java.io.PrintStream; import java.net.URI; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.Vector; import jargs.gnu.CmdLineParser; import org.apache.commons.io.IOUtils; @@ -76,7 +79,6 @@ private static void usage(PrintStream s, String msg) { * torrent files. See usage for details. *

* - * TODO: support multiple announce URLs. */ public static void main(String[] args) { BasicConfigurator.configure(new ConsoleAppender( @@ -109,17 +111,22 @@ public static void main(String[] args) { } Boolean createFlag = (Boolean)parser.getOptionValue(create); - String announceURL = (String)parser.getOptionValue(announce); + + //For repeated announce urls + @SuppressWarnings("unchecked") + Vector announceURLs = (Vector)parser.getOptionValues(announce); + String[] otherArgs = parser.getRemainingArgs(); if (Boolean.TRUE.equals(createFlag) && - (otherArgs.length != 1 || announceURL == null)) { + (otherArgs.length != 1 || announceURLs.isEmpty())) { usage(System.err, "Announce URL and a file or directory must be " + "provided to create a torrent file!"); System.exit(1); } - + + OutputStream fos = null; try { if (Boolean.TRUE.equals(createFlag)) { @@ -129,7 +136,17 @@ public static void main(String[] args) { fos = System.out; } - URI announceURI = new URI(announceURL); + //Process the announce URLs into URIs + List announceURIs = new ArrayList(); + for (String url : announceURLs) { + announceURIs.add(new URI(url)); + } + + //Create the announce-list as a list of lists of URIs + //Assume all the URI's are first tier trackers + List> announceList = new ArrayList>(); + announceList.add(announceURIs); + File source = new File(otherArgs[0]); if (!source.exists() || !source.canRead()) { throw new IllegalArgumentException( @@ -145,9 +162,9 @@ public static void main(String[] args) { File[] files = source.listFiles(); Arrays.sort(files); torrent = Torrent.create(source, Arrays.asList(files), - announceURI, creator); + announceList, creator); } else { - torrent = Torrent.create(source, announceURI, creator); + torrent = Torrent.create(source, announceList, creator); } torrent.save(fos); From 856284ed89896932f3d570ff5115c7b0078a914e Mon Sep 17 00:00:00 2001 From: Chris Malloy Date: Tue, 4 Nov 2014 19:46:59 -0400 Subject: [PATCH 38/68] Added request strategy interface to abstract different strategy implementations. Moved existing implementation into RequestStrategyImplRarest. Added an addition sequential implementation. --- .../turn/ttorrent/client/SharedTorrent.java | 80 ++++++++++++------- .../client/strategy/RequestStrategy.java | 29 +++++++ .../strategy/RequestStrategyImplRarest.java | 52 ++++++++++++ .../RequestStrategyImplSequential.java | 24 ++++++ 4 files changed, 156 insertions(+), 29 deletions(-) create mode 100644 src/main/java/com/turn/ttorrent/client/strategy/RequestStrategy.java create mode 100644 src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplRarest.java create mode 100644 src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplSequential.java diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index 88144c477..554311b45 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -18,23 +18,21 @@ import com.turn.ttorrent.bcodec.InvalidBEncodingException; import com.turn.ttorrent.common.Torrent; import com.turn.ttorrent.client.peer.PeerActivityListener; -import com.turn.ttorrent.client.peer.Rate; import com.turn.ttorrent.client.peer.SharingPeer; import com.turn.ttorrent.client.storage.TorrentByteStorage; import com.turn.ttorrent.client.storage.FileStorage; import com.turn.ttorrent.client.storage.FileCollectionStorage; +import com.turn.ttorrent.client.strategy.RequestStrategy; +import com.turn.ttorrent.client.strategy.RequestStrategyImplRarest; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; - -import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import java.util.Random; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.Callable; @@ -67,10 +65,6 @@ public class SharedTorrent extends Torrent implements PeerActivityListener { private static final Logger logger = LoggerFactory.getLogger(SharedTorrent.class); - /** Randomly select the next piece to download from a peer from the - * RAREST_PIECE_JITTER available from it. */ - private static final int RAREST_PIECE_JITTER = 42; - /** End-game trigger ratio. * *

@@ -82,7 +76,6 @@ public class SharedTorrent extends Torrent implements PeerActivityListener { */ private static final float ENG_GAME_COMPLETION_RATIO = 0.95f; - private Random random; private boolean stop; private long uploaded; @@ -99,6 +92,7 @@ public class SharedTorrent extends Torrent implements PeerActivityListener { private SortedSet rarest; private BitSet completedPieces; private BitSet requestedPieces; + private RequestStrategy requestStrategy; private double maxUploadRate = 0.0; private double maxDownloadRate = 0.0; @@ -141,7 +135,31 @@ public SharedTorrent(Torrent torrent, File destDir) */ public SharedTorrent(Torrent torrent, File destDir, boolean seeder) throws FileNotFoundException, IOException { - this(torrent.getEncoded(), destDir, seeder); + this(torrent.getEncoded(), destDir, seeder, null); + } + + /** + * Create a new shared torrent from a base Torrent object. + * + *

+ * This will recreate a SharedTorrent object from the provided Torrent + * object's encoded meta-info data. + *

+ * + * @param torrent The Torrent object. + * @param destDir The destination directory or location of the torrent + * files. + * @param seeder Whether we're a seeder for this torrent or not (disables + * validation). + * @param requestStrategy The request strategy implementation. + * @throws FileNotFoundException If the torrent file location or + * destination directory does not exist and can't be created. + * @throws IOException If the torrent file cannot be read or decoded. + */ + public SharedTorrent(Torrent torrent, File destDir, boolean seeder, + RequestStrategy requestStrategy) + throws FileNotFoundException, IOException { + this(torrent.getEncoded(), destDir, seeder, requestStrategy); } /** @@ -172,6 +190,24 @@ public SharedTorrent(byte[] torrent, File destDir) */ public SharedTorrent(byte[] torrent, File parent, boolean seeder) throws FileNotFoundException, IOException { + this(torrent, parent, seeder, null); + } + + /** + * Create a new shared torrent from meta-info binary data. + * + * @param torrent The meta-info byte data. + * @param parent The parent directory or location the torrent files. + * @param seeder Whether we're a seeder for this torrent or not (disables + * validation). + * @param requestStrategy The request strategy implementation. + * @throws FileNotFoundException If the torrent file location or + * destination directory does not exist and can't be created. + * @throws IOException If the torrent file cannot be read or decoded. + */ + public SharedTorrent(byte[] torrent, File parent, boolean seeder, + RequestStrategy requestStrategy) + throws FileNotFoundException, IOException { super(torrent, seeder); if (parent == null || !parent.isDirectory()) { @@ -211,7 +247,6 @@ public SharedTorrent(byte[] torrent, File parent, boolean seeder) } this.bucket = new FileCollectionStorage(files, this.getSize()); - this.random = new Random(System.currentTimeMillis()); this.stop = false; this.uploaded = 0; @@ -223,6 +258,10 @@ public SharedTorrent(byte[] torrent, File parent, boolean seeder) this.rarest = Collections.synchronizedSortedSet(new TreeSet()); this.completedPieces = new BitSet(); this.requestedPieces = new BitSet(); + + //TODO: should switch to guice + if (requestStrategy == null) requestStrategy = new RequestStrategyImplRarest(); + this.requestStrategy = requestStrategy; } /** @@ -645,24 +684,7 @@ public synchronized void handlePeerReady(SharingPeer peer) { "that was already requested from another peer."); } - // Extract the RAREST_PIECE_JITTER rarest pieces from the interesting - // pieces of this peer. - ArrayList choice = new ArrayList(RAREST_PIECE_JITTER); - synchronized (this.rarest) { - for (Piece piece : this.rarest) { - if (interesting.get(piece.getIndex())) { - choice.add(piece); - if (choice.size() >= RAREST_PIECE_JITTER) { - break; - } - } - } - } - - Piece chosen = choice.get( - this.random.nextInt( - Math.min(choice.size(), - RAREST_PIECE_JITTER))); + Piece chosen = requestStrategy.choosePiece(rarest, interesting, pieces); this.requestedPieces.set(chosen.getIndex()); logger.trace("Requesting {} from {}, we now have {} " + diff --git a/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategy.java b/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategy.java new file mode 100644 index 000000000..f1cb4bf50 --- /dev/null +++ b/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategy.java @@ -0,0 +1,29 @@ +package com.turn.ttorrent.client.strategy; + +import java.util.BitSet; +import java.util.SortedSet; + +import com.turn.ttorrent.client.Piece; + +/** + * Interface for a piece request strategy provider. + * + * @author cjmalloy + * + */ +public interface RequestStrategy { + + /** + * Choose a piece from the remaining pieces. + * + * @param rarest + * A set sorted by how rare the piece is + * @param interesting + * A set of the index of all interesting pieces + * @param pieces + * The complete array of pieces + * + * @return The chosen piece, or null if no piece is interesting + */ + Piece choosePiece(SortedSet rarest, BitSet interesting, Piece[] pieces); +} diff --git a/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplRarest.java b/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplRarest.java new file mode 100644 index 000000000..1bdc85920 --- /dev/null +++ b/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplRarest.java @@ -0,0 +1,52 @@ +package com.turn.ttorrent.client.strategy; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Random; +import java.util.SortedSet; + +import com.turn.ttorrent.client.Piece; + +/** + * The default request strategy implementation- rarest first. + * + * @author cjmalloy + * + */ +public class RequestStrategyImplRarest implements RequestStrategy { + + /** Randomly select the next piece to download from a peer from the + * RAREST_PIECE_JITTER available from it. */ + private static final int RAREST_PIECE_JITTER = 42; + + private Random random; + + public RequestStrategyImplRarest() { + this.random = new Random(System.currentTimeMillis()); + } + + @Override + public Piece choosePiece(SortedSet rarest, BitSet interesting, Piece[] pieces) { + // Extract the RAREST_PIECE_JITTER rarest pieces from the interesting + // pieces of this peer. + ArrayList choice = new ArrayList(RAREST_PIECE_JITTER); + synchronized (rarest) { + for (Piece piece : rarest) { + if (interesting.get(piece.getIndex())) { + choice.add(piece); + if (choice.size() >= RAREST_PIECE_JITTER) { + break; + } + } + } + } + + if (choice.size() == 0) return null; + + Piece chosen = choice.get( + this.random.nextInt( + Math.min(choice.size(), + RAREST_PIECE_JITTER))); + return chosen; + } +} diff --git a/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplSequential.java b/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplSequential.java new file mode 100644 index 000000000..92bc69995 --- /dev/null +++ b/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplSequential.java @@ -0,0 +1,24 @@ +package com.turn.ttorrent.client.strategy; + +import java.util.BitSet; +import java.util.SortedSet; + +import com.turn.ttorrent.client.Piece; + +/** + * A sequential request strategy implementation. + * + * @author cjmalloy + * + */ +public class RequestStrategyImplSequential implements RequestStrategy { + + @Override + public Piece choosePiece(SortedSet rarest, BitSet interesting, Piece[] pieces) { + + for (Piece p : pieces) { + if (interesting.get(p.getIndex())) return p; + } + return null; + } +} From e9b07e54ec74f42d8666b4f4272e0f1b74d647a0 Mon Sep 17 00:00:00 2001 From: Chris Malloy Date: Fri, 7 Nov 2014 20:36:23 -0400 Subject: [PATCH 39/68] Moved default request strategy to explicit constant. --- .../java/com/turn/ttorrent/client/SharedTorrent.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index 554311b45..111404712 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -76,6 +76,12 @@ public class SharedTorrent extends Torrent implements PeerActivityListener { */ private static final float ENG_GAME_COMPLETION_RATIO = 0.95f; + /** Default Request Strategy. + * + * Use the rarest-first strategy by default. + */ + private static final RequestStrategy DEFAULT_REQUEST_STRATEGY = new RequestStrategyImplRarest(); + private boolean stop; private long uploaded; @@ -135,7 +141,7 @@ public SharedTorrent(Torrent torrent, File destDir) */ public SharedTorrent(Torrent torrent, File destDir, boolean seeder) throws FileNotFoundException, IOException { - this(torrent.getEncoded(), destDir, seeder, null); + this(torrent.getEncoded(), destDir, seeder, DEFAULT_REQUEST_STRATEGY); } /** @@ -190,7 +196,7 @@ public SharedTorrent(byte[] torrent, File destDir) */ public SharedTorrent(byte[] torrent, File parent, boolean seeder) throws FileNotFoundException, IOException { - this(torrent, parent, seeder, null); + this(torrent, parent, seeder, DEFAULT_REQUEST_STRATEGY); } /** @@ -260,7 +266,6 @@ public SharedTorrent(byte[] torrent, File parent, boolean seeder, this.requestedPieces = new BitSet(); //TODO: should switch to guice - if (requestStrategy == null) requestStrategy = new RequestStrategyImplRarest(); this.requestStrategy = requestStrategy; } From 848f07f0c6596229c62e67fab6f3f2668d6f49b6 Mon Sep 17 00:00:00 2001 From: Chris Malloy Date: Sun, 9 Nov 2014 21:22:38 -0400 Subject: [PATCH 40/68] Fixed file list for multi-file torrents. File.listFiles() will return files and 1st-level subdirectories. Any subdirectories in files parameter of Torrent.create(4) will throw an error. Also, the intention was probably to do a fully-recursive search. FileUtils.listFiles() only returns files and is fully recursive. --- src/main/java/com/turn/ttorrent/cli/TorrentMain.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/cli/TorrentMain.java b/src/main/java/com/turn/ttorrent/cli/TorrentMain.java index b7094fe84..601d33b66 100644 --- a/src/main/java/com/turn/ttorrent/cli/TorrentMain.java +++ b/src/main/java/com/turn/ttorrent/cli/TorrentMain.java @@ -24,11 +24,14 @@ import java.net.URI; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Vector; import jargs.gnu.CmdLineParser; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.filefilter.TrueFileFilter; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.PatternLayout; @@ -159,9 +162,9 @@ public static void main(String[] args) { Torrent torrent = null; if (source.isDirectory()) { - File[] files = source.listFiles(); - Arrays.sort(files); - torrent = Torrent.create(source, Arrays.asList(files), + List files = new ArrayList(FileUtils.listFiles(source, TrueFileFilter.TRUE, TrueFileFilter.TRUE)); + Collections.sort(files); + torrent = Torrent.create(source, files, announceList, creator); } else { torrent = Torrent.create(source, announceList, creator); From c072a12400fa1b9b0ce931e7ca1e53c223d30ace Mon Sep 17 00:00:00 2001 From: Rajiv Ambegaokar Date: Mon, 24 Nov 2014 14:41:40 +0530 Subject: [PATCH 41/68] Add a piece length option to the torrent creator CLI --- .../com/turn/ttorrent/cli/TorrentMain.java | 15 ++++++- .../com/turn/ttorrent/common/Torrent.java | 43 +++++++++++-------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/cli/TorrentMain.java b/src/main/java/com/turn/ttorrent/cli/TorrentMain.java index 601d33b66..7fb8c1ff5 100644 --- a/src/main/java/com/turn/ttorrent/cli/TorrentMain.java +++ b/src/main/java/com/turn/ttorrent/cli/TorrentMain.java @@ -70,6 +70,7 @@ private static void usage(PrintStream s, String msg) { s.println(); s.println(" -c,--create Create a new torrent file using " + "the given announce URL and data."); + s.println(" -l,--length Define the piece length for hashing data"); s.println(" -a,--announce Tracker URL (can be repeated)."); s.println(); } @@ -91,6 +92,7 @@ public static void main(String[] args) { CmdLineParser.Option help = parser.addBooleanOption('h', "help"); CmdLineParser.Option filename = parser.addStringOption('t', "torrent"); CmdLineParser.Option create = parser.addBooleanOption('c', "create"); + CmdLineParser.Option pieceLength = parser.addIntegerOption('l', "length"); CmdLineParser.Option announce = parser.addStringOption('a', "announce"); try { @@ -113,6 +115,15 @@ public static void main(String[] args) { System.exit(1); } + Integer pieceLengthVal = (Integer) parser.getOptionValue(pieceLength); + if (pieceLengthVal == null) { + pieceLengthVal = Torrent.DEFAULT_PIECE_LENGTH; + } + else { + pieceLengthVal = pieceLengthVal * 1024; + } + logger.info("Using piece length of {} bytes.", pieceLengthVal); + Boolean createFlag = (Boolean)parser.getOptionValue(create); //For repeated announce urls @@ -164,10 +175,10 @@ public static void main(String[] args) { if (source.isDirectory()) { List files = new ArrayList(FileUtils.listFiles(source, TrueFileFilter.TRUE, TrueFileFilter.TRUE)); Collections.sort(files); - torrent = Torrent.create(source, files, + torrent = Torrent.create(source, files, pieceLengthVal, announceList, creator); } else { - torrent = Torrent.create(source, announceList, creator); + torrent = Torrent.create(source, pieceLengthVal, announceList, creator); } torrent.save(fos); diff --git a/src/main/java/com/turn/ttorrent/common/Torrent.java b/src/main/java/com/turn/ttorrent/common/Torrent.java index 5ee4f471c..5829aaff1 100644 --- a/src/main/java/com/turn/ttorrent/common/Torrent.java +++ b/src/main/java/com/turn/ttorrent/common/Torrent.java @@ -76,7 +76,7 @@ public class Torrent { LoggerFactory.getLogger(Torrent.class); /** Torrent file piece length (in bytes), we use 512 kB. */ - private static final int PIECE_LENGTH = 512 * 1024; + public static final int DEFAULT_PIECE_LENGTH = 512 * 1024; public static final int PIECE_HASH_SIZE = 20; @@ -115,6 +115,8 @@ public TorrentFile(File file, long size) { private final String createdBy; private final String name; private final long size; + private final int pieceLength; + protected final List files; private final boolean seeder; @@ -208,6 +210,7 @@ public Torrent(byte[] torrent, boolean seeder) throws IOException { ? this.decoded.get("created by").getString() : null; this.name = this.decoded_info.get("name").getString(); + this.pieceLength = this.decoded_info.get("piece length").getInt(); this.files = new LinkedList(); @@ -509,7 +512,8 @@ public static Torrent load(File torrent, boolean seeder) */ public static Torrent create(File source, URI announce, String createdBy) throws InterruptedException, IOException { - return Torrent.create(source, null, announce, null, createdBy); + return Torrent.create(source, null, DEFAULT_PIECE_LENGTH, + announce, null, createdBy); } /** @@ -531,7 +535,8 @@ public static Torrent create(File source, URI announce, String createdBy) */ public static Torrent create(File parent, List files, URI announce, String createdBy) throws InterruptedException, IOException { - return Torrent.create(parent, files, announce, null, createdBy); + return Torrent.create(parent, files, DEFAULT_PIECE_LENGTH, + announce, null, createdBy); } /** @@ -549,9 +554,10 @@ public static Torrent create(File parent, List files, URI announce, * @param createdBy The creator's name, or any string identifying the * torrent's creator. */ - public static Torrent create(File source, List> announceList, + public static Torrent create(File source, int pieceLength, List> announceList, String createdBy) throws InterruptedException, IOException { - return Torrent.create(source, null, null, announceList, createdBy); + return Torrent.create(source, null, pieceLength, + null, announceList, createdBy); } /** @@ -572,10 +578,11 @@ public static Torrent create(File source, List> announceList, * @param createdBy The creator's name, or any string identifying the * torrent's creator. */ - public static Torrent create(File source, List files, + public static Torrent create(File source, List files, int pieceLength, List> announceList, String createdBy) throws InterruptedException, IOException { - return Torrent.create(source, files, null, announceList, createdBy); + return Torrent.create(source, files, pieceLength, + null, announceList, createdBy); } /** @@ -597,8 +604,8 @@ public static Torrent create(File source, List files, * @param createdBy The creator's name, or any string identifying the * torrent's creator. */ - private static Torrent create(File parent, List files, URI announce, - List> announceList, String createdBy) + private static Torrent create(File parent, List files, int pieceLength, + URI announce, List> announceList, String createdBy) throws InterruptedException, IOException { if (files == null || files.isEmpty()) { logger.info("Creating single-file torrent for {}...", @@ -630,11 +637,11 @@ private static Torrent create(File parent, List files, URI announce, Map info = new TreeMap(); info.put("name", new BEValue(parent.getName())); - info.put("piece length", new BEValue(Torrent.PIECE_LENGTH)); + info.put("piece length", new BEValue(pieceLength)); if (files == null || files.isEmpty()) { info.put("length", new BEValue(parent.length())); - info.put("pieces", new BEValue(Torrent.hashFile(parent), + info.put("pieces", new BEValue(Torrent.hashFile(parent, pieceLength), Torrent.BYTE_ENCODING)); } else { List fileInfo = new LinkedList(); @@ -656,7 +663,7 @@ private static Torrent create(File parent, List files, URI announce, fileInfo.add(new BEValue(fileMap)); } info.put("files", new BEValue(fileInfo)); - info.put("pieces", new BEValue(Torrent.hashFiles(files), + info.put("pieces", new BEValue(Torrent.hashFiles(files, pieceLength), Torrent.BYTE_ENCODING)); } torrent.put("info", new BEValue(info)); @@ -709,16 +716,16 @@ public String call() throws UnsupportedEncodingException { * * @param file The file to hash. */ - private static String hashFile(File file) + private static String hashFile(File file, int pieceLenght) throws InterruptedException, IOException { - return Torrent.hashFiles(Arrays.asList(new File[] { file })); + return Torrent.hashFiles(Arrays.asList(new File[] { file }), pieceLenght); } - private static String hashFiles(List files) + private static String hashFiles(List files, int pieceLenght) throws InterruptedException, IOException { int threads = getHashingThreadsCount(); ExecutorService executor = Executors.newFixedThreadPool(threads); - ByteBuffer buffer = ByteBuffer.allocate(Torrent.PIECE_LENGTH); + ByteBuffer buffer = ByteBuffer.allocate(pieceLenght); List> results = new LinkedList>(); StringBuilder hashes = new StringBuilder(); @@ -732,7 +739,7 @@ private static String hashFiles(List files) file.getName(), threads, (int) (Math.ceil( - (double)file.length() / Torrent.PIECE_LENGTH)) + (double)file.length() / pieceLenght)) }); length += file.length(); @@ -781,7 +788,7 @@ private static String hashFiles(List files) long elapsed = System.nanoTime() - start; int expectedPieces = (int) (Math.ceil( - (double)length / Torrent.PIECE_LENGTH)); + (double)length / pieceLenght)); logger.info("Hashed {} file(s) ({} bytes) in {} pieces ({} expected) in {}ms.", new Object[] { files.size(), From 23ec122cab0a9adb8d01d1ab008554cd4edcf118 Mon Sep 17 00:00:00 2001 From: Chris Malloy Date: Thu, 19 Feb 2015 20:11:12 -0400 Subject: [PATCH 42/68] Refactored out dependency on log4j. There are now two submodules in this project, cli and core. --- .gitignore | 2 +- cli/.gitignore | 5 ++ cli/pom.xml | 77 ++++++++++++++++ .../com/turn/ttorrent/cli/ClientMain.java | 0 .../com/turn/ttorrent/cli/TorrentMain.java | 0 .../com/turn/ttorrent/cli/TrackerMain.java | 0 core/.gitignore | 5 ++ core/pom.xml | 42 +++++++++ .../com/turn/ttorrent/bcodec/BDecoder.java | 0 .../com/turn/ttorrent/bcodec/BEValue.java | 0 .../com/turn/ttorrent/bcodec/BEncoder.java | 0 .../bcodec/InvalidBEncodingException.java | 0 .../java/com/turn/ttorrent/client/Client.java | 0 .../ttorrent/client/ConnectionHandler.java | 0 .../com/turn/ttorrent/client/Handshake.java | 0 .../client/IncomingConnectionListener.java | 0 .../java/com/turn/ttorrent/client/Piece.java | 0 .../turn/ttorrent/client/SharedTorrent.java | 0 .../ttorrent/client/announce/Announce.java | 0 .../client/announce/AnnounceException.java | 0 .../announce/AnnounceResponseListener.java | 0 .../client/announce/HTTPTrackerClient.java | 0 .../client/announce/TrackerClient.java | 0 .../client/announce/UDPTrackerClient.java | 0 .../ttorrent/client/peer/MessageListener.java | 0 .../client/peer/PeerActivityListener.java | 0 .../ttorrent/client/peer/PeerExchange.java | 0 .../com/turn/ttorrent/client/peer/Rate.java | 0 .../ttorrent/client/peer/SharingPeer.java | 0 .../client/storage/FileCollectionStorage.java | 0 .../ttorrent/client/storage/FileStorage.java | 0 .../client/storage/TorrentByteStorage.java | 0 .../client/strategy/RequestStrategy.java | 0 .../strategy/RequestStrategyImplRarest.java | 0 .../RequestStrategyImplSequential.java | 0 .../java/com/turn/ttorrent/common/Peer.java | 0 .../com/turn/ttorrent/common/Torrent.java | 0 .../ttorrent/common/protocol/PeerMessage.java | 0 .../common/protocol/TrackerMessage.java | 0 .../http/HTTPAnnounceRequestMessage.java | 0 .../http/HTTPAnnounceResponseMessage.java | 0 .../http/HTTPTrackerErrorMessage.java | 0 .../protocol/http/HTTPTrackerMessage.java | 0 .../udp/UDPAnnounceRequestMessage.java | 0 .../udp/UDPAnnounceResponseMessage.java | 0 .../udp/UDPConnectRequestMessage.java | 0 .../udp/UDPConnectResponseMessage.java | 0 .../protocol/udp/UDPTrackerErrorMessage.java | 0 .../protocol/udp/UDPTrackerMessage.java | 0 .../turn/ttorrent/tracker/TrackedPeer.java | 0 .../turn/ttorrent/tracker/TrackedTorrent.java | 0 .../com/turn/ttorrent/tracker/Tracker.java | 0 .../turn/ttorrent/tracker/TrackerService.java | 0 .../storage/FileCollectionStorageTest.java | 0 pom.xml | 88 ++----------------- 55 files changed, 136 insertions(+), 83 deletions(-) create mode 100644 cli/.gitignore create mode 100644 cli/pom.xml rename {src => cli/src}/main/java/com/turn/ttorrent/cli/ClientMain.java (100%) rename {src => cli/src}/main/java/com/turn/ttorrent/cli/TorrentMain.java (100%) rename {src => cli/src}/main/java/com/turn/ttorrent/cli/TrackerMain.java (100%) create mode 100644 core/.gitignore create mode 100644 core/pom.xml rename {src => core/src}/main/java/com/turn/ttorrent/bcodec/BDecoder.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/bcodec/BEValue.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/bcodec/BEncoder.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/bcodec/InvalidBEncodingException.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/Client.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/ConnectionHandler.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/Handshake.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/IncomingConnectionListener.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/Piece.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/SharedTorrent.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/announce/Announce.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/announce/AnnounceException.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/announce/AnnounceResponseListener.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/announce/TrackerClient.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/announce/UDPTrackerClient.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/peer/MessageListener.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/peer/PeerActivityListener.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/peer/PeerExchange.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/peer/Rate.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/peer/SharingPeer.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/storage/FileCollectionStorage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/storage/FileStorage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/storage/TorrentByteStorage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/strategy/RequestStrategy.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplRarest.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplSequential.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/Peer.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/Torrent.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/TrackerMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceResponseMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/http/HTTPTrackerErrorMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/http/HTTPTrackerMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceResponseMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/udp/UDPConnectRequestMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/udp/UDPConnectResponseMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/udp/UDPTrackerErrorMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/common/protocol/udp/UDPTrackerMessage.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/tracker/TrackedPeer.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/tracker/Tracker.java (100%) rename {src => core/src}/main/java/com/turn/ttorrent/tracker/TrackerService.java (100%) rename {test => core/src/test}/main/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java (100%) diff --git a/.gitignore b/.gitignore index 72fd2a12d..3a8962f90 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ *.swp *.bak *~ -*~ + diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 000000000..2177fc127 --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1,5 @@ +/target/ + +/.classpath +/.project +/.settings diff --git a/cli/pom.xml b/cli/pom.xml new file mode 100644 index 000000000..166670756 --- /dev/null +++ b/cli/pom.xml @@ -0,0 +1,77 @@ + + 4.0.0 + + + com.turn + ttorrent + 1.5-SNAPSHOT + + + Java BitTorrent library CLI + ttorrent-cli + jar + + + + com.turn + ttorrent-core + 1.5-SNAPSHOT + + + + org.slf4j + slf4j-log4j12 + 1.6.4 + + + net.sf + jargs + 1.0 + + + + + package + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + + + + ** + + + + + + maven-shade-plugin + 2.1 + + + package + + shade + + + ${project.build.directory}/${project.artifactId}-${project.version}-shaded.jar + + + + com.turn.ttorrent.cli.ClientMain + + + + + + + + + + diff --git a/src/main/java/com/turn/ttorrent/cli/ClientMain.java b/cli/src/main/java/com/turn/ttorrent/cli/ClientMain.java similarity index 100% rename from src/main/java/com/turn/ttorrent/cli/ClientMain.java rename to cli/src/main/java/com/turn/ttorrent/cli/ClientMain.java diff --git a/src/main/java/com/turn/ttorrent/cli/TorrentMain.java b/cli/src/main/java/com/turn/ttorrent/cli/TorrentMain.java similarity index 100% rename from src/main/java/com/turn/ttorrent/cli/TorrentMain.java rename to cli/src/main/java/com/turn/ttorrent/cli/TorrentMain.java diff --git a/src/main/java/com/turn/ttorrent/cli/TrackerMain.java b/cli/src/main/java/com/turn/ttorrent/cli/TrackerMain.java similarity index 100% rename from src/main/java/com/turn/ttorrent/cli/TrackerMain.java rename to cli/src/main/java/com/turn/ttorrent/cli/TrackerMain.java diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 000000000..2177fc127 --- /dev/null +++ b/core/.gitignore @@ -0,0 +1,5 @@ +/target/ + +/.classpath +/.project +/.settings diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 000000000..7930bdc4f --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + + com.turn + ttorrent + 1.5-SNAPSHOT + + + Java BitTorrent library core + ttorrent-core + jar + + + + commons-codec + commons-codec + 1.8 + + + commons-io + commons-io + 2.4 + + + org.simpleframework + simple + 4.1.21 + + + org.slf4j + slf4j-api + 1.6.4 + + + + + + + + + diff --git a/src/main/java/com/turn/ttorrent/bcodec/BDecoder.java b/core/src/main/java/com/turn/ttorrent/bcodec/BDecoder.java similarity index 100% rename from src/main/java/com/turn/ttorrent/bcodec/BDecoder.java rename to core/src/main/java/com/turn/ttorrent/bcodec/BDecoder.java diff --git a/src/main/java/com/turn/ttorrent/bcodec/BEValue.java b/core/src/main/java/com/turn/ttorrent/bcodec/BEValue.java similarity index 100% rename from src/main/java/com/turn/ttorrent/bcodec/BEValue.java rename to core/src/main/java/com/turn/ttorrent/bcodec/BEValue.java diff --git a/src/main/java/com/turn/ttorrent/bcodec/BEncoder.java b/core/src/main/java/com/turn/ttorrent/bcodec/BEncoder.java similarity index 100% rename from src/main/java/com/turn/ttorrent/bcodec/BEncoder.java rename to core/src/main/java/com/turn/ttorrent/bcodec/BEncoder.java diff --git a/src/main/java/com/turn/ttorrent/bcodec/InvalidBEncodingException.java b/core/src/main/java/com/turn/ttorrent/bcodec/InvalidBEncodingException.java similarity index 100% rename from src/main/java/com/turn/ttorrent/bcodec/InvalidBEncodingException.java rename to core/src/main/java/com/turn/ttorrent/bcodec/InvalidBEncodingException.java diff --git a/src/main/java/com/turn/ttorrent/client/Client.java b/core/src/main/java/com/turn/ttorrent/client/Client.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/Client.java rename to core/src/main/java/com/turn/ttorrent/client/Client.java diff --git a/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java b/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/ConnectionHandler.java rename to core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java diff --git a/src/main/java/com/turn/ttorrent/client/Handshake.java b/core/src/main/java/com/turn/ttorrent/client/Handshake.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/Handshake.java rename to core/src/main/java/com/turn/ttorrent/client/Handshake.java diff --git a/src/main/java/com/turn/ttorrent/client/IncomingConnectionListener.java b/core/src/main/java/com/turn/ttorrent/client/IncomingConnectionListener.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/IncomingConnectionListener.java rename to core/src/main/java/com/turn/ttorrent/client/IncomingConnectionListener.java diff --git a/src/main/java/com/turn/ttorrent/client/Piece.java b/core/src/main/java/com/turn/ttorrent/client/Piece.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/Piece.java rename to core/src/main/java/com/turn/ttorrent/client/Piece.java diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/SharedTorrent.java rename to core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java diff --git a/src/main/java/com/turn/ttorrent/client/announce/Announce.java b/core/src/main/java/com/turn/ttorrent/client/announce/Announce.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/announce/Announce.java rename to core/src/main/java/com/turn/ttorrent/client/announce/Announce.java diff --git a/src/main/java/com/turn/ttorrent/client/announce/AnnounceException.java b/core/src/main/java/com/turn/ttorrent/client/announce/AnnounceException.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/announce/AnnounceException.java rename to core/src/main/java/com/turn/ttorrent/client/announce/AnnounceException.java diff --git a/src/main/java/com/turn/ttorrent/client/announce/AnnounceResponseListener.java b/core/src/main/java/com/turn/ttorrent/client/announce/AnnounceResponseListener.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/announce/AnnounceResponseListener.java rename to core/src/main/java/com/turn/ttorrent/client/announce/AnnounceResponseListener.java diff --git a/src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java b/core/src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java rename to core/src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java diff --git a/src/main/java/com/turn/ttorrent/client/announce/TrackerClient.java b/core/src/main/java/com/turn/ttorrent/client/announce/TrackerClient.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/announce/TrackerClient.java rename to core/src/main/java/com/turn/ttorrent/client/announce/TrackerClient.java diff --git a/src/main/java/com/turn/ttorrent/client/announce/UDPTrackerClient.java b/core/src/main/java/com/turn/ttorrent/client/announce/UDPTrackerClient.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/announce/UDPTrackerClient.java rename to core/src/main/java/com/turn/ttorrent/client/announce/UDPTrackerClient.java diff --git a/src/main/java/com/turn/ttorrent/client/peer/MessageListener.java b/core/src/main/java/com/turn/ttorrent/client/peer/MessageListener.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/peer/MessageListener.java rename to core/src/main/java/com/turn/ttorrent/client/peer/MessageListener.java diff --git a/src/main/java/com/turn/ttorrent/client/peer/PeerActivityListener.java b/core/src/main/java/com/turn/ttorrent/client/peer/PeerActivityListener.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/peer/PeerActivityListener.java rename to core/src/main/java/com/turn/ttorrent/client/peer/PeerActivityListener.java diff --git a/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java rename to core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java diff --git a/src/main/java/com/turn/ttorrent/client/peer/Rate.java b/core/src/main/java/com/turn/ttorrent/client/peer/Rate.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/peer/Rate.java rename to core/src/main/java/com/turn/ttorrent/client/peer/Rate.java diff --git a/src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java b/core/src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java rename to core/src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java diff --git a/src/main/java/com/turn/ttorrent/client/storage/FileCollectionStorage.java b/core/src/main/java/com/turn/ttorrent/client/storage/FileCollectionStorage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/storage/FileCollectionStorage.java rename to core/src/main/java/com/turn/ttorrent/client/storage/FileCollectionStorage.java diff --git a/src/main/java/com/turn/ttorrent/client/storage/FileStorage.java b/core/src/main/java/com/turn/ttorrent/client/storage/FileStorage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/storage/FileStorage.java rename to core/src/main/java/com/turn/ttorrent/client/storage/FileStorage.java diff --git a/src/main/java/com/turn/ttorrent/client/storage/TorrentByteStorage.java b/core/src/main/java/com/turn/ttorrent/client/storage/TorrentByteStorage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/storage/TorrentByteStorage.java rename to core/src/main/java/com/turn/ttorrent/client/storage/TorrentByteStorage.java diff --git a/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategy.java b/core/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategy.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/strategy/RequestStrategy.java rename to core/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategy.java diff --git a/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplRarest.java b/core/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplRarest.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplRarest.java rename to core/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplRarest.java diff --git a/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplSequential.java b/core/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplSequential.java similarity index 100% rename from src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplSequential.java rename to core/src/main/java/com/turn/ttorrent/client/strategy/RequestStrategyImplSequential.java diff --git a/src/main/java/com/turn/ttorrent/common/Peer.java b/core/src/main/java/com/turn/ttorrent/common/Peer.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/Peer.java rename to core/src/main/java/com/turn/ttorrent/common/Peer.java diff --git a/src/main/java/com/turn/ttorrent/common/Torrent.java b/core/src/main/java/com/turn/ttorrent/common/Torrent.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/Torrent.java rename to core/src/main/java/com/turn/ttorrent/common/Torrent.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/TrackerMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/TrackerMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/TrackerMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/TrackerMessage.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceResponseMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceResponseMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceResponseMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceResponseMessage.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPTrackerErrorMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPTrackerErrorMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/http/HTTPTrackerErrorMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPTrackerErrorMessage.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPTrackerMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPTrackerMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/http/HTTPTrackerMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPTrackerMessage.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceResponseMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceResponseMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceResponseMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceResponseMessage.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPConnectRequestMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPConnectRequestMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/udp/UDPConnectRequestMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPConnectRequestMessage.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPConnectResponseMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPConnectResponseMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/udp/UDPConnectResponseMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPConnectResponseMessage.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPTrackerErrorMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPTrackerErrorMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/udp/UDPTrackerErrorMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPTrackerErrorMessage.java diff --git a/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPTrackerMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPTrackerMessage.java similarity index 100% rename from src/main/java/com/turn/ttorrent/common/protocol/udp/UDPTrackerMessage.java rename to core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPTrackerMessage.java diff --git a/src/main/java/com/turn/ttorrent/tracker/TrackedPeer.java b/core/src/main/java/com/turn/ttorrent/tracker/TrackedPeer.java similarity index 100% rename from src/main/java/com/turn/ttorrent/tracker/TrackedPeer.java rename to core/src/main/java/com/turn/ttorrent/tracker/TrackedPeer.java diff --git a/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java b/core/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java similarity index 100% rename from src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java rename to core/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java diff --git a/src/main/java/com/turn/ttorrent/tracker/Tracker.java b/core/src/main/java/com/turn/ttorrent/tracker/Tracker.java similarity index 100% rename from src/main/java/com/turn/ttorrent/tracker/Tracker.java rename to core/src/main/java/com/turn/ttorrent/tracker/Tracker.java diff --git a/src/main/java/com/turn/ttorrent/tracker/TrackerService.java b/core/src/main/java/com/turn/ttorrent/tracker/TrackerService.java similarity index 100% rename from src/main/java/com/turn/ttorrent/tracker/TrackerService.java rename to core/src/main/java/com/turn/ttorrent/tracker/TrackerService.java diff --git a/test/main/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java b/core/src/test/main/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java similarity index 100% rename from test/main/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java rename to core/src/test/main/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java diff --git a/pom.xml b/pom.xml index 7e6c7d4d8..445d945b5 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,12 @@ com.turn ttorrent 1.5-SNAPSHOT - jar + pom + + + core + cli + scm:git:git://github.com/mpetazzoni/ttorrent.git @@ -71,49 +76,7 @@ - - - commons-codec - commons-codec - 1.8 - - - - commons-io - commons-io - 2.4 - - - - org.simpleframework - simple - 4.1.21 - - - - org.slf4j - slf4j-log4j12 - 1.6.4 - - - - org.testng - testng - 6.1.1 - test - - - - net.sf - jargs - 1.0 - - - - package - ${basedir}/build - org.apache.maven.plugins @@ -125,22 +88,6 @@ - - org.apache.maven.plugins - maven-jar-plugin - 2.4 - - - - true - - - - ** - - - - org.apache.maven.plugins maven-javadoc-plugin @@ -156,29 +103,6 @@ maven-release-plugin 2.4.2 - - - maven-shade-plugin - 2.1 - - - package - - shade - - - ${project.build.directory}/${project.artifactId}-${project.version}-shaded.jar - - - - com.turn.ttorrent.cli.ClientMain - - - - - - -
From 600836baf1443ed588b9b1a6250e56218578f9c6 Mon Sep 17 00:00:00 2001 From: Chris Malloy Date: Fri, 20 Feb 2015 08:11:16 -0400 Subject: [PATCH 43/68] Updated maven example. ArtifactId has changed to ttorrent-core. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8319e1894..75014b3e3 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ section: ... com.turn - ttorrent + ttorrent-core 1.4 From 5eb5ecb152822778342709f8d4a4023019185da2 Mon Sep 17 00:00:00 2001 From: Chris Malloy Date: Fri, 20 Feb 2015 08:30:49 -0400 Subject: [PATCH 44/68] Updated install instructions. The install instructions have changes a bit because of the multi-module reorg. --- INSTALL | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/INSTALL b/INSTALL index d33121eee..a06ba85ff 100644 --- a/INSTALL +++ b/INSTALL @@ -1,20 +1,21 @@ -Howto build and use the BitTorrent library +Howto build and use the ttorrent library ========================================== Dependencies ------------ This Java implementation of the BitTorrent protocol implements a BitTorrent -tracker (an HTTP service), and a BitTorrent client. The only dependencies of -the BitTorrent library are: +tracker (an HTTP service), and a BitTorrent client. All dependencies are managed +by maven. The only dependencies of ttorrent-core are: -* the log4j library * the slf4j logging library * the SimpleHTTPFramework +* the Apache Commons (Codec and IO) -These libraries are provided in the lib/ directory, and are automatically -included in the JAR file created by the build process. +The CLI module also depends on: +* the log4j library +* the jargs library Building the distribution JAR ----------------------------- @@ -23,6 +24,14 @@ Simply execute the following command: $ mvn package -To build the library's JAR file (in the target/ directory). You can then import +To build the library's JAR file (in the core/target/ directory). You can then import this JAR file into your Java project and start using the Java BitTorrent library. + +This will also create a shaded JAR (in the cli/target/ directory). You can then use +this JAR file in conjunction with the three scripts in the bin/ folder. Each script +allows execution of one of the following entry points: + +* ClientMain - for running a torrent client +* TorrentMain - for creating .torrent files +* TrackerMain - for running a tracking server From a9bff22b24ca15f2e13c58070bfc2833f2586b1a Mon Sep 17 00:00:00 2001 From: Chris Malloy Date: Fri, 20 Feb 2015 17:48:57 -0400 Subject: [PATCH 45/68] Enabled tests. Fixed a path error to move the unit tests to the maven test folder. Added testng dependency as it is now required. --- core/pom.xml | 12 ++++++------ .../client/storage/FileCollectionStorageTest.java | 0 2 files changed, 6 insertions(+), 6 deletions(-) rename core/src/test/{main => }/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java (100%) diff --git a/core/pom.xml b/core/pom.xml index 7930bdc4f..60eeb9738 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -32,11 +32,11 @@ slf4j-api 1.6.4 - - - - - - + + org.testng + testng + 6.1.1 + test +
diff --git a/core/src/test/main/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java b/core/src/test/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java similarity index 100% rename from core/src/test/main/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java rename to core/src/test/java/com/turn/ttorrent/client/storage/FileCollectionStorageTest.java From 520ec2b5215f5a95007e49ae4ad6c26cc8f91b85 Mon Sep 17 00:00:00 2001 From: halfmanhalffish Date: Wed, 1 Apr 2015 09:36:25 +0100 Subject: [PATCH 46/68] fix order of fields in the 'send announce' packets going to udp trackers --- .../ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java index 3150ea890..82b9f8e60 100644 --- a/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java +++ b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java @@ -229,8 +229,8 @@ public static UDPAnnounceRequestMessage craft(long connectionId, data.put(infoHash); data.put(peerId); data.putLong(downloaded); - data.putLong(uploaded); data.putLong(left); + data.putLong(uploaded); data.putInt(event.getId()); data.put(ip.getAddress()); data.putInt(key); From e7155251788c77bda4ac2c036558ba2a8dd4b9d6 Mon Sep 17 00:00:00 2001 From: Hagai Date: Fri, 17 Apr 2015 17:03:36 +0300 Subject: [PATCH 47/68] fixed wrong maven artifactId --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 75014b3e3..8319e1894 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ section: ... com.turn - ttorrent-core + ttorrent 1.4 From 8a514996e98a8595fc764f2225b03bddd1b2f5cb Mon Sep 17 00:00:00 2001 From: tyler Date: Fri, 22 May 2015 14:31:32 -0400 Subject: [PATCH 48/68] Use a larger port range, fixes #132 --- core/src/main/java/com/turn/ttorrent/client/Client.java | 2 +- .../main/java/com/turn/ttorrent/client/ConnectionHandler.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/turn/ttorrent/client/Client.java b/core/src/main/java/com/turn/ttorrent/client/Client.java index 00325f078..653598016 100644 --- a/core/src/main/java/com/turn/ttorrent/client/Client.java +++ b/core/src/main/java/com/turn/ttorrent/client/Client.java @@ -135,7 +135,7 @@ public Client(InetAddress address, SharedTorrent torrent) this.self = new Peer( this.service.getSocketAddress() .getAddress().getHostAddress(), - (short)this.service.getSocketAddress().getPort(), + this.service.getSocketAddress().getPort(), ByteBuffer.wrap(id.getBytes(Torrent.BYTE_ENCODING))); // Initialize the announce request thread, and register ourselves to it diff --git a/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java b/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java index 4653f9466..f15d71f53 100644 --- a/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java +++ b/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java @@ -79,8 +79,8 @@ public class ConnectionHandler implements Runnable { private static final Logger logger = LoggerFactory.getLogger(ConnectionHandler.class); - public static final int PORT_RANGE_START = 6881; - public static final int PORT_RANGE_END = 6889; + public static final int PORT_RANGE_START = 49152; + public static final int PORT_RANGE_END = 65534; private static final int OUTBOUND_CONNECTIONS_POOL_SIZE = 20; private static final int OUTBOUND_CONNECTIONS_THREAD_KEEP_ALIVE_SECS = 10; From 87c53b71f9a2d6ab076c4a98e801e3e520f243a2 Mon Sep 17 00:00:00 2001 From: someone Date: Tue, 19 May 2015 12:10:07 +0100 Subject: [PATCH 49/68] fix bug by which not all peers that the server reports are reported back to clients --- .../common/protocol/udp/UDPAnnounceResponseMessage.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceResponseMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceResponseMessage.java index 612012d3b..badf8667a 100644 --- a/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceResponseMessage.java +++ b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceResponseMessage.java @@ -102,7 +102,11 @@ public static UDPAnnounceResponseMessage parse(ByteBuffer data) int complete = data.getInt(); List peers = new LinkedList(); - for (int i=0; i < data.remaining() / 6; i++) { + // the line below replaces this: for (int i=0; i < data.remaining() / 6; i++) + // That for loop fails when data.remaining() is 6, even if data.remaining() / 6 is + // placed in parentheses. The reason why it fails is not clear. Replacing it + // with while (data.remaining() > 5) works however. + while(data.remaining() > 5) { try { byte[] ipBytes = new byte[4]; data.get(ipBytes); From fe1ab72634d6713ed68e2f2e4430295c8ebd8238 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Tue, 5 Jan 2016 06:23:39 -0500 Subject: [PATCH 50/68] Remove outdated comment Signed-off-by: Maxime Petazzoni --- .../main/java/com/turn/ttorrent/client/SharedTorrent.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index 111404712..7628d9ab3 100644 --- a/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -53,11 +53,6 @@ * and logic required by the BitTorrent client implementation. *

* - *

- * Note: this implementation currently only supports single-file - * torrents. - *

- * * @author mpetazzoni */ public class SharedTorrent extends Torrent implements PeerActivityListener { From 3ce7fab1ff965b5cb78758eca7880f7c6078141c Mon Sep 17 00:00:00 2001 From: Philipp Henkel Date: Sat, 16 Jan 2016 21:04:23 +0100 Subject: [PATCH 51/68] Fixed bitfield payload in bitfield message Bitfield payload was truncated to the highest bit that was set. As a consequence clients dropped the connection. This is one of two changes required to resolve issues 91: download resume not working https://github.com/mpetazzoni/ttorrent/issues/91 --- .../ttorrent/client/peer/PeerExchange.java | 2 +- .../ttorrent/common/protocol/PeerMessage.java | 25 +-- .../common/protocol/PeerMessageTest.java | 146 ++++++++++++++++++ 3 files changed, 162 insertions(+), 11 deletions(-) create mode 100644 core/src/test/java/com/turn/ttorrent/common/protocol/PeerMessageTest.java diff --git a/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index 359bd2fed..365cd8dda 100644 --- a/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -131,7 +131,7 @@ public PeerExchange(SharingPeer peer, SharedTorrent torrent, // If we have pieces, start by sending a BITFIELD message to the peer. BitSet pieces = this.torrent.getCompletedPieces(); if (pieces.cardinality() > 0) { - this.send(PeerMessage.BitfieldMessage.craft(pieces)); + this.send(PeerMessage.BitfieldMessage.craft(pieces, torrent.getPieceCount())); } } diff --git a/core/src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java index d9d01d31b..66a1f66bb 100644 --- a/core/src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java +++ b/core/src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java @@ -427,20 +427,25 @@ public static BitfieldMessage parse(ByteBuffer buffer, .validate(torrent); } - public static BitfieldMessage craft(BitSet availablePieces) { - byte[] bitfield = new byte[ - (int) Math.ceil((double)availablePieces.length()/8)]; - for (int i=availablePieces.nextSetBit(0); i >= 0; - i=availablePieces.nextSetBit(i+1)) { - bitfield[i/8] |= 1 << (7 -(i % 8)); + public static BitfieldMessage craft(BitSet availablePieces, int pieceCount) { + BitSet bitfield = new BitSet(); + int bitfieldBufferSize= (pieceCount + 8 - 1) / 8; + byte[] bitfieldBuffer = new byte[bitfieldBufferSize]; + + for (int i=availablePieces.nextSetBit(0); + 0 <= i && i < pieceCount; + i=availablePieces.nextSetBit(i+1)) { + bitfieldBuffer[i/8] |= 1 << (7 -(i % 8)); + bitfield.set(i); } ByteBuffer buffer = ByteBuffer.allocateDirect( - MESSAGE_LENGTH_FIELD_SIZE + BitfieldMessage.BASE_SIZE + bitfield.length); - buffer.putInt(BitfieldMessage.BASE_SIZE + bitfield.length); + MESSAGE_LENGTH_FIELD_SIZE + BitfieldMessage.BASE_SIZE + bitfieldBufferSize); + buffer.putInt(BitfieldMessage.BASE_SIZE + bitfieldBufferSize); buffer.put(PeerMessage.Type.BITFIELD.getTypeByte()); - buffer.put(ByteBuffer.wrap(bitfield)); - return new BitfieldMessage(buffer, availablePieces); + buffer.put(ByteBuffer.wrap(bitfieldBuffer)); + + return new BitfieldMessage(buffer, bitfield); } public String toString() { diff --git a/core/src/test/java/com/turn/ttorrent/common/protocol/PeerMessageTest.java b/core/src/test/java/com/turn/ttorrent/common/protocol/PeerMessageTest.java new file mode 100644 index 000000000..3bc911fa6 --- /dev/null +++ b/core/src/test/java/com/turn/ttorrent/common/protocol/PeerMessageTest.java @@ -0,0 +1,146 @@ +package com.turn.ttorrent.common; + +import com.turn.ttorrent.common.protocol.PeerMessage; + +import org.testng.annotations.Test; + +import java.nio.ByteBuffer; +import java.util.BitSet; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertEqualsNoOrder; +import static org.testng.Assert.assertTrue; + + +public class PeerMessageTest { + + @Test + public void testCraftBitfieldMessage() { + // See https://wiki.theory.org/BitTorrentSpecification#bitfield + + // Create message with 744 (= 93 * 8) pieces + BitSet availablePieces = new BitSet(); + availablePieces.set(0); + availablePieces.set(700); + availablePieces.set(743); // last piece + availablePieces.set(744); // out of range - should be ignored + PeerMessage.BitfieldMessage msg = PeerMessage.BitfieldMessage.craft(availablePieces, 744); + + // Check bitfield + assertEquals(3, msg.getBitfield().cardinality()); + assertEquals(true, msg.getBitfield().get(0)); + assertEquals(true, msg.getBitfield().get(700)); + assertEquals(true, msg.getBitfield().get(743)); + + // Check raw data - bitfield: + ByteBuffer buffer = msg.getData(); + + // total size + assertEquals(4 + 1 + 93, buffer.remaining()); + + // len + assertEquals(0, buffer.get(0)); + assertEquals(0, buffer.get(1)); + assertEquals(0, buffer.get(2)); + assertEquals(1 + 93, (int)buffer.get(3)); + + // id + assertEquals(5, buffer.get(4)); + + // bitfield + buffer.position(5); + ByteBuffer bitfieldBuffer = buffer.slice(); + BitSet bitfield = convertByteBufferToBitfieldBitSet(bitfieldBuffer); + assertEquals(3, bitfield.cardinality()); + assertEquals(true, bitfield.get(00)); + assertEquals(true, bitfield.get(700)); + assertEquals(true, bitfield.get(743)); + } + + @Test + public void testCraftBitfieldMessageEmpty() { + // See https://wiki.theory.org/BitTorrentSpecification#bitfield + + // Create message with 744 (= 93 * 8) pieces + BitSet availablePieces = new BitSet(); + PeerMessage.BitfieldMessage msg = PeerMessage.BitfieldMessage.craft(availablePieces, 744); + + // Check bitfield + assertEquals(0, msg.getBitfield().cardinality()); + + // Check raw data - bitfield: + ByteBuffer buffer = msg.getData(); + + // total size + assertEquals(4 + 1 + 93, buffer.remaining()); + + // len + assertEquals(0, buffer.get(0)); + assertEquals(0, buffer.get(1)); + assertEquals(0, buffer.get(2)); + assertEquals(1 + 93, (int)buffer.get(3)); + + // id + assertEquals(5, buffer.get(4)); + + // bitfield + buffer.position(5); + ByteBuffer bitfieldBuffer = buffer.slice(); + BitSet bitfield = convertByteBufferToBitfieldBitSet(bitfieldBuffer); + assertEquals(0, bitfield.cardinality()); + } + + @Test + public void testCreateBitfieldMessageWithSparseBits() { + // See https://wiki.theory.org/BitTorrentSpecification#bitfield + + // Create message with 745 (= 93 * 8 + 1) pieces + BitSet availablePieces = new BitSet(); + availablePieces.set(10); + availablePieces.set(700); + availablePieces.set(744); + availablePieces.set(745); // out of range - should be ignored + PeerMessage.BitfieldMessage msg = PeerMessage.BitfieldMessage.craft(availablePieces, 745); + + // Check bitfield + assertEquals(3, msg.getBitfield().cardinality()); + assertEquals(true, msg.getBitfield().get(10)); + assertEquals(true, msg.getBitfield().get(700)); + assertEquals(true, msg.getBitfield().get(744)); + + // Check raw data - bitfield: + ByteBuffer buffer = msg.getData(); + + // total size + assertEquals(4 + 1 + 94, buffer.remaining()); + + // len + assertEquals(0, buffer.get(0)); + assertEquals(0, buffer.get(1)); + assertEquals(0, buffer.get(2)); + assertEquals(1 + 94, (int)buffer.get(3)); + + // id + assertEquals(5, buffer.get(4)); + + // bitfield with 7 spare bits + buffer.position(5); + ByteBuffer bitfieldBuffer = buffer.slice(); + BitSet bitfield = convertByteBufferToBitfieldBitSet(bitfieldBuffer); + assertEquals(3, bitfield.cardinality()); + assertEquals(true, bitfield.get(10)); + assertEquals(true, bitfield.get(700)); + assertEquals(true, bitfield.get(744)); + } + + private BitSet convertByteBufferToBitfieldBitSet(ByteBuffer buffer) { + BitSet bitfield = new BitSet(); + for (int i=0; i < buffer.remaining()*8; i++) { + if ((buffer.get(i/8) & (1 << (7 -(i % 8)))) > 0) { + bitfield.set(i); + } + } + return bitfield; + } + +} From 7b18f1b588328a35ea040d50384d0e81b3955a76 Mon Sep 17 00:00:00 2001 From: Philipp Henkel Date: Sat, 16 Jan 2016 21:53:59 +0100 Subject: [PATCH 52/68] Allow explicit start of peer message exchange MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change fixes a race condition in peer message exchange. First incoming messages can be lost because incoming thread is started before it is possible to register a message listener. There is a high probability of missing the peer’s bitfield message immediately after handshake and as a consequence downloads will not resume. This fix is required to resolve issue #91 https://github.com/mpetazzoni/ttorrent/issues/91 --- .../ttorrent/client/peer/PeerExchange.java | 20 ++++++++++++++----- .../ttorrent/client/peer/SharingPeer.java | 3 ++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index 365cd8dda..7b7b27dcd 100644 --- a/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -120,10 +120,7 @@ public PeerExchange(SharingPeer peer, SharedTorrent torrent, this.peer.getShortHexPeerId() + ")-send"); this.out.setDaemon(true); - // Automatically start the exchange activity loops this.stop = false; - this.in.start(); - this.out.start(); logger.debug("Started peer exchange with {} for {}.", this.peer, this.torrent); @@ -135,6 +132,7 @@ public PeerExchange(SharingPeer peer, SharedTorrent torrent, } } + /** * Register a new message listener to receive messages. * @@ -172,13 +170,25 @@ public void send(PeerMessage message) { } /** - * Close and stop the peer exchange. + * Start the peer exchange. + * + *

+ * Starts both incoming and outgoing thread. + *

+ */ + public void start() { + this.in.start(); + this.out.start(); + } + + /** + * Stop the peer exchange. * *

* Closes the socket channel and stops both incoming and outgoing threads. *

*/ - public void close() { + public void stop() { this.stop = true; if (this.channel.isConnected()) { diff --git a/core/src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java b/core/src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java index c24244ec6..36e773425 100644 --- a/core/src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java +++ b/core/src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java @@ -270,6 +270,7 @@ public synchronized void bind(SocketChannel channel) throws SocketException { this.exchange = new PeerExchange(this, this.torrent, channel); this.exchange.register(this); + this.exchange.start(); this.download = new Rate(); this.download.reset(); @@ -308,7 +309,7 @@ public void unbind(boolean force) { synchronized (this.exchangeLock) { if (this.exchange != null) { - this.exchange.close(); + this.exchange.stop(); this.exchange = null; } } From 7fc52791f9e70af40c42893c523795eb4cf09ca0 Mon Sep 17 00:00:00 2001 From: Philipp Henkel Date: Tue, 19 Jan 2016 21:36:14 +0100 Subject: [PATCH 53/68] Remove dependency on Apache commons-codec Resolves: mpetazzoni/ttorrent#144 See also: mpetazzoni/ttorrent#146 --- core/pom.xml | 5 --- .../java/com/turn/ttorrent/client/Piece.java | 7 +++- .../turn/ttorrent/client/SharedTorrent.java | 15 ++++---- .../com/turn/ttorrent/common/Torrent.java | 38 ++++++++++--------- .../turn/ttorrent/tracker/TrackedTorrent.java | 7 ++-- 5 files changed, 39 insertions(+), 33 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 60eeb9738..c7f5254ba 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,11 +12,6 @@ jar - - commons-codec - commons-codec - 1.8 - commons-io commons-io diff --git a/core/src/main/java/com/turn/ttorrent/client/Piece.java b/core/src/main/java/com/turn/ttorrent/client/Piece.java index 5cd8534ac..36154020d 100644 --- a/core/src/main/java/com/turn/ttorrent/client/Piece.java +++ b/core/src/main/java/com/turn/ttorrent/client/Piece.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.concurrent.Callable; @@ -161,7 +162,11 @@ public synchronized boolean validate() throws IOException { ByteBuffer buffer = this._read(0, this.length); byte[] data = new byte[(int)this.length]; buffer.get(data); - this.valid = Arrays.equals(Torrent.hash(data), this.hash); + try { + this.valid = Arrays.equals(Torrent.hash(data), this.hash); + } catch (NoSuchAlgorithmException e) { + this.valid = false; + } return this.isValid(); } diff --git a/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index 7628d9ab3..70c7ab85c 100644 --- a/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -29,6 +29,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; import java.util.BitSet; import java.util.Collections; import java.util.LinkedList; @@ -113,7 +114,7 @@ public class SharedTorrent extends Torrent implements PeerActivityListener { * @throws IOException If the torrent file cannot be read or decoded. */ public SharedTorrent(Torrent torrent, File destDir) - throws FileNotFoundException, IOException { + throws FileNotFoundException, IOException, NoSuchAlgorithmException { this(torrent, destDir, false); } @@ -135,7 +136,7 @@ public SharedTorrent(Torrent torrent, File destDir) * @throws IOException If the torrent file cannot be read or decoded. */ public SharedTorrent(Torrent torrent, File destDir, boolean seeder) - throws FileNotFoundException, IOException { + throws FileNotFoundException, IOException, NoSuchAlgorithmException { this(torrent.getEncoded(), destDir, seeder, DEFAULT_REQUEST_STRATEGY); } @@ -159,7 +160,7 @@ public SharedTorrent(Torrent torrent, File destDir, boolean seeder) */ public SharedTorrent(Torrent torrent, File destDir, boolean seeder, RequestStrategy requestStrategy) - throws FileNotFoundException, IOException { + throws FileNotFoundException, IOException, NoSuchAlgorithmException { this(torrent.getEncoded(), destDir, seeder, requestStrategy); } @@ -174,7 +175,7 @@ public SharedTorrent(Torrent torrent, File destDir, boolean seeder, * @throws IOException If the torrent file cannot be read or decoded. */ public SharedTorrent(byte[] torrent, File destDir) - throws FileNotFoundException, IOException { + throws FileNotFoundException, IOException, NoSuchAlgorithmException { this(torrent, destDir, false); } @@ -190,7 +191,7 @@ public SharedTorrent(byte[] torrent, File destDir) * @throws IOException If the torrent file cannot be read or decoded. */ public SharedTorrent(byte[] torrent, File parent, boolean seeder) - throws FileNotFoundException, IOException { + throws FileNotFoundException, IOException, NoSuchAlgorithmException { this(torrent, parent, seeder, DEFAULT_REQUEST_STRATEGY); } @@ -208,7 +209,7 @@ public SharedTorrent(byte[] torrent, File parent, boolean seeder) */ public SharedTorrent(byte[] torrent, File parent, boolean seeder, RequestStrategy requestStrategy) - throws FileNotFoundException, IOException { + throws FileNotFoundException, IOException, NoSuchAlgorithmException { super(torrent, seeder); if (parent == null || !parent.isDirectory()) { @@ -273,7 +274,7 @@ public SharedTorrent(byte[] torrent, File parent, boolean seeder, * @throws IOException When the torrent file cannot be read or decoded. */ public static SharedTorrent fromFile(File source, File parent) - throws IOException { + throws IOException, NoSuchAlgorithmException { byte[] data = FileUtils.readFileToByteArray(source); return new SharedTorrent(data, parent); } diff --git a/core/src/main/java/com/turn/ttorrent/common/Torrent.java b/core/src/main/java/com/turn/ttorrent/common/Torrent.java index 5829aaff1..648927d7b 100644 --- a/core/src/main/java/com/turn/ttorrent/common/Torrent.java +++ b/core/src/main/java/com/turn/ttorrent/common/Torrent.java @@ -26,11 +26,13 @@ import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.math.BigInteger; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -47,8 +49,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -132,7 +132,7 @@ public TorrentFile(File file, long size) { * @throws IOException When the info dictionary can't be read or * encoded and hashed back to create the torrent's SHA-1 hash. */ - public Torrent(byte[] torrent, boolean seeder) throws IOException { + public Torrent(byte[] torrent, boolean seeder) throws IOException, NoSuchAlgorithmException { this.encoded = torrent; this.seeder = seeder; @@ -404,8 +404,12 @@ public void save(OutputStream output) throws IOException { output.write(this.getEncoded()); } - public static byte[] hash(byte[] data) { - return DigestUtils.sha1(data); + public static byte[] hash(byte[] data) throws NoSuchAlgorithmException { + MessageDigest crypt; + crypt = MessageDigest.getInstance("SHA-1"); + crypt.reset(); + crypt.update(data); + return crypt.digest(); } /** @@ -415,7 +419,7 @@ public static byte[] hash(byte[] data) { * @param bytes The byte array to convert. */ public static String byteArrayToHexString(byte[] bytes) { - return new String(Hex.encodeHex(bytes, false)); + return new BigInteger(1, bytes).toString(16); } /** @@ -475,7 +479,7 @@ protected static int getHashingThreadsCount() { * .torrent file to load. * @throws IOException When the torrent file cannot be read. */ - public static Torrent load(File torrent) throws IOException { + public static Torrent load(File torrent) throws IOException, NoSuchAlgorithmException { return Torrent.load(torrent, false); } @@ -489,7 +493,7 @@ public static Torrent load(File torrent) throws IOException { * @throws IOException When the torrent file cannot be read. */ public static Torrent load(File torrent, boolean seeder) - throws IOException { + throws IOException, NoSuchAlgorithmException { byte[] data = FileUtils.readFileToByteArray(torrent); return new Torrent(data, seeder); } @@ -511,7 +515,7 @@ public static Torrent load(File torrent, boolean seeder) * torrent's creator. */ public static Torrent create(File source, URI announce, String createdBy) - throws InterruptedException, IOException { + throws InterruptedException, IOException, NoSuchAlgorithmException { return Torrent.create(source, null, DEFAULT_PIECE_LENGTH, announce, null, createdBy); } @@ -534,7 +538,7 @@ public static Torrent create(File source, URI announce, String createdBy) * torrent's creator. */ public static Torrent create(File parent, List files, URI announce, - String createdBy) throws InterruptedException, IOException { + String createdBy) throws InterruptedException, IOException, NoSuchAlgorithmException { return Torrent.create(parent, files, DEFAULT_PIECE_LENGTH, announce, null, createdBy); } @@ -555,7 +559,7 @@ public static Torrent create(File parent, List files, URI announce, * torrent's creator. */ public static Torrent create(File source, int pieceLength, List> announceList, - String createdBy) throws InterruptedException, IOException { + String createdBy) throws InterruptedException, IOException, NoSuchAlgorithmException { return Torrent.create(source, null, pieceLength, null, announceList, createdBy); } @@ -580,7 +584,7 @@ public static Torrent create(File source, int pieceLength, List> annou */ public static Torrent create(File source, List files, int pieceLength, List> announceList, String createdBy) - throws InterruptedException, IOException { + throws InterruptedException, IOException, NoSuchAlgorithmException { return Torrent.create(source, files, pieceLength, null, announceList, createdBy); } @@ -606,7 +610,7 @@ public static Torrent create(File source, List files, int pieceLength, */ private static Torrent create(File parent, List files, int pieceLength, URI announce, List> announceList, String createdBy) - throws InterruptedException, IOException { + throws InterruptedException, IOException, NoSuchAlgorithmException { if (files == null || files.isEmpty()) { logger.info("Creating single-file torrent for {}...", parent.getName()); @@ -683,8 +687,8 @@ private static class CallableChunkHasher implements Callable { private final MessageDigest md; private final ByteBuffer data; - CallableChunkHasher(ByteBuffer buffer) { - this.md = DigestUtils.getSha1Digest(); + CallableChunkHasher(ByteBuffer buffer) throws NoSuchAlgorithmException { + this.md = MessageDigest.getInstance("SHA-1"); this.data = ByteBuffer.allocate(buffer.remaining()); buffer.mark(); @@ -717,12 +721,12 @@ public String call() throws UnsupportedEncodingException { * @param file The file to hash. */ private static String hashFile(File file, int pieceLenght) - throws InterruptedException, IOException { + throws InterruptedException, IOException, NoSuchAlgorithmException { return Torrent.hashFiles(Arrays.asList(new File[] { file }), pieceLenght); } private static String hashFiles(List files, int pieceLenght) - throws InterruptedException, IOException { + throws InterruptedException, IOException, NoSuchAlgorithmException { int threads = getHashingThreadsCount(); ExecutorService executor = Executors.newFixedThreadPool(threads); ByteBuffer buffer = ByteBuffer.allocate(pieceLenght); diff --git a/core/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java b/core/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java index 3e8e500d6..35125b875 100644 --- a/core/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java +++ b/core/src/main/java/com/turn/ttorrent/tracker/TrackedTorrent.java @@ -24,6 +24,7 @@ import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -77,7 +78,7 @@ public class TrackedTorrent extends Torrent { * @throws IOException When the info dictionary can't be * encoded and hashed back to create the torrent's SHA-1 hash. */ - public TrackedTorrent(byte[] torrent) throws IOException { + public TrackedTorrent(byte[] torrent) throws IOException, NoSuchAlgorithmException { super(torrent, false); this.peers = new ConcurrentHashMap(); @@ -85,7 +86,7 @@ public TrackedTorrent(byte[] torrent) throws IOException { this.announceInterval = TrackedTorrent.DEFAULT_ANNOUNCE_INTERVAL_SECONDS; } - public TrackedTorrent(Torrent torrent) throws IOException { + public TrackedTorrent(Torrent torrent) throws IOException, NoSuchAlgorithmException { this(torrent.getEncoded()); } @@ -289,7 +290,7 @@ public List getSomePeers(TrackedPeer peer) { * .torrent file to load. * @throws IOException When the torrent file cannot be read. */ - public static TrackedTorrent load(File torrent) throws IOException { + public static TrackedTorrent load(File torrent) throws IOException, NoSuchAlgorithmException { byte[] data = FileUtils.readFileToByteArray(torrent); return new TrackedTorrent(data); } From e228a0d5534ed85bd394cbce430ed25308cdb023 Mon Sep 17 00:00:00 2001 From: Philipp Henkel Date: Fri, 29 Jan 2016 20:55:46 +0100 Subject: [PATCH 54/68] Allow immedidate shutdown of peerexchange by notifying out-going thread --- .../turn/ttorrent/client/peer/PeerExchange.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index 7b7b27dcd..29fa62c47 100644 --- a/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -78,6 +78,7 @@ class PeerExchange { LoggerFactory.getLogger(PeerExchange.class); private static final int KEEP_ALIVE_IDLE_MINUTES = 2; + private static final PeerMessage STOP = PeerMessage.KeepAliveMessage.craft(); private SharingPeer peer; private SharedTorrent torrent; @@ -191,6 +192,13 @@ public void start() { public void stop() { this.stop = true; + try { + // Wake-up and shutdown out-going thread immediately + this.sendQueue.put(STOP); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + if (this.channel.isConnected()) { IOUtils.closeQuietly(this.channel); } @@ -402,11 +410,11 @@ public void run() { PeerExchange.KEEP_ALIVE_IDLE_MINUTES, TimeUnit.MINUTES); - if (message == null) { - if (stop) { - return; - } + if (message == STOP) { + return; + } + if (message == null) { message = PeerMessage.KeepAliveMessage.craft(); } From ede03918ea6fe06171db27f826e7572e4ba5b5fa Mon Sep 17 00:00:00 2001 From: Philipp Henkel Date: Wed, 2 Mar 2016 23:14:55 +0100 Subject: [PATCH 55/68] Change the length of an existing storage file only if required This change prevents the modification of a storage file in case the file is already complete and all pieces are available. --- .../com/turn/ttorrent/client/storage/FileStorage.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/turn/ttorrent/client/storage/FileStorage.java b/core/src/main/java/com/turn/ttorrent/client/storage/FileStorage.java index a47f053cb..05e8207bf 100644 --- a/core/src/main/java/com/turn/ttorrent/client/storage/FileStorage.java +++ b/core/src/main/java/com/turn/ttorrent/client/storage/FileStorage.java @@ -80,9 +80,11 @@ public FileStorage(File file, long offset, long size) this.raf = new RandomAccessFile(this.current, "rw"); - // Set the file length to the appropriate size, eventually truncating - // or extending the file if it already exists with a different size. - this.raf.setLength(this.size); + if (file.length() != this.size) { + // Set the file length to the appropriate size, eventually truncating + // or extending the file if it already exists with a different size. + this.raf.setLength(this.size); + } this.channel = raf.getChannel(); logger.info("Initialized byte storage file at {} " + From 760bd1b8cf13ade0b1098dd881f40a0fe6bd0732 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Tue, 8 Mar 2016 17:29:03 -0800 Subject: [PATCH 56/68] Check for selector key validity before trying to use it. Fixes #169 Signed-off-by: Maxime Petazzoni --- .../main/java/com/turn/ttorrent/client/peer/PeerExchange.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index 29fa62c47..12b94d73a 100644 --- a/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -299,7 +299,7 @@ private long read(Selector selector, ByteBuffer buffer) throws IOException { Iterator it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey key = (SelectionKey) it.next(); - if (key.isReadable()) { + if (key.isValid() && key.isReadable()) { int read = ((SocketChannel) key.channel()).read(buffer); if (read < 0) { throw new IOException("Unexpected end-of-stream while reading"); From a397f130a67094e27aba1d05d61ae38f69a006b7 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Tue, 8 Mar 2016 17:36:47 -0800 Subject: [PATCH 57/68] Include documentation about Client observable. Fixes #165 Signed-off-by: Maxime Petazzoni --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 8319e1894..52726a158 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,23 @@ tracker.start(); tracker.stop(); ``` +### Track download progress + +You can track the progress of the download and the state of the torrent +by registering an `Observer` on your `Client` instance. The observer is +updated every time a piece of the download completes: + +```java +client.addObserver(new Observer() { + @Override + public void update(Observable observable, Object data) { + Client client = (Client) observable; + float progress = client.getTorrent().getCompletion(); + // Do something with progress. + } +}); +``` + License ------- @@ -173,6 +190,7 @@ Authors and contributors * Alexey Ptashniy Fixed an integer overflow in the calculation of a torrent's full size. +And many other helpful contributors on GitHub! Thanks to all of you. Caveats ------- From 7c8b4076dce0becd9d358a0932852d4f0080adcc Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Tue, 8 Mar 2016 18:19:17 -0800 Subject: [PATCH 58/68] Javadoc markup fixes and release prep Signed-off-by: Maxime Petazzoni --- .gitignore | 6 ++++-- .../java/com/turn/ttorrent/client/Client.java | 4 ++-- .../ttorrent/client/ConnectionHandler.java | 3 +-- .../turn/ttorrent/client/SharedTorrent.java | 4 ++-- .../ttorrent/client/peer/SharingPeer.java | 4 ++-- .../ttorrent/common/protocol/PeerMessage.java | 20 +++++++++---------- .../com/turn/ttorrent/tracker/Tracker.java | 4 ++-- 7 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 3a8962f90..6be42962f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,12 @@ # Ignore build output /build/* +/target/* /release.properties # Ignore Javadoc output -/doc/* +cli/doc/* +core/doc/* # Ignore any eventual Eclipse project files, these don't belong in the # repository. @@ -18,4 +20,4 @@ *.swp *.bak *~ - +pom.xml.releaseBackup diff --git a/core/src/main/java/com/turn/ttorrent/client/Client.java b/core/src/main/java/com/turn/ttorrent/client/Client.java index 653598016..c4380ca66 100644 --- a/core/src/main/java/com/turn/ttorrent/client/Client.java +++ b/core/src/main/java/com/turn/ttorrent/client/Client.java @@ -159,7 +159,7 @@ public Client(InetAddress address, SharedTorrent torrent) /** * Set the maximum download rate (in kB/second) for this - * torrent. A setting of <= 0.0 disables rate limiting. + * torrent. A setting of <= 0.0 disables rate limiting. * * @param rate The maximum download rate */ @@ -169,7 +169,7 @@ public void setMaxDownloadRate(double rate) { /** * Set the maximum upload rate (in kB/second) for this - * torrent. A setting of <= 0.0 disables rate limiting. + * torrent. A setting of <= 0.0 disables rate limiting. * * @param rate The maximum upload rate */ diff --git a/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java b/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java index f15d71f53..3042eb6aa 100644 --- a/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java +++ b/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java @@ -67,8 +67,7 @@ * *

* This class does nothing more. All further peer-to-peer communication happens - * in the {@link com.turn.ttorrent.client.peer.PeerExchange PeerExchange} - * class. + * in the PeerExchange class. *

* * @author mpetazzoni diff --git a/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index 70c7ab85c..aec96d517 100644 --- a/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/core/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -285,7 +285,7 @@ public double getMaxUploadRate() { /** * Set the maximum upload rate (in kB/second) for this - * torrent. A setting of <= 0.0 disables rate limiting. + * torrent. A setting of <= 0.0 disables rate limiting. * * @param rate The maximum upload rate */ @@ -299,7 +299,7 @@ public double getMaxDownloadRate() { /** * Set the maximum download rate (in kB/second) for this - * torrent. A setting of <= 0.0 disables rate limiting. + * torrent. A setting of <= 0.0 disables rate limiting. * * @param rate The maximum download rate */ diff --git a/core/src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java b/core/src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java index 36e773425..e0fb53c01 100644 --- a/core/src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java +++ b/core/src/main/java/com/turn/ttorrent/client/peer/SharingPeer.java @@ -748,7 +748,7 @@ private void fireIOException(IOException ioe) { *

* * @author mpetazzoni - * @see Rate.RateComparator + * @see Rate#RATE_COMPARATOR */ public static class DLRateComparator implements Comparator, Serializable { @@ -769,7 +769,7 @@ public int compare(SharingPeer a, SharingPeer b) { *

* * @author mpetazzoni - * @see Rate.RateComparator + * @see Rate#RATE_COMPARATOR */ public static class ULRateComparator implements Comparator, Serializable { diff --git a/core/src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java index 66a1f66bb..2c7075ca3 100644 --- a/core/src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java +++ b/core/src/main/java/com/turn/ttorrent/common/protocol/PeerMessage.java @@ -198,7 +198,7 @@ public MessageValidationException(PeerMessage m) { /** * Keep alive message. * - * + * <len=0000> */ public static class KeepAliveMessage extends PeerMessage { @@ -225,7 +225,7 @@ public static KeepAliveMessage craft() { /** * Choke message. * - * + * <len=0001><id=0> */ public static class ChokeMessage extends PeerMessage { @@ -253,7 +253,7 @@ public static ChokeMessage craft() { /** * Unchoke message. * - * + * <len=0001><id=1> */ public static class UnchokeMessage extends PeerMessage { @@ -281,7 +281,7 @@ public static UnchokeMessage craft() { /** * Interested message. * - * + * <len=0001<>id=2> */ public static class InterestedMessage extends PeerMessage { @@ -309,7 +309,7 @@ public static InterestedMessage craft() { /** * Not interested message. * - * + * <len=0001><id=3> */ public static class NotInterestedMessage extends PeerMessage { @@ -337,7 +337,7 @@ public static NotInterestedMessage craft() { /** * Have message. * - * + * <len=0005><id=4><piece index=xxxx> */ public static class HaveMessage extends PeerMessage { @@ -387,7 +387,7 @@ public String toString() { /** * Bitfield message. * - * + * <len=0001+X><id=5><bitfield> */ public static class BitfieldMessage extends PeerMessage { @@ -456,7 +456,7 @@ public String toString() { /** * Request message. * - * + * <len=00013><id=6><piece index><block offset><block length> */ public static class RequestMessage extends PeerMessage { @@ -533,7 +533,7 @@ public String toString() { /** * Piece message. * - * + * <len=0009+X><id=7><piece index><block offset><block data> */ public static class PieceMessage extends PeerMessage { @@ -605,7 +605,7 @@ public String toString() { /** * Cancel message. * - * + * <len=00013><id=8><piece index><block offset><block length> */ public static class CancelMessage extends PeerMessage { diff --git a/core/src/main/java/com/turn/ttorrent/tracker/Tracker.java b/core/src/main/java/com/turn/ttorrent/tracker/Tracker.java index 638fae259..84bf03e04 100644 --- a/core/src/main/java/com/turn/ttorrent/tracker/Tracker.java +++ b/core/src/main/java/com/turn/ttorrent/tracker/Tracker.java @@ -38,8 +38,8 @@ * *

* The tracker usually listens on port 6969 (the standard BitTorrent tracker - * port). Torrents must be registered directly to this tracker with the - * {@link #announce(TrackedTorrent torrent)} method. + * port). Torrents must be registered directly to this tracker with the {@link + * #announce(TrackedTorrent torrent)} method. *

* * @author mpetazzoni From 7dcb78de1cc80e333b6f013aba3025f95a973bf1 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Tue, 8 Mar 2016 18:26:59 -0800 Subject: [PATCH 59/68] Document usage of dependency version 1.5. Fixes #127 In preparation of incoming 1.5 release, with the cli/core split. Signed-off-by: Maxime Petazzoni --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 52726a158..97e60d35e 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,8 @@ section: ... com.turn - ttorrent - 1.4 + ttorrent-core + 1.5
``` From 4b9cafa74dc3781db63a5c184be635c5190b5008 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Tue, 8 Mar 2016 18:54:36 -0800 Subject: [PATCH 60/68] Prepare for 1.5 release --- cli/pom.xml | 4 +-- core/pom.xml | 14 ++++----- pom.xml | 83 ++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 77 insertions(+), 24 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 166670756..5e5bf8c2a 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -4,7 +4,7 @@ com.turn ttorrent - 1.5-SNAPSHOT + 1.5 Java BitTorrent library CLI @@ -15,7 +15,7 @@ com.turn ttorrent-core - 1.5-SNAPSHOT + 1.5 diff --git a/core/pom.xml b/core/pom.xml index c7f5254ba..5b0e056ef 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ com.turn ttorrent - 1.5-SNAPSHOT + 1.5 Java BitTorrent library core @@ -27,11 +27,11 @@ slf4j-api 1.6.4 - - org.testng - testng - 6.1.1 - test - + + org.testng + testng + 6.1.1 + test + diff --git a/pom.xml b/pom.xml index 445d945b5..b1b945c8c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,13 +1,6 @@ 4.0.0 - - org.sonatype.oss - oss-parent - 7 - - - Java BitTorrent library ttorrent is a pure-Java implementation of the BitTorrent protocol, @@ -17,18 +10,18 @@ http://mpetazzoni.github.io/ttorrent/ com.turn ttorrent - 1.5-SNAPSHOT + 1.5 pom - - core - cli - + + core + cli + scm:git:git://github.com/mpetazzoni/ttorrent.git scm:git:ssh://git@github.com/mpetazzoni/ttorrent.git - http://github.com/mpetazzoni/ttorrent + https://github.com/mpetazzoni/ttorrent master @@ -50,8 +43,8 @@ Maxime Petazzoni maxime.petazzoni@bulix.org http://www.bulix.org - SignalFuse, Inc - http://www.signalfuse.com + SignalFx, Inc + http://www.signalfx.com maintainer architect @@ -68,6 +61,17 @@ UTF-8 + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + jboss-thirdparty-releases @@ -88,6 +92,20 @@
+ + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + org.apache.maven.plugins maven-javadoc-plugin @@ -96,6 +114,29 @@ ${basedir} doc + + + attach-javadocs + + jar + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + @@ -103,6 +144,18 @@ maven-release-plugin 2.4.2 + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.3 + true + + ossrh + https://oss.sonatype.org/ + true + + From 081bab49f7928679217d4fd937456f69b6ab7da2 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Tue, 8 Mar 2016 19:04:47 -0800 Subject: [PATCH 61/68] Move version forward to 1.6-SNAPSHOT Signed-off-by: Maxime Petazzoni --- cli/pom.xml | 4 ++-- core/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 5e5bf8c2a..0f97f7b7d 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -4,7 +4,7 @@ com.turn ttorrent - 1.5 + 1.6-SNAPSHOT Java BitTorrent library CLI @@ -15,7 +15,7 @@ com.turn ttorrent-core - 1.5 + 1.6-SNAPSHOT diff --git a/core/pom.xml b/core/pom.xml index 5b0e056ef..a86c7531e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ com.turn ttorrent - 1.5 + 1.6-SNAPSHOT Java BitTorrent library core diff --git a/pom.xml b/pom.xml index b1b945c8c..da75c0813 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ http://mpetazzoni.github.io/ttorrent/ com.turn ttorrent - 1.5 + 1.6-SNAPSHOT pom From e9e9773d7b5f5432f194efb08a7ad43dd3afee4f Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Tue, 8 Mar 2016 19:10:10 -0800 Subject: [PATCH 62/68] Extra Maven build plugins specific to release in a release profile Signed-off-by: Maxime Petazzoni --- pom.xml | 141 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 75 insertions(+), 66 deletions(-) diff --git a/pom.xml b/pom.xml index da75c0813..d36cbc66a 100644 --- a/pom.xml +++ b/pom.xml @@ -80,6 +80,62 @@ + + + release + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.4.2 + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.3 + true + + ossrh + https://oss.sonatype.org/ + true + + + + + + + @@ -92,70 +148,23 @@ - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.8.1 - - ${basedir} - doc - - - - attach-javadocs - - jar - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - - - - org.apache.maven.plugins - maven-release-plugin - 2.4.2 - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.3 - true - - ossrh - https://oss.sonatype.org/ - true - - - - + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8.1 + + ${basedir} + doc + + + + attach-javadocs + + jar + + + + + + From 4471694ea826b2982750b87cb653e93d60ff1c1d Mon Sep 17 00:00:00 2001 From: Philipp Henkel Date: Sat, 12 Mar 2016 21:44:56 +0100 Subject: [PATCH 63/68] Fix byteArrayToHexString and add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old implementation of byteArrayToHexString drops the byte array’s leading zeros. --- .../ttorrent/client/ConnectionHandler.java | 9 +-- .../java/com/turn/ttorrent/common/Peer.java | 2 +- .../com/turn/ttorrent/common/Torrent.java | 14 +--- .../java/com/turn/ttorrent/common/Utils.java | 41 +++++++++++ .../http/HTTPAnnounceRequestMessage.java | 3 +- .../udp/UDPAnnounceRequestMessage.java | 5 +- .../com/turn/ttorrent/common/UtilsTest.java | 70 +++++++++++++++++++ 7 files changed, 124 insertions(+), 20 deletions(-) create mode 100644 core/src/main/java/com/turn/ttorrent/common/Utils.java create mode 100644 core/src/test/java/com/turn/ttorrent/common/UtilsTest.java diff --git a/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java b/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java index f15d71f53..e84fadc12 100644 --- a/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java +++ b/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java @@ -17,6 +17,7 @@ import com.turn.ttorrent.common.Torrent; import com.turn.ttorrent.client.peer.SharingPeer; +import com.turn.ttorrent.common.Utils; import java.io.IOException; import java.net.InetAddress; @@ -392,15 +393,15 @@ private Handshake validateHandshake(SocketChannel channel, byte[] peerId) Handshake hs = Handshake.parse(data); if (!Arrays.equals(hs.getInfoHash(), this.torrent.getInfoHash())) { throw new ParseException("Handshake for unknow torrent " + - Torrent.byteArrayToHexString(hs.getInfoHash()) + + Utils.bytesToHex(hs.getInfoHash()) + " from " + this.socketRepr(channel) + ".", pstrlen + 9); } if (peerId != null && !Arrays.equals(hs.getPeerId(), peerId)) { throw new ParseException("Announced peer ID " + - Torrent.byteArrayToHexString(hs.getPeerId()) + + Utils.bytesToHex(hs.getPeerId()) + " did not match expected peer ID " + - Torrent.byteArrayToHexString(peerId) + ".", pstrlen + 29); + Utils.bytesToHex(peerId) + ".", pstrlen + 29); } return hs; @@ -501,7 +502,7 @@ public void run() { ? this.peer.getPeerId().array() : null)); logger.info("Handshaked with {}, peer ID is {}.", - this.peer, Torrent.byteArrayToHexString(hs.getPeerId())); + this.peer, Utils.bytesToHex(hs.getPeerId())); // Go to non-blocking mode for peer interaction channel.configureBlocking(false); diff --git a/core/src/main/java/com/turn/ttorrent/common/Peer.java b/core/src/main/java/com/turn/ttorrent/common/Peer.java index 86af9fcf0..38745d897 100644 --- a/core/src/main/java/com/turn/ttorrent/common/Peer.java +++ b/core/src/main/java/com/turn/ttorrent/common/Peer.java @@ -107,7 +107,7 @@ public ByteBuffer getPeerId() { public void setPeerId(ByteBuffer peerId) { if (peerId != null) { this.peerId = peerId; - this.hexPeerId = Torrent.byteArrayToHexString(peerId.array()); + this.hexPeerId = Utils.bytesToHex(peerId.array()); } else { this.peerId = null; this.hexPeerId = null; diff --git a/core/src/main/java/com/turn/ttorrent/common/Torrent.java b/core/src/main/java/com/turn/ttorrent/common/Torrent.java index 648927d7b..2b3af8812 100644 --- a/core/src/main/java/com/turn/ttorrent/common/Torrent.java +++ b/core/src/main/java/com/turn/ttorrent/common/Torrent.java @@ -144,7 +144,7 @@ public Torrent(byte[] torrent, boolean seeder) throws IOException, NoSuchAlgorit BEncoder.bencode(this.decoded_info, baos); this.encoded_info = baos.toByteArray(); this.info_hash = Torrent.hash(this.encoded_info); - this.hex_info_hash = Torrent.byteArrayToHexString(this.info_hash); + this.hex_info_hash = Utils.bytesToHex(this.info_hash); /** * Parses the announce information from the decoded meta-info @@ -412,16 +412,6 @@ public static byte[] hash(byte[] data) throws NoSuchAlgorithmException { return crypt.digest(); } - /** - * Convert a byte string to a string containing an hexadecimal - * representation of the original data. - * - * @param bytes The byte array to convert. - */ - public static String byteArrayToHexString(byte[] bytes) { - return new BigInteger(1, bytes).toString(16); - } - /** * Return an hexadecimal representation of the bytes contained in the * given string, following the default, expected byte encoding. @@ -431,7 +421,7 @@ public static String byteArrayToHexString(byte[] bytes) { public static String toHexString(String input) { try { byte[] bytes = input.getBytes(Torrent.BYTE_ENCODING); - return Torrent.byteArrayToHexString(bytes); + return Utils.bytesToHex(bytes); } catch (UnsupportedEncodingException uee) { return null; } diff --git a/core/src/main/java/com/turn/ttorrent/common/Utils.java b/core/src/main/java/com/turn/ttorrent/common/Utils.java new file mode 100644 index 000000000..5ba60b878 --- /dev/null +++ b/core/src/main/java/com/turn/ttorrent/common/Utils.java @@ -0,0 +1,41 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.turn.ttorrent.common; + +public class Utils { + + private final static char[] HEX_SYMBOLS = "0123456789ABCDEF".toCharArray(); + + private Utils() { + } + + /** + * Convert a byte string to a string containing the hexadecimal + * representation of the original data. + * + * @param bytes The byte array to convert. + * @see http://stackoverflow.com/questions/332079/a> + */ + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_SYMBOLS[v >>> 4]; + hexChars[j * 2 + 1] = HEX_SYMBOLS[v & 0x0F]; + } + return new String(hexChars); + } + +} diff --git a/core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java index 3645573d0..5524afd43 100644 --- a/core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java +++ b/core/src/main/java/com/turn/ttorrent/common/protocol/http/HTTPAnnounceRequestMessage.java @@ -21,6 +21,7 @@ import com.turn.ttorrent.bcodec.InvalidBEncodingException; import com.turn.ttorrent.common.Peer; import com.turn.ttorrent.common.Torrent; +import com.turn.ttorrent.common.Utils; import com.turn.ttorrent.common.protocol.TrackerMessage.AnnounceRequestMessage; import java.io.IOException; @@ -81,7 +82,7 @@ public byte[] getInfoHash() { @Override public String getHexInfoHash() { - return Torrent.byteArrayToHexString(this.infoHash); + return Utils.bytesToHex(this.infoHash); } @Override diff --git a/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java index 82b9f8e60..b6e019a83 100644 --- a/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java +++ b/core/src/main/java/com/turn/ttorrent/common/protocol/udp/UDPAnnounceRequestMessage.java @@ -16,6 +16,7 @@ package com.turn.ttorrent.common.protocol.udp; import com.turn.ttorrent.common.Torrent; +import com.turn.ttorrent.common.Utils; import com.turn.ttorrent.common.protocol.TrackerMessage; import java.net.InetAddress; @@ -89,7 +90,7 @@ public byte[] getInfoHash() { @Override public String getHexInfoHash() { - return Torrent.byteArrayToHexString(this.infoHash); + return Utils.bytesToHex(this.infoHash); } @Override @@ -99,7 +100,7 @@ public byte[] getPeerId() { @Override public String getHexPeerId() { - return Torrent.byteArrayToHexString(this.peerId); + return Utils.bytesToHex(this.peerId); } @Override diff --git a/core/src/test/java/com/turn/ttorrent/common/UtilsTest.java b/core/src/test/java/com/turn/ttorrent/common/UtilsTest.java new file mode 100644 index 000000000..5916c34b0 --- /dev/null +++ b/core/src/test/java/com/turn/ttorrent/common/UtilsTest.java @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2016 Philipp Henkel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package com.turn.ttorrent.common; + +import org.testng.annotations.Test; + +import static org.testng.AssertJUnit.assertEquals; + + +public class UtilsTest { + + @Test(expectedExceptions = NullPointerException.class) + public void testBytesToHexWithNull() { + Utils.bytesToHex(null); + } + + @Test + public void testBytesToHexWithEmptyByteArray() { + assertEquals("", Utils.bytesToHex(new byte[0])); + } + + @Test + public void testBytesToHexWithSingleByte() { + assertEquals("BC", Utils.bytesToHex(new byte[]{ + (byte) 0xBC + })); + } + + @Test + public void testBytesToHexWithZeroByte() { + assertEquals("00", Utils.bytesToHex(new byte[1])); + } + + @Test + public void testBytesToHexWithLeadingZero() { + assertEquals("0053FF", Utils.bytesToHex(new byte[]{ + (byte) 0x00, (byte) 0x53, (byte) 0xFF + })); + } + + @Test + public void testBytesToHexTrailingZero() { + assertEquals("AA004500", Utils.bytesToHex(new byte[]{ + (byte) 0xAA, (byte) 0x00, (byte) 0x45, (byte) 0x00 + })); + } + + @Test + public void testBytesToHexAllSymbols() { + assertEquals("0123456789ABCDEF", Utils.bytesToHex(new byte[]{ + (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67, + (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF + })); + } + +} From 5d4a14915cf930bc5eb6a82cccff32b8bc70a174 Mon Sep 17 00:00:00 2001 From: bwzhou Date: Fri, 8 Apr 2016 16:53:36 -0700 Subject: [PATCH 64/68] Unchoke the random peer in optimistic unchoking --- core/src/main/java/com/turn/ttorrent/client/Client.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/turn/ttorrent/client/Client.java b/core/src/main/java/com/turn/ttorrent/client/Client.java index c4380ca66..f300bd1b6 100644 --- a/core/src/main/java/com/turn/ttorrent/client/Client.java +++ b/core/src/main/java/com/turn/ttorrent/client/Client.java @@ -642,6 +642,7 @@ private synchronized void unchokePeers(boolean optimistic) { for (SharingPeer peer : choked) { if (optimistic && peer == randomPeer) { logger.debug("Optimistic unchoke of {}.", peer); + peer.unchoke(); continue; } From 4aca5acd7c953191365da0b9baa53d6eefd27519 Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Fri, 17 Jun 2016 20:16:49 -0700 Subject: [PATCH 65/68] Fix indent in pom.xml --- pom.xml | 170 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/pom.xml b/pom.xml index d36cbc66a..606ea9383 100644 --- a/pom.xml +++ b/pom.xml @@ -1,84 +1,84 @@ - 4.0.0 + 4.0.0 - Java BitTorrent library - - ttorrent is a pure-Java implementation of the BitTorrent protocol, - including support for several BEPs. It also provides a standalone client, - a tracker and a torrent manipulation utility. - - http://mpetazzoni.github.io/ttorrent/ - com.turn - ttorrent - 1.6-SNAPSHOT - pom + Java BitTorrent library + + ttorrent is a pure-Java implementation of the BitTorrent protocol, + including support for several BEPs. It also provides a standalone client, + a tracker and a torrent manipulation utility. + + http://mpetazzoni.github.io/ttorrent/ + com.turn + ttorrent + 1.6-SNAPSHOT + pom - - core - cli - + + core + cli + - - scm:git:git://github.com/mpetazzoni/ttorrent.git - scm:git:ssh://git@github.com/mpetazzoni/ttorrent.git - https://github.com/mpetazzoni/ttorrent - master - + + scm:git:git://github.com/mpetazzoni/ttorrent.git + scm:git:ssh://git@github.com/mpetazzoni/ttorrent.git + https://github.com/mpetazzoni/ttorrent + master + - - - Apache Software License version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - + + + Apache Software License version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + - - GitHub - https://github.com/mpetazzoni/ttorrent/issues - + + GitHub + https://github.com/mpetazzoni/ttorrent/issues + - - - mpetazzoni - Maxime Petazzoni - maxime.petazzoni@bulix.org - http://www.bulix.org - SignalFx, Inc - http://www.signalfx.com - - maintainer - architect - developer - - -8 - - https://secure.gravatar.com/avatar/6f705e0c299bca294444de3a6a3308b3 - - - + + + mpetazzoni + Maxime Petazzoni + maxime.petazzoni@bulix.org + http://www.bulix.org + SignalFx, Inc + http://www.signalfx.com + + maintainer + architect + developer + + -8 + + https://secure.gravatar.com/avatar/6f705e0c299bca294444de3a6a3308b3 + + + - - UTF-8 - + + UTF-8 + - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + - - - jboss-thirdparty-releases - JBoss Thirdparty Releases - https://repository.jboss.org/nexus/content/repositories/thirdparty-releases/ - - + + + jboss-thirdparty-releases + JBoss Thirdparty Releases + https://repository.jboss.org/nexus/content/repositories/thirdparty-releases/ + + @@ -132,21 +132,21 @@ - - - + + + - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - - + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + org.apache.maven.plugins From 4a8e2a6b146f459fc396627d4bc030a55e51724e Mon Sep 17 00:00:00 2001 From: Maxime Petazzoni Date: Sat, 18 Jun 2016 14:41:58 -0700 Subject: [PATCH 66/68] Fix Javadoc link in common.Utils --- core/src/main/java/com/turn/ttorrent/common/Utils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/turn/ttorrent/common/Utils.java b/core/src/main/java/com/turn/ttorrent/common/Utils.java index 5ba60b878..25ac88e6e 100644 --- a/core/src/main/java/com/turn/ttorrent/common/Utils.java +++ b/core/src/main/java/com/turn/ttorrent/common/Utils.java @@ -26,7 +26,7 @@ private Utils() { * representation of the original data. * * @param bytes The byte array to convert. - * @see http://stackoverflow.com/questions/332079/a> + * @see http://stackoverflow.com/questions/332079 */ public static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; From fce1fc7990ee06b0b3968b7682166d0187cc78c7 Mon Sep 17 00:00:00 2001 From: bwzhou Date: Thu, 30 Jun 2016 18:03:45 -0700 Subject: [PATCH 67/68] Fixed a BufferUnderflowException in message parsing when PeerExchange is closing --- .../java/com/turn/ttorrent/client/peer/PeerExchange.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java b/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java index 12b94d73a..fab7a5fa5 100644 --- a/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java +++ b/core/src/main/java/com/turn/ttorrent/client/peer/PeerExchange.java @@ -350,7 +350,14 @@ public void run() { } buffer.rewind(); - + + if (stop) { + // The buffer may contain the type from the last message + // if we were stopped before reading the payload and cause + // BufferUnderflowException in parsing. + break; + } + try { PeerMessage message = PeerMessage.parse(buffer, torrent); logger.trace("Received {} from {}", message, peer); From 9962183275e7cd8a9d9708ba746f7318c4ba0b35 Mon Sep 17 00:00:00 2001 From: Rafael Zanella Date: Wed, 5 Oct 2016 11:00:58 -0300 Subject: [PATCH 68/68] Closing channel unable to be used but already given fd by OS --- .../main/java/com/turn/ttorrent/client/ConnectionHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java b/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java index 8eca68578..23b62f003 100644 --- a/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java +++ b/core/src/main/java/com/turn/ttorrent/client/ConnectionHandler.java @@ -134,6 +134,8 @@ public class ConnectionHandler implements Runnable { } catch (IOException ioe) { // Ignore, try next port logger.warn("Could not bind to {}, trying next port...", tryAddress); + + this.channel.close(); } }