From 82fb65193f3f685e9eb7e52f5cad0832e2f32b71 Mon Sep 17 00:00:00 2001 From: Dmitry Panin Date: Wed, 10 Apr 2013 12:37:53 +0400 Subject: [PATCH 01/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] [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/51] [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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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/51] 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 3b2e21f5138cfb60bf0e87e859e401899b67dfa8 Mon Sep 17 00:00:00 2001 From: pierre_chatelier Date: Wed, 28 Oct 2015 22:26:39 +0100 Subject: [PATCH 50/51] adding headers in http request to avoid bot protection in the tracker --- .../com/turn/ttorrent/client/announce/HTTPTrackerClient.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java b/core/src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java index ae338afdf..8c1a54b65 100644 --- a/core/src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java +++ b/core/src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java @@ -44,6 +44,8 @@ public class HTTPTrackerClient extends TrackerClient { protected static final Logger logger = LoggerFactory.getLogger(HTTPTrackerClient.class); + private final static String USER_AGENT = "Mozilla/5.0"; + private final static String USER_AGENT_HEADER = "User-Agent"; /** * Create a new HTTP announcer for the given torrent. @@ -105,6 +107,7 @@ public void announce(AnnounceRequestMessage.RequestEvent event, InputStream in = null; try { conn = (HttpURLConnection)target.openConnection(); + conn.setRequestProperty(USER_AGENT_HEADER,USER_AGENT); in = conn.getInputStream(); } catch (IOException ioe) { if (conn != null) { From e055ca14278967050d7efdd36cc3cabe66da93ad Mon Sep 17 00:00:00 2001 From: pierre_chatelier Date: Wed, 28 Oct 2015 22:30:38 +0100 Subject: [PATCH 51/51] invert static and final for the style --- .../com/turn/ttorrent/client/announce/HTTPTrackerClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java b/core/src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java index 8c1a54b65..e646d6ff1 100644 --- a/core/src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java +++ b/core/src/main/java/com/turn/ttorrent/client/announce/HTTPTrackerClient.java @@ -44,8 +44,8 @@ public class HTTPTrackerClient extends TrackerClient { protected static final Logger logger = LoggerFactory.getLogger(HTTPTrackerClient.class); - private final static String USER_AGENT = "Mozilla/5.0"; - private final static String USER_AGENT_HEADER = "User-Agent"; + private static final String USER_AGENT = "Mozilla/5.0"; + private static final String USER_AGENT_HEADER = "User-Agent"; /** * Create a new HTTP announcer for the given torrent.