mirror of
https://github.com/Rogiel/torrent4j
synced 2026-01-31 07:12:47 +00:00
Change-Id: I0f4f7ffe65dcfbaaaa792ebb43566c7d07c7569d
This commit is contained in:
11
.classpath
Normal file
11
.classpath
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
|
||||||
|
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
</classpath>
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
23
.project
Normal file
23
.project
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>libtorrent-java</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.maven.ide.eclipse.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.maven.ide.eclipse.maven2Nature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
3
.settings/org.eclipse.jdt.core.prefs
Normal file
3
.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#Wed Apr 27 21:19:52 BRT 2011
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||||
8
.settings/org.maven.ide.eclipse.prefs
Normal file
8
.settings/org.maven.ide.eclipse.prefs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#Wed Apr 27 21:17:36 BRT 2011
|
||||||
|
activeProfiles=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
fullBuildGoals=process-test-resources
|
||||||
|
resolveWorkspaceProjects=true
|
||||||
|
resourceFilterGoals=process-resources resources\:testResources
|
||||||
|
skipCompilerPlugin=true
|
||||||
|
version=1
|
||||||
202
LICENSE
Normal file
202
LICENSE
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
||||||
28
pom.xml
Normal file
28
pom.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>net.bittorrent</groupId>
|
||||||
|
<artifactId>bittorrent-java</artifactId>
|
||||||
|
<version>1.0.0-alpha</version>
|
||||||
|
<name>Java Bittorrent Library</name>
|
||||||
|
<description>Download and upload torrents with this embeddable library!</description>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>repository.jboss.org</id>
|
||||||
|
<url>https://repository.jboss.org/nexus/content/repositories/releases/</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.netty</groupId>
|
||||||
|
<artifactId>netty</artifactId>
|
||||||
|
<version>3.2.4.Final</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
173
src/main/java/net/torrent/BitTorrentClient.java
Normal file
173
src/main/java/net/torrent/BitTorrentClient.java
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
import net.torrent.protocol.algorithm.TorrentAlgorithm;
|
||||||
|
import net.torrent.protocol.peerwire.PeerWireManager;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.torrent.context.TorrentContext;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main class used to controll your torrent transfer. It is not
|
||||||
|
* recommended to directly instantiate this class, instead use
|
||||||
|
* {@link BitTorrentClientFactory} to create new instances.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class BitTorrentClient implements Runnable {
|
||||||
|
/**
|
||||||
|
* Configuration of an BitTorrentClient.
|
||||||
|
*/
|
||||||
|
private final BitTorrentConfiguration config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The torrent context
|
||||||
|
*/
|
||||||
|
private final TorrentContext context;
|
||||||
|
/**
|
||||||
|
* The torrent manager
|
||||||
|
*/
|
||||||
|
private final TorrentManager manager;
|
||||||
|
/**
|
||||||
|
* The peer wire protocol manager
|
||||||
|
*/
|
||||||
|
private final PeerWireManager peerWire;
|
||||||
|
/**
|
||||||
|
* The torrent algorithm
|
||||||
|
*/
|
||||||
|
private final TorrentAlgorithm algorithm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer used to create new connections
|
||||||
|
*/
|
||||||
|
private final Timer connectorTimer = new Timer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param config
|
||||||
|
* the configuration
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager
|
||||||
|
* @param peerWire
|
||||||
|
* the peer wire protocol manager
|
||||||
|
* @param algorithm
|
||||||
|
* the torrent algorithm
|
||||||
|
*/
|
||||||
|
public BitTorrentClient(final BitTorrentConfiguration config,
|
||||||
|
TorrentManager manager, PeerWireManager peerWire,
|
||||||
|
TorrentAlgorithm algorithm) {
|
||||||
|
this.config = config;
|
||||||
|
this.context = manager.getContext();
|
||||||
|
this.manager = manager;
|
||||||
|
this.peerWire = peerWire;
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start this torrent
|
||||||
|
*/
|
||||||
|
public void start() {
|
||||||
|
start((InetSocketAddress[]) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start this torrent. Once network is up, tries to connect to all the peers
|
||||||
|
* in <tt>addrs</tt>.
|
||||||
|
*
|
||||||
|
* @param addrs
|
||||||
|
* addresses
|
||||||
|
*/
|
||||||
|
public void start(InetSocketAddress... addrs) {
|
||||||
|
if (config.getListenPort() > 0)
|
||||||
|
peerWire.listen(config.getListenPort());
|
||||||
|
|
||||||
|
// run every 10 seconds - only 1 connection per turn
|
||||||
|
connectorTimer.schedule(new ConnectorTimerTask(), 0, 10 * 1000);
|
||||||
|
|
||||||
|
if (addrs != null)
|
||||||
|
for (final InetSocketAddress addr : addrs) {
|
||||||
|
peerWire.connect(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task that creates new connections at a certain repeat rate. Only one
|
||||||
|
* connection per turn.
|
||||||
|
*
|
||||||
|
* @author Rogiel Josias Sulzbach (<a
|
||||||
|
* href="http://www.rogiel.com/">http://www.rogiel.com/</a>)
|
||||||
|
*/
|
||||||
|
public class ConnectorTimerTask extends TimerTask {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
TorrentPeer peer = null;
|
||||||
|
while ((peer = algorithm.getPeerAlgorithm().connect()) != null) {
|
||||||
|
peerWire.connect(peer.getSocketAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent context
|
||||||
|
*
|
||||||
|
* @return the torrent context
|
||||||
|
*/
|
||||||
|
public TorrentContext getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent manager
|
||||||
|
*
|
||||||
|
* @return the torrent manager
|
||||||
|
*/
|
||||||
|
public TorrentManager getManager() {
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The peerwire manager
|
||||||
|
*
|
||||||
|
* @return the peerwire manager
|
||||||
|
*/
|
||||||
|
public PeerWireManager getPeerWire() {
|
||||||
|
return peerWire;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The torret
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public BitTorrentConfiguration getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentAlgorithm getAlgorithm() {
|
||||||
|
return algorithm;
|
||||||
|
}
|
||||||
|
}
|
||||||
231
src/main/java/net/torrent/BitTorrentClientFactory.java
Normal file
231
src/main/java/net/torrent/BitTorrentClientFactory.java
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.algorithm.TorrentAlgorithm;
|
||||||
|
import net.torrent.protocol.algorithm.impl.TorrentStdAlgorithm;
|
||||||
|
import net.torrent.protocol.datastore.TorrentDatastore;
|
||||||
|
import net.torrent.protocol.datastore.impl.PlainTorrentDatastore;
|
||||||
|
import net.torrent.protocol.peerwire.PeerWireManager;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.torrent.Torrent;
|
||||||
|
import net.torrent.torrent.context.TorrentContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory class for {@link BitTorrentClient}.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class BitTorrentClientFactory {
|
||||||
|
/**
|
||||||
|
* The client's configuration
|
||||||
|
*/
|
||||||
|
private BitTorrentConfiguration config = new BitTorrentConfiguration();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The torrent context
|
||||||
|
*/
|
||||||
|
private final TorrentContext context;
|
||||||
|
/**
|
||||||
|
* The torrent datastore
|
||||||
|
*/
|
||||||
|
private TorrentDatastore datastore;
|
||||||
|
/**
|
||||||
|
* The torrent manager
|
||||||
|
*/
|
||||||
|
private TorrentManager manager;
|
||||||
|
/**
|
||||||
|
* The torrent algorithm
|
||||||
|
*/
|
||||||
|
private TorrentAlgorithm algorithm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new standard {@link BitTorrentClient BitTorrent client}
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* the torrent file
|
||||||
|
* @return a new client
|
||||||
|
* @throws IOException
|
||||||
|
* @throws URISyntaxException
|
||||||
|
*/
|
||||||
|
public static BitTorrentClient newStandardBitTorrentClient(File file)
|
||||||
|
throws IOException, URISyntaxException {
|
||||||
|
return new BitTorrentClientFactory(Torrent.load(file))
|
||||||
|
.newBitTorrentClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new factory instance
|
||||||
|
*
|
||||||
|
* @param torrent
|
||||||
|
* the torrent
|
||||||
|
*/
|
||||||
|
public BitTorrentClientFactory(final Torrent torrent) {
|
||||||
|
context = new TorrentContext(torrent);
|
||||||
|
datastore = new PlainTorrentDatastore(new File("store.bin"));
|
||||||
|
manager = new TorrentManager(context, datastore);
|
||||||
|
algorithm = new TorrentStdAlgorithm(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new factory instance
|
||||||
|
*
|
||||||
|
* @param torrent
|
||||||
|
* the torrent
|
||||||
|
* @param algorithm
|
||||||
|
* the torrent algorithm
|
||||||
|
*/
|
||||||
|
public BitTorrentClientFactory(final Torrent torrent,
|
||||||
|
final TorrentAlgorithm algorithm) {
|
||||||
|
context = new TorrentContext(torrent);
|
||||||
|
datastore = new PlainTorrentDatastore(null);
|
||||||
|
manager = new TorrentManager(context, datastore);
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new factory instance
|
||||||
|
*
|
||||||
|
* @param torrent
|
||||||
|
* the torrent
|
||||||
|
* @param datastore
|
||||||
|
* the torrent datastore
|
||||||
|
*/
|
||||||
|
public BitTorrentClientFactory(final Torrent torrent,
|
||||||
|
TorrentDatastore datastore) {
|
||||||
|
context = new TorrentContext(torrent);
|
||||||
|
this.datastore = datastore;
|
||||||
|
manager = new TorrentManager(context, datastore);
|
||||||
|
algorithm = new TorrentStdAlgorithm(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new factory instance
|
||||||
|
*
|
||||||
|
* @param torrent
|
||||||
|
* the torrent
|
||||||
|
* @param datastore
|
||||||
|
* the torrent datastore
|
||||||
|
* @param algorithm
|
||||||
|
* the torrent algorithm
|
||||||
|
*/
|
||||||
|
public BitTorrentClientFactory(final Torrent torrent,
|
||||||
|
TorrentDatastore datastore, final TorrentAlgorithm algorithm) {
|
||||||
|
context = new TorrentContext(torrent);
|
||||||
|
this.datastore = datastore;
|
||||||
|
manager = new TorrentManager(context, datastore);
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the {@link BitTorrentClient} object
|
||||||
|
*
|
||||||
|
* @return the created {@link BitTorrentClient}
|
||||||
|
*/
|
||||||
|
public BitTorrentClient newBitTorrentClient() {
|
||||||
|
final PeerWireManager peerWire = new PeerWireManager(manager, algorithm);
|
||||||
|
return new BitTorrentClient(config, manager, peerWire, algorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the client configuration
|
||||||
|
*
|
||||||
|
* @return the client configuration
|
||||||
|
*/
|
||||||
|
public BitTorrentConfiguration getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the client configuration
|
||||||
|
*
|
||||||
|
* @param config
|
||||||
|
* the client configuration
|
||||||
|
*/
|
||||||
|
public void setConfig(BitTorrentConfiguration config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the datastore
|
||||||
|
*
|
||||||
|
* @return the datastore
|
||||||
|
*/
|
||||||
|
public TorrentDatastore getDatastore() {
|
||||||
|
return datastore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the datastore
|
||||||
|
*
|
||||||
|
* @param datastore
|
||||||
|
* the datastore
|
||||||
|
*/
|
||||||
|
public void setDatastore(TorrentDatastore datastore) {
|
||||||
|
this.datastore = datastore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent manager
|
||||||
|
*
|
||||||
|
* @return the torrent manager
|
||||||
|
*/
|
||||||
|
public TorrentManager getManager() {
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the torrent manager
|
||||||
|
*
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager
|
||||||
|
*/
|
||||||
|
public void setManager(TorrentManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent algorithm
|
||||||
|
*
|
||||||
|
* @return the torrent algorithm
|
||||||
|
*/
|
||||||
|
public TorrentAlgorithm getAlgorithm() {
|
||||||
|
return algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the torrent algorithm
|
||||||
|
*
|
||||||
|
* @param algorithm
|
||||||
|
* the torrent algorithm
|
||||||
|
*/
|
||||||
|
public void setAlgorithm(TorrentAlgorithm algorithm) {
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent context
|
||||||
|
*
|
||||||
|
* @return the torrent context
|
||||||
|
*/
|
||||||
|
public TorrentContext getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
71
src/main/java/net/torrent/BitTorrentConfiguration.java
Normal file
71
src/main/java/net/torrent/BitTorrentConfiguration.java
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configurations for an {@link BitTorrentClient BitTorrent client} instance.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class BitTorrentConfiguration {
|
||||||
|
/**
|
||||||
|
* Default peerwire listen port
|
||||||
|
*/
|
||||||
|
private int listenPort = 58462;
|
||||||
|
/**
|
||||||
|
* Default peer id
|
||||||
|
*/
|
||||||
|
private byte[] peerID = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the port
|
||||||
|
*
|
||||||
|
* @return the port
|
||||||
|
*/
|
||||||
|
public int getListenPort() {
|
||||||
|
return listenPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the listening port for the server.<br />
|
||||||
|
* 0 will disable server.
|
||||||
|
*
|
||||||
|
* @param listenPort
|
||||||
|
* the port
|
||||||
|
*/
|
||||||
|
public void setListenPort(int listenPort) {
|
||||||
|
this.listenPort = listenPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the peer id.
|
||||||
|
*
|
||||||
|
* @return the peer id
|
||||||
|
*/
|
||||||
|
public byte[] getPeerID() {
|
||||||
|
return peerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the peer id. If null a random peerid will be generated.
|
||||||
|
*
|
||||||
|
* @param peerID
|
||||||
|
* the peerid. Can be null.
|
||||||
|
*/
|
||||||
|
public void setPeerID(byte[] peerID) {
|
||||||
|
this.peerID = peerID;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.algorithm;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.handler.PeerWireAlgorithmHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link TorrentAlgorithm} defines the rules for download, upload and
|
||||||
|
* connection management. These algorithms provide limited control, in the
|
||||||
|
* boundaries of standard torrent behavior. If you wish more control, at the
|
||||||
|
* protocol layer, try implementing a new {@link PeerWireAlgorithmHandler}.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @see TorrentPeerAlgorithm
|
||||||
|
* @see TorrentInterestAlgorithm
|
||||||
|
* @see TorrentPieceDownloadAlgorithm
|
||||||
|
* @see TorrentPieceUploadAlgorithm
|
||||||
|
*/
|
||||||
|
public interface TorrentAlgorithm {
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@link TorrentPeerAlgorithm}.
|
||||||
|
*
|
||||||
|
* @return the new {@link TorrentPeerAlgorithm} instance
|
||||||
|
*/
|
||||||
|
TorrentPeerAlgorithm getPeerAlgorithm();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@link TorrentInterestAlgorithm}.
|
||||||
|
*
|
||||||
|
* @return the new {@link TorrentInterestAlgorithm} instance
|
||||||
|
*/
|
||||||
|
TorrentInterestAlgorithm getInterestAlgorithm();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@link TorrentPieceDownloadAlgorithm}.
|
||||||
|
*
|
||||||
|
* @return the new {@link TorrentPieceDownloadAlgorithm} instance
|
||||||
|
*/
|
||||||
|
TorrentPieceDownloadAlgorithm getDownloadAlgorithm();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@link TorrentPieceUploadAlgorithm}.
|
||||||
|
*
|
||||||
|
* @return the new {@link TorrentPieceUploadAlgorithm} instance
|
||||||
|
*/
|
||||||
|
TorrentPieceUploadAlgorithm getUploadAlgorithm();
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.algorithm;
|
||||||
|
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.ChokingState;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.InterestState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Algorithm used to determine the interest and choking in peers.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public interface TorrentInterestAlgorithm {
|
||||||
|
/**
|
||||||
|
* Test if we are interested in this peer pieces. Interest is for download
|
||||||
|
* only.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @return our interest
|
||||||
|
* @see InterestState
|
||||||
|
*/
|
||||||
|
InterestState interested(TorrentPeer peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if we want to choke this peer. This is normally invoked when we have
|
||||||
|
* no more interest in the peer pieces.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @return the choking state
|
||||||
|
* @see ChokingState
|
||||||
|
*/
|
||||||
|
ChokingState choke(TorrentPeer peer);
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.algorithm;
|
||||||
|
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.ChokingState;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.InterestState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Algorithm that processes peer interest, choking state and connections.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public interface TorrentPeerAlgorithm {
|
||||||
|
/**
|
||||||
|
* Get an peer to be connected. This method is invoked in many situations.
|
||||||
|
*
|
||||||
|
* @return the new peer to be connected
|
||||||
|
* @see TorrentPeer
|
||||||
|
*/
|
||||||
|
TorrentPeer connect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once a new peer is discovered, this method is called to test if we want
|
||||||
|
* to connect to it or not.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the new discovered peer
|
||||||
|
* @return the desired action
|
||||||
|
*/
|
||||||
|
PeerDiscoveredAction discovered(TorrentPeer peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action to be performed when an peer is idle.
|
||||||
|
*
|
||||||
|
* @author Rogiel Josias Sulzbach (<a
|
||||||
|
* href="http://www.rogiel.com/">http://www.rogiel.com/</a>)
|
||||||
|
*/
|
||||||
|
public enum PeerDiscoveredAction {
|
||||||
|
/**
|
||||||
|
* Try to establish an connection to this new peer
|
||||||
|
*/
|
||||||
|
CONNECT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove this peer from list
|
||||||
|
*/
|
||||||
|
REMOVE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nothing is done.
|
||||||
|
*/
|
||||||
|
NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test to keep this connection alive. The value sent represents the action
|
||||||
|
* which the handler will do with the idle peer.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @return the action to be done
|
||||||
|
* @see KeepAliveAction KeepAliveAction for a list of actions
|
||||||
|
*/
|
||||||
|
KeepAliveAction keepAlive(TorrentPeer peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action to be performed when an peer is idle.
|
||||||
|
*
|
||||||
|
* @author Rogiel Josias Sulzbach (<a
|
||||||
|
* href="http://www.rogiel.com/">http://www.rogiel.com/</a>)
|
||||||
|
*/
|
||||||
|
public enum KeepAliveAction {
|
||||||
|
/**
|
||||||
|
* Keep this connection alive
|
||||||
|
*/
|
||||||
|
KEEP_ALIVE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect the peer. No new connection is made.
|
||||||
|
*/
|
||||||
|
DISCONNECT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect the peer AND connects another peer.
|
||||||
|
*/
|
||||||
|
CONNECT_NEW_PEER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nothing is done.
|
||||||
|
*/
|
||||||
|
NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the peer interested in us changes. Return the desired choke
|
||||||
|
* or unchoked action.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @param interest
|
||||||
|
* the new interest (it is also already set in the peer object)
|
||||||
|
* @return the desired choke/unchoke action
|
||||||
|
* @see ChokingState
|
||||||
|
*/
|
||||||
|
ChokingState interested(TorrentPeer peer, InterestState interest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the peer choke state change with us (that is, <b>when the
|
||||||
|
* peer choke or unchoke</b>!).
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @param state
|
||||||
|
* the new choking state
|
||||||
|
* @return the desired action to be taken
|
||||||
|
* @see PeerChokedAction
|
||||||
|
*/
|
||||||
|
PeerChokedAction choked(TorrentPeer peer, ChokingState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action to be performed when the peer changes it's choke state
|
||||||
|
*
|
||||||
|
* @author Rogiel Josias Sulzbach (<a
|
||||||
|
* href="http://www.rogiel.com/">http://www.rogiel.com/</a>)
|
||||||
|
*/
|
||||||
|
public enum PeerChokedAction {
|
||||||
|
/**
|
||||||
|
* Disconnects the current peer and connects a new one
|
||||||
|
*/
|
||||||
|
CONNECT_NEW_PEER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only disconnects the peer, does not initiate a new connection with
|
||||||
|
* anyone.
|
||||||
|
*/
|
||||||
|
DISCONNECT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download a new piece (this is only valid if unchoked)
|
||||||
|
*/
|
||||||
|
DOWNLOAD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do nothing, ignore.
|
||||||
|
*/
|
||||||
|
NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.algorithm;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.handler.PeerWireAlgorithmHandler;
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This algorithm is used to return the {@link TorrentPart part} for download
|
||||||
|
* and validates if an certain {@link TorrentPiece piece} is complete. Please
|
||||||
|
* note that this algorithm DOES NOT do the checksum in the piece! The checksum
|
||||||
|
* is done at the {@link PeerWireAlgorithmHandler handler} level.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public interface TorrentPieceDownloadAlgorithm {
|
||||||
|
/**
|
||||||
|
* Return the next part desired for download
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @param part
|
||||||
|
* the part which has completed. Might be null!
|
||||||
|
* @return the new part
|
||||||
|
* @see TorrentPart
|
||||||
|
*/
|
||||||
|
TorrentPart getNextPart(TorrentPeer peer, TorrentPart part);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issued when an suggestion is received. If wished to accept it, return the
|
||||||
|
* first part of it, otherwise return null.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the suggesting peer
|
||||||
|
* @param piece
|
||||||
|
* the suggested piece
|
||||||
|
*/
|
||||||
|
TorrentPart sugested(TorrentPeer peer, TorrentPiece piece);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issued when allowed to fast download an given piece, even while choked.
|
||||||
|
* If multiple pieces are allowed, one call per piece will be done. If
|
||||||
|
* willing to download pieces, return the first part of the piece.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the allowing peer
|
||||||
|
* @param piece
|
||||||
|
* the allowed piece
|
||||||
|
*/
|
||||||
|
TorrentPart allowedFast(TorrentPeer peer, TorrentPiece piece);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if an certain piece has all its parts already download. If true, a
|
||||||
|
* checksum will be performed and a message informing we have this piece
|
||||||
|
* will be broadcasted. This call is only valid once the next part has
|
||||||
|
* already been taken.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @param piece
|
||||||
|
* the piece to test
|
||||||
|
* @return true if complete, false otherwise.
|
||||||
|
*/
|
||||||
|
boolean isComplete(TorrentPeer peer, TorrentPiece piece);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an piece is complete but found to be corrupted
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer who send the piece (more precisely the completing
|
||||||
|
* part)
|
||||||
|
* @param piece
|
||||||
|
* the piece
|
||||||
|
* @return the action to be performed
|
||||||
|
* @see CorruptedAction
|
||||||
|
*/
|
||||||
|
CorruptedAction corrupted(TorrentPeer peer, TorrentPiece piece);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actions to be taken when a corrupted piece is downloaded.
|
||||||
|
*
|
||||||
|
* @author Rogiel Josias Sulzbach (<a
|
||||||
|
* href="http://www.rogiel.com/">http://www.rogiel.com/</a>)
|
||||||
|
*/
|
||||||
|
public enum CorruptedAction {
|
||||||
|
/**
|
||||||
|
* Only disconnects the peer, does not initiate a new connection with
|
||||||
|
* anyone.
|
||||||
|
*/
|
||||||
|
DISCONNECT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects the current peer and connects a new one
|
||||||
|
*/
|
||||||
|
CONNECT_NEW_PEER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choke this peer
|
||||||
|
*/
|
||||||
|
CHOKE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retry to download the piece
|
||||||
|
*/
|
||||||
|
CONTINUE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do nothing, ignore. Might cause peer to become idle.
|
||||||
|
*/
|
||||||
|
CANCEL;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.algorithm;
|
||||||
|
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Algorithm used for upload management. It verifies if we have interest in
|
||||||
|
* uploading an piece and handles cancel requests. TODO how to handle cancels?
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public interface TorrentPieceUploadAlgorithm {
|
||||||
|
/**
|
||||||
|
* Called when an peer has requested a piece to be uploaded.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer requesting this piece
|
||||||
|
* @param part
|
||||||
|
* the part requested
|
||||||
|
* @return true if allowed to upload, false otherwise.
|
||||||
|
*/
|
||||||
|
RequestAction request(TorrentPeer peer, TorrentPart part);
|
||||||
|
|
||||||
|
public enum RequestAction {
|
||||||
|
/**
|
||||||
|
* Only disconnects the peer, does not initiate a new connection with
|
||||||
|
* anyone.
|
||||||
|
*/
|
||||||
|
DISCONNECT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects the current peer and connects a new one
|
||||||
|
*/
|
||||||
|
CONNECT_NEW_PEER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reject this request (only if supports Fast Extension). If not
|
||||||
|
* supported will fall back to {@link RequestAction#NONE}
|
||||||
|
*/
|
||||||
|
REJECT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choke this peer
|
||||||
|
*/
|
||||||
|
CHOKE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload a new piece (this is only valid if unchoked)
|
||||||
|
*/
|
||||||
|
UPLOAD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do nothing, ignore.
|
||||||
|
*/
|
||||||
|
NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels a part request.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @param part
|
||||||
|
* the part
|
||||||
|
* @return TODO
|
||||||
|
*/
|
||||||
|
boolean cancel(TorrentPeer peer, TorrentPart part);
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.algorithm.impl;
|
||||||
|
|
||||||
|
import net.torrent.protocol.algorithm.TorrentAlgorithm;
|
||||||
|
import net.torrent.protocol.algorithm.TorrentInterestAlgorithm;
|
||||||
|
import net.torrent.protocol.algorithm.TorrentPeerAlgorithm;
|
||||||
|
import net.torrent.protocol.algorithm.TorrentPieceDownloadAlgorithm;
|
||||||
|
import net.torrent.protocol.algorithm.TorrentPieceUploadAlgorithm;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard torrent algorithm
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentStdAlgorithm implements TorrentAlgorithm {
|
||||||
|
private final TorrentPeerAlgorithm peerAlgorithm;
|
||||||
|
private final TorrentInterestAlgorithm interestAlgorithm;
|
||||||
|
private final TorrentPieceDownloadAlgorithm downloadAlgorithm;
|
||||||
|
private final TorrentPieceUploadAlgorithm uploadAlgorithm;
|
||||||
|
|
||||||
|
public TorrentStdAlgorithm(final TorrentManager manager) {
|
||||||
|
peerAlgorithm = new TorrentStdPeerAlgorithm(manager);
|
||||||
|
interestAlgorithm = new TorrentStdInterestAlgorithm(manager);
|
||||||
|
downloadAlgorithm = new TorrentStdPieceDownloadAlgorithm(manager);
|
||||||
|
uploadAlgorithm = new TorrentStdPieceUploadAlgorithm(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TorrentPeerAlgorithm getPeerAlgorithm() {
|
||||||
|
return peerAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TorrentInterestAlgorithm getInterestAlgorithm() {
|
||||||
|
return interestAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TorrentPieceDownloadAlgorithm getDownloadAlgorithm() {
|
||||||
|
return downloadAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TorrentPieceUploadAlgorithm getUploadAlgorithm() {
|
||||||
|
return uploadAlgorithm;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.algorithm.impl;
|
||||||
|
|
||||||
|
import net.torrent.protocol.algorithm.TorrentInterestAlgorithm;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.ChokingState;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.InterestState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard torrent interest algorithm
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentStdInterestAlgorithm implements TorrentInterestAlgorithm {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final TorrentManager manager;
|
||||||
|
|
||||||
|
public TorrentStdInterestAlgorithm(TorrentManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InterestState interested(TorrentPeer peer) {
|
||||||
|
// if(peer.getPort() == 25944)
|
||||||
|
// return InterestState.UNINTERESTED;
|
||||||
|
return InterestState.INTERESTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChokingState choke(TorrentPeer peer) {
|
||||||
|
return ChokingState.UNCHOKED;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.algorithm.impl;
|
||||||
|
|
||||||
|
import net.torrent.protocol.algorithm.TorrentPeerAlgorithm;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.ChokingState;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.InterestState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard torrent peer algorithm
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentStdPeerAlgorithm implements TorrentPeerAlgorithm {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final TorrentManager manager;
|
||||||
|
|
||||||
|
public TorrentStdPeerAlgorithm(TorrentManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TorrentPeer connect() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PeerDiscoveredAction discovered(TorrentPeer peer) {
|
||||||
|
return PeerDiscoveredAction.CONNECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeepAliveAction keepAlive(TorrentPeer peer) {
|
||||||
|
return KeepAliveAction.KEEP_ALIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChokingState interested(TorrentPeer peer, InterestState interest) {
|
||||||
|
switch (interest) {
|
||||||
|
case INTERESTED:
|
||||||
|
return ChokingState.UNCHOKED;
|
||||||
|
case UNINTERESTED:
|
||||||
|
return ChokingState.CHOKED;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PeerChokedAction choked(TorrentPeer peer, ChokingState state) {
|
||||||
|
switch (state) {
|
||||||
|
case CHOKED:
|
||||||
|
return PeerChokedAction.NONE;
|
||||||
|
case UNCHOKED:
|
||||||
|
return PeerChokedAction.DOWNLOAD;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.algorithm.impl;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.torrent.protocol.algorithm.TorrentPieceDownloadAlgorithm;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
import net.torrent.torrent.piece.PieceSelector;
|
||||||
|
import net.torrent.torrent.piece.RandomPieceSelector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This standard implementation of {@link TorrentPieceDownloadAlgorithm} chooses
|
||||||
|
* a random missing piece and tries to download all the parts from the same
|
||||||
|
* peer, following the standard behavior of most of torrent clients.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentStdPieceDownloadAlgorithm implements
|
||||||
|
TorrentPieceDownloadAlgorithm {
|
||||||
|
/**
|
||||||
|
* The torrent manager
|
||||||
|
*/
|
||||||
|
private final TorrentManager manager;
|
||||||
|
// private final TorrentContext context;
|
||||||
|
// private final Torrent torrent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This selector is used to find the next piece to be downloaded. Parts are
|
||||||
|
* managed inside this algorithm.
|
||||||
|
*/
|
||||||
|
private final PieceSelector selector;
|
||||||
|
/**
|
||||||
|
* Maps all unchecked completed pieces. The piece is removed from the list
|
||||||
|
* once
|
||||||
|
* {@link TorrentPieceDownloadAlgorithm#isComplete(TorrentPeer, TorrentPiece)}
|
||||||
|
* is called.
|
||||||
|
*/
|
||||||
|
private Set<TorrentPiece> completedPieces = new HashSet<TorrentPiece>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of this algorithm.
|
||||||
|
*
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager instance. With this object is possible to
|
||||||
|
* retrieve current downloads/uploads and connections.
|
||||||
|
*/
|
||||||
|
public TorrentStdPieceDownloadAlgorithm(TorrentManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
// this.context = this.manager.getContext();
|
||||||
|
// this.torrent = this.manager.getTorrent();
|
||||||
|
selector = new RandomPieceSelector(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TorrentPart getNextPart(TorrentPeer peer, TorrentPart part) {
|
||||||
|
if (part != null) {
|
||||||
|
if (part.isLast()) {
|
||||||
|
completedPieces.add(part.getPiece());
|
||||||
|
} else {
|
||||||
|
return part.getNextPart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TorrentPiece piece = selector.select(peer);
|
||||||
|
if (piece == null)
|
||||||
|
// no piece, return null. The default handler will check, again, the
|
||||||
|
// interest on this peer.
|
||||||
|
return null;
|
||||||
|
return piece.getFirstPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TorrentPart sugested(TorrentPeer peer, TorrentPiece piece) {
|
||||||
|
return piece.getFirstPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TorrentPart allowedFast(TorrentPeer peer, TorrentPiece piece) {
|
||||||
|
return piece.getFirstPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isComplete(TorrentPeer peer, TorrentPiece piece) {
|
||||||
|
if (manager.getContext().getBitfield().hasPiece(piece))
|
||||||
|
return true;
|
||||||
|
// minimum overhead possible, will return true if was on list
|
||||||
|
return completedPieces.remove(piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CorruptedAction corrupted(TorrentPeer peer, TorrentPiece piece) {
|
||||||
|
// TODO ban peer sending many corrupted pieces
|
||||||
|
return CorruptedAction.CANCEL;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.algorithm.impl;
|
||||||
|
|
||||||
|
import net.torrent.protocol.algorithm.TorrentPieceUploadAlgorithm;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard torrent upload algorithm
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentStdPieceUploadAlgorithm implements
|
||||||
|
TorrentPieceUploadAlgorithm {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final TorrentManager manager;
|
||||||
|
|
||||||
|
public TorrentStdPieceUploadAlgorithm(TorrentManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RequestAction request(TorrentPeer peer, TorrentPart part) {
|
||||||
|
return RequestAction.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cancel(TorrentPeer peer, TorrentPart part) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.datastore;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
import net.torrent.protocol.datastore.impl.FileAwareTorrentDatastore;
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract implementation of {@link TorrentDatastore}. The only implemented
|
||||||
|
* method is {@link TorrentDatastore#checksum(TorrentPiece)} since it always the
|
||||||
|
* same.
|
||||||
|
* <p>
|
||||||
|
* Since it is noticeable that this interface does not use the concept of files,
|
||||||
|
* it might be much more useful implementing {@link FileAwareTorrentDatastore}
|
||||||
|
* class instead of this one.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public abstract class AbstractTorrentDatastore implements TorrentDatastore {
|
||||||
|
@Override
|
||||||
|
public boolean checksum(TorrentPiece piece) throws IOException {
|
||||||
|
final MessageDigest digest = piece.getHash().getType()
|
||||||
|
.getMessageDigest();
|
||||||
|
if (digest == null)
|
||||||
|
throw new NullPointerException("Digest is null");
|
||||||
|
byte[] hash = digest.digest(this.read(piece.asSinglePart()).array());
|
||||||
|
return piece.getHash().compare(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.datastore;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import net.torrent.protocol.datastore.impl.FileAwareTorrentDatastore;
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The datastore is responsible for storing data downloaded from peers and read
|
||||||
|
* them for further upload. The storage should be done as fast as possible,
|
||||||
|
* since slow operations might and will slowdown download and upload rates.
|
||||||
|
* <p>
|
||||||
|
* Since it is noticeable that this interface does not use the concept of files,
|
||||||
|
* it might be much more useful implementing {@link FileAwareTorrentDatastore}
|
||||||
|
* class instead of this interface.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public interface TorrentDatastore {
|
||||||
|
/**
|
||||||
|
* Read an segment of data relative to {@link TorrentPart part}.
|
||||||
|
* <p>
|
||||||
|
* <b>Warning</b>: Synchronization <b>MUST</b> be dealt by implementations.
|
||||||
|
*
|
||||||
|
* @param part
|
||||||
|
* the part readed
|
||||||
|
* @return {@link ByteBuffer} with the contents
|
||||||
|
* @throws IOException
|
||||||
|
* if exception occur at IO level
|
||||||
|
*/
|
||||||
|
ByteBuffer read(TorrentPart part) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores an segment of data relative to {@link TorrentPart part}.
|
||||||
|
* <p>
|
||||||
|
* <b>Warning</b>: Synchronization <b>MUST</b> be dealt by implementations.
|
||||||
|
*
|
||||||
|
* @param part
|
||||||
|
* the part to written
|
||||||
|
* @param buffer
|
||||||
|
* the buffer containing the data
|
||||||
|
* @return {@link ByteBuffer} with the contents
|
||||||
|
* @throws IOException
|
||||||
|
* if exception occur at IO level
|
||||||
|
*/
|
||||||
|
boolean write(TorrentPart part, ByteBuffer buffer) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the checksum of an {@link TorrentPiece piece}.
|
||||||
|
*
|
||||||
|
* @param piece
|
||||||
|
* the piece to be tested
|
||||||
|
* @return true if checksum is correct, false otherwise.
|
||||||
|
* @throws IOException
|
||||||
|
* if exception occur at IO level
|
||||||
|
*/
|
||||||
|
boolean checksum(TorrentPiece piece) throws IOException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.datastore.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import net.torrent.protocol.datastore.AbstractTorrentDatastore;
|
||||||
|
import net.torrent.protocol.datastore.TorrentDatastore;
|
||||||
|
import net.torrent.torrent.Torrent;
|
||||||
|
import net.torrent.torrent.TorrentFile;
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
import net.torrent.util.Range;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since {@link TorrentDatastore} does not uses the concept of files when
|
||||||
|
* storing and/or retrieving data, this abstract class provides abstraction for
|
||||||
|
* that issue and allows, easily to store and/or retrieve data in separated
|
||||||
|
* files.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public abstract class FileAwareTorrentDatastore extends
|
||||||
|
AbstractTorrentDatastore {
|
||||||
|
/**
|
||||||
|
* Read data contained withing <tt>file</tt> in the given <tt>range</tt>
|
||||||
|
*
|
||||||
|
* @param buffer
|
||||||
|
* the buffer in which data should be readed.
|
||||||
|
* @param file
|
||||||
|
* the file to be read
|
||||||
|
* @param range
|
||||||
|
* range of data being readed.
|
||||||
|
* @throws IOException
|
||||||
|
* if any exception occur at IO level
|
||||||
|
*/
|
||||||
|
protected abstract void read(ByteBuffer buffer, TorrentFile file,
|
||||||
|
Range range) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data contained in <tt>buffer</tt> to <tt>file</tt> in the given
|
||||||
|
* <tt>range</tt>
|
||||||
|
*
|
||||||
|
* @param buffer
|
||||||
|
* the buffer in which data should be written.
|
||||||
|
* @param file
|
||||||
|
* the file to be written
|
||||||
|
* @param range
|
||||||
|
* range of data being written.
|
||||||
|
* @throws IOException
|
||||||
|
* if any exception occur at IO level
|
||||||
|
*/
|
||||||
|
protected abstract boolean write(ByteBuffer buffer, TorrentFile file,
|
||||||
|
Range range) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer read(TorrentPart part) throws IOException {
|
||||||
|
final ByteBuffer buffer = allocate(part);
|
||||||
|
|
||||||
|
final Torrent torrent = part.getTorrent();
|
||||||
|
Range partRange = part.asTorrentRange();
|
||||||
|
|
||||||
|
for (final TorrentFile file : torrent.getFiles()) {
|
||||||
|
final Range fileRange = file.asTorrentRange();
|
||||||
|
if (fileRange.intersects(partRange)) {
|
||||||
|
final Range range = part.asFileRange(file).intersection(
|
||||||
|
fileRange);
|
||||||
|
buffer.limit((int) (buffer.position() + range.getLength()));
|
||||||
|
this.read(buffer, file, range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.flip();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean write(TorrentPart part, ByteBuffer buffer)
|
||||||
|
throws IOException {
|
||||||
|
final Torrent torrent = part.getTorrent();
|
||||||
|
Range partRange = part.asTorrentRange();
|
||||||
|
|
||||||
|
for (final TorrentFile file : torrent.getFiles()) {
|
||||||
|
final Range fileRange = file.asFileRange();
|
||||||
|
if (fileRange.intersects(partRange)) {
|
||||||
|
final Range range = part.asFileRange(file);
|
||||||
|
buffer.limit((int) (buffer.position() + range.getLength()));
|
||||||
|
if (!this.write(buffer, file, range))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates a new {@link ByteBuffer}.
|
||||||
|
*
|
||||||
|
* @param part
|
||||||
|
* the {@link TorrentPart}s
|
||||||
|
* @return a newly allocated ByteBuffer
|
||||||
|
*/
|
||||||
|
private ByteBuffer allocate(TorrentPart part) {
|
||||||
|
return ByteBuffer.allocate(part.getLength());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.datastore.impl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
|
||||||
|
import net.torrent.protocol.datastore.AbstractTorrentDatastore;
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores data into a single plain file, ignoring any file metadata into the
|
||||||
|
* original .torrent file.
|
||||||
|
* <p>
|
||||||
|
* Unless for a single torrent file, data might (and possibly will) not be
|
||||||
|
* readable outside this library.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class PlainTorrentDatastore extends AbstractTorrentDatastore {
|
||||||
|
/**
|
||||||
|
* The target file
|
||||||
|
*/
|
||||||
|
private final File file;
|
||||||
|
/**
|
||||||
|
* The file channel
|
||||||
|
*/
|
||||||
|
private FileChannel channel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new datastore instance
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* the single file to be used
|
||||||
|
*/
|
||||||
|
public PlainTorrentDatastore(File file) {
|
||||||
|
this.file = file;
|
||||||
|
try {
|
||||||
|
this.channel = new RandomAccessFile(this.file, "rw").getChannel();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized ByteBuffer read(TorrentPart part) throws IOException {
|
||||||
|
final ByteBuffer buffer = ByteBuffer.allocate(part.getLength());
|
||||||
|
synchronized (channel) {
|
||||||
|
channel.position(part.getOffset());
|
||||||
|
while (buffer.hasRemaining()) {
|
||||||
|
if (channel.read(buffer) == -1) // EOF
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.flip();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean write(TorrentPart part, ByteBuffer buffer)
|
||||||
|
throws IOException {
|
||||||
|
synchronized (channel) {
|
||||||
|
channel.position(part.getOffset());
|
||||||
|
while (buffer.hasRemaining()) {
|
||||||
|
if (channel.write(buffer) == -1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.datastore.impl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.torrent.torrent.TorrentFile;
|
||||||
|
import net.torrent.util.Range;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An simple implementation for torrent. Stores files in local file system.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class SimpleTorrentDatastore extends FileAwareTorrentDatastore {
|
||||||
|
private final Map<TorrentFile, FileChannel> cache = new HashMap<TorrentFile, FileChannel>();
|
||||||
|
private final File root;
|
||||||
|
|
||||||
|
public SimpleTorrentDatastore(File root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleTorrentDatastore() {
|
||||||
|
this(new File("."));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void read(ByteBuffer buffer, TorrentFile file, Range range)
|
||||||
|
throws IOException {
|
||||||
|
final FileChannel channel = cache(file);
|
||||||
|
synchronized (channel) {
|
||||||
|
channel.position(file.getOffset() + range.getStart());
|
||||||
|
while (buffer.hasRemaining()) {
|
||||||
|
if (channel.read(buffer) == -1) // EOF
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// don't flip the buffer!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean write(ByteBuffer buffer, TorrentFile file, Range range)
|
||||||
|
throws IOException {
|
||||||
|
final FileChannel channel = cache(file);
|
||||||
|
synchronized (channel) {
|
||||||
|
channel.position(file.getOffset() + range.getStart());
|
||||||
|
while (buffer.hasRemaining()) {
|
||||||
|
if (channel.write(buffer) == -1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an cached {@link FileChannel channel}. If not cached, a new will
|
||||||
|
* be open in read/write mode and cached.
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* the {@link TorrentFile file}
|
||||||
|
* @return the cached {@link FileChannel channel}
|
||||||
|
* @throws FileNotFoundException
|
||||||
|
* if parent folder does not exists.
|
||||||
|
*/
|
||||||
|
private FileChannel cache(TorrentFile file) throws FileNotFoundException {
|
||||||
|
FileChannel channel = cache.get(file);
|
||||||
|
if (channel == null) {
|
||||||
|
channel = new RandomAccessFile(new File(root,
|
||||||
|
file.getRelativePath()), "rw").getChannel();
|
||||||
|
cache.put(file, channel);
|
||||||
|
}
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.datastore.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import net.torrent.protocol.datastore.AbstractTorrentDatastore;
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is much like the <tt>/dev/null</tt> and <tt>/dev/zero</tt> pseudo-file
|
||||||
|
* in UNIX systems. All data written is discarded and all readed data is
|
||||||
|
* composed of <tt>NULL</tt> (<tt>0x00</tt>). Checksums always succeed.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class VoidTorrentDatastore extends AbstractTorrentDatastore {
|
||||||
|
@Override
|
||||||
|
public ByteBuffer read(TorrentPart part) throws IOException {
|
||||||
|
return ByteBuffer.allocate(part.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean write(TorrentPart part, ByteBuffer buffer) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checksum(TorrentPiece piece) throws IOException {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/main/java/net/torrent/protocol/dht/DHTClient.java
Normal file
23
src/main/java/net/torrent/protocol/dht/DHTClient.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.dht;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class DHTClient {
|
||||||
|
|
||||||
|
}
|
||||||
197
src/main/java/net/torrent/protocol/peerwire/PeerWireManager.java
Normal file
197
src/main/java/net/torrent/protocol/peerwire/PeerWireManager.java
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import net.torrent.protocol.algorithm.TorrentAlgorithm;
|
||||||
|
import net.torrent.protocol.peerwire.handler.PeerWireAlgorithmHandler;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
|
||||||
|
import org.jboss.netty.bootstrap.ClientBootstrap;
|
||||||
|
import org.jboss.netty.bootstrap.ServerBootstrap;
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
|
import org.jboss.netty.channel.ChannelHandler;
|
||||||
|
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
|
||||||
|
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for the peerwire protocol. Can be used to start or stop the server
|
||||||
|
* and initiate new connections.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class PeerWireManager {
|
||||||
|
/**
|
||||||
|
* The Netty client's {@link ClientBootstrap bootstrap}
|
||||||
|
*/
|
||||||
|
private final ClientBootstrap client = new ClientBootstrap(
|
||||||
|
new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),
|
||||||
|
Executors.newCachedThreadPool()));
|
||||||
|
/**
|
||||||
|
* The Netty server's {@link ServerBootstrap bootstrap}
|
||||||
|
*/
|
||||||
|
private final ServerBootstrap server = new ServerBootstrap(
|
||||||
|
new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),
|
||||||
|
Executors.newCachedThreadPool()));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The server's listen address
|
||||||
|
*/
|
||||||
|
private InetSocketAddress listenAddress;
|
||||||
|
/**
|
||||||
|
* The server's channel
|
||||||
|
*/
|
||||||
|
private Channel serverChannel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pipeline channel factory
|
||||||
|
*/
|
||||||
|
private final PeerWirePipelineFactory pipelineFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager
|
||||||
|
* @param algorithm
|
||||||
|
* the torrent algorithm
|
||||||
|
* @param listenAddress
|
||||||
|
* the server's listen address
|
||||||
|
*/
|
||||||
|
public PeerWireManager(TorrentManager manager, ChannelHandler algorithm,
|
||||||
|
InetSocketAddress listenAddress) {
|
||||||
|
pipelineFactory = new PeerWirePipelineFactory(manager, algorithm);
|
||||||
|
|
||||||
|
client.setPipelineFactory(pipelineFactory);
|
||||||
|
server.setPipelineFactory(pipelineFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager
|
||||||
|
* @param algorithm
|
||||||
|
* the torrent algorithm
|
||||||
|
*/
|
||||||
|
public PeerWireManager(TorrentManager manager, TorrentAlgorithm algorithm) {
|
||||||
|
this(manager, new PeerWireAlgorithmHandler(manager, algorithm), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen the server to the given port
|
||||||
|
*
|
||||||
|
* @param port
|
||||||
|
* the port
|
||||||
|
* @return the server {@link Channel}.
|
||||||
|
*/
|
||||||
|
public Channel listen(int port) {
|
||||||
|
if (listenAddress == null)
|
||||||
|
listenAddress = new InetSocketAddress(port);
|
||||||
|
return (serverChannel = server.bind(listenAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a new client
|
||||||
|
*
|
||||||
|
* @param address
|
||||||
|
* the address
|
||||||
|
* @return the {@link ChannelFuture} for monitoring the connection progress
|
||||||
|
*/
|
||||||
|
public ChannelFuture connect(InetSocketAddress address) {
|
||||||
|
return client.connect(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a new client and wait until is complete.
|
||||||
|
*
|
||||||
|
* @param address
|
||||||
|
* the peer address
|
||||||
|
* @param wait
|
||||||
|
* wait time
|
||||||
|
* @param unit
|
||||||
|
* unit of <tt>wait</tt>
|
||||||
|
* @return the newly created {@link Channel}
|
||||||
|
*/
|
||||||
|
public Channel connectWait(InetSocketAddress address, long wait,
|
||||||
|
TimeUnit unit) {
|
||||||
|
final ChannelFuture future = connect(address);
|
||||||
|
if (future.awaitUninterruptibly(wait, unit)) {
|
||||||
|
if (future.isSuccess())
|
||||||
|
return future.getChannel();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the server and the client, and then release the external resources.
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
if (serverChannel != null)
|
||||||
|
serverChannel.close().awaitUninterruptibly();
|
||||||
|
client.releaseExternalResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the server's listening channel
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Channel getServerChannel() {
|
||||||
|
return serverChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Netty client's {@link ClientBootstrap bootstrap}
|
||||||
|
*
|
||||||
|
* @return the {@link ClientBootstrap}
|
||||||
|
*/
|
||||||
|
protected ClientBootstrap getClientBootstrap() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Netty server's {@link ServerBootstrap bootstrap}
|
||||||
|
*
|
||||||
|
* @return the {@link ServerBootstrap}
|
||||||
|
*/
|
||||||
|
protected ServerBootstrap getServerBootstrap() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current server's listening address
|
||||||
|
*
|
||||||
|
* @return the listen address
|
||||||
|
*/
|
||||||
|
public InetSocketAddress getListenAddress() {
|
||||||
|
return listenAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current server's listening address
|
||||||
|
*
|
||||||
|
* @param listenAddress
|
||||||
|
* the listen address
|
||||||
|
*/
|
||||||
|
public void setListenAddress(InetSocketAddress listenAddress) {
|
||||||
|
this.listenAddress = listenAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
401
src/main/java/net/torrent/protocol/peerwire/PeerWirePeer.java
Normal file
401
src/main/java/net/torrent/protocol/peerwire/PeerWirePeer.java
Normal file
@@ -0,0 +1,401 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.BitfieldMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.CancelMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.ChokeMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.HandshakeMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.HaveMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.InterestedMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.KeepAliveMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.NotInterestedMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.PieceMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.PortMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.RequestMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.UnchokeMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.fast.AllowedFastMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.fast.HaveAllMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.fast.HaveNoneMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.fast.RejectMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.fast.SuggestPieceMessage;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.ChokingState;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.InterestState;
|
||||||
|
import net.torrent.torrent.context.TorrentPeerCapabilities.TorrentPeerCapability;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
|
import org.jboss.netty.channel.ChannelFutureListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An PeerWire Peer manages the {@link Channel channel} (the current peer
|
||||||
|
* connection) and the {@link TorrentPeer torrent peer} object.
|
||||||
|
* <p>
|
||||||
|
* This object contains handy methods to send messages to the peer.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class PeerWirePeer {
|
||||||
|
/**
|
||||||
|
* The active {@link Channel}
|
||||||
|
*/
|
||||||
|
private final Channel channel;
|
||||||
|
/**
|
||||||
|
* The active {@link TorrentPeer}. It might no be available until handshake.
|
||||||
|
*/
|
||||||
|
private TorrentPeer peer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param channel
|
||||||
|
* the channel
|
||||||
|
* @param peer
|
||||||
|
* the peer. Can be null.
|
||||||
|
*/
|
||||||
|
public PeerWirePeer(Channel channel, TorrentPeer peer) {
|
||||||
|
this.channel = channel;
|
||||||
|
this.peer = peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an handshake message
|
||||||
|
*
|
||||||
|
* @param infohash
|
||||||
|
* the info hash
|
||||||
|
* @param peerId
|
||||||
|
* the peer id
|
||||||
|
* @param reserved
|
||||||
|
* the capabilities {@link BitSet}
|
||||||
|
* @return the {@link ChannelFuture} for the message
|
||||||
|
*/
|
||||||
|
public ChannelFuture handshake(byte[] infohash, byte[] peerId,
|
||||||
|
BitSet reserved) {
|
||||||
|
return write(new HandshakeMessage(infohash, peerId, reserved));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an bitfield message
|
||||||
|
*
|
||||||
|
* @param bitset
|
||||||
|
* the bitfield bits
|
||||||
|
* @return the {@link ChannelFuture} for the message
|
||||||
|
*/
|
||||||
|
public ChannelFuture bitfield(BitSet bitset) {
|
||||||
|
return write(new BitfieldMessage(bitset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an choke message
|
||||||
|
*/
|
||||||
|
public void choke() {
|
||||||
|
if (peer.getChokingState() == ChokingState.CHOKED)
|
||||||
|
return;
|
||||||
|
write(new ChokeMessage()).addListener(new ChannelFutureListener() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(ChannelFuture future)
|
||||||
|
throws Exception {
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
peer.setChokingState(ChokingState.CHOKED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an unchoke message
|
||||||
|
*/
|
||||||
|
public void unchoke() {
|
||||||
|
if (peer.getChokingState() == ChokingState.UNCHOKED)
|
||||||
|
return;
|
||||||
|
write(new UnchokeMessage()).addListener(new ChannelFutureListener() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(ChannelFuture future)
|
||||||
|
throws Exception {
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
peer.setChokingState(ChokingState.UNCHOKED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an interest message
|
||||||
|
*/
|
||||||
|
public void interested() {
|
||||||
|
if (peer.getInterestState() == InterestState.INTERESTED)
|
||||||
|
return;
|
||||||
|
write(new InterestedMessage()).addListener(new ChannelFutureListener() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(ChannelFuture future)
|
||||||
|
throws Exception {
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
peer.setInterestState(InterestState.INTERESTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an not interest message
|
||||||
|
*/
|
||||||
|
public void uninterested() {
|
||||||
|
if (peer.getInterestState() == InterestState.UNINTERESTED)
|
||||||
|
return;
|
||||||
|
write(new NotInterestedMessage()).addListener(
|
||||||
|
new ChannelFutureListener() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(ChannelFuture future)
|
||||||
|
throws Exception {
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
peer.setInterestState(InterestState.UNINTERESTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an request message
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
* the piece index
|
||||||
|
* @param start
|
||||||
|
* the part start
|
||||||
|
* @param length
|
||||||
|
* the part length
|
||||||
|
* @return the {@link ChannelFuture} for this message
|
||||||
|
*/
|
||||||
|
public ChannelFuture request(int index, int start, int length) {
|
||||||
|
return write(new RequestMessage(index, start, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an upload message
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
* the index
|
||||||
|
* @param start
|
||||||
|
* the start
|
||||||
|
* @param length
|
||||||
|
* the length
|
||||||
|
* @param data
|
||||||
|
* the upload {@link ByteBuffer content}
|
||||||
|
*/
|
||||||
|
public void upload(int index, int start, int length, ByteBuffer data) {
|
||||||
|
this.unchoke();
|
||||||
|
write(new PieceMessage(index, start, length, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an have message
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
* the piece index
|
||||||
|
*/
|
||||||
|
public void have(int index) {
|
||||||
|
write(new HaveMessage(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an cancel message
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
* the index
|
||||||
|
* @param start
|
||||||
|
* the start
|
||||||
|
* @param length
|
||||||
|
* the length
|
||||||
|
*/
|
||||||
|
public void cancel(int index, int start, int length) {
|
||||||
|
write(new CancelMessage(index, start, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an keep alive message
|
||||||
|
*/
|
||||||
|
public void keepAlive() {
|
||||||
|
write(new KeepAliveMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *** REQUIRES {@link TorrentPeerCapability#DHT DHT Protocol} ***
|
||||||
|
* <p>
|
||||||
|
* Send and port message. The port is used for UDP.
|
||||||
|
*
|
||||||
|
* @param port
|
||||||
|
* the port number
|
||||||
|
*/
|
||||||
|
public void port(short port) {
|
||||||
|
write(new PortMessage(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *** REQUIRES {@link TorrentPeerCapability#EXTENSION_PROTOCOL Extension
|
||||||
|
* Protocol} ***
|
||||||
|
* <p>
|
||||||
|
* Send an have none message.
|
||||||
|
*/
|
||||||
|
public void haveNone() {
|
||||||
|
write(new HaveNoneMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *** REQUIRES {@link TorrentPeerCapability#EXTENSION_PROTOCOL Extension
|
||||||
|
* Protocol} ***
|
||||||
|
* <p>
|
||||||
|
* Send an have all message.
|
||||||
|
*/
|
||||||
|
public void haveAll() {
|
||||||
|
write(new HaveAllMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *** REQUIRES {@link TorrentPeerCapability#EXTENSION_PROTOCOL Extension
|
||||||
|
* Protocol} ***
|
||||||
|
* <p>
|
||||||
|
* Send an reject message.
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
* the piece index
|
||||||
|
* @param start
|
||||||
|
* the part start
|
||||||
|
* @param length
|
||||||
|
* the part length
|
||||||
|
*/
|
||||||
|
public void reject(int index, int start, int length) {
|
||||||
|
this.choke();
|
||||||
|
write(new RejectMessage(index, start, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *** REQUIRES {@link TorrentPeerCapability#EXTENSION_PROTOCOL Extension
|
||||||
|
* Protocol} ***
|
||||||
|
* <p>
|
||||||
|
* Send an suggest message
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
* the piece index
|
||||||
|
*/
|
||||||
|
public void suggest(int index) {
|
||||||
|
write(new SuggestPieceMessage(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *** REQUIRES {@link TorrentPeerCapability#EXTENSION_PROTOCOL Extension
|
||||||
|
* Protocol} ***
|
||||||
|
* <p>
|
||||||
|
* Send an list of pieces allowed to download fast (download even when
|
||||||
|
* choked)
|
||||||
|
*
|
||||||
|
* @param indexes
|
||||||
|
* the piece indexes
|
||||||
|
*/
|
||||||
|
public void allowedFast(int... indexes) {
|
||||||
|
write(new AllowedFastMessage(indexes));
|
||||||
|
}
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an message to the peer
|
||||||
|
*
|
||||||
|
* @return an {@link ChannelFuture} to monitor write progress
|
||||||
|
* @see Channel#write(Object)
|
||||||
|
*/
|
||||||
|
public ChannelFuture write(PeerWireWritableMessage message) {
|
||||||
|
return channel.write(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect the peer
|
||||||
|
*
|
||||||
|
* @return an {@link ChannelFuture} to monitor disconnect progress
|
||||||
|
* @see Channel#diconnect()
|
||||||
|
*/
|
||||||
|
public ChannelFuture disconnect() {
|
||||||
|
return channel.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the peer {@link Channel channel}
|
||||||
|
*
|
||||||
|
* @return an {@link ChannelFuture} to monitor close progress
|
||||||
|
* @see Channel#close()
|
||||||
|
*/
|
||||||
|
public ChannelFuture close() {
|
||||||
|
return channel.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the peer {@link Channel channel}
|
||||||
|
*
|
||||||
|
* @return the peer active {@link Channel}
|
||||||
|
*/
|
||||||
|
public Channel getChannel() {
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the active {@link TorrentPeer}
|
||||||
|
*
|
||||||
|
* @return the active peer
|
||||||
|
*/
|
||||||
|
public TorrentPeer getTorrentPeer() {
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the active peer
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the active peer
|
||||||
|
*/
|
||||||
|
public void setTorrentPeer(TorrentPeer peer) {
|
||||||
|
this.peer = peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((channel == null) ? 0 : channel.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
PeerWirePeer other = (PeerWirePeer) obj;
|
||||||
|
if (channel == null) {
|
||||||
|
if (other.channel != null)
|
||||||
|
return false;
|
||||||
|
} else if (!channel.equals(other.channel))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire;
|
||||||
|
|
||||||
|
import static org.jboss.netty.channel.Channels.pipeline;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireDecoder;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireEncoder;
|
||||||
|
import net.torrent.protocol.peerwire.handler.PeerWireManagerHeadHandler;
|
||||||
|
import net.torrent.protocol.peerwire.handler.PeerWireManagerTailHandler;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.ChannelHandler;
|
||||||
|
import org.jboss.netty.channel.ChannelPipeline;
|
||||||
|
import org.jboss.netty.channel.ChannelPipelineFactory;
|
||||||
|
import org.jboss.netty.handler.logging.LoggingHandler;
|
||||||
|
import org.jboss.netty.logging.InternalLogLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ChannelPipeline} factory for all PeerWire connections.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class PeerWirePipelineFactory implements ChannelPipelineFactory {
|
||||||
|
/**
|
||||||
|
* The logging handler
|
||||||
|
*/
|
||||||
|
private final LoggingHandler loggingHandler = new LoggingHandler(
|
||||||
|
InternalLogLevel.WARN);
|
||||||
|
/**
|
||||||
|
* The algorithm handler
|
||||||
|
*/
|
||||||
|
private final ChannelHandler algorithmHandler;
|
||||||
|
/**
|
||||||
|
* The head manager handler
|
||||||
|
*/
|
||||||
|
private final PeerWireManagerHeadHandler headManagerHandler;
|
||||||
|
/**
|
||||||
|
* The tail manager handler
|
||||||
|
*/
|
||||||
|
private final PeerWireManagerTailHandler tailManagerHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager
|
||||||
|
* @param algorithmHandler
|
||||||
|
* the algorithm handler
|
||||||
|
*/
|
||||||
|
public PeerWirePipelineFactory(TorrentManager manager,
|
||||||
|
ChannelHandler algorithmHandler) {
|
||||||
|
this.algorithmHandler = algorithmHandler;
|
||||||
|
this.headManagerHandler = new PeerWireManagerHeadHandler(manager);
|
||||||
|
this.tailManagerHandler = new PeerWireManagerTailHandler(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelPipeline getPipeline() throws Exception {
|
||||||
|
final ChannelPipeline pipeline = pipeline();
|
||||||
|
|
||||||
|
// TODO create traffic shape handler
|
||||||
|
// TODO create firewall handler - block connections from unwanted peers
|
||||||
|
|
||||||
|
pipeline.addLast("decoder", new PeerWireDecoder());
|
||||||
|
pipeline.addLast("encoder", new PeerWireEncoder());
|
||||||
|
|
||||||
|
pipeline.addLast("logging", loggingHandler);
|
||||||
|
|
||||||
|
pipeline.addLast("head-handler", headManagerHandler);
|
||||||
|
pipeline.addLast("algorithm", algorithmHandler);
|
||||||
|
pipeline.addLast("tail-handler", tailManagerHandler);
|
||||||
|
|
||||||
|
return pipeline;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.codec;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.message.BitfieldMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.CancelMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.ChokeMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.HandshakeMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.HaveMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.InterestedMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.KeepAliveMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.NotInterestedMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.PieceMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.PortMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.RequestMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.UnchokeMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.handler.codec.frame.CorruptedFrameException;
|
||||||
|
import org.jboss.netty.handler.codec.frame.FrameDecoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BitTorrent has two types of message headers:
|
||||||
|
* <p>
|
||||||
|
* <h1>Handshake message</h1> Handshake messages are composed of 1 byte, which
|
||||||
|
* indicates the size of protocol string ("BitTorrent protocol").
|
||||||
|
* <p>
|
||||||
|
* <h1>Messages</h1> All other messages are 4-byte integer containing the
|
||||||
|
* message length, followed by 1 byte opcode.
|
||||||
|
* <p>
|
||||||
|
* <p>
|
||||||
|
* Instances of this class keep channel state content and must not be shared nor
|
||||||
|
* cached.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class PeerWireDecoder extends FrameDecoder {
|
||||||
|
private boolean handshaked = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object decode(ChannelHandlerContext ctx, Channel channel,
|
||||||
|
ChannelBuffer buffer) throws Exception {
|
||||||
|
buffer.markReaderIndex();
|
||||||
|
|
||||||
|
if (!handshaked) {
|
||||||
|
if (buffer.readableBytes() <= 47) // at least 47 bytes
|
||||||
|
return null;
|
||||||
|
|
||||||
|
final int pstrlen = buffer.readByte();
|
||||||
|
if (buffer.readableBytes() < pstrlen + 47) {
|
||||||
|
buffer.resetReaderIndex();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
buffer.readerIndex(buffer.readerIndex() - 1);
|
||||||
|
|
||||||
|
final HandshakeMessage message = new HandshakeMessage();
|
||||||
|
message.read(buffer);
|
||||||
|
handshaked = true;
|
||||||
|
|
||||||
|
return message;
|
||||||
|
} else {
|
||||||
|
if (buffer.readableBytes() <= 4) {
|
||||||
|
buffer.resetReaderIndex();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = buffer.readInt();
|
||||||
|
if (len == 0) {
|
||||||
|
return new KeepAliveMessage();
|
||||||
|
} else if (buffer.readableBytes() < len) {
|
||||||
|
buffer.resetReaderIndex();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte id = buffer.readByte();
|
||||||
|
final PeerWireReadableMessage message = getMessage(id);
|
||||||
|
if (message == null)
|
||||||
|
// force connection to be closed
|
||||||
|
throw new CorruptedFrameException("unknown message " + id);
|
||||||
|
message.read(buffer);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the message represented by <tt>id</tt>. Will return null if
|
||||||
|
* message id is unknown.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* the id of the message
|
||||||
|
* @return the message
|
||||||
|
*/
|
||||||
|
private PeerWireReadableMessage getMessage(byte id) {
|
||||||
|
PeerWireReadableMessage message = null;
|
||||||
|
switch (id) {
|
||||||
|
case BitfieldMessage.MESSAGE_ID:
|
||||||
|
message = new BitfieldMessage();
|
||||||
|
break;
|
||||||
|
case CancelMessage.MESSAGE_ID:
|
||||||
|
message = new CancelMessage();
|
||||||
|
break;
|
||||||
|
case ChokeMessage.MESSAGE_ID:
|
||||||
|
message = new ChokeMessage();
|
||||||
|
break;
|
||||||
|
case HaveMessage.MESSAGE_ID:
|
||||||
|
message = new HaveMessage();
|
||||||
|
break;
|
||||||
|
case InterestedMessage.MESSAGE_ID:
|
||||||
|
message = new InterestedMessage();
|
||||||
|
break;
|
||||||
|
case NotInterestedMessage.MESSAGE_ID:
|
||||||
|
message = new NotInterestedMessage();
|
||||||
|
break;
|
||||||
|
case PieceMessage.MESSAGE_ID:
|
||||||
|
message = new PieceMessage();
|
||||||
|
break;
|
||||||
|
case PortMessage.MESSAGE_ID:
|
||||||
|
message = new PortMessage();
|
||||||
|
break;
|
||||||
|
case RequestMessage.MESSAGE_ID:
|
||||||
|
message = new RequestMessage();
|
||||||
|
break;
|
||||||
|
case UnchokeMessage.MESSAGE_ID:
|
||||||
|
message = new UnchokeMessage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.codec;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.message.HandshakeMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffers;
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Messages are encoded in {@link PeerWireWritableMessage#write(ChannelBuffer)}
|
||||||
|
* method. Message length is measured automatically by the encoder.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class PeerWireEncoder extends OneToOneEncoder {
|
||||||
|
@Override
|
||||||
|
protected Object encode(ChannelHandlerContext ctx, Channel channel,
|
||||||
|
Object msg) throws Exception {
|
||||||
|
ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
|
||||||
|
if (!(msg instanceof PeerWireWritableMessage))
|
||||||
|
return msg;
|
||||||
|
|
||||||
|
if (msg instanceof HandshakeMessage) {
|
||||||
|
final HandshakeMessage message = (HandshakeMessage) msg;
|
||||||
|
message.write(buffer);
|
||||||
|
} else {
|
||||||
|
final PeerWireWritableMessage message = (PeerWireWritableMessage) msg;
|
||||||
|
buffer.writeInt(0); // allocate 4 bytes for header
|
||||||
|
message.write(buffer);
|
||||||
|
int len = buffer.readableBytes();
|
||||||
|
buffer.setInt(0, len - 4);
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.codec;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An readable message in the BitTorrent protocol
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public interface PeerWireReadableMessage {
|
||||||
|
/**
|
||||||
|
* Read the content of the message contained in this buffer.
|
||||||
|
*
|
||||||
|
* @param buffer
|
||||||
|
* the buffer
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
void read(ChannelBuffer buffer) throws IOException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.codec;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An writable message in the BitTorrent protocol
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public interface PeerWireWritableMessage {
|
||||||
|
/**
|
||||||
|
* Write the content of the message to this buffer.
|
||||||
|
*
|
||||||
|
* @param buffer
|
||||||
|
* the buffer
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
void write(ChannelBuffer buffer) throws IOException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,481 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.handler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
import net.torrent.protocol.algorithm.TorrentAlgorithm;
|
||||||
|
import net.torrent.protocol.algorithm.TorrentInterestAlgorithm;
|
||||||
|
import net.torrent.protocol.algorithm.TorrentPeerAlgorithm;
|
||||||
|
import net.torrent.protocol.algorithm.TorrentPieceDownloadAlgorithm;
|
||||||
|
import net.torrent.protocol.algorithm.TorrentPieceUploadAlgorithm;
|
||||||
|
import net.torrent.protocol.datastore.TorrentDatastore;
|
||||||
|
import net.torrent.protocol.peerwire.PeerWirePeer;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.protocol.peerwire.message.BitfieldMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.ChokeMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.HaveMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.InterestedMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.KeepAliveMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.NotInterestedMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.PieceMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.RequestMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.UnchokeMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.fast.AllowedFastMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.fast.SuggestPieceMessage;
|
||||||
|
import net.torrent.torrent.Torrent;
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.ChokingState;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer.InterestState;
|
||||||
|
import net.torrent.torrent.context.TorrentPeerCapabilities.TorrentPeerCapability;
|
||||||
|
import net.torrent.util.PeerCallback;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.channel.ChannelStateEvent;
|
||||||
|
import org.jboss.netty.channel.ExceptionEvent;
|
||||||
|
import org.jboss.netty.channel.MessageEvent;
|
||||||
|
import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
|
||||||
|
import org.jboss.netty.handler.timeout.IdleStateEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard handler responsible for forwarding calls to {@link TorrentAlgorithm}
|
||||||
|
* methods. This class handles low-level protocol specific behavior.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class PeerWireAlgorithmHandler extends IdleStateAwareChannelHandler {
|
||||||
|
/**
|
||||||
|
* The torrent manager
|
||||||
|
*/
|
||||||
|
private final TorrentManager manager;
|
||||||
|
/**
|
||||||
|
* The torrent datastore
|
||||||
|
*/
|
||||||
|
private final TorrentDatastore datastore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The peer algorithm
|
||||||
|
*/
|
||||||
|
private final TorrentPeerAlgorithm peerAlgorithm;
|
||||||
|
/**
|
||||||
|
* The interest algorithm
|
||||||
|
*/
|
||||||
|
private final TorrentInterestAlgorithm interestAlgorithm;
|
||||||
|
/**
|
||||||
|
* The download algorithm
|
||||||
|
*/
|
||||||
|
private final TorrentPieceDownloadAlgorithm downloadAlgorithm;
|
||||||
|
/**
|
||||||
|
* The upload algorithm
|
||||||
|
*/
|
||||||
|
private final TorrentPieceUploadAlgorithm uploadAlgorithm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new handler
|
||||||
|
*
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager
|
||||||
|
* @param algorithm
|
||||||
|
* the algorithm
|
||||||
|
*/
|
||||||
|
public PeerWireAlgorithmHandler(TorrentManager manager,
|
||||||
|
final TorrentAlgorithm algorithm) {
|
||||||
|
this.manager = manager;
|
||||||
|
this.datastore = manager.getDatastore();
|
||||||
|
this.peerAlgorithm = algorithm.getPeerAlgorithm();
|
||||||
|
this.interestAlgorithm = algorithm.getInterestAlgorithm();
|
||||||
|
this.downloadAlgorithm = algorithm.getDownloadAlgorithm();
|
||||||
|
this.uploadAlgorithm = algorithm.getUploadAlgorithm();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
|
||||||
|
throws Exception {
|
||||||
|
final PeerWirePeer peer = manager.getPeerManager().getPeer(
|
||||||
|
e.getChannel());
|
||||||
|
// TODO handshake with random peer id
|
||||||
|
peer.handshake(manager.getTorrent().getInfoHash().toByteArray(),
|
||||||
|
"-TR2050-mcm14ye4h2mq".getBytes(), manager.getContext()
|
||||||
|
.getCapabilites().toBitSet());
|
||||||
|
peer.port((short) 1541);
|
||||||
|
super.channelConnected(ctx, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
|
||||||
|
throws Exception {
|
||||||
|
final PeerWirePeer peer = manager.getPeerManager().getPeer(
|
||||||
|
e.getChannel());
|
||||||
|
final Object msg = e.getMessage();
|
||||||
|
|
||||||
|
if (msg instanceof BitfieldMessage) {
|
||||||
|
final BitfieldMessage bitfield = (BitfieldMessage) msg;
|
||||||
|
final BitSet bitset = bitfield.getBitfield();
|
||||||
|
peer.getTorrentPeer().getBitfield().setBits(bitset);
|
||||||
|
testInterest(peer);
|
||||||
|
} else if (msg instanceof InterestedMessage) {
|
||||||
|
peer.getTorrentPeer()
|
||||||
|
.setPeerInterestState(InterestState.INTERESTED);
|
||||||
|
peerIntrestUpdate(peer, peer.getTorrentPeer()
|
||||||
|
.getPeerInterestState());
|
||||||
|
} else if (msg instanceof NotInterestedMessage) {
|
||||||
|
peer.getTorrentPeer().setPeerInterestState(
|
||||||
|
InterestState.UNINTERESTED);
|
||||||
|
peerIntrestUpdate(peer, peer.getTorrentPeer()
|
||||||
|
.getPeerInterestState());
|
||||||
|
} else if (msg instanceof UnchokeMessage) {
|
||||||
|
peer.getTorrentPeer().setPeerChokingState(ChokingState.UNCHOKED);
|
||||||
|
peerChokeUpdate(peer, peer.getTorrentPeer().getPeerChokingState());
|
||||||
|
} else if (msg instanceof ChokeMessage) {
|
||||||
|
peer.getTorrentPeer().setPeerChokingState(ChokingState.CHOKED);
|
||||||
|
peerChokeUpdate(peer, peer.getTorrentPeer().getPeerChokingState());
|
||||||
|
} else if (msg instanceof RequestMessage) {
|
||||||
|
final RequestMessage request = (RequestMessage) msg;
|
||||||
|
final Torrent torrent = manager.getTorrent();
|
||||||
|
final TorrentPart part = torrent.getPart(request.getIndex(),
|
||||||
|
request.getStart(), request.getLength());
|
||||||
|
processRequest(peer, part);
|
||||||
|
} else if (msg instanceof PieceMessage) {
|
||||||
|
final PieceMessage pieceMsg = (PieceMessage) msg;
|
||||||
|
final Torrent torrent = manager.getTorrent();
|
||||||
|
final TorrentPart part = torrent.getPart(pieceMsg.getIndex(),
|
||||||
|
pieceMsg.getStart(), pieceMsg.getLength());
|
||||||
|
processDownload(peer, part, pieceMsg.getBlock());
|
||||||
|
} else if (msg instanceof HaveMessage) {
|
||||||
|
final HaveMessage have = (HaveMessage) msg;
|
||||||
|
final Torrent torrent = manager.getTorrent();
|
||||||
|
final TorrentPiece piece = torrent.getPiece(have.getPiece());
|
||||||
|
peer.getTorrentPeer().getBitfield().setPiece(piece, true);
|
||||||
|
testInterest(peer);
|
||||||
|
} else if (msg instanceof KeepAliveMessage) {
|
||||||
|
keepAlive(peer);
|
||||||
|
} else if (msg instanceof AllowedFastMessage) {
|
||||||
|
final AllowedFastMessage fast = (AllowedFastMessage) msg;
|
||||||
|
final Torrent torrent = manager.getTorrent();
|
||||||
|
for (final int index : fast.getPieces()) {
|
||||||
|
final TorrentPiece piece = torrent.getPiece(index);
|
||||||
|
allowedFast(peer, piece);
|
||||||
|
}
|
||||||
|
} else if (msg instanceof SuggestPieceMessage) {
|
||||||
|
final SuggestPieceMessage suggest = (SuggestPieceMessage) msg;
|
||||||
|
final Torrent torrent = manager.getTorrent();
|
||||||
|
final TorrentPiece piece = torrent.getPiece(suggest.getPiece());
|
||||||
|
suggested(peer, piece);
|
||||||
|
}
|
||||||
|
super.messageReceived(ctx, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
|
||||||
|
throws Exception {
|
||||||
|
// any exception thrown, the channel is disconnected.
|
||||||
|
e.getCause().printStackTrace();
|
||||||
|
e.getChannel().disconnect();
|
||||||
|
super.exceptionCaught(ctx, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
|
||||||
|
throws Exception {
|
||||||
|
final PeerWirePeer peer = manager.getPeerManager().getPeer(
|
||||||
|
e.getChannel());
|
||||||
|
keepAlive(peer);
|
||||||
|
super.channelIdle(ctx, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the interest in the peer
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
*/
|
||||||
|
private void testInterest(PeerWirePeer peer) {
|
||||||
|
switch (interestAlgorithm.interested(peer.getTorrentPeer())) {
|
||||||
|
case INTERESTED:
|
||||||
|
peer.interested();
|
||||||
|
return;
|
||||||
|
case UNINTERESTED:
|
||||||
|
testChoke(peer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test choke interest to this peer
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
*/
|
||||||
|
private void testChoke(PeerWirePeer peer) {
|
||||||
|
switch (interestAlgorithm.choke(peer.getTorrentPeer())) {
|
||||||
|
case CHOKED:
|
||||||
|
peer.choke();
|
||||||
|
return;
|
||||||
|
case UNCHOKED:
|
||||||
|
peer.unchoke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the peer choke interest change.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @param state
|
||||||
|
* the new interest state
|
||||||
|
*/
|
||||||
|
private void peerIntrestUpdate(PeerWirePeer peer, InterestState state) {
|
||||||
|
switch (peerAlgorithm.interested(peer.getTorrentPeer(), state)) {
|
||||||
|
case CHOKED:
|
||||||
|
peer.choke();
|
||||||
|
return;
|
||||||
|
|
||||||
|
case UNCHOKED:
|
||||||
|
peer.unchoke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the peer choke change.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @param state
|
||||||
|
* the choke state
|
||||||
|
*/
|
||||||
|
private void peerChokeUpdate(PeerWirePeer peer, ChokingState state) {
|
||||||
|
switch (peerAlgorithm.choked(peer.getTorrentPeer(), state)) {
|
||||||
|
case DISCONNECT:
|
||||||
|
peer.disconnect();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONNECT_NEW_PEER:
|
||||||
|
peer.disconnect();
|
||||||
|
connect(peerAlgorithm.connect());
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DOWNLOAD:
|
||||||
|
download(peer,
|
||||||
|
downloadAlgorithm.getNextPart(peer.getTorrentPeer(), null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the upload request
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @param part
|
||||||
|
* the part
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void processRequest(PeerWirePeer peer, TorrentPart part)
|
||||||
|
throws IOException {
|
||||||
|
switch (uploadAlgorithm.request(peer.getTorrentPeer(), part)) {
|
||||||
|
case DISCONNECT:
|
||||||
|
peer.disconnect();
|
||||||
|
break;
|
||||||
|
case REJECT:
|
||||||
|
if (!peer.getTorrentPeer().getCapabilities()
|
||||||
|
.supports(TorrentPeerCapability.FAST_PEERS))
|
||||||
|
return;
|
||||||
|
peer.reject(part.getPiece().getIndex(), part.getStart(),
|
||||||
|
part.getLength());
|
||||||
|
break;
|
||||||
|
case CONNECT_NEW_PEER:
|
||||||
|
peer.disconnect();
|
||||||
|
connect(peerAlgorithm.connect());
|
||||||
|
break;
|
||||||
|
case CHOKE:
|
||||||
|
peer.choke();
|
||||||
|
break;
|
||||||
|
case UPLOAD:
|
||||||
|
upload(peer, part);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes an download
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the uploader peer
|
||||||
|
* @param part
|
||||||
|
* the part
|
||||||
|
* @param data
|
||||||
|
* the downloaded content
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void processDownload(final PeerWirePeer peer,
|
||||||
|
final TorrentPart part, ByteBuffer data) throws IOException {
|
||||||
|
final TorrentPart nextPart = downloadAlgorithm.getNextPart(
|
||||||
|
peer.getTorrentPeer(), part);
|
||||||
|
boolean complete = downloadAlgorithm.isComplete(peer.getTorrentPeer(),
|
||||||
|
part.getPiece());
|
||||||
|
final ChannelFuture future = download(peer, nextPart);
|
||||||
|
|
||||||
|
// store piece
|
||||||
|
if (!datastore.write(part, data))
|
||||||
|
return;
|
||||||
|
if (!complete)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (datastore.checksum(part.getPiece())) {
|
||||||
|
manager.getContext().getBitfield().setPiece(part.getPiece(), true);
|
||||||
|
manager.getPeerManager().executeActive(new PeerCallback() {
|
||||||
|
@Override
|
||||||
|
public void callback(PeerWirePeer peer) {
|
||||||
|
peer.have(part.getPiece().getIndex());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
System.exit(0);
|
||||||
|
manager.getContext().getBitfield().setPiece(part.getPiece(), false);
|
||||||
|
switch (downloadAlgorithm.corrupted(peer.getTorrentPeer(),
|
||||||
|
part.getPiece())) {
|
||||||
|
case CHOKE:
|
||||||
|
if (future != null && !future.cancel())
|
||||||
|
peer.cancel(nextPart.getPiece().getIndex(),
|
||||||
|
nextPart.getStart(), nextPart.getLength());
|
||||||
|
peer.choke();
|
||||||
|
break;
|
||||||
|
case DISCONNECT:
|
||||||
|
peer.disconnect();
|
||||||
|
break;
|
||||||
|
case CONNECT_NEW_PEER:
|
||||||
|
peer.disconnect();
|
||||||
|
connect(peerAlgorithm.connect());
|
||||||
|
break;
|
||||||
|
case CONTINUE:
|
||||||
|
break;
|
||||||
|
case CANCEL:
|
||||||
|
if (future != null && !future.cancel())
|
||||||
|
peer.cancel(nextPart.getPiece().getIndex(),
|
||||||
|
nextPart.getStart(), nextPart.getLength());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check keep alive interest in this peer.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
*/
|
||||||
|
private void keepAlive(PeerWirePeer peer) {
|
||||||
|
switch (peerAlgorithm.keepAlive(peer.getTorrentPeer())) {
|
||||||
|
case DISCONNECT:
|
||||||
|
peer.disconnect();
|
||||||
|
break;
|
||||||
|
case CONNECT_NEW_PEER:
|
||||||
|
peer.disconnect();
|
||||||
|
connect(peerAlgorithm.connect());
|
||||||
|
break;
|
||||||
|
case KEEP_ALIVE:
|
||||||
|
peer.keepAlive();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do the part request
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the uploader peer
|
||||||
|
* @param part
|
||||||
|
* the part
|
||||||
|
* @return an {@link ChannelFuture}. Can be used to cancel the message send.
|
||||||
|
*/
|
||||||
|
private ChannelFuture download(PeerWirePeer peer, TorrentPart part) {
|
||||||
|
if (part == null) {
|
||||||
|
testInterest(peer);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return peer.request(part.getPiece().getIndex(), part.getStart(),
|
||||||
|
part.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do the part upload
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the downloader peer
|
||||||
|
* @param part
|
||||||
|
* the part
|
||||||
|
*/
|
||||||
|
private void upload(PeerWirePeer peer, TorrentPart part) throws IOException {
|
||||||
|
if (part == null) {
|
||||||
|
testChoke(peer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final ByteBuffer data = datastore.read(part);
|
||||||
|
peer.upload(part.getPiece().getIndex(), part.getStart(),
|
||||||
|
part.getLength(), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to establish an connection to <tt>peer</tt>
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer to be connected.
|
||||||
|
*/
|
||||||
|
private void connect(TorrentPeer peer) {
|
||||||
|
if (peer == null)
|
||||||
|
return;
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the suggested piece
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the suggesting peer
|
||||||
|
* @param piece
|
||||||
|
* the suggested piece
|
||||||
|
*/
|
||||||
|
private void suggested(PeerWirePeer peer, TorrentPiece piece) {
|
||||||
|
final TorrentPart part = downloadAlgorithm.sugested(
|
||||||
|
peer.getTorrentPeer(), piece);
|
||||||
|
if (part == null)
|
||||||
|
return;
|
||||||
|
download(peer, part);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the allowed fast piece
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the allowing peer
|
||||||
|
* @param piece
|
||||||
|
* the allowed piece
|
||||||
|
*/
|
||||||
|
private void allowedFast(PeerWirePeer peer, TorrentPiece piece) {
|
||||||
|
final TorrentPart part = downloadAlgorithm.allowedFast(
|
||||||
|
peer.getTorrentPeer(), piece);
|
||||||
|
if (part == null)
|
||||||
|
return;
|
||||||
|
download(peer, part);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.handler;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.PeerWirePeer;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.protocol.peerwire.message.HandshakeMessage;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
import net.torrent.torrent.context.TorrentPeerID;
|
||||||
|
import net.torrent.torrent.context.TorrentPeerCapabilities.TorrentPeerCapability;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.channel.ChannelStateEvent;
|
||||||
|
import org.jboss.netty.channel.MessageEvent;
|
||||||
|
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles pre-algoritihm handler stuff.
|
||||||
|
* <p>
|
||||||
|
* Updates {@link TorrentManager} state.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class PeerWireManagerHeadHandler extends SimpleChannelHandler {
|
||||||
|
/**
|
||||||
|
* The torrent manager
|
||||||
|
*/
|
||||||
|
private final TorrentManager manager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager
|
||||||
|
*/
|
||||||
|
public PeerWireManagerHeadHandler(TorrentManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
|
||||||
|
throws Exception {
|
||||||
|
manager.getConnectionManager().add(e.getChannel());
|
||||||
|
manager.getPeerManager().add(e.getChannel(), null);
|
||||||
|
super.channelOpen(ctx, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
|
||||||
|
throws Exception {
|
||||||
|
if (!manager.getConnectionManager().update(e.getChannel())) {
|
||||||
|
e.getChannel().close();
|
||||||
|
}
|
||||||
|
manager.getPeerManager().update(e.getChannel());
|
||||||
|
super.channelConnected(ctx, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
|
||||||
|
throws Exception {
|
||||||
|
final Channel channel = e.getChannel();
|
||||||
|
final PeerWirePeer pwpeer = manager.getPeerManager().getPeer(
|
||||||
|
e.getChannel());
|
||||||
|
final Object msg = e.getMessage();
|
||||||
|
|
||||||
|
if (msg instanceof HandshakeMessage) {
|
||||||
|
final HandshakeMessage handshake = (HandshakeMessage) msg;
|
||||||
|
final TorrentPeer peer = manager.getContext().getPeer(
|
||||||
|
TorrentPeerID.create(handshake.getPeerId()),
|
||||||
|
(InetSocketAddress) channel.getRemoteAddress());
|
||||||
|
pwpeer.setTorrentPeer(peer);
|
||||||
|
peer.setSocketAddress((InetSocketAddress) channel
|
||||||
|
.getRemoteAddress());
|
||||||
|
peer.getCapabilities().setCapabilities(handshake.getReserved());
|
||||||
|
|
||||||
|
// TODO send bitfield
|
||||||
|
if (peer.getCapabilities().supports(
|
||||||
|
TorrentPeerCapability.FAST_PEERS)) {
|
||||||
|
pwpeer.haveAll();
|
||||||
|
} else {
|
||||||
|
// pwpeer.bitfield(manager.getContext().getBitfield().getBits());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.messageReceived(ctx, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.handler;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.PeerWirePeer;
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.protocol.peerwire.message.CancelMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.PieceMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.RequestMessage;
|
||||||
|
import net.torrent.protocol.peerwire.message.fast.RejectMessage;
|
||||||
|
import net.torrent.torrent.Torrent;
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
|
import org.jboss.netty.channel.ChannelFutureListener;
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.channel.ChannelStateEvent;
|
||||||
|
import org.jboss.netty.channel.MessageEvent;
|
||||||
|
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles post-algorithm handler stuff.
|
||||||
|
* <p>
|
||||||
|
* Updates {@link TorrentManager} state.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class PeerWireManagerTailHandler extends SimpleChannelHandler {
|
||||||
|
/**
|
||||||
|
* The torrent manager
|
||||||
|
*/
|
||||||
|
private final TorrentManager manager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager
|
||||||
|
*/
|
||||||
|
public PeerWireManagerTailHandler(TorrentManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
|
||||||
|
throws Exception {
|
||||||
|
final Object msg = e.getMessage();
|
||||||
|
|
||||||
|
if (msg instanceof PieceMessage) {
|
||||||
|
final PieceMessage pieceMsg = (PieceMessage) msg;
|
||||||
|
final Torrent torrent = manager.getTorrent();
|
||||||
|
final TorrentPart part = torrent.getPart(pieceMsg.getIndex(),
|
||||||
|
pieceMsg.getStart(), pieceMsg.getLength());
|
||||||
|
manager.getDownloadManager().remove(part);
|
||||||
|
} else if (msg instanceof RejectMessage) {
|
||||||
|
final RejectMessage reject = (RejectMessage) msg;
|
||||||
|
final Torrent torrent = manager.getTorrent();
|
||||||
|
final TorrentPart part = torrent.getPart(reject.getIndex(),
|
||||||
|
reject.getStart(), reject.getLength());
|
||||||
|
manager.getDownloadManager().remove(part);
|
||||||
|
}
|
||||||
|
super.messageReceived(ctx, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeRequested(ChannelHandlerContext ctx, MessageEvent e)
|
||||||
|
throws Exception {
|
||||||
|
final PeerWirePeer peer = manager.getPeerManager().getPeer(
|
||||||
|
e.getChannel());
|
||||||
|
final Object msg = e.getMessage();
|
||||||
|
|
||||||
|
if (msg instanceof PieceMessage) {
|
||||||
|
final PieceMessage message = (PieceMessage) msg;
|
||||||
|
final Torrent torrent = manager.getContext().getTorrent();
|
||||||
|
final TorrentPart part = torrent.getPart(message.getIndex(),
|
||||||
|
message.getStart(), message.getLength());
|
||||||
|
manager.getUploadManager().add(part, peer.getTorrentPeer());
|
||||||
|
e.getFuture().addListener(new ChannelFutureListener() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(ChannelFuture future)
|
||||||
|
throws Exception {
|
||||||
|
manager.getUploadManager().remove(part);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (msg instanceof RequestMessage) {
|
||||||
|
final RequestMessage message = (RequestMessage) msg;
|
||||||
|
final Torrent torrent = manager.getContext().getTorrent();
|
||||||
|
final TorrentPart part = torrent.getPart(message.getIndex(),
|
||||||
|
message.getStart(), message.getLength());
|
||||||
|
manager.getDownloadManager().add(part, peer.getTorrentPeer());
|
||||||
|
} else if (msg instanceof CancelMessage) {
|
||||||
|
final CancelMessage message = (CancelMessage) msg;
|
||||||
|
final Torrent torrent = manager.getContext().getTorrent();
|
||||||
|
final TorrentPart part = torrent.getPart(message.getIndex(),
|
||||||
|
message.getStart(), message.getLength());
|
||||||
|
e.getFuture().addListener(new ChannelFutureListener() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(ChannelFuture future)
|
||||||
|
throws Exception {
|
||||||
|
manager.getDownloadManager().remove(part);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
super.writeRequested(ctx, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelDisconnected(ChannelHandlerContext ctx,
|
||||||
|
ChannelStateEvent e) throws Exception {
|
||||||
|
manager.getConnectionManager().update(e.getChannel());
|
||||||
|
final PeerWirePeer peer = manager.getPeerManager().update(
|
||||||
|
e.getChannel());
|
||||||
|
if (peer.getTorrentPeer() != null) {
|
||||||
|
manager.getDownloadManager().remove(peer.getTorrentPeer());
|
||||||
|
manager.getUploadManager().remove(peer.getTorrentPeer());
|
||||||
|
}
|
||||||
|
super.channelDisconnected(ctx, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
|
||||||
|
throws Exception {
|
||||||
|
manager.getConnectionManager().remove(e.getChannel());
|
||||||
|
final PeerWirePeer peer = manager.getPeerManager().remove(
|
||||||
|
e.getChannel());
|
||||||
|
if (peer.getTorrentPeer() != null) {
|
||||||
|
manager.getDownloadManager().remove(peer.getTorrentPeer());
|
||||||
|
manager.getUploadManager().remove(peer.getTorrentPeer());
|
||||||
|
}
|
||||||
|
super.channelClosed(ctx, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.manager;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.torrent.torrent.context.TorrentContext;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection manager: manages active and inactive connections.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class ConnectionManager implements Iterable<Channel> {
|
||||||
|
/**
|
||||||
|
* The torrent context
|
||||||
|
*/
|
||||||
|
private final TorrentContext context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of active channels
|
||||||
|
*/
|
||||||
|
private final Set<Channel> activeChannels = new HashSet<Channel>();
|
||||||
|
/**
|
||||||
|
* The list of inactive channels
|
||||||
|
*/
|
||||||
|
private final Set<Channel> inactiveChannels = new HashSet<Channel>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* the torrent context
|
||||||
|
*/
|
||||||
|
public ConnectionManager(TorrentContext context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new channel
|
||||||
|
*
|
||||||
|
* @param channel
|
||||||
|
* the channel
|
||||||
|
* @return true if channel was added. False if was already in list.
|
||||||
|
*/
|
||||||
|
public boolean add(Channel channel) {
|
||||||
|
if (channel.isConnected()) {
|
||||||
|
return activeChannels.add(channel);
|
||||||
|
} else {
|
||||||
|
return inactiveChannels.add(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if contains the channel.
|
||||||
|
*
|
||||||
|
* @param channel
|
||||||
|
* the channel
|
||||||
|
* @return true if channel is registered
|
||||||
|
*/
|
||||||
|
public boolean contains(Channel channel) {
|
||||||
|
if (activeChannels.contains(channel))
|
||||||
|
return true;
|
||||||
|
if (inactiveChannels.contains(channel))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the channel
|
||||||
|
*
|
||||||
|
* @param channel
|
||||||
|
* the channel
|
||||||
|
* @return true if channel was registered
|
||||||
|
*/
|
||||||
|
public boolean remove(Channel channel) {
|
||||||
|
if (activeChannels.remove(channel))
|
||||||
|
return true;
|
||||||
|
if (inactiveChannels.remove(channel))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the state of the channel. In practice, remove and adds again the
|
||||||
|
* channel.
|
||||||
|
*
|
||||||
|
* @param channel
|
||||||
|
* the channel
|
||||||
|
* @return true if channel has been added successfully.
|
||||||
|
*/
|
||||||
|
public boolean update(Channel channel) {
|
||||||
|
if (!remove(channel))
|
||||||
|
return false;
|
||||||
|
return add(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get to count of active connections
|
||||||
|
*
|
||||||
|
* @return the amount of active connections
|
||||||
|
*/
|
||||||
|
public int getActiveConnections() {
|
||||||
|
return activeChannels.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get to count of inactive connections
|
||||||
|
*
|
||||||
|
* @return the amount of inactive connections
|
||||||
|
*/
|
||||||
|
public int getInactiveConnections() {
|
||||||
|
return inactiveChannels.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of active channels
|
||||||
|
*
|
||||||
|
* @return list of active channels
|
||||||
|
*/
|
||||||
|
public Set<Channel> getActiveChannels() {
|
||||||
|
return Collections.unmodifiableSet(activeChannels);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of inactive channels
|
||||||
|
*
|
||||||
|
* @return list of inactive channels
|
||||||
|
*/
|
||||||
|
public Set<Channel> getInactiveChannels() {
|
||||||
|
return Collections.unmodifiableSet(inactiveChannels);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Channel> iterator() {
|
||||||
|
return activeChannels.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torent context
|
||||||
|
*
|
||||||
|
* @return the torrent context
|
||||||
|
*/
|
||||||
|
public TorrentContext getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.manager;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
import net.torrent.torrent.context.TorrentContext;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
|
||||||
|
public class DownloadManager implements Iterable<TorrentPart> {
|
||||||
|
private final TorrentContext context;
|
||||||
|
|
||||||
|
private final Map<TorrentPart, TorrentPeer> activeParts = new HashMap<TorrentPart, TorrentPeer>();
|
||||||
|
|
||||||
|
public DownloadManager(TorrentContext context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDownloading(TorrentPart torrentPart) {
|
||||||
|
return activeParts.containsKey(torrentPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDownloading(TorrentPiece piece) {
|
||||||
|
for (final TorrentPart part : activeParts.keySet()) {
|
||||||
|
if (part.getPiece().equals(piece))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDownloading(TorrentPeer peer) {
|
||||||
|
return activeParts.containsValue(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentPeer getPeer(TorrentPart torrentPart) {
|
||||||
|
return activeParts.get(torrentPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<TorrentPart> getTorrentParts(TorrentPeer peer) {
|
||||||
|
final Set<TorrentPart> parts = new HashSet<TorrentPart>();
|
||||||
|
for (final Entry<TorrentPart, TorrentPeer> entry : activeParts
|
||||||
|
.entrySet()) {
|
||||||
|
if (entry.getValue().equals(peer))
|
||||||
|
parts.add(entry.getKey());
|
||||||
|
}
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInactive() {
|
||||||
|
return activeParts.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentPeer add(TorrentPart torrentPart, TorrentPeer peer) {
|
||||||
|
return activeParts.put(torrentPart, peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentPeer remove(TorrentPart torrentPart) {
|
||||||
|
return activeParts.remove(torrentPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<TorrentPart> remove(TorrentPeer peer) {
|
||||||
|
final Set<TorrentPart> parts = new HashSet<TorrentPart>();
|
||||||
|
for (TorrentPart part : getTorrentParts(peer)) {
|
||||||
|
if (activeParts.remove(part) != null)
|
||||||
|
parts.add(part);
|
||||||
|
}
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getActiveDownloadsCount() {
|
||||||
|
return activeParts.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<TorrentPart, TorrentPeer> getActiveDownloads() {
|
||||||
|
return Collections.unmodifiableMap(activeParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<TorrentPart> iterator() {
|
||||||
|
return activeParts.keySet().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentContext getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.manager;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.PeerWirePeer;
|
||||||
|
import net.torrent.torrent.context.TorrentContext;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
import net.torrent.util.PeerCallback;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
|
||||||
|
public class PeerManager implements Iterable<PeerWirePeer> {
|
||||||
|
private final TorrentContext context;
|
||||||
|
private final ConnectionManager connectionManager;
|
||||||
|
|
||||||
|
private final Map<Channel, PeerWirePeer> activePeers = new HashMap<Channel, PeerWirePeer>();
|
||||||
|
private final Map<Channel, PeerWirePeer> inactivePeers = new HashMap<Channel, PeerWirePeer>();
|
||||||
|
|
||||||
|
public PeerManager(TorrentContext context,
|
||||||
|
ConnectionManager connectionManager) {
|
||||||
|
this.context = context;
|
||||||
|
this.connectionManager = connectionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Channel channel) {
|
||||||
|
if (activePeers.containsKey(channel))
|
||||||
|
return true;
|
||||||
|
if (inactivePeers.containsKey(channel))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(PeerWirePeer peer) {
|
||||||
|
if (activePeers.containsValue(peer))
|
||||||
|
return true;
|
||||||
|
if (inactivePeers.containsValue(peer))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerWirePeer getPeer(Channel channel) {
|
||||||
|
PeerWirePeer peer = activePeers.get(channel);
|
||||||
|
if (peer == null)
|
||||||
|
peer = inactivePeers.get(channel);
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Channel getChannel(PeerWirePeer peer) {
|
||||||
|
for (final Entry<Channel, PeerWirePeer> entry : activePeers.entrySet()) {
|
||||||
|
if (entry.getValue().equals(peer))
|
||||||
|
return entry.getKey();
|
||||||
|
}
|
||||||
|
for (final Entry<Channel, PeerWirePeer> entry : inactivePeers
|
||||||
|
.entrySet()) {
|
||||||
|
if (entry.getValue().equals(peer))
|
||||||
|
return entry.getKey();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return activePeers.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerWirePeer add(Channel channel, TorrentPeer peer) {
|
||||||
|
if (channel.isConnected()) {
|
||||||
|
return activePeers.put(channel, new PeerWirePeer(channel, peer));
|
||||||
|
} else {
|
||||||
|
return inactivePeers.put(channel, new PeerWirePeer(channel, peer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerWirePeer remove(Channel channel) {
|
||||||
|
PeerWirePeer peer;
|
||||||
|
if ((peer = activePeers.remove(channel)) != null)
|
||||||
|
return peer;
|
||||||
|
if ((peer = inactivePeers.remove(channel)) != null)
|
||||||
|
return peer;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerWirePeer remove(PeerWirePeer peer) {
|
||||||
|
final Channel channel = getChannel(peer);
|
||||||
|
PeerWirePeer peerRemoved;
|
||||||
|
if ((peerRemoved = activePeers.remove(channel)) != null)
|
||||||
|
return peerRemoved;
|
||||||
|
if ((peerRemoved = inactivePeers.remove(channel)) != null)
|
||||||
|
return peerRemoved;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerWirePeer update(Channel channel) {
|
||||||
|
PeerWirePeer peer;
|
||||||
|
if ((peer = remove(channel)) == null)
|
||||||
|
return null;
|
||||||
|
return add(channel, peer.getTorrentPeer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getActivePeersCount() {
|
||||||
|
return activePeers.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImactivePeersCount() {
|
||||||
|
return activePeers.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Channel, PeerWirePeer> getActivePeers() {
|
||||||
|
return Collections.unmodifiableMap(activePeers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Channel, PeerWirePeer> getInactivePeers() {
|
||||||
|
return Collections.unmodifiableMap(inactivePeers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Channel> getActiveChannels() {
|
||||||
|
return Collections.unmodifiableSet(activePeers.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Channel> getInactiveChannels() {
|
||||||
|
return Collections.unmodifiableSet(inactivePeers.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void executeActive(PeerCallback callback) {
|
||||||
|
for (final Entry<Channel, PeerWirePeer> entry : this.activePeers
|
||||||
|
.entrySet()) {
|
||||||
|
callback.callback(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void executeInactive(PeerCallback callback) {
|
||||||
|
for (final Entry<Channel, PeerWirePeer> entry : this.inactivePeers
|
||||||
|
.entrySet()) {
|
||||||
|
callback.callback(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(PeerCallback callback) {
|
||||||
|
executeActive(callback);
|
||||||
|
executeInactive(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<PeerWirePeer> iterator() {
|
||||||
|
return activePeers.values().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentContext getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionManager getConnectionManager() {
|
||||||
|
return connectionManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.manager;
|
||||||
|
|
||||||
|
import net.torrent.protocol.datastore.TorrentDatastore;
|
||||||
|
import net.torrent.torrent.Torrent;
|
||||||
|
import net.torrent.torrent.context.TorrentContext;
|
||||||
|
|
||||||
|
public class TorrentManager {
|
||||||
|
private final TorrentContext context;
|
||||||
|
|
||||||
|
private final ConnectionManager connectionManager;
|
||||||
|
private final PeerManager peerManager;
|
||||||
|
private final DownloadManager downloadManager;
|
||||||
|
private final UploadManager uploadManager;
|
||||||
|
|
||||||
|
private final TorrentDatastore datastore;
|
||||||
|
|
||||||
|
public TorrentManager(TorrentContext context, TorrentDatastore datastore) {
|
||||||
|
this.context = context;
|
||||||
|
this.datastore = datastore;
|
||||||
|
connectionManager = new ConnectionManager(context);
|
||||||
|
peerManager = new PeerManager(context, connectionManager);
|
||||||
|
downloadManager = new DownloadManager(context);
|
||||||
|
uploadManager = new UploadManager(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentContext getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentDatastore getDatastore() {
|
||||||
|
return datastore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionManager getConnectionManager() {
|
||||||
|
return connectionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerManager getPeerManager() {
|
||||||
|
return peerManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownloadManager getDownloadManager() {
|
||||||
|
return downloadManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UploadManager getUploadManager() {
|
||||||
|
return uploadManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Torrent getTorrent() {
|
||||||
|
return context.getTorrent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.manager;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.torrent.torrent.TorrentPart;
|
||||||
|
import net.torrent.torrent.context.TorrentContext;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
|
||||||
|
public class UploadManager implements Iterable<TorrentPart> {
|
||||||
|
private final TorrentContext context;
|
||||||
|
|
||||||
|
private final Map<TorrentPart, TorrentPeer> activeParts = new HashMap<TorrentPart, TorrentPeer>();
|
||||||
|
|
||||||
|
public UploadManager(TorrentContext context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUploading(TorrentPart torrentPart) {
|
||||||
|
return activeParts.containsKey(torrentPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUploading(TorrentPeer peer) {
|
||||||
|
return activeParts.containsValue(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentPeer getPeer(TorrentPart torrentPart) {
|
||||||
|
return activeParts.get(torrentPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<TorrentPart> getTorrentParts(TorrentPeer peer) {
|
||||||
|
final Set<TorrentPart> parts = new HashSet<TorrentPart>();
|
||||||
|
for (final Entry<TorrentPart, TorrentPeer> entry : activeParts
|
||||||
|
.entrySet()) {
|
||||||
|
if (entry.getValue().equals(peer))
|
||||||
|
parts.add(entry.getKey());
|
||||||
|
}
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInactive() {
|
||||||
|
return activeParts.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentPeer add(TorrentPart torrentPart, TorrentPeer peer) {
|
||||||
|
return activeParts.put(torrentPart, peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentPeer remove(TorrentPart torrentPart) {
|
||||||
|
return activeParts.remove(torrentPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<TorrentPart> remove(TorrentPeer peer) {
|
||||||
|
final Set<TorrentPart> parts = new HashSet<TorrentPart>();
|
||||||
|
for (TorrentPart part : getTorrentParts(peer)) {
|
||||||
|
if (activeParts.remove(part) != null)
|
||||||
|
parts.add(part);
|
||||||
|
}
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getActiveUploadsCount() {
|
||||||
|
return activeParts.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<TorrentPart, TorrentPeer> getActiveUploads() {
|
||||||
|
return Collections.unmodifiableMap(activeParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<TorrentPart> iterator() {
|
||||||
|
return activeParts.keySet().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentContext getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* bitfield: [len=0001+X][id=5][bitfield]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The bitfield message may only be sent immediately after the handshaking
|
||||||
|
* sequence is completed, and before any other messages are sent. It is
|
||||||
|
* optional, and need not be sent if a client has no pieces. The bitfield
|
||||||
|
* message is variable length, where X is the length of the bitfield. The
|
||||||
|
* payload is a bitfield representing the pieces that have been successfully
|
||||||
|
* downloaded. The high bit in the first byte corresponds to piece index 0. Bits
|
||||||
|
* that are cleared indicated a missing piece, and set bits indicate a valid and
|
||||||
|
* available piece. Spare bits at the end are set to zero. A bitfield of the
|
||||||
|
* wrong length is considered an error. Clients should drop the connection if
|
||||||
|
* they receive bitfields that are not of the correct size, or if the bitfield
|
||||||
|
* has any of the spare bits set.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source BitTorrent documentation
|
||||||
|
*/
|
||||||
|
public class BitfieldMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x05;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of bits with the following scheme:
|
||||||
|
* <ul>
|
||||||
|
* <li><b>true</b>: has the piece</li>
|
||||||
|
* </li><b>false</b>: piece is missing</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
private BitSet bitfield;
|
||||||
|
|
||||||
|
public BitfieldMessage(BitSet bitfield) {
|
||||||
|
this.bitfield = bitfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitfieldMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.readerIndex(buffer.readerIndex() - 5);
|
||||||
|
int len = buffer.readInt() - 2;
|
||||||
|
buffer.readByte(); // unk
|
||||||
|
|
||||||
|
bitfield = new BitSet(len * 8);
|
||||||
|
int i = 0;
|
||||||
|
int read = 0;
|
||||||
|
while (read <= len) {
|
||||||
|
byte b = buffer.readByte();
|
||||||
|
for (int j = 128; j > 0; j >>= 1) {
|
||||||
|
bitfield.set(i++, (b & j) != 0);
|
||||||
|
}
|
||||||
|
read++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
// byte[] bytes = new byte[bitfield.size() / 8 - 4];
|
||||||
|
// for (int i = 0; i < bitfield.size(); i++) {
|
||||||
|
// if((bytes.length - i / 8 - 1) > bytes.length)
|
||||||
|
// break;
|
||||||
|
// if (bitfield.get(i)) {
|
||||||
|
// bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// buffer.put(bytes);
|
||||||
|
for (int i = 0; i < bitfield.size();) {
|
||||||
|
byte data = 0;
|
||||||
|
for (int j = 128; i < bitfield.size() && j > 0; j >>= 1, i++) {
|
||||||
|
if (bitfield.get(i)) {
|
||||||
|
data |= j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.writeByte(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitSet getBitfield() {
|
||||||
|
return bitfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBitfield(BitSet bitfield) {
|
||||||
|
this.bitfield = bitfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BitfieldMessage [bitfield=" + bitfield + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* cancel: [len=0013][id=8][index][begin][length]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The cancel message is fixed length, and is used to cancel block requests. The
|
||||||
|
* payload is identical to that of the "request" message. It is typically used
|
||||||
|
* during "End Game" (check for algorithms).
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source BitTorrent documentation
|
||||||
|
*/
|
||||||
|
public class CancelMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x08;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The piece index
|
||||||
|
*/
|
||||||
|
private int index;
|
||||||
|
/**
|
||||||
|
* The start offset
|
||||||
|
*/
|
||||||
|
private int start;
|
||||||
|
/**
|
||||||
|
* The length
|
||||||
|
*/
|
||||||
|
private int length;
|
||||||
|
|
||||||
|
public CancelMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public CancelMessage(int index, int start, int length) {
|
||||||
|
this.index = index;
|
||||||
|
this.start = start;
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
this.index = buffer.readInt();
|
||||||
|
this.start = buffer.readInt();
|
||||||
|
this.length = buffer.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
buffer.writeInt(index);
|
||||||
|
buffer.writeInt(start);
|
||||||
|
buffer.writeInt(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStart(int start) {
|
||||||
|
this.start = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLength(int length) {
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "CancelMessage [index=" + index + ", start=" + start
|
||||||
|
+ ", length=" + length + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* choke: [len=0001][id=0]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The choke message is fixed-length and has no payload.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source BitTorrent documentation
|
||||||
|
*/
|
||||||
|
public class ChokeMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x00;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ChokeMessage []";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO documentation
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class HandshakeMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
/**
|
||||||
|
* Length of "BitTorrent protocol" string.
|
||||||
|
*/
|
||||||
|
private int pstrlen = 19;
|
||||||
|
/**
|
||||||
|
* The protocol string, must match to validate the protocol connection
|
||||||
|
*/
|
||||||
|
private String pstr = "BitTorrent protocol";
|
||||||
|
/**
|
||||||
|
* Addons supported by the client (bitfield)
|
||||||
|
*/
|
||||||
|
private BitSet reserved = new BitSet(64);
|
||||||
|
/**
|
||||||
|
* The torrent's hash
|
||||||
|
*/
|
||||||
|
private byte[] infohash;
|
||||||
|
/**
|
||||||
|
* The peer id
|
||||||
|
*/
|
||||||
|
private byte[] peerId;
|
||||||
|
|
||||||
|
public HandshakeMessage(byte[] infohash, byte[] peerId, BitSet reserved) {
|
||||||
|
this.infohash = infohash;
|
||||||
|
this.peerId = peerId;
|
||||||
|
this.reserved = reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HandshakeMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
this.pstrlen = buffer.readByte();
|
||||||
|
this.pstr = new String(buffer.readBytes(pstrlen).array());
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
byte b = buffer.readByte();
|
||||||
|
for (int j = 0; j < 8; j++) {
|
||||||
|
if ((b & (1 << j)) > 0) {
|
||||||
|
reserved.set(i * 8 + j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println(reserved);
|
||||||
|
// int bit = 0;
|
||||||
|
// for (int i = 0; i < 8; i++) {
|
||||||
|
// byte b = buffer.readByte();
|
||||||
|
// for (int j = 128; j > 0; j >>= 1) {
|
||||||
|
// reserved.set(bit++, (b & j) != 0);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// this.reserved = buffer.readBytes(8).array();
|
||||||
|
this.infohash = buffer.readBytes(20).array();
|
||||||
|
this.peerId = buffer.readBytes(20).array();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte((byte) this.pstr.length());
|
||||||
|
buffer.writeBytes(this.pstr.getBytes());
|
||||||
|
|
||||||
|
for (int i = 0; i < 64;) {
|
||||||
|
byte data = 0;
|
||||||
|
for (int j = 128; i < reserved.size() && j > 0; j >>= 1, i++) {
|
||||||
|
if (reserved.get(i)) {
|
||||||
|
data |= j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.writeByte(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// buffer.writeBytes(reserved);
|
||||||
|
buffer.writeBytes(this.infohash);
|
||||||
|
buffer.writeBytes(this.peerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPstrlen() {
|
||||||
|
return pstrlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPstrlen(int pstrlen) {
|
||||||
|
this.pstrlen = pstrlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPstr() {
|
||||||
|
return pstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPstr(String pstr) {
|
||||||
|
this.pstr = pstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitSet getReserved() {
|
||||||
|
return reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReserved(BitSet reserved) {
|
||||||
|
this.reserved = reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getInfohash() {
|
||||||
|
return infohash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInfohash(byte[] infohash) {
|
||||||
|
this.infohash = infohash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getPeerId() {
|
||||||
|
return peerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeerId(byte[] peerId) {
|
||||||
|
this.peerId = peerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final int maxLen = 10;
|
||||||
|
return "HandshakeMessage [pstrlen="
|
||||||
|
+ pstrlen
|
||||||
|
+ ", pstr="
|
||||||
|
+ pstr
|
||||||
|
+ ", reserved="
|
||||||
|
+ reserved
|
||||||
|
+ ", infohash="
|
||||||
|
+ (infohash != null ? Arrays.toString(Arrays.copyOf(infohash,
|
||||||
|
Math.min(infohash.length, maxLen))) : null)
|
||||||
|
+ ", peerId="
|
||||||
|
+ (peerId != null ? Arrays.toString(Arrays.copyOf(peerId,
|
||||||
|
Math.min(peerId.length, maxLen))) : null) + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* have: [len=0005][id=4][piece index(int)]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The have message is fixed length. The payload is the zero-based index of a
|
||||||
|
* that has just been successfully downloaded and verified via the hash.<br>
|
||||||
|
* <br>
|
||||||
|
* <b>Implementer's Note</b>: That is the strict definition, in reality some
|
||||||
|
* games may be played. In particular because peers are extremely unlikely to
|
||||||
|
* download pieces that they already have, a peer may choose not to advertise
|
||||||
|
* having a piece to a peer that already has that piece. At a minimum
|
||||||
|
* "HAVE supression" will result in a 50% reduction in the number of HAVE
|
||||||
|
* messages, this translates to around a 25-35% reduction in protocol overhead.
|
||||||
|
* At the same time, it may be worthwhile to send a HAVE message to a peer that
|
||||||
|
* has that piece already since it will be useful in determining which piece is
|
||||||
|
* rare. A malicious peer might also choose to advertise having pieces that it
|
||||||
|
* knows the peer will never download. Due to this attempting to model peers
|
||||||
|
* using this information is a bad idea.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source BitTorrent documentation
|
||||||
|
*/
|
||||||
|
public class HaveMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x04;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The new obtained piece index
|
||||||
|
*/
|
||||||
|
private int piece;
|
||||||
|
|
||||||
|
public HaveMessage(int piece) {
|
||||||
|
this.piece = piece;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HaveMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
this.piece = buffer.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
buffer.writeInt(piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPiece() {
|
||||||
|
return piece;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPiece(int piece) {
|
||||||
|
this.piece = piece;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "HaveMessage [piece=" + piece + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* interested: [len=0001][id=2]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The interested message is fixed-length and has no payload.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source BitTorrent documentation
|
||||||
|
*/
|
||||||
|
public class InterestedMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x02;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "InterestedMessage []";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* keep-alive: [len=0000]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The keep-alive message is a message with zero bytes, specified with the
|
||||||
|
* length prefix set to zero. There is no message ID and no payload. Peers may
|
||||||
|
* close a connection if they receive no messages (keep-alive or any other
|
||||||
|
* message) for a certain period of time, so a keep-alive message must be sent
|
||||||
|
* to maintain the connection alive if no command have been sent for a given
|
||||||
|
* amount of time. This amount of time is generally two minutes.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source BitTorrent documentation
|
||||||
|
*/
|
||||||
|
public class KeepAliveMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "KeepAliveMessage []";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* not interested: [len=0001][id=3]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The not interested message is fixed-length and has no payload.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source BitTorrent documentation
|
||||||
|
*/
|
||||||
|
public class NotInterestedMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x03;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "NotInterestedMessage []";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* piece: [len=0009+X][id=7][index][begin][block]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The piece message is variable length, where X is the length of the block. The
|
||||||
|
* payload contains the following information:<br>
|
||||||
|
* index: integer specifying the zero-based piece index<br>
|
||||||
|
* begin: integer specifying the zero-based byte offset within the piece<br>
|
||||||
|
* block: block of data, which is a subset of the piece specified by index.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source BitTorrent documentation
|
||||||
|
*/
|
||||||
|
public class PieceMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x07;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The piece index
|
||||||
|
*/
|
||||||
|
private int index;
|
||||||
|
/**
|
||||||
|
* The part start offset
|
||||||
|
*/
|
||||||
|
private int start;
|
||||||
|
/**
|
||||||
|
* The part length
|
||||||
|
*/
|
||||||
|
private int length;
|
||||||
|
/**
|
||||||
|
* The downloaded/uploaded data
|
||||||
|
*/
|
||||||
|
private ByteBuffer block;
|
||||||
|
|
||||||
|
public PieceMessage(RequestMessage request, ByteBuffer block) {
|
||||||
|
this.index = request.getIndex();
|
||||||
|
this.start = request.getStart();
|
||||||
|
this.length = request.getLength();
|
||||||
|
this.block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PieceMessage(int index, int start, int length, ByteBuffer block) {
|
||||||
|
this.index = index;
|
||||||
|
this.start = start;
|
||||||
|
this.length = length;
|
||||||
|
this.block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PieceMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
this.index = buffer.readInt();
|
||||||
|
this.start = buffer.readInt();
|
||||||
|
this.length = buffer.readableBytes();
|
||||||
|
this.block = buffer.readBytes(length).toByteBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
buffer.writeInt(index);
|
||||||
|
buffer.writeInt(start);
|
||||||
|
buffer.writeBytes(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStart(int start) {
|
||||||
|
this.start = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLength(int length) {
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteBuffer getBlock() {
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlock(ByteBuffer block) {
|
||||||
|
this.block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "PieceMessage [index=" + index + ", start=" + start
|
||||||
|
+ ", length=" + length + ", block=" + block + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* port: [len=0003][id=9][listen-port]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The port message is sent by newer versions of the Mainline that implements a
|
||||||
|
* DHT tracker. The listen port is the port this peer's DHT node is listening
|
||||||
|
* on. This peer should be inserted in the local routing table (if DHT tracker
|
||||||
|
* is supported).
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source BitTorrent documentation
|
||||||
|
*/
|
||||||
|
public class PortMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x09;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The DHT port
|
||||||
|
*/
|
||||||
|
private short port;
|
||||||
|
|
||||||
|
public PortMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortMessage(short port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
this.port = buffer.readShort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
buffer.writeShort(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPort(short port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "PortMessage [port=" + port + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* request: [len=0013][id=6][index][begin][length]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The request message is fixed length, and is used to request a block. The
|
||||||
|
* payload contains the following information:<br>
|
||||||
|
* index: integer specifying the zero-based piece index<br>
|
||||||
|
* begin: integer specifying the zero-based byte offset within the piece<br>
|
||||||
|
* length: integer specifying the requested length.<br>
|
||||||
|
* View #1 According to the official specification, "All current implementations
|
||||||
|
* use 2^15 (32KB), and close connections which request an amount greater than
|
||||||
|
* 2^17 (128KB)." As early as version 3 or 2004, this behavior was changed to
|
||||||
|
* use 2^14 (16KB) blocks. As of version 4.0 or mid-2005, the mainline
|
||||||
|
* disconnected on requests larger than 2^14 (16KB); and some clients have
|
||||||
|
* followed suit. Note that block requests are smaller than pieces (>=2^18
|
||||||
|
* bytes), so multiple requests will be needed to download a whole piece.<br>
|
||||||
|
* Strictly, the specification allows 2^15 (32KB) requests. The reality is near
|
||||||
|
* all clients will now use 2^14 (16KB) requests. Due to clients that enforce
|
||||||
|
* that size, it is recommended that implementations make requests of that size.
|
||||||
|
* Due to smaller requests resulting in higher overhead due to tracking a
|
||||||
|
* greater number of requests, implementers are advised against going below 2^14
|
||||||
|
* (16KB). The choice of request block size limit enforcement is not nearly so
|
||||||
|
* clear cut. With mainline version 4 enforcing 16KB requests, most clients will
|
||||||
|
* use that size.<br>
|
||||||
|
* At the same time 2^14 (16KB) is the semi-official (only semi because the
|
||||||
|
* official protocol document has not been updated) limit now, so enforcing that
|
||||||
|
* isn't wrong. At the same time, allowing larger requests enlarges the set of
|
||||||
|
* possible peers, and except on very low bandwidth connections (<256kbps)
|
||||||
|
* multiple blocks will be downloaded in one choke-timeperiod, thus merely
|
||||||
|
* enforcing the old limit causes minimal performance degradation. Due to this
|
||||||
|
* factor, it is recommended that only the older 2^17 (128KB) maximum size limit
|
||||||
|
* be enforced. View #2 This section has contained falsehoods for a large
|
||||||
|
* portion of the time this page has existed. This is the third time I (uau) am
|
||||||
|
* correcting this same section for incorrect information being added, so I
|
||||||
|
* won't rewrite it completely since it'll probably be broken again... Current
|
||||||
|
* version has at least the following errors: Mainline started using 2^14
|
||||||
|
* (16384) byte requests when it was still the only client in existence; only
|
||||||
|
* the "official specification" still talked about the obsolete 32768 byte value
|
||||||
|
* which was in reality neither the default size nor maximum allowed. In version
|
||||||
|
* 4 the request behavior did not change, but the maximum allowed size did
|
||||||
|
* change to equal the default size. In latest mainline versions the max has
|
||||||
|
* changed to 32768 (note that this is the first appearance of 32768 for either
|
||||||
|
* default or max size since the first ancient versions).<br>
|
||||||
|
* "Most older clients use 32KB requests" is false. Discussion of larger
|
||||||
|
* requests fails to take latency effects into account.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source BitTorrent documentation
|
||||||
|
*/
|
||||||
|
public class RequestMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x06;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The piece index
|
||||||
|
*/
|
||||||
|
private int index;
|
||||||
|
/**
|
||||||
|
* The piece start offset
|
||||||
|
*/
|
||||||
|
private int start;
|
||||||
|
/**
|
||||||
|
* The part length
|
||||||
|
*/
|
||||||
|
private int length;
|
||||||
|
|
||||||
|
public RequestMessage(int index, int start, int length) {
|
||||||
|
this.index = index;
|
||||||
|
this.start = start;
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequestMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
this.index = buffer.readInt();
|
||||||
|
this.start = buffer.readInt();
|
||||||
|
this.length = buffer.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
buffer.writeInt(index);
|
||||||
|
buffer.writeInt(start);
|
||||||
|
buffer.writeInt(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStart(int start) {
|
||||||
|
this.start = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLength(int length) {
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RequestMessage [index=" + index + ", start=" + start
|
||||||
|
+ ", length=" + length + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* unchoke: [len=0001][id=1]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The unchoke message is fixed-length and has no payload.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source BitTorrent documentation
|
||||||
|
*/
|
||||||
|
public class UnchokeMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x01;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "UnchokeMessage []";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message.fast;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* AllowedFast: [len=0x0004+pieces(4 bytes each)][id=0x11][piece indexes(int)]*
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* With the BitTorrent protocol specified in BEP 0003 [2], new peers take
|
||||||
|
* several minutes to ramp up before they can effectively engage in BitTorrent's
|
||||||
|
* tit-for-tat. The reason is simple: starting peers have few pieces to trade.
|
||||||
|
* <p>
|
||||||
|
* Allowed Fast is an advisory message which means
|
||||||
|
* "if you ask for this piece, I'll give it to you even if you're choked."
|
||||||
|
* Allowed Fast thus shortens the awkward stage during which the peer obtains
|
||||||
|
* occasional optimistic unchokes but cannot sufficiently reciprocate to remain
|
||||||
|
* unchoked.
|
||||||
|
* <p>
|
||||||
|
* The pieces that can be downloaded when choked constitute a peer's allowed
|
||||||
|
* fast set. The set is generated using a canonical algorithm that produces
|
||||||
|
* piece indices unique to the message receiver so that if two peers offer k
|
||||||
|
* pieces fast it will be the same k, and if one offers k+1 it will be the same
|
||||||
|
* k plus one more. k should be small enough to avoid abuse, but large enough to
|
||||||
|
* ramp up tit-for-tat. We currently set k to 10, but peers are free to change
|
||||||
|
* this number, e.g., to suit load.
|
||||||
|
* <p>
|
||||||
|
* The message sender MAY list pieces that the message sender does not have. The
|
||||||
|
* receiver MUST NOT interpret an Allowed Fast message as meaning that the
|
||||||
|
* message sender has the piece. This allows peers to generate and communicate
|
||||||
|
* allowed fast sets at the beginning of a connection. However, a peer MAY send
|
||||||
|
* Allowed Fast messages at any time.
|
||||||
|
* <p>
|
||||||
|
* A peer SHOULD send Allowed Fast messages to any starting peer unless the
|
||||||
|
* local peer lacks sufficient resources. A peer MAY reject requests for already
|
||||||
|
* Allowed Fast pieces if the local peer lacks sufficient resources, if the
|
||||||
|
* requested piece has already been sent to the requesting peer, or if the
|
||||||
|
* requesting peer is not a starting peer. Our current implementation rejects
|
||||||
|
* requests for Allowed Fast messages whenever the requesting peer has more than
|
||||||
|
* * k * pieces.
|
||||||
|
* <p>
|
||||||
|
* When the fast extension is disabled, if a peer recieves an Allowed Fast
|
||||||
|
* message then the peer MUST close the connection.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source http://www.bittorrent.org/beps/bep_0006.html
|
||||||
|
*/
|
||||||
|
public class AllowedFastMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x0D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pieces indexes
|
||||||
|
*/
|
||||||
|
private int[] pieces;
|
||||||
|
|
||||||
|
public AllowedFastMessage(int... pieces) {
|
||||||
|
this.pieces = pieces;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AllowedFastMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
this.pieces = new int[buffer.readableBytes() / 4];
|
||||||
|
for (int i = 0; i < pieces.length; i++) {
|
||||||
|
pieces[i] = buffer.readInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
for (final int piece : pieces) {
|
||||||
|
buffer.writeInt(piece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getPieces() {
|
||||||
|
return pieces;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPieces(int[] pieces) {
|
||||||
|
this.pieces = pieces;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AllowedFastMessage [pieces="
|
||||||
|
+ (pieces != null ? Arrays.toString(pieces) : null) + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message.fast;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* HaveAll: [len=0x0001][id=0x0E]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Have All and Have None specify that the message sender has all or none of the
|
||||||
|
* pieces respectively. When present, Have All or Have None replace the Have
|
||||||
|
* Bitfield. Exactly one of Have All, Have None, or Have Bitfield MUST appear
|
||||||
|
* and only immediately after the handshake. The reason for these messages is to
|
||||||
|
* save bandwidth. Also slightly to remove the idiosyncrasy of sending no
|
||||||
|
* message when a peer has no pieces.
|
||||||
|
* <p>
|
||||||
|
* When the fast extension is disabled, if a peer receives Have All or Have None
|
||||||
|
* then the peer MUST close the connection.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source http://www.bittorrent.org/beps/bep_0006.html
|
||||||
|
*/
|
||||||
|
public class HaveAllMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x0E;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "HaveAllMessage []";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message.fast;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* HaveNone: [len=0x0001][id=0x0F]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Have All and Have None specify that the message sender has all or none of the
|
||||||
|
* pieces respectively. When present, Have All or Have None replace the Have
|
||||||
|
* Bitfield. Exactly one of Have All, Have None, or Have Bitfield MUST appear
|
||||||
|
* and only immediately after the handshake. The reason for these messages is to
|
||||||
|
* save bandwidth. Also slightly to remove the idiosyncrasy of sending no
|
||||||
|
* message when a peer has no pieces.
|
||||||
|
* <p>
|
||||||
|
* When the fast extension is disabled, if a peer receives Have All or Have None
|
||||||
|
* then the peer MUST close the connection.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source http://www.bittorrent.org/beps/bep_0006.html
|
||||||
|
*/
|
||||||
|
public class HaveNoneMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x0F;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "HaveNoneMessage []";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message.fast;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Reject: [len=0013][id=0x10][index][begin][length]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Reject Request notifies a requesting peer that its request will not be
|
||||||
|
* satisfied.
|
||||||
|
*
|
||||||
|
* If the fast extension is disabled and a peer receives a reject request then
|
||||||
|
* the peer MUST close the connection.
|
||||||
|
* <p>
|
||||||
|
* When the fast extension is enabled:
|
||||||
|
* <ul>
|
||||||
|
* <li>If a peer receives a reject for a request that was never sent then the
|
||||||
|
* peer SHOULD close the connection.</li>
|
||||||
|
* <li>If a peer sends a choke, it MUST reject all requests from the peer to
|
||||||
|
* whom the choke was sent except it SHOULD NOT reject requests for pieces that
|
||||||
|
* are in the allowed fast set. A peer SHOULD choke first and then reject
|
||||||
|
* requests so that the peer receiving the choke does not re-request the pieces.
|
||||||
|
* </li>
|
||||||
|
* <li>If a peer receives a request from a peer its choking, the peer receiving
|
||||||
|
* the request SHOULD send a reject unless the piece is in the allowed fast set.
|
||||||
|
* </li>
|
||||||
|
* <li>If a peer receives an excessive number of requests from a peer it is
|
||||||
|
* choking, the peer receiving the requests MAY close the connection rather than
|
||||||
|
* reject the request. However, consider that it can take several seconds for
|
||||||
|
* buffers to drain and messages to propagate once a peer is choked.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source http://www.bittorrent.org/beps/bep_0006.html
|
||||||
|
*/
|
||||||
|
public class RejectMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x06;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The piece index
|
||||||
|
*/
|
||||||
|
private int index;
|
||||||
|
/**
|
||||||
|
* The piece start offset
|
||||||
|
*/
|
||||||
|
private int start;
|
||||||
|
/**
|
||||||
|
* The part length
|
||||||
|
*/
|
||||||
|
private int length;
|
||||||
|
|
||||||
|
public RejectMessage(int index, int start, int length) {
|
||||||
|
this.index = index;
|
||||||
|
this.start = start;
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RejectMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
this.index = buffer.readInt();
|
||||||
|
this.start = buffer.readInt();
|
||||||
|
this.length = buffer.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
buffer.writeInt(index);
|
||||||
|
buffer.writeInt(start);
|
||||||
|
buffer.writeInt(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStart(int start) {
|
||||||
|
this.start = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLength(int length) {
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RejectMessage [index=" + index + ", start=" + start
|
||||||
|
+ ", length=" + length + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.peerwire.message.fast;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
|
||||||
|
import net.torrent.protocol.peerwire.codec.PeerWireWritableMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* SuggestPiece: [len=0x0005][id=0x0D][piece index(int)]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Suggest Piece is an advisory message meaning
|
||||||
|
* "you might like to download this piece." The intended usage is for
|
||||||
|
* 'super-seeding' without throughput reduction, to avoid redundant downloads,
|
||||||
|
* and so that a seed which is disk I/O bound can upload continguous or
|
||||||
|
* identical pieces to avoid excessive disk seeks. In all cases, the seed SHOULD
|
||||||
|
* operate to maintain a roughly equal number of copies of each piece in the
|
||||||
|
* network. A peer MAY send more than one suggest piece message at any given
|
||||||
|
* time. A peer receiving multiple suggest piece messages MAY interpret this as
|
||||||
|
* meaning that all of the suggested pieces are equally appropriate.
|
||||||
|
* <p>
|
||||||
|
* When the fast extension is disabled, if a peer receives a Suggest Piece
|
||||||
|
* message, the peer MUST close the connection.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @source http://www.bittorrent.org/beps/bep_0006.html
|
||||||
|
*/
|
||||||
|
public class SuggestPieceMessage implements PeerWireWritableMessage,
|
||||||
|
PeerWireReadableMessage {
|
||||||
|
public static final byte MESSAGE_ID = 0x0D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The new obtained piece index
|
||||||
|
*/
|
||||||
|
private int piece;
|
||||||
|
|
||||||
|
public SuggestPieceMessage(int piece) {
|
||||||
|
this.piece = piece;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SuggestPieceMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ChannelBuffer buffer) throws IOException {
|
||||||
|
this.piece = buffer.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelBuffer buffer) throws IOException {
|
||||||
|
buffer.writeByte(MESSAGE_ID);
|
||||||
|
buffer.writeInt(piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPiece() {
|
||||||
|
return piece;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPiece(int piece) {
|
||||||
|
this.piece = piece;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SuggestPieceMessage [piece=" + piece + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import net.torrent.protocol.tracker.message.AnnounceMessage;
|
||||||
|
import net.torrent.protocol.tracker.message.AnnounceMessage.Event;
|
||||||
|
import net.torrent.torrent.Torrent;
|
||||||
|
import net.torrent.torrent.TorrentTracker;
|
||||||
|
|
||||||
|
import org.jboss.netty.bootstrap.ClientBootstrap;
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
|
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
|
||||||
|
|
||||||
|
public class HttpTorrentTrackerAnnouncer {
|
||||||
|
private final ClientBootstrap client = new ClientBootstrap(
|
||||||
|
new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),
|
||||||
|
Executors.newCachedThreadPool()));
|
||||||
|
|
||||||
|
public HttpTorrentTrackerAnnouncer() {
|
||||||
|
client.setPipelineFactory(new HttpTorrentTrackerPipelineFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean announce(Torrent torrent, TorrentTracker tracker)
|
||||||
|
throws UnsupportedEncodingException, MalformedURLException {
|
||||||
|
final AnnounceMessage announceMessage = new AnnounceMessage(tracker
|
||||||
|
.getURL().toString(), torrent.getInfoHash().toByteArray(),
|
||||||
|
torrent.getInfoHash().toByteArray(), 10, 0, 0, 0, true, false,
|
||||||
|
Event.STARTED);
|
||||||
|
int port = (tracker.getURL().getPort() > 0 ? tracker.getURL().getPort()
|
||||||
|
: tracker.getURL().getDefaultPort());
|
||||||
|
final ChannelFuture chFuture = client.connect(new InetSocketAddress(
|
||||||
|
tracker.getURL().getHost(), port));
|
||||||
|
chFuture.awaitUninterruptibly(60, TimeUnit.SECONDS);
|
||||||
|
if (!chFuture.isSuccess())
|
||||||
|
return false;
|
||||||
|
return chFuture.getChannel().write(announceMessage.write())
|
||||||
|
.awaitUninterruptibly(60, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker;
|
||||||
|
|
||||||
|
import static org.jboss.netty.channel.Channels.pipeline;
|
||||||
|
import net.torrent.protocol.tracker.codec.ISO8859HttpRequestEncoder;
|
||||||
|
import net.torrent.protocol.tracker.codec.TorrentTrackerBDecoder;
|
||||||
|
import net.torrent.protocol.tracker.codec.TorrentTrackerDecoder;
|
||||||
|
import net.torrent.protocol.tracker.codec.TorrentTrackerEncoder;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.ChannelPipeline;
|
||||||
|
import org.jboss.netty.channel.ChannelPipelineFactory;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpResponseDecoder;
|
||||||
|
import org.jboss.netty.handler.logging.LoggingHandler;
|
||||||
|
import org.jboss.netty.logging.InternalLogLevel;
|
||||||
|
|
||||||
|
public class HttpTorrentTrackerPipelineFactory implements
|
||||||
|
ChannelPipelineFactory {
|
||||||
|
@Override
|
||||||
|
public ChannelPipeline getPipeline() throws Exception {
|
||||||
|
final ChannelPipeline pipeline = pipeline();
|
||||||
|
|
||||||
|
// log binary data input and object output
|
||||||
|
// pipeline.addFirst("logging", new LoggingHandler());
|
||||||
|
|
||||||
|
pipeline.addLast("tracker.encoder", new TorrentTrackerEncoder());
|
||||||
|
pipeline.addLast("encoder", new ISO8859HttpRequestEncoder());
|
||||||
|
|
||||||
|
pipeline.addLast("interceptor", new Interceptor());
|
||||||
|
|
||||||
|
pipeline.addLast("decoder", new HttpResponseDecoder());
|
||||||
|
pipeline.addLast("bdecoder", new TorrentTrackerBDecoder());
|
||||||
|
pipeline.addLast("tracker.decoder", new TorrentTrackerDecoder());
|
||||||
|
|
||||||
|
pipeline.addLast("handler", new TrackerHandler());
|
||||||
|
|
||||||
|
pipeline.addLast("logging", new LoggingHandler(InternalLogLevel.WARN));
|
||||||
|
|
||||||
|
return pipeline;
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/main/java/net/torrent/protocol/tracker/Interceptor.java
Normal file
36
src/main/java/net/torrent/protocol/tracker/Interceptor.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
|
||||||
|
|
||||||
|
public class Interceptor extends OneToOneEncoder {
|
||||||
|
@Override
|
||||||
|
protected Object encode(ChannelHandlerContext ctx, Channel channel,
|
||||||
|
Object msg) throws Exception {
|
||||||
|
System.out.println(">" + msg);
|
||||||
|
if (!(msg instanceof ChannelBuffer))
|
||||||
|
return msg;
|
||||||
|
|
||||||
|
final ChannelBuffer buffer = (ChannelBuffer) msg;
|
||||||
|
System.out.println(new String(buffer.array()));
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.channel.MessageEvent;
|
||||||
|
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
|
||||||
|
|
||||||
|
public class TrackerHandler extends SimpleChannelUpstreamHandler {
|
||||||
|
@Override
|
||||||
|
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
|
||||||
|
throws Exception {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
super.messageReceived(ctx, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker.codec;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpMessage;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpRequestEncoder;
|
||||||
|
|
||||||
|
public class ISO8859HttpRequestEncoder extends HttpRequestEncoder {
|
||||||
|
static final byte SP = 32;
|
||||||
|
static final byte CR = 13;
|
||||||
|
static final byte LF = 10;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encodeInitialLine(ChannelBuffer buf, HttpMessage message)
|
||||||
|
throws Exception {
|
||||||
|
HttpRequest request = (HttpRequest) message;
|
||||||
|
buf.writeBytes(request.getMethod().toString().getBytes());
|
||||||
|
buf.writeByte(SP);
|
||||||
|
buf.writeBytes(request.getUri().getBytes());
|
||||||
|
buf.writeByte(SP);
|
||||||
|
buf.writeBytes(request.getProtocolVersion().toString().getBytes());
|
||||||
|
buf.writeByte(CR);
|
||||||
|
buf.writeByte(LF);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker.codec;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
|
||||||
|
import net.torrent.util.bencoding.BEncodedInputStream;
|
||||||
|
import net.torrent.util.bencoding.BMap;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpChunk;
|
||||||
|
import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
|
||||||
|
|
||||||
|
public class TorrentTrackerBDecoder extends OneToOneDecoder {
|
||||||
|
@Override
|
||||||
|
protected Object decode(ChannelHandlerContext ctx, Channel channel,
|
||||||
|
Object msg) throws Exception {
|
||||||
|
if (!(msg instanceof HttpChunk))
|
||||||
|
return msg;
|
||||||
|
final HttpChunk message = (HttpChunk) msg;
|
||||||
|
|
||||||
|
System.out.println(new String(message.getContent().array()));
|
||||||
|
if (message.getContent().readableBytes() <= 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
final BEncodedInputStream in = new BEncodedInputStream(
|
||||||
|
new ByteArrayInputStream(message.getContent().array()));
|
||||||
|
final BMap map = (BMap) in.readElement();
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker.codec;
|
||||||
|
|
||||||
|
import net.torrent.protocol.tracker.message.PeerListMessage;
|
||||||
|
import net.torrent.util.bencoding.BMap;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
|
||||||
|
|
||||||
|
public class TorrentTrackerDecoder extends OneToOneDecoder {
|
||||||
|
@Override
|
||||||
|
protected Object decode(ChannelHandlerContext ctx, Channel channel,
|
||||||
|
Object msg) throws Exception {
|
||||||
|
if (!(msg instanceof BMap))
|
||||||
|
return msg;
|
||||||
|
|
||||||
|
final BMap bencodedData = (BMap) msg;
|
||||||
|
final TorrentTrackerResponseMessage message = new PeerListMessage();
|
||||||
|
message.read(bencodedData);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker.codec;
|
||||||
|
|
||||||
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
|
||||||
|
|
||||||
|
public class TorrentTrackerEncoder extends OneToOneEncoder {
|
||||||
|
@Override
|
||||||
|
protected Object encode(ChannelHandlerContext ctx, Channel channel,
|
||||||
|
Object msg) throws Exception {
|
||||||
|
if (!(msg instanceof TorrentTrackerRequestMessage))
|
||||||
|
return msg;
|
||||||
|
|
||||||
|
final TorrentTrackerRequestMessage message = (TorrentTrackerRequestMessage) msg;
|
||||||
|
return message.write();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker.codec;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||||
|
|
||||||
|
public interface TorrentTrackerRequestMessage {
|
||||||
|
HttpRequest write() throws UnsupportedEncodingException,
|
||||||
|
MalformedURLException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker.codec;
|
||||||
|
|
||||||
|
import net.torrent.util.bencoding.BMap;
|
||||||
|
import net.torrent.util.bencoding.BTypeException;
|
||||||
|
|
||||||
|
public interface TorrentTrackerResponseMessage {
|
||||||
|
void read(BMap map) throws BTypeException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker.message;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
import net.torrent.protocol.tracker.codec.TorrentTrackerRequestMessage;
|
||||||
|
|
||||||
|
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpMethod;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpVersion;
|
||||||
|
|
||||||
|
public class AnnounceMessage implements TorrentTrackerRequestMessage {
|
||||||
|
private String url;
|
||||||
|
private byte[] infoHash;
|
||||||
|
private byte[] peerId;
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
private long uploaded;
|
||||||
|
private long downloaded;
|
||||||
|
private long left;
|
||||||
|
|
||||||
|
private boolean compact;
|
||||||
|
private boolean noPeerId;
|
||||||
|
|
||||||
|
private Event event;
|
||||||
|
|
||||||
|
public enum Event {
|
||||||
|
UPDATE(null), STARTED("started"), STOPPED("stopped"), COMPLETED(
|
||||||
|
"completed");
|
||||||
|
private String urlArg;
|
||||||
|
|
||||||
|
Event(String arg) {
|
||||||
|
urlArg = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String urlArg() {
|
||||||
|
return urlArg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String ip;
|
||||||
|
private Integer numWant = 10;
|
||||||
|
private String key = "avtbyit8";
|
||||||
|
private String trackerId;
|
||||||
|
|
||||||
|
public AnnounceMessage(String url, byte[] infoHash, byte[] peerId,
|
||||||
|
int port, long uploaded, long downloaded, long left,
|
||||||
|
boolean compact, boolean noPeerId, Event event, String ip,
|
||||||
|
Integer numWant, String key, String trackerId) {
|
||||||
|
this.url = url;
|
||||||
|
this.infoHash = infoHash;
|
||||||
|
this.peerId = peerId;
|
||||||
|
this.port = port;
|
||||||
|
this.uploaded = uploaded;
|
||||||
|
this.downloaded = downloaded;
|
||||||
|
this.left = left;
|
||||||
|
this.compact = compact;
|
||||||
|
this.noPeerId = noPeerId;
|
||||||
|
this.event = event;
|
||||||
|
this.ip = ip;
|
||||||
|
this.numWant = numWant;
|
||||||
|
this.key = key;
|
||||||
|
this.trackerId = trackerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnounceMessage(String url, byte[] infoHash, byte[] peerId,
|
||||||
|
int port, long uploaded, long downloaded, long left,
|
||||||
|
boolean compact, boolean noPeerId, Event event) {
|
||||||
|
this.url = url;
|
||||||
|
this.infoHash = infoHash;
|
||||||
|
this.peerId = peerId;
|
||||||
|
this.port = port;
|
||||||
|
this.uploaded = uploaded;
|
||||||
|
this.downloaded = downloaded;
|
||||||
|
this.left = left;
|
||||||
|
this.compact = compact;
|
||||||
|
this.noPeerId = noPeerId;
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpRequest write() throws UnsupportedEncodingException,
|
||||||
|
MalformedURLException {
|
||||||
|
StringBuilder builder = new StringBuilder(url);
|
||||||
|
if (url.contains("?")) {
|
||||||
|
builder.append("&");
|
||||||
|
} else {
|
||||||
|
builder.append("?");
|
||||||
|
}
|
||||||
|
|
||||||
|
addLowerCase(builder, "info_hash", infoHash);
|
||||||
|
add(builder, "peer_id", "-TR2130-g4mvcv2iyehf");
|
||||||
|
add(builder, "port", port);
|
||||||
|
|
||||||
|
add(builder, "uploaded", Long.toString(uploaded));
|
||||||
|
add(builder, "downloaded", Long.toString(downloaded));
|
||||||
|
add(builder, "left", Long.toString(left));
|
||||||
|
|
||||||
|
add(builder, "compact", compact);
|
||||||
|
add(builder, "no_peer_id", noPeerId);
|
||||||
|
if (event != Event.UPDATE)
|
||||||
|
add(builder, "event", event.urlArg());
|
||||||
|
|
||||||
|
add(builder, "ip", ip);
|
||||||
|
add(builder, "numwant", numWant);
|
||||||
|
add(builder, "key", key);
|
||||||
|
add(builder, "trackerid", trackerId);
|
||||||
|
|
||||||
|
builder.setLength(builder.length() - 1);// trim last character it is an
|
||||||
|
// unnecessary &.
|
||||||
|
final URL url = new URL(builder.toString());
|
||||||
|
|
||||||
|
return new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
|
||||||
|
url.getPath() + "?" + url.getQuery());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(StringBuilder builder, String key, String value)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
if (value == null)
|
||||||
|
return;
|
||||||
|
builder.append(key + "=" + value).append("&");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(StringBuilder builder, String key, byte[] value)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
if (value == null)
|
||||||
|
return;
|
||||||
|
add(builder, key, URLEncoder.encode(new String(value), "ISO-8859-1")
|
||||||
|
.replaceAll("\\+", "%20"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addLowerCase(StringBuilder builder, String key, byte[] value)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
if (value == null)
|
||||||
|
return;
|
||||||
|
add(builder, key,
|
||||||
|
URLEncoder
|
||||||
|
.encode(new String(value, "ISO-8859-1"), "ISO-8859-1")
|
||||||
|
.replaceAll("\\+", "%20").toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(StringBuilder builder, String key, Number value)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
if (value == null)
|
||||||
|
return;
|
||||||
|
add(builder, key, value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(StringBuilder builder, String key, Boolean value)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
if (value == null)
|
||||||
|
return;
|
||||||
|
add(builder, key, (value ? "1" : "0"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.protocol.tracker.message;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.torrent.protocol.tracker.codec.TorrentTrackerResponseMessage;
|
||||||
|
import net.torrent.util.bencoding.BList;
|
||||||
|
import net.torrent.util.bencoding.BMap;
|
||||||
|
import net.torrent.util.bencoding.BTypeException;
|
||||||
|
|
||||||
|
public class PeerListMessage implements TorrentTrackerResponseMessage {
|
||||||
|
private static final String FAILURE_REASON = "failure reason";
|
||||||
|
private static final String WARNING_MESSAGE = "warning message";
|
||||||
|
private static final String INTERVAL = "interval";
|
||||||
|
private static final String MIN_INTERVAL = "min interval";
|
||||||
|
private static final String TRACKER_ID = "tracker id";
|
||||||
|
private static final String COMPLETE = "complete";
|
||||||
|
private static final String INCOMPLETE = "incomplete";
|
||||||
|
private static final String PEERS = "peers";
|
||||||
|
|
||||||
|
private String failureReason;
|
||||||
|
private String warningMessage;
|
||||||
|
|
||||||
|
private Integer interval;
|
||||||
|
private Integer minInterval;
|
||||||
|
private byte[] trackerID;
|
||||||
|
private Integer complete;
|
||||||
|
private Integer incomplete;
|
||||||
|
private List<PeerInfo> peerList;
|
||||||
|
private boolean compact;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(BMap response) throws BTypeException {
|
||||||
|
if (response == null)
|
||||||
|
throw new InvalidParameterException("Tracker response is null");
|
||||||
|
if (response.containsKey(FAILURE_REASON)) {
|
||||||
|
failureReason = response.getString(FAILURE_REASON);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.interval = response.getInteger(INTERVAL);
|
||||||
|
|
||||||
|
warningMessage = response.getString(WARNING_MESSAGE);
|
||||||
|
interval = response.getInteger(INTERVAL);
|
||||||
|
minInterval = response.getInteger(MIN_INTERVAL);
|
||||||
|
trackerID = (byte[]) response.get(TRACKER_ID);
|
||||||
|
|
||||||
|
complete = response.getInteger(COMPLETE);
|
||||||
|
incomplete = response.getInteger(INCOMPLETE);
|
||||||
|
|
||||||
|
Object peers = response.get(PEERS);
|
||||||
|
if (peers instanceof BList) {
|
||||||
|
BList list = (BList) peers;
|
||||||
|
peerList = new ArrayList<PeerInfo>(list.size());
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
peerList.add(PeerInfo.fromBMap(list.getMap(i)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
byte[] list = (byte[]) peers;
|
||||||
|
if (list.length % 6 != 0)
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Peerlist not in format IPv4:port!");
|
||||||
|
peerList = new ArrayList<PeerInfo>(list.length / 6);
|
||||||
|
for (int i = 0; i < list.length; i += 6) {
|
||||||
|
peerList.add(PeerInfo.fromRawIP(list, i, 6));
|
||||||
|
}
|
||||||
|
compact = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFailureReason() {
|
||||||
|
return failureReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWarningMessage() {
|
||||||
|
return warningMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getInterval() {
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getMinInterval() {
|
||||||
|
return minInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getTrackerID() {
|
||||||
|
return trackerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getComplete() {
|
||||||
|
return complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getIncomplete() {
|
||||||
|
return incomplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PeerInfo> getPeerList() {
|
||||||
|
return peerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompact() {
|
||||||
|
return compact;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PeerInfo {
|
||||||
|
private byte[] peerId;
|
||||||
|
private String ip;
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
public PeerInfo(byte[] peerId, String ip, int port) {
|
||||||
|
this.peerId = peerId;
|
||||||
|
this.ip = ip;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getPeerId() {
|
||||||
|
return peerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeerId(byte[] peerId) {
|
||||||
|
this.peerId = peerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIp() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIp(String ip) {
|
||||||
|
this.ip = ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPort(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PeerInfo fromBMap(BMap map) throws BTypeException {
|
||||||
|
return new PeerInfo((byte[]) map.get("peer id"),
|
||||||
|
map.getString("peer ip"), map.getInteger("port"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PeerInfo fromRawIP(byte[] list, int i, int j) {
|
||||||
|
byte[] addr = new byte[4];
|
||||||
|
System.arraycopy(list, i, addr, 0, 4);
|
||||||
|
InetAddress address;
|
||||||
|
try {
|
||||||
|
address = InetAddress.getByAddress(addr);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
InetSocketAddress socketAddress = new InetSocketAddress(
|
||||||
|
address.getHostAddress(), ((list[i + j - 2] & 0xFF) << 8)
|
||||||
|
+ (list[i + j - 1] & 0xFF));
|
||||||
|
return new PeerInfo(null, socketAddress.getHostName(),
|
||||||
|
socketAddress.getPort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
362
src/main/java/net/torrent/torrent/Torrent.java
Normal file
362
src/main/java/net/torrent/torrent/Torrent.java
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.torrent.torrent.TorrentInfo.TorrentType;
|
||||||
|
import net.torrent.util.bencoding.BEncodedInputStream;
|
||||||
|
import net.torrent.util.bencoding.BList;
|
||||||
|
import net.torrent.util.bencoding.BMap;
|
||||||
|
import net.torrent.util.bencoding.BTypeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An class representing an .torrent file.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class Torrent {
|
||||||
|
/**
|
||||||
|
* Dictionary key for <tt>announce</tt>
|
||||||
|
*/
|
||||||
|
private static final String ANNOUNCE = "announce";
|
||||||
|
/**
|
||||||
|
* Dictionary key for <tt>announce-list</tt>
|
||||||
|
*/
|
||||||
|
private static final String ANNOUNCE_LIST = "announce-list";
|
||||||
|
/**
|
||||||
|
* Dictionary key for <tt>info</tt>
|
||||||
|
*/
|
||||||
|
private static final String INFO = "info";
|
||||||
|
/**
|
||||||
|
* Dictionary key for <tt>comment</tt>
|
||||||
|
*/
|
||||||
|
private static final String COMMENT = "comment";
|
||||||
|
/**
|
||||||
|
* Dictionary key for <tt>creation date</tt>
|
||||||
|
*/
|
||||||
|
private static final String CREATION_DATE = "creation date";
|
||||||
|
/**
|
||||||
|
* Dictionary key for <tt>created by</tt>
|
||||||
|
*/
|
||||||
|
private static final String CREATED_BY = "created by";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The torrent information
|
||||||
|
*/
|
||||||
|
private final TorrentInfo info;
|
||||||
|
/**
|
||||||
|
* The list of trackers
|
||||||
|
*/
|
||||||
|
private final Set<TorrentTracker> trackers = new HashSet<TorrentTracker>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The torrent comment
|
||||||
|
*/
|
||||||
|
private final String comment;
|
||||||
|
/**
|
||||||
|
* The creation date of the torrent
|
||||||
|
*/
|
||||||
|
private final Date creationDate;
|
||||||
|
/**
|
||||||
|
* The torrent creator
|
||||||
|
*/
|
||||||
|
private final String createdBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param info
|
||||||
|
* the torrent information
|
||||||
|
* @param comment
|
||||||
|
* the comment
|
||||||
|
* @param creationDate
|
||||||
|
* the date of creation
|
||||||
|
* @param createdBy
|
||||||
|
* the creator
|
||||||
|
* @param trackers
|
||||||
|
* the list of trackers
|
||||||
|
*/
|
||||||
|
public Torrent(TorrentInfo info, String comment, Date creationDate,
|
||||||
|
String createdBy, TorrentTracker... trackers) {
|
||||||
|
if (info == null)
|
||||||
|
throw new InvalidParameterException("Torrent info is null");
|
||||||
|
if (trackers == null || trackers.length == 0)
|
||||||
|
throw new InvalidParameterException(
|
||||||
|
"Trackers null or empty: DHT is not yet implemented, a tracker is needed.");
|
||||||
|
|
||||||
|
Collections.addAll(this.trackers, trackers);
|
||||||
|
|
||||||
|
this.info = info;
|
||||||
|
this.comment = comment;
|
||||||
|
this.creationDate = creationDate;
|
||||||
|
this.createdBy = createdBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param info
|
||||||
|
* the torrent information
|
||||||
|
* @param trackers
|
||||||
|
* the list o trackers
|
||||||
|
*/
|
||||||
|
public Torrent(TorrentInfo info, TorrentTracker... trackers) {
|
||||||
|
this(info, null, null, null, trackers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param torrent
|
||||||
|
* the torrent bencoded map
|
||||||
|
* @throws BTypeException
|
||||||
|
* @throws URISyntaxException
|
||||||
|
*/
|
||||||
|
protected Torrent(BMap torrent) throws BTypeException, URISyntaxException {
|
||||||
|
this.comment = torrent.getString(COMMENT);
|
||||||
|
|
||||||
|
final Long time = torrent.getLong(CREATION_DATE);
|
||||||
|
this.creationDate = (time != null ? new Date(time * 1000) : null);
|
||||||
|
|
||||||
|
this.createdBy = torrent.getString(CREATED_BY);
|
||||||
|
|
||||||
|
BList aLists = torrent.getList(ANNOUNCE_LIST);
|
||||||
|
if (aLists != null) {
|
||||||
|
for (int i = 0; i < aLists.size(); i++) {
|
||||||
|
BList list = aLists.getList(i);
|
||||||
|
URI primary = null;
|
||||||
|
URI[] backup = new URI[(list.size() > 1 ? list.size() - 1 : 0)];
|
||||||
|
int backups = 0;
|
||||||
|
for (int j = 0; j < list.size(); j++) {
|
||||||
|
if (j == 0) {
|
||||||
|
final String url = list.getString(j);
|
||||||
|
if (!url.startsWith("http"))
|
||||||
|
break;
|
||||||
|
primary = new URI(url);
|
||||||
|
} else {
|
||||||
|
final String url = list.getString(j);
|
||||||
|
if (url.startsWith("http"))
|
||||||
|
backup[backups++] = new URI(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.trackers.add(new TorrentTracker(this, primary, Arrays
|
||||||
|
.copyOf(backup, backups)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (torrent.containsKey(ANNOUNCE)) {
|
||||||
|
this.trackers.add(new TorrentTracker(this, new URI(torrent
|
||||||
|
.getString(ANNOUNCE))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info = new TorrentInfo(this, torrent.getMap(INFO));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get torrent information
|
||||||
|
*
|
||||||
|
* @return the torrent information
|
||||||
|
*/
|
||||||
|
public TorrentInfo getInfo() {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of trackers
|
||||||
|
*
|
||||||
|
* @return the trackers
|
||||||
|
*/
|
||||||
|
public Set<TorrentTracker> getTrackers() {
|
||||||
|
return Collections.unmodifiableSet(trackers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent comment
|
||||||
|
*
|
||||||
|
* @return the torrent comment
|
||||||
|
*/
|
||||||
|
public String getComment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the creation adte
|
||||||
|
*
|
||||||
|
* @return the creation date
|
||||||
|
*/
|
||||||
|
public Date getCreationDate() {
|
||||||
|
return creationDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent creator
|
||||||
|
*
|
||||||
|
* @return the torrent creator
|
||||||
|
*/
|
||||||
|
public String getCreatedBy() {
|
||||||
|
return createdBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent info hash
|
||||||
|
*
|
||||||
|
* @return the torrent info hash
|
||||||
|
*/
|
||||||
|
public TorrentHash getInfoHash() {
|
||||||
|
return info.getInfoHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of files
|
||||||
|
*
|
||||||
|
* @return the file list
|
||||||
|
*/
|
||||||
|
public List<TorrentFile> getFiles() {
|
||||||
|
return info.getFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of pieces
|
||||||
|
*
|
||||||
|
* @return the list of pieces
|
||||||
|
*/
|
||||||
|
public TorrentPiece[] getPieces() {
|
||||||
|
return info.getPieces();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent type
|
||||||
|
*
|
||||||
|
* @return the torrent type
|
||||||
|
*/
|
||||||
|
public TorrentType getType() {
|
||||||
|
return info.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the piece by index
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
* the piece index
|
||||||
|
* @return the found piece
|
||||||
|
*/
|
||||||
|
public TorrentPiece getPiece(int index) {
|
||||||
|
return info.getPiece(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an certain part
|
||||||
|
*
|
||||||
|
* @param piece
|
||||||
|
* the piece
|
||||||
|
* @param start
|
||||||
|
* the part start
|
||||||
|
* @param len
|
||||||
|
* the part length
|
||||||
|
* @return the found part
|
||||||
|
*/
|
||||||
|
public TorrentPart getPart(TorrentPiece piece, int start, int len) {
|
||||||
|
return info.getPart(piece, start, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an certain part
|
||||||
|
*
|
||||||
|
* @param piece
|
||||||
|
* the piece index
|
||||||
|
* @param start
|
||||||
|
* the part start
|
||||||
|
* @param len
|
||||||
|
* the part length
|
||||||
|
* @return the found part
|
||||||
|
*/
|
||||||
|
public TorrentPart getPart(int index, int start, int len) {
|
||||||
|
return info.getPart(index, start, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result
|
||||||
|
+ ((info == null) ? 0 : info.getInfoHash().hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
Torrent other = (Torrent) obj;
|
||||||
|
if (info == null) {
|
||||||
|
if (other.info != null)
|
||||||
|
return false;
|
||||||
|
} else if (!info.equals(other.info))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an torrent from an {@link InputStream}
|
||||||
|
*
|
||||||
|
* @param in
|
||||||
|
* the {@link InputStream}
|
||||||
|
* @return the loaded {@link Torrent} instance
|
||||||
|
* @throws IOException
|
||||||
|
* @throws URISyntaxException
|
||||||
|
*/
|
||||||
|
public static Torrent load(InputStream in) throws IOException,
|
||||||
|
URISyntaxException {
|
||||||
|
if (in == null)
|
||||||
|
throw new InvalidParameterException("InputStream cannot be null");
|
||||||
|
|
||||||
|
final BMap map = (BMap) new BEncodedInputStream(in).readElement();
|
||||||
|
return new Torrent(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an torrent from an {@link File}
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* the {@link File}
|
||||||
|
* @return the loaded {@link Torrent} instance
|
||||||
|
* @throws IOException
|
||||||
|
* @throws URISyntaxException
|
||||||
|
*/
|
||||||
|
public static Torrent load(File file) throws IOException,
|
||||||
|
URISyntaxException {
|
||||||
|
if (file == null)
|
||||||
|
throw new InvalidParameterException("File cannot be null");
|
||||||
|
return load(new FileInputStream(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
172
src/main/java/net/torrent/torrent/TorrentFile.java
Normal file
172
src/main/java/net/torrent/torrent/TorrentFile.java
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent;
|
||||||
|
|
||||||
|
import net.torrent.util.Range;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An file inside an {@link Torrent torrent} file
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentFile {
|
||||||
|
/**
|
||||||
|
* The torrent information
|
||||||
|
*/
|
||||||
|
private final TorrentInfo info;
|
||||||
|
/**
|
||||||
|
* The filename
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
/**
|
||||||
|
* The file length
|
||||||
|
*/
|
||||||
|
private final long length;
|
||||||
|
/**
|
||||||
|
* The file offset
|
||||||
|
*/
|
||||||
|
private final long offset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param info
|
||||||
|
* the torrent information
|
||||||
|
* @param name
|
||||||
|
* the filename
|
||||||
|
* @param length
|
||||||
|
* the file length
|
||||||
|
* @param offset
|
||||||
|
* the file offset
|
||||||
|
*/
|
||||||
|
public TorrentFile(TorrentInfo info, String name, long length, long offset) {
|
||||||
|
this.info = info;
|
||||||
|
this.name = name;
|
||||||
|
this.length = length;
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent informatiom
|
||||||
|
*
|
||||||
|
* @return the torrent information
|
||||||
|
*/
|
||||||
|
public TorrentInfo getInfo() {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the filename
|
||||||
|
*
|
||||||
|
* @return the filename
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the filesize
|
||||||
|
*
|
||||||
|
* @return the filesize
|
||||||
|
*/
|
||||||
|
public long getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file offset
|
||||||
|
*
|
||||||
|
* @return the offset
|
||||||
|
*/
|
||||||
|
public long getOffset() {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the base directory name
|
||||||
|
*
|
||||||
|
* @return the base directory name
|
||||||
|
* @see TorrentInfo#getDirectoryName()
|
||||||
|
*/
|
||||||
|
public String getBaseDirectoryName() {
|
||||||
|
return info.getDirectoryName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the relative name. This is the same as:
|
||||||
|
* <p>
|
||||||
|
* <code>
|
||||||
|
* String name = getBaseDirectoryName() + "/" + getName();
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @return the relative name
|
||||||
|
*/
|
||||||
|
public String getRelativePath() {
|
||||||
|
if (getBaseDirectoryName() != null) {
|
||||||
|
return getBaseDirectoryName() + "/" + name;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file as torrent range
|
||||||
|
*
|
||||||
|
* @return the torrent range representing the file
|
||||||
|
*/
|
||||||
|
public Range asTorrentRange() {
|
||||||
|
return Range.getRangeByLength(offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file as file range
|
||||||
|
*
|
||||||
|
* @return the file range representing the file
|
||||||
|
*/
|
||||||
|
public Range asFileRange() {
|
||||||
|
return Range.getRangeByLength(0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + (int) (length ^ (length >>> 32));
|
||||||
|
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||||
|
result = prime * result + (int) (offset ^ (offset >>> 32));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
TorrentFile other = (TorrentFile) obj;
|
||||||
|
if (length != other.length)
|
||||||
|
return false;
|
||||||
|
if (name == null) {
|
||||||
|
if (other.name != null)
|
||||||
|
return false;
|
||||||
|
} else if (!name.equals(other.name))
|
||||||
|
return false;
|
||||||
|
if (offset != other.offset)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
221
src/main/java/net/torrent/torrent/TorrentHash.java
Normal file
221
src/main/java/net/torrent/torrent/TorrentHash.java
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent;
|
||||||
|
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an {@link TorrentPiece} hash.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentHash {
|
||||||
|
/**
|
||||||
|
* An table containing hexadecimal characters
|
||||||
|
*/
|
||||||
|
private static final char[] HEX_TABLE = { '0', '1', '2', '3', '4', '5',
|
||||||
|
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||||
|
/**
|
||||||
|
* The hash
|
||||||
|
*/
|
||||||
|
private final byte[] hash;
|
||||||
|
/**
|
||||||
|
* The hash type
|
||||||
|
*/
|
||||||
|
private final HashType type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hashes type
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public enum HashType {
|
||||||
|
/**
|
||||||
|
* SHA1 -> 20 bytes
|
||||||
|
*/
|
||||||
|
SHA1(20, "SHA1"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MD5 -> 16 bytes
|
||||||
|
*/
|
||||||
|
MD5(16, "MD5");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hash length
|
||||||
|
*/
|
||||||
|
private final int length;
|
||||||
|
/**
|
||||||
|
* The digest algorithm
|
||||||
|
*/
|
||||||
|
private final String digestAlgorithm;
|
||||||
|
/**
|
||||||
|
* The {@link MessageDigest} instance
|
||||||
|
*/
|
||||||
|
private MessageDigest digest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param length
|
||||||
|
* the hash length
|
||||||
|
* @param digestAlgorithm
|
||||||
|
* the algorithm
|
||||||
|
*/
|
||||||
|
HashType(int length, String digestAlgorithm) {
|
||||||
|
this.length = length;
|
||||||
|
this.digestAlgorithm = digestAlgorithm;
|
||||||
|
try {
|
||||||
|
this.digest = MessageDigest.getInstance(digestAlgorithm);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
this.digest = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hash length
|
||||||
|
*
|
||||||
|
* @return the hash length
|
||||||
|
*/
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hash algorithm
|
||||||
|
*
|
||||||
|
* @return the hash algorithm
|
||||||
|
*/
|
||||||
|
public String getDigestAlgorithm() {
|
||||||
|
return digestAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link MessageDigest}
|
||||||
|
*
|
||||||
|
* @return the {@link MessageDigest}
|
||||||
|
*/
|
||||||
|
public MessageDigest getMessageDigest() {
|
||||||
|
return digest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param hash
|
||||||
|
* the hash
|
||||||
|
* @param type
|
||||||
|
* the hash type
|
||||||
|
*/
|
||||||
|
public TorrentHash(byte[] hash, HashType type) {
|
||||||
|
if (hash == null)
|
||||||
|
throw new IllegalArgumentException("hash is null");
|
||||||
|
if (type == null)
|
||||||
|
throw new IllegalArgumentException("type is null");
|
||||||
|
if (hash.length != type.length)
|
||||||
|
throw new IllegalArgumentException("hash does not have "
|
||||||
|
+ type.length + " bytes");
|
||||||
|
this.hash = Arrays.copyOf(hash, hash.length);
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get hash as an hexadecimal string
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String asHexString() {
|
||||||
|
StringBuilder b = new StringBuilder(hash.length * 2);
|
||||||
|
for (int i = 0; i < hash.length; i++) {
|
||||||
|
b.append(HEX_TABLE[((hash[i] & 0xff) >> 4)]);
|
||||||
|
b.append(HEX_TABLE[hash[i] & 15]);
|
||||||
|
}
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare the two hashes
|
||||||
|
*
|
||||||
|
* @param hash
|
||||||
|
* the hash
|
||||||
|
* @return true if are equal
|
||||||
|
*/
|
||||||
|
public boolean compare(byte[] hash) {
|
||||||
|
if (hash == null)
|
||||||
|
throw new InvalidParameterException("Hash is null");
|
||||||
|
return Arrays.equals(this.hash, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hash byte array
|
||||||
|
*
|
||||||
|
* @return the hash byte array
|
||||||
|
*/
|
||||||
|
public byte[] toByteArray() {
|
||||||
|
return Arrays.copyOf(hash, hash.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hash length
|
||||||
|
*
|
||||||
|
* @return the hash length
|
||||||
|
*/
|
||||||
|
public int getHashLength() {
|
||||||
|
return hash.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hash type
|
||||||
|
*
|
||||||
|
* @return the hash type
|
||||||
|
*/
|
||||||
|
public HashType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + Arrays.hashCode(hash);
|
||||||
|
result = prime * result + type.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
TorrentHash other = (TorrentHash) obj;
|
||||||
|
if (!Arrays.equals(hash, other.hash))
|
||||||
|
return false;
|
||||||
|
if (type != other.type)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return type + ":" + asHexString();
|
||||||
|
}
|
||||||
|
}
|
||||||
198
src/main/java/net/torrent/torrent/TorrentInfo.java
Normal file
198
src/main/java/net/torrent/torrent/TorrentInfo.java
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.torrent.torrent.TorrentHash.HashType;
|
||||||
|
import net.torrent.util.bencoding.BEncodedOutputStream;
|
||||||
|
import net.torrent.util.bencoding.BList;
|
||||||
|
import net.torrent.util.bencoding.BMap;
|
||||||
|
import net.torrent.util.bencoding.BTypeException;
|
||||||
|
|
||||||
|
public class TorrentInfo {
|
||||||
|
private static final String PIECE_LENGTH = "piece length";
|
||||||
|
private static final String PIECES = "pieces";
|
||||||
|
private static final String NAME = "name";
|
||||||
|
private static final String LENGTH = "length";
|
||||||
|
private static final String FILES = "files";
|
||||||
|
private static final String PATH = "path";
|
||||||
|
private static final String PRIVATE = "private";
|
||||||
|
|
||||||
|
private final Torrent torrent;
|
||||||
|
private final TorrentHash infohash;
|
||||||
|
|
||||||
|
private final List<TorrentFile> files = new ArrayList<TorrentFile>();
|
||||||
|
private final TorrentPiece[] pieces;
|
||||||
|
|
||||||
|
private long size;
|
||||||
|
private final int pieceLength;
|
||||||
|
private final boolean privTracker;
|
||||||
|
private final TorrentType type;
|
||||||
|
|
||||||
|
public enum TorrentType {
|
||||||
|
SINGLE_FILE, DIRECTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String directoryName;
|
||||||
|
|
||||||
|
public TorrentInfo(Torrent torrent, TorrentHash infohash,
|
||||||
|
TorrentPiece[] pieces, int pieceLength, boolean privTracker,
|
||||||
|
TorrentType type, String directoryName) {
|
||||||
|
this.torrent = torrent;
|
||||||
|
this.infohash = infohash;
|
||||||
|
this.pieces = pieces;
|
||||||
|
this.pieceLength = pieceLength;
|
||||||
|
this.privTracker = privTracker;
|
||||||
|
this.type = type;
|
||||||
|
this.directoryName = directoryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentInfo(Torrent torrent, BMap info) throws BTypeException {
|
||||||
|
if (torrent == null)
|
||||||
|
throw new InvalidParameterException("Torrent is null");
|
||||||
|
if (info == null)
|
||||||
|
throw new InvalidParameterException("Info BMap is null");
|
||||||
|
this.torrent = torrent;
|
||||||
|
|
||||||
|
infohash = calculateInfoHash(info);
|
||||||
|
privTracker = Integer.valueOf(1).equals(info.getInteger(PRIVATE));
|
||||||
|
pieceLength = ((BigInteger) info.get(PIECE_LENGTH)).intValue();
|
||||||
|
|
||||||
|
String base = info.getString(NAME);
|
||||||
|
if (base == null)
|
||||||
|
throw new IllegalStateException("Missing a required key: " + NAME);
|
||||||
|
if (info.containsKey(LENGTH)) { // single file mode
|
||||||
|
type = TorrentType.SINGLE_FILE;
|
||||||
|
directoryName = null;
|
||||||
|
files.add(new TorrentFile(this, base, ((BigInteger) info
|
||||||
|
.get(LENGTH)).longValue(), 0));
|
||||||
|
} else {
|
||||||
|
type = TorrentType.DIRECTORY;
|
||||||
|
directoryName = base;
|
||||||
|
|
||||||
|
long offset = 0;
|
||||||
|
BList fList = info.getList(FILES);
|
||||||
|
for (int i = 0; i < fList.size(); i++) {
|
||||||
|
BMap file = fList.getMap(i);
|
||||||
|
StringBuilder path = new StringBuilder();
|
||||||
|
BList elements = file.getList(PATH);
|
||||||
|
for (int j = 0; j < elements.size(); j++) {
|
||||||
|
String element = elements.getString(j);
|
||||||
|
if (path.length() > 0) {
|
||||||
|
path.append(File.separatorChar);
|
||||||
|
}
|
||||||
|
path.append(element);
|
||||||
|
}
|
||||||
|
if (files.add(new TorrentFile(this, path.toString(),
|
||||||
|
((BigInteger) file.get(LENGTH)).longValue(), offset))) {
|
||||||
|
offset += ((BigInteger) file.get(LENGTH)).longValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calculateSize();
|
||||||
|
|
||||||
|
byte[] hashes = (byte[]) info.get(PIECES);
|
||||||
|
if (hashes == null)
|
||||||
|
throw new IllegalStateException("Pieces hash is null");
|
||||||
|
if (hashes.length % 20 != 0)
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Pieces hash dictionary has an invalid size: "
|
||||||
|
+ hashes.length);
|
||||||
|
pieces = new TorrentPiece[(hashes.length / 20)];
|
||||||
|
for (int index = 0; index < pieces.length; index++) {
|
||||||
|
int len = pieceLength;
|
||||||
|
if (pieces.length - 1 == index) // last piece
|
||||||
|
len = (int) (size - (pieceLength * (pieces.length - 1)));
|
||||||
|
|
||||||
|
pieces[index] = new TorrentPiece(this, Arrays.copyOfRange(hashes,
|
||||||
|
index * 20, index * 20 + 20), index, len);
|
||||||
|
}
|
||||||
|
// Arrays.sort(pieces, PieceIndexComparator.SHARED_INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateSize() {
|
||||||
|
for (TorrentFile file : files) {
|
||||||
|
size += file.getLength();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TorrentHash calculateInfoHash(Map<String, ?> info) {
|
||||||
|
try {
|
||||||
|
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
|
||||||
|
sha1.update(BEncodedOutputStream.bencode(info));
|
||||||
|
return new TorrentHash(sha1.digest(), HashType.SHA1);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Torrent getTorrent() {
|
||||||
|
return torrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentHash getInfoHash() {
|
||||||
|
return infohash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TorrentFile> getFiles() {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentPiece[] getPieces() {
|
||||||
|
return pieces;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPieceLength() {
|
||||||
|
return pieceLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrivTracker() {
|
||||||
|
return privTracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDirectoryName() {
|
||||||
|
return directoryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentPiece getPiece(int index) {
|
||||||
|
for (final TorrentPiece piece : pieces) {
|
||||||
|
if (piece.getIndex() == index)
|
||||||
|
return piece;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentPart getPart(TorrentPiece piece, int start, int len) {
|
||||||
|
return piece.getPart(start, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorrentPart getPart(int index, int start, int len) {
|
||||||
|
return getPart(getPiece(index), start, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
239
src/main/java/net/torrent/torrent/TorrentPart.java
Normal file
239
src/main/java/net/torrent/torrent/TorrentPart.java
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.message.RequestMessage;
|
||||||
|
import net.torrent.util.Range;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BitTorrent files are divided in small chunks of hashes, each hash represent
|
||||||
|
* an <b>piece</b> and each of those pieces are divided into smaller parts. Each
|
||||||
|
* part can be requested to an peer and received afterwards.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
* @see RequestMessage RequestMessage for more about requests and parts
|
||||||
|
*/
|
||||||
|
public class TorrentPart {
|
||||||
|
/**
|
||||||
|
* The standard part length -- most clients respect this!
|
||||||
|
*/
|
||||||
|
public static final int PART_LENGTH = 16 * 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The piece index, only for internal management
|
||||||
|
*/
|
||||||
|
private int index; // internal use only
|
||||||
|
/**
|
||||||
|
* The piece of this part
|
||||||
|
*/
|
||||||
|
private final TorrentPiece piece;
|
||||||
|
/**
|
||||||
|
* The start offset of data
|
||||||
|
*/
|
||||||
|
private final int start;
|
||||||
|
/**
|
||||||
|
* The length of data
|
||||||
|
*/
|
||||||
|
private final int length;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param piece
|
||||||
|
* the piece
|
||||||
|
* @param start
|
||||||
|
* the start offset
|
||||||
|
* @param length
|
||||||
|
* the length
|
||||||
|
*/
|
||||||
|
public TorrentPart(TorrentPiece piece, int start, int length) {
|
||||||
|
this.index = start / length;
|
||||||
|
this.piece = piece;
|
||||||
|
this.start = start;
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
* the part index. <b>Note</b>: internal use only!
|
||||||
|
* @param piece
|
||||||
|
* the piece
|
||||||
|
* @param start
|
||||||
|
* the start offset
|
||||||
|
* @param length
|
||||||
|
* the length
|
||||||
|
*/
|
||||||
|
public TorrentPart(int index, TorrentPiece piece, int start, int length) {
|
||||||
|
this.index = index;
|
||||||
|
this.piece = piece;
|
||||||
|
this.start = start;
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the piece
|
||||||
|
*
|
||||||
|
* @return the piece
|
||||||
|
*/
|
||||||
|
public TorrentPiece getPiece() {
|
||||||
|
return piece;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent
|
||||||
|
*
|
||||||
|
* @return the torrent
|
||||||
|
*/
|
||||||
|
public Torrent getTorrent() {
|
||||||
|
return piece.getTorrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data start offset
|
||||||
|
*
|
||||||
|
* @return the start offset
|
||||||
|
*/
|
||||||
|
public int getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data length
|
||||||
|
*
|
||||||
|
* @return the data length
|
||||||
|
*/
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the offset of part inside the torrent.
|
||||||
|
*
|
||||||
|
* @return the part's torrent offset
|
||||||
|
*/
|
||||||
|
public long getOffset() {
|
||||||
|
return piece.getOffset() + start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this is the last part in this piece.
|
||||||
|
* <p>
|
||||||
|
* <code>
|
||||||
|
* TorrentPart part = ...;<br>
|
||||||
|
* boolean first = (part.getStart() + part.getLength() == part.getPiece().getLength());
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @return true if last
|
||||||
|
*/
|
||||||
|
public boolean isLast() {
|
||||||
|
return (start + length == piece.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this is the first part in this piece.
|
||||||
|
* <p>
|
||||||
|
* <code>
|
||||||
|
* TorrentPart part = ...;<br>
|
||||||
|
* boolean first = (part.getStart() == 0);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @return true if first
|
||||||
|
*/
|
||||||
|
public boolean isFirst() {
|
||||||
|
return start == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next part. Null if last.
|
||||||
|
*
|
||||||
|
* @return the next part. Might be null.
|
||||||
|
*/
|
||||||
|
public TorrentPart getNextPart() {
|
||||||
|
if (isLast())
|
||||||
|
return null;
|
||||||
|
return piece.getParts()[index + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the range inside the piece.
|
||||||
|
*
|
||||||
|
* @return the range in piece
|
||||||
|
*/
|
||||||
|
public Range asPieceRange() {
|
||||||
|
return Range.getRangeByLength(start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the range inside the torrent
|
||||||
|
*
|
||||||
|
* @return the range in torrent
|
||||||
|
*/
|
||||||
|
public Range asTorrentRange() {
|
||||||
|
return Range.getRangeByLength(piece.getOffset() + start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the range inside the file
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* the file
|
||||||
|
* @return the range in file
|
||||||
|
*/
|
||||||
|
public Range asFileRange(TorrentFile file) {
|
||||||
|
return Range.getRangeByLength(
|
||||||
|
piece.getOffset() - file.getOffset() + start, length)
|
||||||
|
.intersection(file.asFileRange());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TorrentPart [piece=" + piece + ", start=" + start + ", length="
|
||||||
|
+ length + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + length;
|
||||||
|
result = prime * result + ((piece == null) ? 0 : piece.hashCode());
|
||||||
|
result = prime * result + start;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
TorrentPart other = (TorrentPart) obj;
|
||||||
|
if (length != other.length)
|
||||||
|
return false;
|
||||||
|
if (piece == null) {
|
||||||
|
if (other.piece != null)
|
||||||
|
return false;
|
||||||
|
} else if (!piece.equals(other.piece))
|
||||||
|
return false;
|
||||||
|
if (start != other.start)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
252
src/main/java/net/torrent/torrent/TorrentPiece.java
Normal file
252
src/main/java/net/torrent/torrent/TorrentPiece.java
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent;
|
||||||
|
|
||||||
|
import net.torrent.torrent.TorrentHash.HashType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Torrent files are divided into small chunks of data called <tt>pieces</tt>.
|
||||||
|
* Each piece has the same size, except for the last which can be equal or
|
||||||
|
* smaller (enough to fit all data).
|
||||||
|
* <p>
|
||||||
|
* This class handles this distribution and takes care of creating
|
||||||
|
* {@link TorrentPart parts}.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentPiece {
|
||||||
|
/**
|
||||||
|
* The Torrent information
|
||||||
|
*/
|
||||||
|
private final TorrentInfo info;
|
||||||
|
/**
|
||||||
|
* The piece hash
|
||||||
|
*/
|
||||||
|
private final TorrentHash hash;
|
||||||
|
/**
|
||||||
|
* The piece index
|
||||||
|
*/
|
||||||
|
private final int index;
|
||||||
|
/**
|
||||||
|
* The piece length
|
||||||
|
*/
|
||||||
|
private final int length;
|
||||||
|
/**
|
||||||
|
* The torrent parts. All parts have the standard length (
|
||||||
|
* {@link TorrentPart#PART_LENGTH})
|
||||||
|
*/
|
||||||
|
private final TorrentPart[] parts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance
|
||||||
|
*
|
||||||
|
* @param info
|
||||||
|
* the torrent information
|
||||||
|
* @param hash
|
||||||
|
* the torrent hash (as {@link TorrentHash})
|
||||||
|
* @param index
|
||||||
|
* the piece index
|
||||||
|
* @param length
|
||||||
|
* the piece length
|
||||||
|
*/
|
||||||
|
public TorrentPiece(TorrentInfo info, final TorrentHash hash, int index,
|
||||||
|
int length) {
|
||||||
|
this.info = info;
|
||||||
|
this.hash = hash;
|
||||||
|
this.index = index;
|
||||||
|
this.length = length;
|
||||||
|
|
||||||
|
int len = TorrentPart.PART_LENGTH;
|
||||||
|
if (len > length)
|
||||||
|
len = length;
|
||||||
|
int partCount = (length / len);
|
||||||
|
if ((length % len) > 0)
|
||||||
|
partCount += 1;
|
||||||
|
|
||||||
|
parts = new TorrentPart[partCount];
|
||||||
|
int remain = length;
|
||||||
|
for (int i = 0; i < parts.length; i++) {
|
||||||
|
int plen = remain;
|
||||||
|
if (remain > len)
|
||||||
|
plen = len;
|
||||||
|
parts[i] = new TorrentPart(i, this, length - remain, plen);
|
||||||
|
remain -= plen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param info
|
||||||
|
* the torrent info
|
||||||
|
* @param hash
|
||||||
|
* the info hash (as byte array)
|
||||||
|
* @param index
|
||||||
|
* the piece index
|
||||||
|
* @param length
|
||||||
|
* the piece length
|
||||||
|
*/
|
||||||
|
public TorrentPiece(TorrentInfo info, final byte[] hash, int index,
|
||||||
|
int length) {
|
||||||
|
this(info, new TorrentHash(hash, HashType.SHA1), index, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent information
|
||||||
|
*
|
||||||
|
* @return the torrent information
|
||||||
|
*/
|
||||||
|
public TorrentInfo getInfo() {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent
|
||||||
|
*
|
||||||
|
* @return the torrent
|
||||||
|
*/
|
||||||
|
public Torrent getTorrent() {
|
||||||
|
return info.getTorrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the piece hash
|
||||||
|
*
|
||||||
|
* @return the piece hash
|
||||||
|
*/
|
||||||
|
public TorrentHash getHash() {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the piece index
|
||||||
|
*
|
||||||
|
* @return the piece index
|
||||||
|
*/
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the piece length
|
||||||
|
*
|
||||||
|
* @return the piece length
|
||||||
|
*/
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the piece offset
|
||||||
|
*
|
||||||
|
* @return the piece offset (relative to torrent)
|
||||||
|
*/
|
||||||
|
public long getOffset() {
|
||||||
|
return (long) index * (long) info.getPieceLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next sequential part
|
||||||
|
*
|
||||||
|
* @return the next part
|
||||||
|
*/
|
||||||
|
public TorrentPiece getNextPiece() {
|
||||||
|
return info.getPiece(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all parts
|
||||||
|
*
|
||||||
|
* @return all the parts
|
||||||
|
*/
|
||||||
|
public TorrentPart[] getParts() {
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the first part
|
||||||
|
*
|
||||||
|
* @return the first part
|
||||||
|
*/
|
||||||
|
public TorrentPart getFirstPart() {
|
||||||
|
return parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last part
|
||||||
|
*
|
||||||
|
* @return the last part
|
||||||
|
*/
|
||||||
|
public TorrentPart getLastPart() {
|
||||||
|
return parts[parts.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The the part starting at <tt>start</tt> with <tt>length</tt>
|
||||||
|
*
|
||||||
|
* @param start
|
||||||
|
* @param length
|
||||||
|
* @return the found or created part
|
||||||
|
*/
|
||||||
|
public TorrentPart getPart(int start, int length) {
|
||||||
|
for (final TorrentPart part : parts) {
|
||||||
|
if (part.getStart() == start && part.getLength() == length)
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
return new TorrentPart(this, start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get this piece as an entire part
|
||||||
|
*
|
||||||
|
* @return the part
|
||||||
|
*/
|
||||||
|
public TorrentPart asSinglePart() {
|
||||||
|
return getPart(0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TorrentPiece [info=" + info + ", hash=" + hash + ", index="
|
||||||
|
+ index + ", length=" + length + ", parts="
|
||||||
|
+ (parts != null ? parts.length : null) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((hash == null) ? 0 : hash.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
TorrentPiece other = (TorrentPiece) obj;
|
||||||
|
if (hash == null) {
|
||||||
|
if (other.hash != null)
|
||||||
|
return false;
|
||||||
|
} else if (!hash.equals(other.hash))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
146
src/main/java/net/torrent/torrent/TorrentTracker.java
Normal file
146
src/main/java/net/torrent/torrent/TorrentTracker.java
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An tracker has the responsibility to discover new peers. Each peer, once
|
||||||
|
* begin downloading, send its own peer id and got registered into an peer table
|
||||||
|
* in the tracker. Once the next peer starts, it receives an copy of that peer
|
||||||
|
* table and connect to some or all peers in that table.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentTracker {
|
||||||
|
/**
|
||||||
|
* The torrent
|
||||||
|
*/
|
||||||
|
private final Torrent torrent;
|
||||||
|
/**
|
||||||
|
* The tracker URI. There are several types of trackers:
|
||||||
|
* <ul>
|
||||||
|
* <li>HTTP</li>
|
||||||
|
* <li>HTTPS</li>
|
||||||
|
* <li>UDP</li>
|
||||||
|
* </ul>
|
||||||
|
* Each type have its own protocol and methods to retrieve peers.
|
||||||
|
*/
|
||||||
|
private final URI uri;
|
||||||
|
/**
|
||||||
|
* The tracker backups uris
|
||||||
|
*/
|
||||||
|
private final Set<URI> backup = new HashSet<URI>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param torrent
|
||||||
|
* the torrent
|
||||||
|
* @param uri
|
||||||
|
* the tracker uri
|
||||||
|
* @param backup
|
||||||
|
* the tracker's backup uris
|
||||||
|
*/
|
||||||
|
public TorrentTracker(Torrent torrent, URI uri, URI... backup) {
|
||||||
|
this.torrent = torrent;
|
||||||
|
this.uri = uri;
|
||||||
|
if (backup != null && backup.length > 0)
|
||||||
|
Collections.addAll(this.backup, backup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param torrent
|
||||||
|
* the torrent
|
||||||
|
* @param uri
|
||||||
|
* the tracker uri
|
||||||
|
*/
|
||||||
|
public TorrentTracker(Torrent torrent, URI uri) {
|
||||||
|
this(torrent, uri, (URI[]) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent
|
||||||
|
*
|
||||||
|
* @return the torrent
|
||||||
|
*/
|
||||||
|
public Torrent getTorrent() {
|
||||||
|
return torrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the tracker {@link URL}
|
||||||
|
*
|
||||||
|
* @return the url
|
||||||
|
*/
|
||||||
|
public URL getURL() {
|
||||||
|
try {
|
||||||
|
return uri.toURL();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the tracker {@link URI}
|
||||||
|
*
|
||||||
|
* @return the tracker uri
|
||||||
|
*/
|
||||||
|
public URI getURI() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the backup {@link URI}s
|
||||||
|
*
|
||||||
|
* @return the tracker's backup URIS
|
||||||
|
*/
|
||||||
|
public Set<URI> getBackup() {
|
||||||
|
return Collections.unmodifiableSet(backup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((uri == null) ? 0 : uri.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
TorrentTracker other = (TorrentTracker) obj;
|
||||||
|
if (uri == null) {
|
||||||
|
if (other.uri != null)
|
||||||
|
return false;
|
||||||
|
} else if (!uri.equals(other.uri))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
260
src/main/java/net/torrent/torrent/context/TorrentBitfield.java
Normal file
260
src/main/java/net/torrent/torrent/context/TorrentBitfield.java
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent.context;
|
||||||
|
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a part bitfield. The only way to check if there is a full piece is to
|
||||||
|
* test all parts.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentBitfield implements Iterable<TorrentPiece> {
|
||||||
|
/**
|
||||||
|
* The torrent context
|
||||||
|
*/
|
||||||
|
private final TorrentContext context;
|
||||||
|
/**
|
||||||
|
* {@link BitSet} containing all completed pieces
|
||||||
|
*/
|
||||||
|
private final BitSet bits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance given the {@link BitSet}.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* the torrent context
|
||||||
|
* @param bits
|
||||||
|
* the {@link BitSet}.
|
||||||
|
*/
|
||||||
|
public TorrentBitfield(TorrentContext context, BitSet bits) {
|
||||||
|
this.context = context;
|
||||||
|
this.bits = bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* the torrent context
|
||||||
|
*/
|
||||||
|
public TorrentBitfield(TorrentContext context) {
|
||||||
|
this(context, new BitSet((context.getTorrent() != null ? context
|
||||||
|
.getTorrent().getPieces().length : 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if this bitfield contains the given piece. Redirect the call to
|
||||||
|
* {@link TorrentBitfield#hasPiece(int)}.
|
||||||
|
*
|
||||||
|
* @param piece
|
||||||
|
* the piece to test
|
||||||
|
* @return true if piece is set, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean hasPiece(TorrentPiece piece) {
|
||||||
|
if (piece == null)
|
||||||
|
throw new InvalidParameterException("Piece is null");
|
||||||
|
return hasPiece(piece.getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if this bitfield contains the given piece <tt>index</tt>
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
* the piece index
|
||||||
|
* @return true if piece is set, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean hasPiece(int index) {
|
||||||
|
return bits.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the state of the given piece.
|
||||||
|
*
|
||||||
|
* @param piece
|
||||||
|
* the piece
|
||||||
|
* @param state
|
||||||
|
* the state
|
||||||
|
*/
|
||||||
|
public void setPiece(TorrentPiece piece, boolean state) {
|
||||||
|
if (piece == null)
|
||||||
|
throw new InvalidParameterException("Piece is null");
|
||||||
|
bits.set(piece.getIndex(), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the bits into this bitfield.
|
||||||
|
*
|
||||||
|
* @param bitSet
|
||||||
|
* the bits
|
||||||
|
*/
|
||||||
|
public void setBits(BitSet bitSet) {
|
||||||
|
bits.xor(bitSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent context
|
||||||
|
*
|
||||||
|
* @return the torrent context
|
||||||
|
*/
|
||||||
|
public TorrentContext getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the backing {@link BitSet}.
|
||||||
|
*
|
||||||
|
* @return the bit set
|
||||||
|
*/
|
||||||
|
public BitSet getBits() {
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an Bitfield representing remaining pieces.
|
||||||
|
*
|
||||||
|
* @return the remain bitfield
|
||||||
|
*/
|
||||||
|
public TorrentBitfield getRemainingPieces() {
|
||||||
|
BitSet set = (BitSet) bits.clone();
|
||||||
|
set.flip(0, bits.size());
|
||||||
|
return new TorrentBitfield(context, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((bits == null) ? 0 : bits.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
TorrentBitfield other = (TorrentBitfield) obj;
|
||||||
|
if (bits == null) {
|
||||||
|
if (other.bits != null)
|
||||||
|
return false;
|
||||||
|
} else if (!bits.equals(other.bits))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<TorrentPiece> iterator() {
|
||||||
|
return iterator(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate to all pieces with <tt>type</tt>. True if set, false if not.
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* the type
|
||||||
|
* @return {@link Iterator} for those pieces
|
||||||
|
*/
|
||||||
|
public Iterator<TorrentPiece> iterator(final boolean type) {
|
||||||
|
return new BitfieldIterator(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate to all pieces with <tt>type</tt>. True if set, false if not.
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* the type
|
||||||
|
* @return {@link Iterable} for those pieces
|
||||||
|
* @see TorrentBitfield#iterator(boolean)
|
||||||
|
*/
|
||||||
|
public Iterable<TorrentPiece> iterate(final boolean type) {
|
||||||
|
return new Iterable<TorrentPiece>() {
|
||||||
|
@Override
|
||||||
|
public Iterator<TorrentPiece> iterator() {
|
||||||
|
return TorrentBitfield.this.iterator(type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitfield iterator
|
||||||
|
*
|
||||||
|
* @author Rogiel Josias Sulzbach (<a
|
||||||
|
* href="http://www.rogiel.com/">http://www.rogiel.com/</a>)
|
||||||
|
*/
|
||||||
|
public class BitfieldIterator implements Iterator<TorrentPiece> {
|
||||||
|
/**
|
||||||
|
* The desired value
|
||||||
|
*/
|
||||||
|
private boolean value = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current index
|
||||||
|
*/
|
||||||
|
private int index = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the desired state
|
||||||
|
*/
|
||||||
|
public BitfieldIterator(boolean value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return getIndex() >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TorrentPiece next() {
|
||||||
|
int index = -2;
|
||||||
|
try {
|
||||||
|
index = getIndex();
|
||||||
|
return context.getTorrent().getPiece(getIndex());
|
||||||
|
} finally {
|
||||||
|
this.index = index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the next index, without increasing it.
|
||||||
|
*
|
||||||
|
* @return the next index, -1 if non existent.
|
||||||
|
*/
|
||||||
|
private int getIndex() {
|
||||||
|
if (value)
|
||||||
|
return index = bits.nextSetBit(index);
|
||||||
|
else
|
||||||
|
return bits.nextClearBit(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
188
src/main/java/net/torrent/torrent/context/TorrentContext.java
Normal file
188
src/main/java/net/torrent/torrent/context/TorrentContext.java
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent.context;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.torrent.protocol.tracker.message.PeerListMessage.PeerInfo;
|
||||||
|
import net.torrent.torrent.Torrent;
|
||||||
|
import net.torrent.torrent.context.TorrentPeerCapabilities.TorrentPeerCapability;
|
||||||
|
|
||||||
|
public class TorrentContext {
|
||||||
|
/**
|
||||||
|
* The torrent metadata object
|
||||||
|
*/
|
||||||
|
private final Torrent torrent;
|
||||||
|
/**
|
||||||
|
* The bitfield
|
||||||
|
*/
|
||||||
|
private final TorrentBitfield bitfield = new TorrentBitfield(this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The capabilities
|
||||||
|
*/
|
||||||
|
private final TorrentPeerCapabilities capabilites = new TorrentPeerCapabilities(
|
||||||
|
TorrentPeerCapability.DHT, TorrentPeerCapability.FAST_PEERS);
|
||||||
|
|
||||||
|
private final Set<TorrentPeer> peers = new HashSet<TorrentPeer>();
|
||||||
|
/**
|
||||||
|
* Unknown peers does not have their IDs, consequently they cannot be
|
||||||
|
* queried using their Id and must be done through IP.
|
||||||
|
*/
|
||||||
|
private final Set<TorrentPeer> unknownPeers = new HashSet<TorrentPeer>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new context
|
||||||
|
*
|
||||||
|
* @param torrent
|
||||||
|
* the torrent metadata
|
||||||
|
*/
|
||||||
|
public TorrentContext(Torrent torrent) {
|
||||||
|
this.torrent = torrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent metadata object
|
||||||
|
*
|
||||||
|
* @return metadata object
|
||||||
|
*/
|
||||||
|
public Torrent getTorrent() {
|
||||||
|
return torrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the context bitfield
|
||||||
|
*
|
||||||
|
* @return the bitfield
|
||||||
|
*/
|
||||||
|
public TorrentBitfield getBitfield() {
|
||||||
|
return bitfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the capabilities of this context
|
||||||
|
*
|
||||||
|
* @return the capabilities
|
||||||
|
*/
|
||||||
|
public TorrentPeerCapabilities getCapabilites() {
|
||||||
|
return capabilites;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if both peer and this context support an given capability.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
* @param capability
|
||||||
|
* the capability
|
||||||
|
* @return true if both support this capability
|
||||||
|
*/
|
||||||
|
public boolean supports(TorrentPeer peer, TorrentPeerCapability capability) {
|
||||||
|
return capabilites.supports(capability)
|
||||||
|
&& peer.getCapabilities().supports(capability);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of known peers (have known peerid)
|
||||||
|
*
|
||||||
|
* @return the list of peers
|
||||||
|
*/
|
||||||
|
public Set<TorrentPeer> getPeers() {
|
||||||
|
return Collections.unmodifiableSet(peers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of unknown peers (don't have known peerid)
|
||||||
|
*
|
||||||
|
* @return the list of peers
|
||||||
|
*/
|
||||||
|
public Set<TorrentPeer> getUnknownPeers() {
|
||||||
|
return Collections.unmodifiableSet(unknownPeers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an peer by its PeerID
|
||||||
|
*
|
||||||
|
* @param peerId
|
||||||
|
* the peer id
|
||||||
|
* @return the found peer. Null if not found.
|
||||||
|
*/
|
||||||
|
public TorrentPeer getPeer(TorrentPeerID peerId) {
|
||||||
|
for (final TorrentPeer peer : peers) {
|
||||||
|
if (peer.getPeerID().equals(peerId))
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an peer by its address
|
||||||
|
*
|
||||||
|
* @param address
|
||||||
|
* the address
|
||||||
|
* @return the found peer. Null if not found.
|
||||||
|
*/
|
||||||
|
public TorrentPeer getPeer(InetSocketAddress address) {
|
||||||
|
for (final TorrentPeer peer : peers) {
|
||||||
|
if (peer.getSocketAddress().equals(address))
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup for a peer first by its id, then by address, if still not found,
|
||||||
|
* creates a new entry.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* the peer id
|
||||||
|
* @param address
|
||||||
|
* the address
|
||||||
|
* @return the found or newly created peer
|
||||||
|
*/
|
||||||
|
public TorrentPeer getPeer(TorrentPeerID id, InetSocketAddress address) {
|
||||||
|
TorrentPeer peer = getPeer(id);
|
||||||
|
if (peer == null) {
|
||||||
|
peer = getPeer(address);
|
||||||
|
if (peer != null) {
|
||||||
|
if (peers.remove(peer))
|
||||||
|
peer = peer.createWithID(id);
|
||||||
|
} else {
|
||||||
|
peer = new TorrentPeer(this, id, null);
|
||||||
|
}
|
||||||
|
peers.add(peer);
|
||||||
|
}
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this peer already exists, will update its IP.
|
||||||
|
*
|
||||||
|
* @param peerInfo
|
||||||
|
* the peer info object, returned from the tracker
|
||||||
|
*/
|
||||||
|
public TorrentPeer addPeerByPeerInfo(PeerInfo peerInfo) {
|
||||||
|
final TorrentPeerID id = TorrentPeerID.create(peerInfo.getPeerId());
|
||||||
|
final InetSocketAddress address = new InetSocketAddress(
|
||||||
|
peerInfo.getIp(), peerInfo.getPort());
|
||||||
|
TorrentPeer peer = getPeer(id, address);
|
||||||
|
peer.setSocketAddress(address);
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
}
|
||||||
369
src/main/java/net/torrent/torrent/context/TorrentPeer.java
Normal file
369
src/main/java/net/torrent/torrent/context/TorrentPeer.java
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent.context;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object representing a peer in the swarm.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentPeer {
|
||||||
|
/**
|
||||||
|
* The torrent context
|
||||||
|
*/
|
||||||
|
private final TorrentContext context;
|
||||||
|
/**
|
||||||
|
* The peer id
|
||||||
|
*/
|
||||||
|
private final TorrentPeerID peerID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of peer capabilities
|
||||||
|
*/
|
||||||
|
private final TorrentPeerCapabilities capabilities = new TorrentPeerCapabilities();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bitfield of the peer
|
||||||
|
*/
|
||||||
|
private final TorrentBitfield bitfield;
|
||||||
|
/**
|
||||||
|
* Local choking state to this peer. By default is
|
||||||
|
* {@link ChokingState#CHOKED choked}.
|
||||||
|
*/
|
||||||
|
private ChokingState chokingState = ChokingState.CHOKED;
|
||||||
|
/**
|
||||||
|
* Remote choking state of this peer. By default is
|
||||||
|
* {@link ChokingState#CHOKED choked}.
|
||||||
|
*/
|
||||||
|
private ChokingState peerChokingState = ChokingState.CHOKED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The choking states
|
||||||
|
*
|
||||||
|
* @author Rogiel Josias Sulzbach (<a
|
||||||
|
* href="http://www.rogiel.com/">http://www.rogiel.com/</a>)
|
||||||
|
*/
|
||||||
|
public enum ChokingState {
|
||||||
|
/**
|
||||||
|
* Choked: communication is "blocked". No pieces can be requested nor
|
||||||
|
* sent. By stantard, this is the default interest once the connection
|
||||||
|
* is made.
|
||||||
|
*/
|
||||||
|
CHOKED,
|
||||||
|
/**
|
||||||
|
* Unchoked: communication is "open": pieces can be requested and
|
||||||
|
* uploaded.
|
||||||
|
*/
|
||||||
|
UNCHOKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local interest to this peer. By default is
|
||||||
|
* {@link InterestState#UNINTERESTED not interested}
|
||||||
|
*/
|
||||||
|
private InterestState interestState = InterestState.UNINTERESTED;
|
||||||
|
/**
|
||||||
|
* Remote interest of this peer. By default is
|
||||||
|
* {@link InterestState#UNINTERESTED not interested}
|
||||||
|
*/
|
||||||
|
private InterestState peerInterestState = InterestState.UNINTERESTED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interest states
|
||||||
|
*
|
||||||
|
* @author Rogiel Josias Sulzbach (<a
|
||||||
|
* href="http://www.rogiel.com/">http://www.rogiel.com/</a>)
|
||||||
|
*/
|
||||||
|
public enum InterestState {
|
||||||
|
/**
|
||||||
|
* The peer is interested in the other pieces. If the peer is willing to
|
||||||
|
* upload, the remote peer is unchoked and data transfer can begin.
|
||||||
|
*/
|
||||||
|
INTERESTED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The peer has not interest in the remote peer pieces. By stantard,
|
||||||
|
* this is the default interest once the connection is made.
|
||||||
|
*/
|
||||||
|
UNINTERESTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The peer socket address. For incoming connections.
|
||||||
|
*/
|
||||||
|
private InetSocketAddress socketAddress;
|
||||||
|
/**
|
||||||
|
* Last time the peer was found online.
|
||||||
|
*/
|
||||||
|
private Date lastReached = null;
|
||||||
|
/**
|
||||||
|
* State of the peer connectivity. Peer behind firewall or NAT will have
|
||||||
|
* false. By default all peers are accessible.
|
||||||
|
*/
|
||||||
|
private boolean accessible = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new peer
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* the torrent context
|
||||||
|
* @param peerID
|
||||||
|
* the peer id
|
||||||
|
* @param socketAddress
|
||||||
|
* the peer address
|
||||||
|
*/
|
||||||
|
public TorrentPeer(TorrentContext context, TorrentPeerID peerID,
|
||||||
|
InetSocketAddress socketAddress) {
|
||||||
|
if (peerID == null && socketAddress == null)
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"PeerID or SocketAddress must be not null");
|
||||||
|
if (context == null)
|
||||||
|
throw new IllegalArgumentException("Context is null");
|
||||||
|
|
||||||
|
this.context = context;
|
||||||
|
this.peerID = peerID;
|
||||||
|
this.socketAddress = socketAddress;
|
||||||
|
this.bitfield = new TorrentBitfield(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the peer id
|
||||||
|
*
|
||||||
|
* @return the peer id
|
||||||
|
*/
|
||||||
|
public TorrentPeerID getPeerID() {
|
||||||
|
return peerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the peer address
|
||||||
|
*
|
||||||
|
* @param address
|
||||||
|
* the address
|
||||||
|
*/
|
||||||
|
public void setSocketAddress(InetSocketAddress address) {
|
||||||
|
this.socketAddress = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the peer address
|
||||||
|
*
|
||||||
|
* @return the address
|
||||||
|
*/
|
||||||
|
public InetSocketAddress getSocketAddress() {
|
||||||
|
return socketAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the peer bitfield
|
||||||
|
*
|
||||||
|
* @return the bitfield
|
||||||
|
*/
|
||||||
|
public TorrentBitfield getBitfield() {
|
||||||
|
return bitfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the peer capabilities
|
||||||
|
*
|
||||||
|
* @return the capabilities
|
||||||
|
*/
|
||||||
|
public TorrentPeerCapabilities getCapabilities() {
|
||||||
|
return capabilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the local choking state to this peer. By default is
|
||||||
|
* {@link ChokingState#CHOKED choked}.
|
||||||
|
*
|
||||||
|
* @return the choking state
|
||||||
|
*/
|
||||||
|
public ChokingState getChokingState() {
|
||||||
|
return chokingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the local choking state to this peer. By default is
|
||||||
|
* {@link ChokingState#CHOKED choked}.
|
||||||
|
*
|
||||||
|
* @param chokingState
|
||||||
|
* the choking state
|
||||||
|
*/
|
||||||
|
public void setChokingState(ChokingState chokingState) {
|
||||||
|
this.chokingState = chokingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Get the remote choking state of this peer. By default is
|
||||||
|
* {@link ChokingState#CHOKED choked}.
|
||||||
|
*
|
||||||
|
* @return the peer choking state
|
||||||
|
*/
|
||||||
|
public ChokingState getPeerChokingState() {
|
||||||
|
return peerChokingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the remote choking state of this peer. By default is
|
||||||
|
* {@link ChokingState#CHOKED choked}.
|
||||||
|
*
|
||||||
|
* @param peerChokingState
|
||||||
|
* the peer choking state
|
||||||
|
*/
|
||||||
|
public void setPeerChokingState(ChokingState peerChokingState) {
|
||||||
|
this.peerChokingState = peerChokingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the local interest to this peer. By default is
|
||||||
|
* {@link InterestState#UNINTERESTED not interested}
|
||||||
|
*
|
||||||
|
* @return the local interest
|
||||||
|
*/
|
||||||
|
public InterestState getInterestState() {
|
||||||
|
return interestState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the local interest to this peer. By default is
|
||||||
|
* {@link InterestState#UNINTERESTED not interested}
|
||||||
|
*
|
||||||
|
* @param interestState
|
||||||
|
* the local interest
|
||||||
|
*/
|
||||||
|
public void setInterestState(InterestState interestState) {
|
||||||
|
this.interestState = interestState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the peer remote interest of this peer. By default is
|
||||||
|
* {@link InterestState#UNINTERESTED not interested}
|
||||||
|
*
|
||||||
|
* @return the peer remote interest
|
||||||
|
*/
|
||||||
|
public InterestState getPeerInterestState() {
|
||||||
|
return peerInterestState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the peer remote interest of this peer. By default is
|
||||||
|
* {@link InterestState#UNINTERESTED not interested}
|
||||||
|
*
|
||||||
|
* @param peerInterestState
|
||||||
|
* the peer remote interest
|
||||||
|
*/
|
||||||
|
public void setPeerInterestState(InterestState peerInterestState) {
|
||||||
|
this.peerInterestState = peerInterestState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last time the peer has been reached by an connection
|
||||||
|
*
|
||||||
|
* @return the date of last connection
|
||||||
|
*/
|
||||||
|
public Date getLastReached() {
|
||||||
|
return lastReached;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the last time the peer has been reached by an connection
|
||||||
|
*
|
||||||
|
* @param lastReached
|
||||||
|
* the date of last connection
|
||||||
|
*/
|
||||||
|
public void setLastReached(Date lastReached) {
|
||||||
|
this.lastReached = lastReached;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State of the peer connectivity. Peer behind firewall or NAT will have
|
||||||
|
* false. By default all peers are accessible.
|
||||||
|
*
|
||||||
|
* @return true if is accessible, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isAccessible() {
|
||||||
|
return accessible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State of the peer connectivity. Peer behind firewall or NAT will have
|
||||||
|
* false. By default all peers are accessible.
|
||||||
|
*
|
||||||
|
* @param accessible
|
||||||
|
* true if is accessible, false otherwise.
|
||||||
|
*/
|
||||||
|
public void setAccessible(boolean accessible) {
|
||||||
|
this.accessible = accessible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the torrent context
|
||||||
|
*
|
||||||
|
* @return the torrent context
|
||||||
|
*/
|
||||||
|
public TorrentContext getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an clone of this peer with the new {@link TorrentPeerID id}.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* the new peer id
|
||||||
|
* @return the new peer with id.
|
||||||
|
*/
|
||||||
|
public TorrentPeer createWithID(TorrentPeerID id) {
|
||||||
|
return new TorrentPeer(context, id, socketAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
TorrentPeer other = (TorrentPeer) obj;
|
||||||
|
if (peerID == null) {
|
||||||
|
if (other.peerID != null)
|
||||||
|
return false;
|
||||||
|
if (socketAddress == null) {
|
||||||
|
if (other.socketAddress != null)
|
||||||
|
return false;
|
||||||
|
} else if (!socketAddress.equals(other.socketAddress))
|
||||||
|
return false;
|
||||||
|
} else if (!peerID.equals(other.peerID))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
if (peerID != null) {
|
||||||
|
result = prime * result + peerID.hashCode();
|
||||||
|
} else if (socketAddress != null) {
|
||||||
|
result = prime * result + socketAddress.hashCode();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent.context;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object containing peers support for certain capabilities.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentPeerCapabilities {
|
||||||
|
/**
|
||||||
|
* BitSet containing available capabilities
|
||||||
|
*/
|
||||||
|
private BitSet capabilities = new BitSet(64);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance
|
||||||
|
*/
|
||||||
|
public TorrentPeerCapabilities() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with the given <tt>capabilities</tt>
|
||||||
|
*
|
||||||
|
* @param capabilities
|
||||||
|
* the capabilities supported
|
||||||
|
*/
|
||||||
|
public TorrentPeerCapabilities(TorrentPeerCapability... capabilities) {
|
||||||
|
for (final TorrentPeerCapability capability : capabilities) {
|
||||||
|
this.capabilities.set(capability.bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with the given <tt>capabilities</tt>
|
||||||
|
*
|
||||||
|
* @param capabilities
|
||||||
|
* the capabilities {@link BitSet} supported
|
||||||
|
*/
|
||||||
|
public TorrentPeerCapabilities(BitSet capabilitites) {
|
||||||
|
this.capabilities = capabilitites;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the given <tt>capability</tt> is supported.
|
||||||
|
*
|
||||||
|
* @param capability
|
||||||
|
* the capability
|
||||||
|
* @return true if capability is supported, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean supports(TorrentPeerCapability capability) {
|
||||||
|
return capabilities.get(capability.getBit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an {@link List} of capabilities supported.
|
||||||
|
*
|
||||||
|
* @return the list os capabilities
|
||||||
|
*/
|
||||||
|
public List<TorrentPeerCapability> getCapabilities() {
|
||||||
|
final List<TorrentPeerCapability> capabilitites = new ArrayList<TorrentPeerCapability>();
|
||||||
|
for (final TorrentPeerCapability capability : TorrentPeerCapability
|
||||||
|
.values()) {
|
||||||
|
if (supports(capability))
|
||||||
|
capabilitites.add(capability);
|
||||||
|
}
|
||||||
|
return capabilitites;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the capabilities {@link BitSet}.
|
||||||
|
*
|
||||||
|
* @param capabilitites
|
||||||
|
* the bitset
|
||||||
|
*/
|
||||||
|
public void setCapabilities(BitSet capabilitites) {
|
||||||
|
this.capabilities = capabilitites;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert this matrix of capabilties to a {@link BitSet}.
|
||||||
|
*
|
||||||
|
* @return an {@link BitSet}.
|
||||||
|
*/
|
||||||
|
public BitSet toBitSet() {
|
||||||
|
return capabilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumeration of known capabilities.
|
||||||
|
*
|
||||||
|
* @author Rogiel Josias Sulzbach (<a
|
||||||
|
* href="http://www.rogiel.com/">http://www.rogiel.com/</a>)
|
||||||
|
*/
|
||||||
|
public enum TorrentPeerCapability {
|
||||||
|
/**
|
||||||
|
* Location aware protocol
|
||||||
|
*/
|
||||||
|
LOCATION_AWARE_PROTOCOL(21),
|
||||||
|
/**
|
||||||
|
* Extension protocol
|
||||||
|
*/
|
||||||
|
EXTENSION_PROTOCOL(44),
|
||||||
|
/**
|
||||||
|
* Fast peers support
|
||||||
|
*/
|
||||||
|
FAST_PEERS(62),
|
||||||
|
/**
|
||||||
|
* DHT Support
|
||||||
|
*/
|
||||||
|
DHT(64);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bit index for this capability
|
||||||
|
*/
|
||||||
|
private final int bit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new capability
|
||||||
|
*
|
||||||
|
* @param bit
|
||||||
|
* the bit marking this capability
|
||||||
|
*/
|
||||||
|
TorrentPeerCapability(int bit) {
|
||||||
|
this.bit = bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the bit marking this capability
|
||||||
|
*
|
||||||
|
* @return the bit marking the capability
|
||||||
|
*/
|
||||||
|
public int getBit() {
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
87
src/main/java/net/torrent/torrent/context/TorrentPeerID.java
Normal file
87
src/main/java/net/torrent/torrent/context/TorrentPeerID.java
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent.context;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Peer id for an peer.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class TorrentPeerID {
|
||||||
|
/**
|
||||||
|
* The peer id in bytes (20 bytes)
|
||||||
|
*/
|
||||||
|
private byte[] peerID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new PeerID.
|
||||||
|
*
|
||||||
|
* @param peerID
|
||||||
|
* the bytes of the peer id
|
||||||
|
*/
|
||||||
|
protected TorrentPeerID(byte[] peerID) {
|
||||||
|
this.peerID = peerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new String(peerID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert to a byte array
|
||||||
|
*
|
||||||
|
* @return the id in bytes
|
||||||
|
*/
|
||||||
|
public byte[] toByteArray() {
|
||||||
|
return Arrays.copyOf(peerID, peerID.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ID
|
||||||
|
*
|
||||||
|
* @param peerID
|
||||||
|
* the id byte array
|
||||||
|
* @return the new instance of {@link TorrentPeerID peer id}
|
||||||
|
*/
|
||||||
|
public static final TorrentPeerID create(byte[] peerID) {
|
||||||
|
return new TorrentPeerID(peerID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + Arrays.hashCode(peerID);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
TorrentPeerID other = (TorrentPeerID) obj;
|
||||||
|
if (!Arrays.equals(peerID, other.peerID))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/main/java/net/torrent/torrent/piece/PieceSelector.java
Normal file
37
src/main/java/net/torrent/torrent/piece/PieceSelector.java
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent.piece;
|
||||||
|
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link PieceSelector} is used to select the desired piece to be
|
||||||
|
* downloaded. Implementations must make sure the piece is not being downloaded
|
||||||
|
* already and that the peer has it.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public interface PieceSelector {
|
||||||
|
/**
|
||||||
|
* Selects the next piece suitable for download.
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer chosen for download
|
||||||
|
* @return the {@link TorrentPiece piece} selected for download.
|
||||||
|
*/
|
||||||
|
public TorrentPiece select(TorrentPeer peer);
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent.piece;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects randomly an piece. Once this instance is created it takes all pieces
|
||||||
|
* and put them into an list, then the list is sorted by an random algorithm.
|
||||||
|
* Once the next piece is called the pieces are returned in-list order.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class RandomPieceSelector extends SortedListPieceSelector {
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager
|
||||||
|
*/
|
||||||
|
public RandomPieceSelector(TorrentManager manager) {
|
||||||
|
super(manager, Arrays.asList(manager.getTorrent().getPieces()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void sort(List<TorrentPiece> pieces) {
|
||||||
|
Collections.shuffle(pieces);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent.piece;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
import net.torrent.util.comparator.PieceIndexComparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select pieces in sequential order. Can be used for streaming purposes.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class SequentialPieceSelector extends SortedListPieceSelector {
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager
|
||||||
|
*/
|
||||||
|
public SequentialPieceSelector(TorrentManager manager) {
|
||||||
|
super(manager, Arrays.asList(manager.getTorrent().getPieces()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void sort(List<TorrentPiece> pieces) {
|
||||||
|
Collections.sort(pieces, PieceIndexComparator.SHARED_INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.torrent.piece;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.manager.TorrentManager;
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
import net.torrent.torrent.context.TorrentPeer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An sorted {@link PieceSelector} select pieces sequentially (whenever
|
||||||
|
* possible) in a pre-sorted list. The list is sorted on construction by the
|
||||||
|
* {@link SortedListPieceSelector#sort(List) sort} method.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public abstract class SortedListPieceSelector implements PieceSelector {
|
||||||
|
/**
|
||||||
|
* The torrent manager
|
||||||
|
*/
|
||||||
|
private final TorrentManager manager;
|
||||||
|
/**
|
||||||
|
* The sorted list of pieces
|
||||||
|
*/
|
||||||
|
private final List<TorrentPiece> pieces;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param manager
|
||||||
|
* the torrent manager
|
||||||
|
* @param pieces
|
||||||
|
* the <b>unsorted</b> list of pieces
|
||||||
|
*/
|
||||||
|
protected SortedListPieceSelector(TorrentManager manager,
|
||||||
|
List<TorrentPiece> pieces) {
|
||||||
|
this.manager = manager;
|
||||||
|
this.pieces = pieces;
|
||||||
|
this.sort(this.pieces);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized TorrentPiece select(TorrentPeer peer) {
|
||||||
|
for (int index = 0; index < pieces.size(); index++) {
|
||||||
|
final TorrentPiece piece = pieces.get(index);
|
||||||
|
if (manager.getContext().getBitfield().hasPiece(piece))
|
||||||
|
continue;
|
||||||
|
if (!peer.getBitfield().hasPiece(piece))
|
||||||
|
continue;
|
||||||
|
if (manager.getDownloadManager().isDownloading(piece))
|
||||||
|
continue;
|
||||||
|
return piece;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the set using an implementation specific algorithm.
|
||||||
|
*
|
||||||
|
* @param pieces
|
||||||
|
* the unsorted pieces list that will be sorted.
|
||||||
|
*/
|
||||||
|
protected abstract void sort(List<TorrentPiece> pieces);
|
||||||
|
}
|
||||||
34
src/main/java/net/torrent/util/PeerCallback.java
Normal file
34
src/main/java/net/torrent/util/PeerCallback.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.util;
|
||||||
|
|
||||||
|
import net.torrent.protocol.peerwire.PeerWirePeer;
|
||||||
|
import net.torrent.protocol.peerwire.manager.PeerManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback used in {@link PeerManager#execute(PeerCallback)}
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public interface PeerCallback {
|
||||||
|
/**
|
||||||
|
* Execute the desired action for <tt>peer</tt>
|
||||||
|
*
|
||||||
|
* @param peer
|
||||||
|
* the peer
|
||||||
|
*/
|
||||||
|
void callback(PeerWirePeer peer);
|
||||||
|
}
|
||||||
178
src/main/java/net/torrent/util/Range.java
Normal file
178
src/main/java/net/torrent/util/Range.java
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.util;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents an interval.
|
||||||
|
*
|
||||||
|
* @author dante
|
||||||
|
*/
|
||||||
|
public final class Range implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final long start, length;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a range with the given parameters
|
||||||
|
*
|
||||||
|
* @param start
|
||||||
|
* the beginning of the range
|
||||||
|
* @param length
|
||||||
|
* the length of the range
|
||||||
|
* @return a range
|
||||||
|
*/
|
||||||
|
public static Range getRangeByLength(long start, long length) {
|
||||||
|
return new Range(start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the smallest possible range containing the given numbers.
|
||||||
|
*
|
||||||
|
* @param number1
|
||||||
|
* number to be within the range
|
||||||
|
* @param number2
|
||||||
|
* number to be within the range
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Range getRangeByNumbers(long number1, long number2) {
|
||||||
|
long s = Math.min(number1, number2);
|
||||||
|
return new Range(s, Math.max(number1, number2) - s + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new range
|
||||||
|
*
|
||||||
|
* @param start
|
||||||
|
* @param length
|
||||||
|
*/
|
||||||
|
private Range(long start, long length) {
|
||||||
|
if (start < 0)
|
||||||
|
throw new IllegalArgumentException("start must be >= 0");
|
||||||
|
if (length < 0)
|
||||||
|
throw new IllegalArgumentException("length must be >= 0");
|
||||||
|
|
||||||
|
this.start = start;
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param range
|
||||||
|
* @return true if the given range is contained within this range
|
||||||
|
*/
|
||||||
|
public boolean contains(Range range) {
|
||||||
|
return getStart() <= range.getStart() && getEnd() >= range.getEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pos
|
||||||
|
* @return true if the given point is contained within this range
|
||||||
|
*/
|
||||||
|
public boolean contains(long pos) {
|
||||||
|
return getStart() <= pos && getEnd() >= pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof Range) {
|
||||||
|
Range r = (Range) obj;
|
||||||
|
return getStart() == r.getStart() && getLength() == r.getLength();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the last index contained in this range
|
||||||
|
*/
|
||||||
|
public long getEnd() {
|
||||||
|
return start + length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the length of this range
|
||||||
|
*/
|
||||||
|
public long getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the first index contained in this range
|
||||||
|
*/
|
||||||
|
public long getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if this range defines a zero-sized range.
|
||||||
|
*
|
||||||
|
* @return true if zero-sized.
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return (int) ((getStart() * 13) & (getEnd() * 137));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a range which contains only the indices contained in the
|
||||||
|
* intersection of this range and the given range.
|
||||||
|
*
|
||||||
|
* @param range
|
||||||
|
* the range to intersect with
|
||||||
|
* @return the intersected range or null if the ranges don't overlap
|
||||||
|
*/
|
||||||
|
public Range intersection(Range range) {
|
||||||
|
if (!intersects(range)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getRangeByNumbers(Math.max(getStart(), range.getStart()),
|
||||||
|
Math.min(getEnd(), range.getEnd()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of indices which are in this range and the given
|
||||||
|
* range.
|
||||||
|
*
|
||||||
|
* @param r
|
||||||
|
* @return 0 if the ranges don't overlap, the length of the intersection
|
||||||
|
* between them otherwise
|
||||||
|
*/
|
||||||
|
public long intersectionLength(Range r) {
|
||||||
|
if (!intersects(r)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Math.min(getEnd(), r.getEnd())
|
||||||
|
- Math.max(getStart(), r.getStart()) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param range
|
||||||
|
* the range to intersect test with
|
||||||
|
* @return true if the ranges overlap
|
||||||
|
*/
|
||||||
|
public boolean intersects(Range range) {
|
||||||
|
return getStart() <= range.getEnd() && getEnd() >= range.getStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[" + getStart() + " - " + getEnd() + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package net.torrent.util.bencoding;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BDecodingException: an error has occured when decoding bencoded data.
|
||||||
|
*
|
||||||
|
* @author Dennis "Bytekeeper" Waldherr
|
||||||
|
*/
|
||||||
|
public class BDecodingException extends IOException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public BDecodingException(Exception e) {
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BDecodingException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,320 @@
|
|||||||
|
package net.torrent.util.bencoding;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PushbackInputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to decode "bencoded" data.
|
||||||
|
*
|
||||||
|
* @author Dennis "Bytekeeper" Waldherr
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BEncodedInputStream extends InputStream {
|
||||||
|
private static final int MAX_STRING_LENGTH = 1024 * 1024;
|
||||||
|
|
||||||
|
private final PushbackInputStream in;
|
||||||
|
|
||||||
|
public BEncodedInputStream(InputStream in) {
|
||||||
|
this.in = new PushbackInputStream(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method to decode exactly one bencoded element.
|
||||||
|
*
|
||||||
|
* @param bencode
|
||||||
|
* @return
|
||||||
|
* @throws BDecodingException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static Object bdecode(byte[] bencode) throws BDecodingException {
|
||||||
|
BEncodedInputStream in = new BEncodedInputStream(
|
||||||
|
new ByteArrayInputStream(bencode));
|
||||||
|
try {
|
||||||
|
return in.readElement();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads one bencoded element.
|
||||||
|
*
|
||||||
|
* @param in
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
* @throws BDecodingException
|
||||||
|
*/
|
||||||
|
public Object readElement() throws IOException, BDecodingException {
|
||||||
|
return bdecode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string result from bencoding to a java String. If the data is
|
||||||
|
* already a String or null it is simply returned.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
* @throws BTypeException
|
||||||
|
*/
|
||||||
|
private static String stringOf(Object data) throws BTypeException {
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (data instanceof byte[]) {
|
||||||
|
return new String((byte[]) data, "UTF-8");
|
||||||
|
} else if (data instanceof String) {
|
||||||
|
return (String) data;
|
||||||
|
} else {
|
||||||
|
throw new BTypeException("Unsupported type: " + data.getClass());
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a constructed or decoded integer to a java Integer. If the data
|
||||||
|
* is already an Integer or null it is simply returned.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
* @throws BTypeException
|
||||||
|
*/
|
||||||
|
private static Integer intOf(Object data) throws BTypeException {
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (data instanceof BigInteger) {
|
||||||
|
return ((BigInteger) data).intValue();
|
||||||
|
} else if (data instanceof Integer) {
|
||||||
|
return (Integer) data;
|
||||||
|
} else {
|
||||||
|
throw new BTypeException("Unsupported type: " + data.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a constructed or decoded integer to a java Integer. If the data
|
||||||
|
* is already an Integer or null it is simply returned.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
* @throws BTypeException
|
||||||
|
*/
|
||||||
|
private static Long longOf(Object data) throws BTypeException {
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (data instanceof BigInteger) {
|
||||||
|
return ((BigInteger) data).longValue();
|
||||||
|
} else if (data instanceof Integer) {
|
||||||
|
return Long.valueOf((Integer) data);
|
||||||
|
} else if (data instanceof Long) {
|
||||||
|
return (Long) data;
|
||||||
|
} else {
|
||||||
|
throw new BTypeException("Unsupported type: " + data.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object bdecode() throws IOException, BDecodingException {
|
||||||
|
int head = in.read();
|
||||||
|
switch (head) {
|
||||||
|
case -1:
|
||||||
|
throw new EOFException();
|
||||||
|
case 'l':
|
||||||
|
return bdecodeList();
|
||||||
|
case 'i':
|
||||||
|
return bdecodeInteger();
|
||||||
|
case 'd':
|
||||||
|
return bdecodeDictionary();
|
||||||
|
default:
|
||||||
|
if (Character.isDigit(head)) {
|
||||||
|
in.unread(head);
|
||||||
|
return bdecodeString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new BDecodingException("Parameter is not bencoded data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private BMap bdecodeDictionary() throws IOException, BDecodingException {
|
||||||
|
assert in != null;
|
||||||
|
|
||||||
|
BMap map = new BMapImpl();
|
||||||
|
int head;
|
||||||
|
while ((head = in.read()) != 'e') {
|
||||||
|
if (head < 0) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
in.unread(head);
|
||||||
|
String key;
|
||||||
|
try {
|
||||||
|
key = stringOf(bdecodeString());
|
||||||
|
} catch (BTypeException e) {
|
||||||
|
throw new BDecodingException(e);
|
||||||
|
}
|
||||||
|
map.put(key, bdecode());
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] bdecodeString() throws IOException, BDecodingException {
|
||||||
|
assert in != null;
|
||||||
|
int len = 0;
|
||||||
|
int head;
|
||||||
|
while ((head = in.read()) != ':') {
|
||||||
|
if (head < 0) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
len = len * 10 + (head - '0');
|
||||||
|
if (len > MAX_STRING_LENGTH) {
|
||||||
|
throw new BDecodingException("Encoded string length exceeds "
|
||||||
|
+ MAX_STRING_LENGTH + " bytes!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte data[] = new byte[len];
|
||||||
|
int off = 0;
|
||||||
|
while (len > 0) {
|
||||||
|
int read = in.read(data, off, len);
|
||||||
|
len -= read;
|
||||||
|
off += read;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object bdecodeInteger() throws IOException, BDecodingException {
|
||||||
|
assert in != null;
|
||||||
|
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
int head;
|
||||||
|
while ((head = in.read()) != 'e') {
|
||||||
|
if (head < 0) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
if (!Character.isDigit(head)) {
|
||||||
|
throw new BDecodingException("Expected digit but got: " + head);
|
||||||
|
}
|
||||||
|
b.append((char) head);
|
||||||
|
}
|
||||||
|
return new BigInteger(b.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private BList bdecodeList() throws IOException, BDecodingException {
|
||||||
|
assert in != null;
|
||||||
|
|
||||||
|
BList list = new BListImpl();
|
||||||
|
int head;
|
||||||
|
while ((head = in.read()) != 'e') {
|
||||||
|
if (head < 0) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
in.unread(head);
|
||||||
|
list.add(bdecode());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return in.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
return in.read(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BListImpl extends ArrayList<Object> implements BList {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getInteger(int index) throws BTypeException {
|
||||||
|
return intOf(get(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getLong(int index) throws BTypeException {
|
||||||
|
return longOf(get(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getString(int index) throws BTypeException {
|
||||||
|
return stringOf(get(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BList getList(int index) throws BTypeException {
|
||||||
|
Object tmp = get(index);
|
||||||
|
if (tmp instanceof BList) {
|
||||||
|
return (BList) tmp;
|
||||||
|
}
|
||||||
|
throw new BTypeException("Unexpected type: " + tmp.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BMap getMap(int index) throws BTypeException {
|
||||||
|
Object tmp = get(index);
|
||||||
|
if (tmp instanceof BMap) {
|
||||||
|
return (BMap) tmp;
|
||||||
|
}
|
||||||
|
throw new BTypeException("Unexpected type: " + tmp.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BMapImpl extends HashMap<String, Object> implements
|
||||||
|
BMap {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getInteger(String key) throws BTypeException {
|
||||||
|
return intOf(get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getLong(String key) throws BTypeException {
|
||||||
|
return longOf(get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getString(String key) throws BTypeException {
|
||||||
|
return stringOf(get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BList getList(String key) throws BTypeException {
|
||||||
|
Object tmp = get(key);
|
||||||
|
if (tmp instanceof BList) {
|
||||||
|
return (BList) tmp;
|
||||||
|
}
|
||||||
|
if (tmp != null)
|
||||||
|
throw new BTypeException("Unexpected type: " + tmp.getClass());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BMap getMap(String key) throws BTypeException {
|
||||||
|
Object tmp = get(key);
|
||||||
|
if (tmp instanceof BMap) {
|
||||||
|
return (BMap) tmp;
|
||||||
|
}
|
||||||
|
throw new BTypeException("Unexpected type: " + tmp.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
package net.torrent.util.bencoding;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to "BEncode" data.
|
||||||
|
*
|
||||||
|
* @author Dennis "Bytekeeper" Waldherr
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class BEncodedOutputStream extends OutputStream {
|
||||||
|
static final Comparator<String> BYTE_COMPARATOR = new Comparator<String>() {
|
||||||
|
@Override
|
||||||
|
public int compare(String o1, String o2) {
|
||||||
|
byte b1[] = bytesOf(o1);
|
||||||
|
byte b2[] = bytesOf(o2);
|
||||||
|
int len = Math.min(b1.length, b2.length);
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (b1[i] > b2[i]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (b1[i] < b2[i]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b1.length - b2.length;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final OutputStream out;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string into the raw byte array representation used to store
|
||||||
|
* into bencoded data.
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static byte[] bytesOf(String s) {
|
||||||
|
try {
|
||||||
|
return s.getBytes("UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BEncodedOutputStream(OutputStream out) {
|
||||||
|
if (out == null)
|
||||||
|
throw new InvalidParameterException("output strean is null");
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param obj
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static byte[] bencode(Object obj) {
|
||||||
|
if (obj == null)
|
||||||
|
throw new InvalidParameterException("Object to encode is null!");
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
BEncodedOutputStream bout = new BEncodedOutputStream(out);
|
||||||
|
bout.writeElement(obj);
|
||||||
|
bout.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BEncodes data and outputs it to an OutputStream.
|
||||||
|
*
|
||||||
|
* @param out
|
||||||
|
* @param obj
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void writeElement(Object obj) throws IOException {
|
||||||
|
if (obj == null)
|
||||||
|
throw new InvalidParameterException("Object to encode is null!");
|
||||||
|
|
||||||
|
if (obj instanceof String) {
|
||||||
|
bencodeString((String) obj);
|
||||||
|
} else if (obj instanceof byte[]) {
|
||||||
|
bencodeString((byte[]) obj);
|
||||||
|
} else if (obj instanceof Integer) {
|
||||||
|
bencodeInteger(BigInteger.valueOf((Integer) obj));
|
||||||
|
} else if (obj instanceof Long) {
|
||||||
|
bencodeInteger(BigInteger.valueOf((Long) obj));
|
||||||
|
} else if (obj instanceof BigInteger) {
|
||||||
|
bencodeInteger((BigInteger) obj);
|
||||||
|
} else if (obj instanceof Collection) {
|
||||||
|
bencodeList((Collection<?>) obj);
|
||||||
|
} else if (obj instanceof Map) {
|
||||||
|
bencodeDictionary((Map<String, ?>) obj);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Type " + obj.getClass()
|
||||||
|
+ " is not bencodable.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bencodeDictionary(Map<String, ?> dict) throws IOException {
|
||||||
|
assert out != null;
|
||||||
|
assert dict != null;
|
||||||
|
|
||||||
|
out.write('d');
|
||||||
|
List<String> sorted = new ArrayList<String>(dict.keySet());
|
||||||
|
Collections.sort(sorted, BYTE_COMPARATOR);
|
||||||
|
for (String key : sorted) {
|
||||||
|
writeElement(key);
|
||||||
|
writeElement(dict.get(key));
|
||||||
|
}
|
||||||
|
out.write('e');
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bencodeList(Collection<?> list) throws IOException {
|
||||||
|
assert out != null;
|
||||||
|
assert list != null;
|
||||||
|
out.write('l');
|
||||||
|
for (Object child : list) {
|
||||||
|
writeElement(child);
|
||||||
|
}
|
||||||
|
out.write('e');
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bencodeInteger(BigInteger integer) throws IOException {
|
||||||
|
assert integer != null;
|
||||||
|
out.write('i');
|
||||||
|
out.write(bytesOf(integer.toString()));
|
||||||
|
out.write('e');
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bencodeString(String string) throws IOException {
|
||||||
|
assert string != null;
|
||||||
|
byte[] bytes = bytesOf(string);
|
||||||
|
bencodeString(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bencodeString(byte[] data) throws IOException {
|
||||||
|
assert data != null;
|
||||||
|
out.write(bytesOf(Integer.toString(data.length)));
|
||||||
|
out.write(':');
|
||||||
|
out.write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int arg0) throws IOException {
|
||||||
|
out.write(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
out.write(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
21
src/main/java/net/torrent/util/bencoding/BList.java
Normal file
21
src/main/java/net/torrent/util/bencoding/BList.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package net.torrent.util.bencoding;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of a bencoded list.
|
||||||
|
*
|
||||||
|
* @author Bytekeeper
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface BList extends List<Object> {
|
||||||
|
String getString(int index) throws BTypeException;
|
||||||
|
|
||||||
|
Integer getInteger(int index) throws BTypeException;
|
||||||
|
|
||||||
|
Long getLong(int index) throws BTypeException;
|
||||||
|
|
||||||
|
BList getList(int index) throws BTypeException;
|
||||||
|
|
||||||
|
BMap getMap(int index) throws BTypeException;
|
||||||
|
}
|
||||||
21
src/main/java/net/torrent/util/bencoding/BMap.java
Normal file
21
src/main/java/net/torrent/util/bencoding/BMap.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package net.torrent.util.bencoding;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of a bencoded map.
|
||||||
|
*
|
||||||
|
* @author Bytekeeper
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface BMap extends Map<String, Object> {
|
||||||
|
String getString(String key) throws BTypeException;
|
||||||
|
|
||||||
|
Integer getInteger(String key) throws BTypeException;
|
||||||
|
|
||||||
|
Long getLong(String key) throws BTypeException;
|
||||||
|
|
||||||
|
BList getList(String key) throws BTypeException;
|
||||||
|
|
||||||
|
BMap getMap(String key) throws BTypeException;
|
||||||
|
}
|
||||||
11
src/main/java/net/torrent/util/bencoding/BTypeException.java
Normal file
11
src/main/java/net/torrent/util/bencoding/BTypeException.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package net.torrent.util.bencoding;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class BTypeException extends IOException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public BTypeException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/main/java/net/torrent/util/bencoding/HashBMap.java
Normal file
32
src/main/java/net/torrent/util/bencoding/HashBMap.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package net.torrent.util.bencoding;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class HashBMap extends HashMap<String, Object> implements BMap {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getInteger(String key) {
|
||||||
|
return (Integer) get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BList getList(String key) {
|
||||||
|
return (BList) get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getLong(String key) {
|
||||||
|
return (Long) get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BMap getMap(String key) {
|
||||||
|
return (BMap) get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getString(String key) {
|
||||||
|
return (String) get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.util.comparator;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
import net.torrent.torrent.TorrentPiece;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two pieces indexes.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class PieceIndexComparator implements Comparator<TorrentPiece> {
|
||||||
|
/**
|
||||||
|
* Shared instance
|
||||||
|
*/
|
||||||
|
public static final PieceIndexComparator SHARED_INSTANCE = new PieceIndexComparator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(TorrentPiece piece1, TorrentPiece piece2) {
|
||||||
|
if (piece1 == null)
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
if (piece2 == null)
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
return piece2.getIndex() - piece1.getIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.torrent.util.comparator;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Random comparator, values returned on each call are always different.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com/">Rogiel Josias Sulzbach</a>
|
||||||
|
*/
|
||||||
|
public class RandomComparator implements Comparator<Object> {
|
||||||
|
/**
|
||||||
|
* Shared instance
|
||||||
|
*/
|
||||||
|
public static final RandomComparator SHARED_INSTANCE = new RandomComparator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Random number generator
|
||||||
|
*/
|
||||||
|
private final Random random = new Random();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(Object o1, Object o2) {
|
||||||
|
return random.nextInt(250 * 10 * (random.nextInt(100) + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Rogiel Josias Sulzbach
|
||||||
|
*
|
||||||
|
* 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 net.bittorrent.protocol.tracker;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
import net.torrent.protocol.tracker.HttpTorrentTrackerAnnouncer;
|
||||||
|
import net.torrent.torrent.Torrent;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class HttpTorrentTrackerAnnouncerTest {
|
||||||
|
@Test
|
||||||
|
public void testAnnounce() throws IOException, URISyntaxException,
|
||||||
|
InterruptedException {
|
||||||
|
final Torrent torrent = Torrent
|
||||||
|
.load(new File(
|
||||||
|
"src/test/resources/Tim Besamusca - Running Away EP Urban Sickness Audio USA1008.torrent"));
|
||||||
|
final HttpTorrentTrackerAnnouncer announcer = new HttpTorrentTrackerAnnouncer();
|
||||||
|
System.out.println(announcer.announce(torrent, torrent.getTrackers()
|
||||||
|
.iterator().next()));
|
||||||
|
|
||||||
|
Thread.sleep(10 * 60 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user