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 35a4b23dca0623e22db0305d987519776348083c Mon Sep 17 00:00:00 2001 From: Rafael Zanella Date: Thu, 29 Sep 2016 15:52:33 -0300 Subject: [PATCH 68/68] Make it "clearer" that the target file might "not be there" The torrent finished state happens after the download completion, but eventually, if the target file has to be used right away you have to "finish" it manually --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 97e60d35e..1d6e518a9 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,9 @@ client.download(); client.waitForCompletion(); // At any time you can call client.stop() to interrupt the download. + +// If you plan on using the downloaded file(s) right away uncomment the line below +//client.getTorrent().finish(); ``` #### Tracker code