diff --git a/.gitignore b/.gitignore index 19f2e00..b461b83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /target +/store.bin diff --git a/src/main/java/net/torrent/BitTorrentClientFactory.java b/src/main/java/net/torrent/BitTorrentClientFactory.java index ee0f979..578cf2c 100644 --- a/src/main/java/net/torrent/BitTorrentClientFactory.java +++ b/src/main/java/net/torrent/BitTorrentClientFactory.java @@ -27,6 +27,8 @@ import net.torrent.protocol.peerwire.PeerWireManager; import net.torrent.protocol.peerwire.manager.TorrentManager; import net.torrent.torrent.Torrent; import net.torrent.torrent.context.TorrentContext; +import net.torrent.torrent.piece.PieceSelector; +import net.torrent.torrent.piece.RandomPieceSelector; /** * Factory class for {@link BitTorrentClient}. @@ -55,6 +57,10 @@ public class BitTorrentClientFactory { * The torrent algorithm */ private TorrentAlgorithm algorithm; + /** + * The piece selector + */ + private PieceSelector selector; /** * Creates a new standard {@link BitTorrentClient BitTorrent client} @@ -78,10 +84,12 @@ public class BitTorrentClientFactory { * 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); + this.context = new TorrentContext(torrent); + this.datastore = new PlainTorrentDatastore(new File("store.bin")); + this.manager = new TorrentManager(context, datastore); + if (this.selector == null) + this.selector = new RandomPieceSelector(manager); + this.algorithm = new TorrentStdAlgorithm(manager, selector); } /** @@ -110,10 +118,12 @@ public class BitTorrentClientFactory { */ public BitTorrentClientFactory(final Torrent torrent, TorrentDatastore datastore) { - context = new TorrentContext(torrent); + this.context = new TorrentContext(torrent); this.datastore = datastore; - manager = new TorrentManager(context, datastore); - algorithm = new TorrentStdAlgorithm(manager); + this.manager = new TorrentManager(context, datastore); + if (this.selector == null) + this.selector = new RandomPieceSelector(manager); + this.algorithm = new TorrentStdAlgorithm(manager, selector); } /** @@ -128,9 +138,9 @@ public class BitTorrentClientFactory { */ public BitTorrentClientFactory(final Torrent torrent, TorrentDatastore datastore, final TorrentAlgorithm algorithm) { - context = new TorrentContext(torrent); + this.context = new TorrentContext(torrent); this.datastore = datastore; - manager = new TorrentManager(context, datastore); + this.manager = new TorrentManager(context, datastore); this.algorithm = algorithm; } diff --git a/src/main/java/net/torrent/protocol/algorithm/TorrentPieceDownloadAlgorithm.java b/src/main/java/net/torrent/protocol/algorithm/TorrentPieceDownloadAlgorithm.java index f30ac0c..6512c57 100644 --- a/src/main/java/net/torrent/protocol/algorithm/TorrentPieceDownloadAlgorithm.java +++ b/src/main/java/net/torrent/protocol/algorithm/TorrentPieceDownloadAlgorithm.java @@ -64,6 +64,50 @@ public interface TorrentPieceDownloadAlgorithm { */ TorrentPart allowedFast(TorrentPeer peer, TorrentPiece piece); + /** + * Issued when the peer has rejected our request. + * + * @param peer + * the rejecting peer + * @param part + * the rejected part + * @see RejectAction + */ + RejectAction rejected(TorrentPeer peer, TorrentPart part); + + public enum RejectAction { + /** + * 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 + */ + NOT_INTERESTED, + + /** + * Request again the same part + */ + RETRY, + + /** + * Try to download another piece + */ + TRY_ANOTHER_PIECE, + + /** + * Do nothing, ignore. Might cause peer to become idle. + */ + IGNORE; + } + /** * 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 diff --git a/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdAlgorithm.java b/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdAlgorithm.java index 9b63f01..4738f5b 100644 --- a/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdAlgorithm.java +++ b/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdAlgorithm.java @@ -21,6 +21,7 @@ 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; +import net.torrent.torrent.piece.PieceSelector; /** * Standard torrent algorithm @@ -33,10 +34,12 @@ public class TorrentStdAlgorithm implements TorrentAlgorithm { private final TorrentPieceDownloadAlgorithm downloadAlgorithm; private final TorrentPieceUploadAlgorithm uploadAlgorithm; - public TorrentStdAlgorithm(final TorrentManager manager) { + public TorrentStdAlgorithm(final TorrentManager manager, + final PieceSelector selector) { peerAlgorithm = new TorrentStdPeerAlgorithm(manager); - interestAlgorithm = new TorrentStdInterestAlgorithm(manager); - downloadAlgorithm = new TorrentStdPieceDownloadAlgorithm(manager); + interestAlgorithm = new TorrentStdInterestAlgorithm(manager, selector); + downloadAlgorithm = new TorrentStdPieceDownloadAlgorithm(manager, + selector); uploadAlgorithm = new TorrentStdPieceUploadAlgorithm(manager); } diff --git a/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdInterestAlgorithm.java b/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdInterestAlgorithm.java index cb512b2..ce84c0e 100644 --- a/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdInterestAlgorithm.java +++ b/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdInterestAlgorithm.java @@ -20,6 +20,7 @@ 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; +import net.torrent.torrent.piece.PieceSelector; /** * Standard torrent interest algorithm @@ -27,18 +28,35 @@ import net.torrent.torrent.context.TorrentPeer.InterestState; * @author Rogiel Josias Sulzbach */ public class TorrentStdInterestAlgorithm implements TorrentInterestAlgorithm { - @SuppressWarnings("unused") + /** + * The torrent manager + */ private final TorrentManager manager; - public TorrentStdInterestAlgorithm(TorrentManager manager) { + /** + * This selector is used to find the next piece to be downloaded. Parts are + * managed inside this algorithm. + */ + private final PieceSelector selector; + + /** + * Creates a new instance + * + * @param manager + * the manager + * @param selector + * the piece selector + */ + public TorrentStdInterestAlgorithm(TorrentManager manager, + PieceSelector selector) { this.manager = manager; + this.selector = selector; } @Override public InterestState interested(TorrentPeer peer) { - // if(peer.getPort() == 25944) - // return InterestState.UNINTERESTED; - return InterestState.INTERESTED; + return (selector.select(peer) == null ? InterestState.UNINTERESTED + : InterestState.INTERESTED); } @Override diff --git a/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdPieceDownloadAlgorithm.java b/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdPieceDownloadAlgorithm.java index 084138b..ff752ac 100644 --- a/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdPieceDownloadAlgorithm.java +++ b/src/main/java/net/torrent/protocol/algorithm/impl/TorrentStdPieceDownloadAlgorithm.java @@ -33,6 +33,7 @@ import net.torrent.torrent.piece.RandomPieceSelector; * * @author Rogiel Josias Sulzbach */ +// TODO separate standard algorithm from extension ones public class TorrentStdPieceDownloadAlgorithm implements TorrentPieceDownloadAlgorithm { /** @@ -61,12 +62,13 @@ public class TorrentStdPieceDownloadAlgorithm implements * @param manager * the torrent manager instance. With this object is possible to * retrieve current downloads/uploads and connections. + * @param selector + * the piece selector */ - public TorrentStdPieceDownloadAlgorithm(TorrentManager manager) { + public TorrentStdPieceDownloadAlgorithm(TorrentManager manager, + PieceSelector selector) { this.manager = manager; - // this.context = this.manager.getContext(); - // this.torrent = this.manager.getTorrent(); - selector = new RandomPieceSelector(manager); + this.selector = selector; } @Override @@ -96,6 +98,11 @@ public class TorrentStdPieceDownloadAlgorithm implements return piece.getFirstPart(); } + @Override + public RejectAction rejected(TorrentPeer peer, TorrentPart part) { + return RejectAction.TRY_ANOTHER_PIECE; + } + @Override public boolean isComplete(TorrentPeer peer, TorrentPiece piece) { if (manager.getContext().getBitfield().hasPiece(piece)) diff --git a/src/main/java/net/torrent/protocol/peerwire/PeerWirePipelineFactory.java b/src/main/java/net/torrent/protocol/peerwire/PeerWirePipelineFactory.java index 154d36d..eac9667 100644 --- a/src/main/java/net/torrent/protocol/peerwire/PeerWirePipelineFactory.java +++ b/src/main/java/net/torrent/protocol/peerwire/PeerWirePipelineFactory.java @@ -16,8 +16,11 @@ 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.codec.PeerWireFrameDecoder; +import net.torrent.protocol.peerwire.codec.PeerWireFrameEncoder; +import net.torrent.protocol.peerwire.codec.PeerWireMessageDecoder; +import net.torrent.protocol.peerwire.codec.PeerWireMessageEncoder; +import net.torrent.protocol.peerwire.handler.PeerWireCodecHandler; import net.torrent.protocol.peerwire.handler.PeerWireManagerHeadHandler; import net.torrent.protocol.peerwire.handler.PeerWireManagerTailHandler; import net.torrent.protocol.peerwire.manager.TorrentManager; @@ -38,7 +41,8 @@ public class PeerWirePipelineFactory implements ChannelPipelineFactory { * The logging handler */ private final LoggingHandler loggingHandler = new LoggingHandler( - InternalLogLevel.WARN); + InternalLogLevel.INFO); + /** * The algorithm handler */ @@ -70,15 +74,28 @@ public class PeerWirePipelineFactory implements ChannelPipelineFactory { @Override public ChannelPipeline getPipeline() throws Exception { final ChannelPipeline pipeline = pipeline(); + final PeerWireState state = new PeerWireState(); - // TODO create traffic shape handler - // TODO create firewall handler - block connections from unwanted peers + // TODO create traffic shape handler once Netty 4.0 is released + // TODO create firewall handler - block connections from unwanted peers. - pipeline.addLast("decoder", new PeerWireDecoder()); - pipeline.addLast("encoder", new PeerWireEncoder()); + // pipeline.addLast("old.decoder", new PeerWireOldDecoder(state)); + // pipeline.addLast("old.encoder", new PeerWireOldEncoder()); + // frame (or header) codec + pipeline.addLast("frame.decoder", new PeerWireFrameDecoder(state)); + pipeline.addLast("frame.encoder", new PeerWireFrameEncoder(state)); + + // message codec + pipeline.addLast("message.decoder", new PeerWireMessageDecoder(state)); + pipeline.addLast("message.encoder", new PeerWireMessageEncoder()); + + pipeline.addLast("codec.handler", new PeerWireCodecHandler(state)); + + // logging handler (before any other handler can take action) pipeline.addLast("logging", loggingHandler); + // handlers pipeline.addLast("head-handler", headManagerHandler); pipeline.addLast("algorithm", algorithmHandler); pipeline.addLast("tail-handler", tailManagerHandler); diff --git a/src/main/java/net/torrent/protocol/peerwire/PeerWireState.java b/src/main/java/net/torrent/protocol/peerwire/PeerWireState.java new file mode 100644 index 0000000..13352a8 --- /dev/null +++ b/src/main/java/net/torrent/protocol/peerwire/PeerWireState.java @@ -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.peerwire; + +import org.jboss.netty.channel.Channel; + +/** + * Maintain an per {@link Channel} information about the connection state. + * + * @author Rogiel Josias Sulzbach + */ +public class PeerWireState { + /** + * The peer handshake state + */ + private boolean peerHandshaked; + + /** + * The handshake state + */ + private boolean handshaked; + + /** + * Returns true if this connection has already been handshaked by the peer + * end. + * + * @return true if connection was handshaked + */ + public boolean hasPeerHandshaked() { + return peerHandshaked; + } + + /** + * Set the handshake state of this connection by the peer end. + * + * @param peerHandshaked + * the handshake state + */ + public void setPeerHandshaked(boolean peerHandshaked) { + this.peerHandshaked = peerHandshaked; + } + + /** + * Returns true if this connection has already been handshaked by the + * library end. + * + * @return true if connection was handshaked + */ + public boolean hasHandshaked() { + return handshaked; + } + + /** + * Set the handshake state of this connection by the library end. + * + * @param handshaked + * the handshake state + */ + public void setHandshaked(boolean handshaked) { + this.handshaked = handshaked; + } +} diff --git a/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireFrameDecoder.java b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireFrameDecoder.java new file mode 100644 index 0000000..250cd04 --- /dev/null +++ b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireFrameDecoder.java @@ -0,0 +1,96 @@ +/* + * 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.PeerWireState; + +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.frame.CorruptedFrameException; +import org.jboss.netty.handler.codec.frame.FrameDecoder; + +/** + * BitTorrent has two types of message headers: + *
+ *
+ *
+ *
+ * Instances of this class keep channel state content and must not be shared nor + * cached. + * + * @author Rogiel Josias Sulzbach + * @see PeerWireFrameDecoder#state + */ +public class PeerWireFrameDecoder extends FrameDecoder { + /** + * This connection state. This need to be shared with other encoders, + * decoders or handlers. But more importantly NEVER share the same + * instance across more than one {@link Channel}. + */ + private final PeerWireState state; + + /** + * Creates a new instance of this decoder + * + * @param state + * the connection state + */ + public PeerWireFrameDecoder(final PeerWireState state) { + this.state = state; + } + + @Override + protected Object decode(ChannelHandlerContext ctx, Channel channel, + ChannelBuffer buffer) throws Exception { + buffer.markReaderIndex(); + final int index = buffer.readerIndex(); + + if (state.hasPeerHandshaked()) { + if (buffer.readableBytes() <= 4) + return null; + int len = buffer.readInt(); + if (len == 0) { + // keep-alive message + return ChannelBuffers.EMPTY_BUFFER; + } else if (buffer.readableBytes() < len) { + buffer.resetReaderIndex(); + return null; + } + buffer.skipBytes(len); + return buffer.slice(index + 4, len); + } else { + if (buffer.readableBytes() <= 1) // at least 1 byte for header + return null; + + final int pstrlen = buffer.readByte(); + if (pstrlen != 19) + throw new CorruptedFrameException( + "Handshake frame is corrupted. pstrlen != 19"); + buffer.resetReaderIndex(); + if (buffer.readableBytes() < pstrlen + 49) { + return null; + } + buffer.skipBytes(pstrlen + 49); + return buffer.slice(index, pstrlen + 49); + } + } +} diff --git a/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireFrameEncoder.java b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireFrameEncoder.java new file mode 100644 index 0000000..51640bd --- /dev/null +++ b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireFrameEncoder.java @@ -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.peerwire.codec; + +import net.torrent.protocol.peerwire.PeerWireState; + +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.oneone.OneToOneEncoder; + +/** + * BitTorrent has two types of message headers: + *
+ *
+ *
+ *
+ * Instances of this class keep channel state content and must not be shared nor + * cached. + * + * @author Rogiel Josias Sulzbach + * @see PeerWireFrameDecoder#state + */ +public class PeerWireFrameEncoder extends OneToOneEncoder { + /** + * This connection state. This need to be shared with other encoders, + * decoders or handlers. But more importantly NEVER share the same + * instance across more than one {@link Channel}. + */ + private final PeerWireState state; + + /** + * Creates a new instance of this encoder + * + * @param state + * the connection state + */ + public PeerWireFrameEncoder(PeerWireState state) { + this.state = state; + } + + @Override + protected Object encode(ChannelHandlerContext ctx, Channel channel, + Object msg) throws Exception { + if (!(msg instanceof ChannelBuffer)) + return msg; + if (!state.hasHandshaked()) + // on handshake message there is no visible "frame" + return msg; + ChannelBuffer buffer = (ChannelBuffer) msg; + if (buffer.readableBytes() < 4) + throw new CorruptedFrameException( + "A frame must have at least 4 bytes!"); + buffer.setInt(0, buffer.readableBytes() - 4); + return buffer; + } +} diff --git a/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireMessageDecoder.java b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireMessageDecoder.java new file mode 100644 index 0000000..eadd25a --- /dev/null +++ b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireMessageDecoder.java @@ -0,0 +1,128 @@ +/* + * 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.util.Arrays; + +import net.torrent.protocol.peerwire.PeerWireState; +import net.torrent.protocol.peerwire.message.HandshakeMessage; +import net.torrent.protocol.peerwire.message.KeepAliveMessage; +import net.torrent.protocol.peerwire.message.header.PeerWireFastPeersMessageHeaderManager; +import net.torrent.protocol.peerwire.message.header.PeerWireMessageHeaderManager; +import net.torrent.protocol.peerwire.message.header.PeerWireSpecificationMessageHeaderManager; + +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.OneToOneDecoder; + +/** + * This decoder decodes a {@link ChannelBuffer} into an + * {@link PeerWireReadableMessage}. + * + * @author Rogiel Josias Sulzbach + * @see PeerWireMessageDecoder#state + */ +public class PeerWireMessageDecoder extends OneToOneDecoder { + /** + * This connection state. This need to be shared with other encoders, + * decoders or handlers. But more importantly NEVER share the same + * instance across more than one {@link Channel}. + */ + private final PeerWireState state; + + /** + * The is an list of header managers that will create message instances for + * each message id passed as argument. + *
+ * {@link PeerWireSpecificationMessageHeaderManager} and + * {@link PeerWireFastPeersMessageHeaderManager} are already in the list. + */ + private PeerWireMessageHeaderManager[] headerManager = new PeerWireMessageHeaderManager[] { + PeerWireSpecificationMessageHeaderManager.SHARED_INSTANCE, + PeerWireFastPeersMessageHeaderManager.SHARED_INSTANCE }; + + /** + * Creates a new instance of this decoder + * + * @param state + * the connection state + */ + public PeerWireMessageDecoder(PeerWireState state) { + this.state = state; + } + + @Override + protected Object decode(ChannelHandlerContext ctx, Channel channel, + Object msg) throws Exception { + if (!(msg instanceof ChannelBuffer)) + return msg; + final ChannelBuffer buffer = (ChannelBuffer) msg; + if (buffer.readableBytes() == 0) + return new KeepAliveMessage(); + + final PeerWireReadableMessage message; + if (state.hasPeerHandshaked()) { + buffer.markReaderIndex(); + + final byte id = buffer.readByte(); + message = getMessage(id); + } else { + message = new HandshakeMessage(); + } + + if (message == null) { // unknown message + buffer.resetReaderIndex(); + return msg; + } + message.read(buffer); + return message; + } + + /** + * Adds a new message header manager to this decoder + * + * @param newHeaderManager + * the new header manager + */ + public void addMessageHeader(PeerWireMessageHeaderManager newHeaderManager) { + headerManager = Arrays + .copyOf(headerManager, (headerManager.length + 1)); + headerManager[(headerManager.length - 1)] = newHeaderManager; + } + + /** + * Return the message represented by id. + *
+ * Iterate over all handlers and try to locate the message. 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; + for (final PeerWireMessageHeaderManager handler : headerManager) { + if (handler == null) + continue; + message = handler.getMessage(id); + if (message != null) + break; + } + return message; + } +} diff --git a/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireMessageEncoder.java b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireMessageEncoder.java new file mode 100644 index 0000000..7b3c387 --- /dev/null +++ b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireMessageEncoder.java @@ -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.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; + +/** + * This encoder encodes {@link PeerWireWritableMessage} to a + * {@link ChannelBuffer}. + * + * @author Rogiel Josias Sulzbach + */ +public class PeerWireMessageEncoder extends OneToOneEncoder { + @Override + protected Object encode(ChannelHandlerContext ctx, Channel channel, + Object msg) throws Exception { + if (!(msg instanceof PeerWireWritableMessage)) + return msg; + final PeerWireWritableMessage message = (PeerWireWritableMessage) msg; + final ChannelBuffer buffer = ChannelBuffers + .buffer(message.length() + 4); + + if (!(message instanceof HandshakeMessage)) { + buffer.writeInt(0x00); + } + message.write(buffer); + + return buffer; + } +} diff --git a/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireDecoder.java b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireOldDecoder.java similarity index 61% rename from src/main/java/net/torrent/protocol/peerwire/codec/PeerWireDecoder.java rename to src/main/java/net/torrent/protocol/peerwire/codec/PeerWireOldDecoder.java index 09d82eb..8e6f3ab 100644 --- a/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireDecoder.java +++ b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireOldDecoder.java @@ -15,18 +15,13 @@ */ 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 java.util.Arrays; + +import net.torrent.protocol.peerwire.PeerWireState; 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.header.PeerWireMessageHeaderManager; +import net.torrent.protocol.peerwire.message.header.PeerWireSpecificationMessageHeaderManager; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; @@ -48,16 +43,39 @@ import org.jboss.netty.handler.codec.frame.FrameDecoder; * cached. * * @author Rogiel Josias Sulzbach + * @see PeerWireOldDecoder#state */ -public class PeerWireDecoder extends FrameDecoder { - private boolean handshaked = false; +@Deprecated +public class PeerWireOldDecoder extends FrameDecoder { + /** + * This connection state. This need to be shared with other encoders or + * decoders. But more importantly NEVER share the same instance + * across more than one {@link Channel}. + */ + private final PeerWireState state; + + /** + * The is an list of handlers that will create message instances for each + * message id passed as argument. + */ + private PeerWireMessageHeaderManager[] handlers = new PeerWireMessageHeaderManager[] { PeerWireSpecificationMessageHeaderManager.SHARED_INSTANCE }; + + /** + * Creates a new instance of this decoder + * + * @param state + * the connection state + */ + public PeerWireOldDecoder(final PeerWireState state) { + this.state = state; + } @Override protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { buffer.markReaderIndex(); - if (!handshaked) { + if (!state.hasHandshaked()) { if (buffer.readableBytes() <= 47) // at least 47 bytes return null; @@ -70,7 +88,7 @@ public class PeerWireDecoder extends FrameDecoder { final HandshakeMessage message = new HandshakeMessage(); message.read(buffer); - handshaked = true; + state.setHandshaked(true); return message; } else { @@ -98,8 +116,21 @@ public class PeerWireDecoder extends FrameDecoder { } /** - * Return the message represented by id. Will return null if - * message id is unknown. + * Adds a new message handler to this decoder + * + * @param handler + * the handler + */ + public void addMessageHandler(PeerWireMessageHeaderManager handler) { + Arrays.copyOf(this.handlers, (this.handlers.length + 1)); + this.handlers[(this.handlers.length - 1)] = handler; + } + + /** + * Return the message represented by id. + *
+ * Iterate over all handlers and try to locate the message. Will
+ * return null if message id is unknown.
*
* @param id
* the id of the message
@@ -107,37 +138,12 @@ public class PeerWireDecoder extends FrameDecoder {
*/
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;
+ for (final PeerWireMessageHeaderManager handler : handlers) {
+ if (handler == null)
+ continue;
+ message = handler.getMessage(id);
+ if (message != null)
+ break;
}
return message;
}
diff --git a/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireEncoder.java b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireOldEncoder.java
similarity index 96%
rename from src/main/java/net/torrent/protocol/peerwire/codec/PeerWireEncoder.java
rename to src/main/java/net/torrent/protocol/peerwire/codec/PeerWireOldEncoder.java
index d4534ad..1fccb44 100644
--- a/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireEncoder.java
+++ b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireOldEncoder.java
@@ -29,7 +29,8 @@ import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
*
* @author Rogiel Josias Sulzbach
*/
-public class PeerWireEncoder extends OneToOneEncoder {
+@Deprecated
+public class PeerWireOldEncoder extends OneToOneEncoder {
@Override
protected Object encode(ChannelHandlerContext ctx, Channel channel,
Object msg) throws Exception {
diff --git a/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireWritableMessage.java b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireWritableMessage.java
index cb70ae8..6dd5a18 100644
--- a/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireWritableMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/codec/PeerWireWritableMessage.java
@@ -33,4 +33,9 @@ public interface PeerWireWritableMessage {
* @throws IOException
*/
void write(ChannelBuffer buffer) throws IOException;
+
+ /**
+ * @return the length of the message
+ */
+ int length();
}
diff --git a/src/main/java/net/torrent/protocol/peerwire/handler/PeerWireAlgorithmHandler.java b/src/main/java/net/torrent/protocol/peerwire/handler/PeerWireAlgorithmHandler.java
index 2713d82..583b5b6 100644
--- a/src/main/java/net/torrent/protocol/peerwire/handler/PeerWireAlgorithmHandler.java
+++ b/src/main/java/net/torrent/protocol/peerwire/handler/PeerWireAlgorithmHandler.java
@@ -37,6 +37,7 @@ 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.RejectMessage;
import net.torrent.protocol.peerwire.message.fast.SuggestPieceMessage;
import net.torrent.torrent.Torrent;
import net.torrent.torrent.TorrentPart;
@@ -66,6 +67,7 @@ import org.jboss.netty.handler.timeout.IdleStateEvent;
*
* @author Rogiel Josias Sulzbach
*/
+// TODO separate extensions handler from algorithm handler
public class PeerWireAlgorithmHandler extends IdleStateAwareChannelHandler {
/**
* The torrent manager
@@ -120,7 +122,7 @@ public class PeerWireAlgorithmHandler extends IdleStateAwareChannelHandler {
peer.handshake(manager.getTorrent().getInfoHash().toByteArray(),
"-TR2050-mcm14ye4h2mq".getBytes(), manager.getContext()
.getCapabilites().toBitSet());
- peer.port((short) 1541);
+ // peer.port((short) 1541);
super.channelConnected(ctx, e);
}
@@ -184,6 +186,12 @@ public class PeerWireAlgorithmHandler extends IdleStateAwareChannelHandler {
final Torrent torrent = manager.getTorrent();
final TorrentPiece piece = torrent.getPiece(suggest.getPiece());
suggested(peer, piece);
+ } else if (msg instanceof RejectMessage) {
+ final RejectMessage request = (RejectMessage) msg;
+ final Torrent torrent = manager.getTorrent();
+ final TorrentPart part = torrent.getPart(request.getIndex(),
+ request.getStart(), request.getLength());
+ rejected(peer, part);
}
super.messageReceived(ctx, e);
}
@@ -218,7 +226,7 @@ public class PeerWireAlgorithmHandler extends IdleStateAwareChannelHandler {
peer.interested();
return;
case UNINTERESTED:
- testChoke(peer);
+ peer.uninterested();
return;
}
}
@@ -483,4 +491,27 @@ public class PeerWireAlgorithmHandler extends IdleStateAwareChannelHandler {
return;
download(peer, part);
}
+
+ private void rejected(PeerWirePeer peer, TorrentPart part) {
+ switch (downloadAlgorithm.rejected(peer.getTorrentPeer(), part)) {
+ case DISCONNECT:
+ peer.disconnect();
+ break;
+ case CONNECT_NEW_PEER:
+ peer.disconnect();
+ connect(peerAlgorithm.connect());
+ break;
+ case NOT_INTERESTED:
+ peer.uninterested();
+ break;
+ case RETRY:
+ download(peer, part);
+ break;
+ case TRY_ANOTHER_PIECE:
+ final TorrentPart nextPart = downloadAlgorithm.getNextPart(
+ peer.getTorrentPeer(), part);
+ download(peer, nextPart);
+ break;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/net/torrent/protocol/peerwire/handler/PeerWireCodecHandler.java b/src/main/java/net/torrent/protocol/peerwire/handler/PeerWireCodecHandler.java
new file mode 100644
index 0000000..4390bea
--- /dev/null
+++ b/src/main/java/net/torrent/protocol/peerwire/handler/PeerWireCodecHandler.java
@@ -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.handler;
+
+import net.torrent.protocol.peerwire.PeerWireState;
+import net.torrent.protocol.peerwire.message.HandshakeMessage;
+
+import org.jboss.netty.channel.Channel;
+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;
+
+/**
+ * Handler used to manage the codecs' state
+ *
+ * @author Rogiel Josias Sulzbach
+ */
+public class PeerWireCodecHandler extends SimpleChannelHandler {
+ /**
+ * This connection state. This need to be shared with other encoders,
+ * decoders or handlers. But more importantly NEVER share the same
+ * instance across more than one {@link Channel}.
+ */
+ private final PeerWireState state;
+
+ /**
+ * Creates a new instance
+ *
+ * @param state
+ * the connection state
+ */
+ public PeerWireCodecHandler(final PeerWireState state) {
+ this.state = state;
+ }
+
+ @Override
+ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
+ throws Exception {
+ if (e.getMessage() instanceof HandshakeMessage) {
+ if (state.hasPeerHandshaked())
+ throw new IllegalStateException(
+ "Peer is trying to handshaked twice");
+ state.setPeerHandshaked(true);
+ }
+ super.messageReceived(ctx, e);
+ }
+
+ @Override
+ public void writeRequested(ChannelHandlerContext ctx, MessageEvent e)
+ throws Exception {
+ if (e.getMessage() instanceof HandshakeMessage) {
+ if (state.hasHandshaked())
+ throw new IllegalStateException("Handshake has already been sent");
+ e.getFuture().addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future)
+ throws Exception {
+ state.setHandshaked(true);
+ }
+ });
+ }
+ super.writeRequested(ctx, e);
+ }
+
+ @Override
+ public void channelDisconnected(ChannelHandlerContext ctx,
+ ChannelStateEvent e) throws Exception {
+ state.setHandshaked(false);
+ state.setPeerHandshaked(false);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/BitfieldMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/BitfieldMessage.java
index aa25c18..6bbc4b0 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/BitfieldMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/BitfieldMessage.java
@@ -104,6 +104,11 @@ public class BitfieldMessage implements PeerWireWritableMessage,
}
}
+ @Override
+ public int length() {
+ return bitfield.size() / 8 + 1;
+ }
+
public BitSet getBitfield() {
return bitfield;
}
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/CancelMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/CancelMessage.java
index a438578..14dcab0 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/CancelMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/CancelMessage.java
@@ -74,6 +74,11 @@ public class CancelMessage implements PeerWireWritableMessage,
buffer.writeInt(start);
buffer.writeInt(length);
}
+
+ @Override
+ public int length() {
+ return 3 * 4 + 1;
+ }
public int getIndex() {
return index;
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/ChokeMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/ChokeMessage.java
index 07a559e..a1a02f9 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/ChokeMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/ChokeMessage.java
@@ -44,6 +44,11 @@ public class ChokeMessage implements PeerWireWritableMessage,
public void write(ChannelBuffer buffer) throws IOException {
buffer.writeByte(MESSAGE_ID);
}
+
+ @Override
+ public int length() {
+ return 1;
+ }
@Override
public String toString() {
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/HandshakeMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/HandshakeMessage.java
index 87dc18c..b088e8c 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/HandshakeMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/HandshakeMessage.java
@@ -74,16 +74,7 @@ public class HandshakeMessage implements PeerWireWritableMessage,
}
}
}
- 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();
}
@@ -108,6 +99,11 @@ public class HandshakeMessage implements PeerWireWritableMessage,
buffer.writeBytes(this.peerId);
}
+ @Override
+ public int length() {
+ return 1 + pstrlen + pstr.length() + 8 + infohash.length + peerId.length;
+ }
+
public int getPstrlen() {
return pstrlen;
}
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/HaveMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/HaveMessage.java
index 9a185ca..41ac4ce 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/HaveMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/HaveMessage.java
@@ -71,6 +71,11 @@ public class HaveMessage implements PeerWireWritableMessage,
buffer.writeByte(MESSAGE_ID);
buffer.writeInt(piece);
}
+
+ @Override
+ public int length() {
+ return 5;
+ }
public int getPiece() {
return piece;
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/InterestedMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/InterestedMessage.java
index c840107..a70442f 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/InterestedMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/InterestedMessage.java
@@ -44,6 +44,11 @@ public class InterestedMessage implements PeerWireWritableMessage,
public void write(ChannelBuffer buffer) throws IOException {
buffer.writeByte(MESSAGE_ID);
}
+
+ @Override
+ public int length() {
+ return 1;
+ }
@Override
public String toString() {
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/KeepAliveMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/KeepAliveMessage.java
index a431dac..6d63356 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/KeepAliveMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/KeepAliveMessage.java
@@ -46,6 +46,11 @@ public class KeepAliveMessage implements PeerWireWritableMessage,
@Override
public void write(ChannelBuffer buffer) throws IOException {
}
+
+ @Override
+ public int length() {
+ return 0;
+ }
@Override
public String toString() {
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/NotInterestedMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/NotInterestedMessage.java
index b65c4c4..240474c 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/NotInterestedMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/NotInterestedMessage.java
@@ -44,6 +44,11 @@ public class NotInterestedMessage implements PeerWireWritableMessage,
public void write(ChannelBuffer buffer) throws IOException {
buffer.writeByte(MESSAGE_ID);
}
+
+ @Override
+ public int length() {
+ return 1;
+ }
@Override
public String toString() {
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/PieceMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/PieceMessage.java
index ffb5119..859894e 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/PieceMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/PieceMessage.java
@@ -90,6 +90,11 @@ public class PieceMessage implements PeerWireWritableMessage,
buffer.writeInt(start);
buffer.writeBytes(block);
}
+
+ @Override
+ public int length() {
+ return 9 + block.capacity();
+ }
public int getIndex() {
return index;
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/PortMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/PortMessage.java
index 3482c66..ac7f200 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/PortMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/PortMessage.java
@@ -61,6 +61,11 @@ public class PortMessage implements PeerWireWritableMessage,
buffer.writeByte(MESSAGE_ID);
buffer.writeShort(port);
}
+
+ @Override
+ public int length() {
+ return 3;
+ }
public short getPort() {
return port;
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/RequestMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/RequestMessage.java
index aef508e..b4329eb 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/RequestMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/RequestMessage.java
@@ -112,6 +112,11 @@ public class RequestMessage implements PeerWireWritableMessage,
buffer.writeInt(start);
buffer.writeInt(length);
}
+
+ @Override
+ public int length() {
+ return 13;
+ }
public int getIndex() {
return index;
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/UnchokeMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/UnchokeMessage.java
index a8e26e7..97d04d8 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/UnchokeMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/UnchokeMessage.java
@@ -44,6 +44,11 @@ public class UnchokeMessage implements PeerWireWritableMessage,
public void write(ChannelBuffer buffer) throws IOException {
buffer.writeByte(MESSAGE_ID);
}
+
+ @Override
+ public int length() {
+ return 1;
+ }
@Override
public String toString() {
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/fast/AllowedFastMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/fast/AllowedFastMessage.java
index 83fcac9..831dc7b 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/fast/AllowedFastMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/fast/AllowedFastMessage.java
@@ -68,7 +68,7 @@ import org.jboss.netty.buffer.ChannelBuffer;
*/
public class AllowedFastMessage implements PeerWireWritableMessage,
PeerWireReadableMessage {
- public static final byte MESSAGE_ID = 0x0D;
+ public static final byte MESSAGE_ID = 0x11;
/**
* The pieces indexes
@@ -97,6 +97,11 @@ public class AllowedFastMessage implements PeerWireWritableMessage,
buffer.writeInt(piece);
}
}
+
+ @Override
+ public int length() {
+ return 1 + pieces.length * 4;
+ }
public int[] getPieces() {
return pieces;
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/fast/HaveAllMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/fast/HaveAllMessage.java
index cf13f95..b9985d7 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/fast/HaveAllMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/fast/HaveAllMessage.java
@@ -52,6 +52,11 @@ public class HaveAllMessage implements PeerWireWritableMessage,
public void write(ChannelBuffer buffer) throws IOException {
buffer.writeByte(MESSAGE_ID);
}
+
+ @Override
+ public int length() {
+ return 1;
+ }
@Override
public String toString() {
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/fast/HaveNoneMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/fast/HaveNoneMessage.java
index c0f1812..289f2eb 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/fast/HaveNoneMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/fast/HaveNoneMessage.java
@@ -52,6 +52,11 @@ public class HaveNoneMessage implements PeerWireWritableMessage,
public void write(ChannelBuffer buffer) throws IOException {
buffer.writeByte(MESSAGE_ID);
}
+
+ @Override
+ public int length() {
+ return 1;
+ }
@Override
public String toString() {
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/fast/RejectMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/fast/RejectMessage.java
index fddb7cc..62ae8be 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/fast/RejectMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/fast/RejectMessage.java
@@ -94,6 +94,11 @@ public class RejectMessage implements PeerWireWritableMessage,
buffer.writeInt(start);
buffer.writeInt(length);
}
+
+ @Override
+ public int length() {
+ return 13;
+ }
public int getIndex() {
return index;
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/fast/SuggestPieceMessage.java b/src/main/java/net/torrent/protocol/peerwire/message/fast/SuggestPieceMessage.java
index 67b742d..7077a7b 100644
--- a/src/main/java/net/torrent/protocol/peerwire/message/fast/SuggestPieceMessage.java
+++ b/src/main/java/net/torrent/protocol/peerwire/message/fast/SuggestPieceMessage.java
@@ -69,6 +69,11 @@ public class SuggestPieceMessage implements PeerWireWritableMessage,
buffer.writeByte(MESSAGE_ID);
buffer.writeInt(piece);
}
+
+ @Override
+ public int length() {
+ return 5;
+ }
public int getPiece() {
return piece;
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/header/PeerWireFastPeersMessageHeaderManager.java b/src/main/java/net/torrent/protocol/peerwire/message/header/PeerWireFastPeersMessageHeaderManager.java
new file mode 100644
index 0000000..6f809ce
--- /dev/null
+++ b/src/main/java/net/torrent/protocol/peerwire/message/header/PeerWireFastPeersMessageHeaderManager.java
@@ -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.header;
+
+import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
+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.TorrentPeerCapabilities.TorrentPeerCapability;
+
+/**
+ * The message header manager for {@link TorrentPeerCapability#FAST_PEERS Fast
+ * Peers extension}.
+ *
+ * @author Rogiel Josias Sulzbach
+ */
+public final class PeerWireFastPeersMessageHeaderManager implements
+ PeerWireMessageHeaderManager {
+ public static final PeerWireFastPeersMessageHeaderManager SHARED_INSTANCE = new PeerWireFastPeersMessageHeaderManager();
+
+ @Override
+ public PeerWireReadableMessage getMessage(byte id) {
+ switch (id) {
+ case AllowedFastMessage.MESSAGE_ID:
+ return new AllowedFastMessage();
+ case HaveAllMessage.MESSAGE_ID:
+ return new HaveAllMessage();
+ case HaveNoneMessage.MESSAGE_ID:
+ return new HaveNoneMessage();
+ case RejectMessage.MESSAGE_ID:
+ return new RejectMessage();
+ case SuggestPieceMessage.MESSAGE_ID:
+ return new SuggestPieceMessage();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/header/PeerWireMessageHeaderManager.java b/src/main/java/net/torrent/protocol/peerwire/message/header/PeerWireMessageHeaderManager.java
new file mode 100644
index 0000000..cde20de
--- /dev/null
+++ b/src/main/java/net/torrent/protocol/peerwire/message/header/PeerWireMessageHeaderManager.java
@@ -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.message.header;
+
+import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
+
+/**
+ * The message header manager return the message to each given message id (aka
+ * opcode).
+ *
+ * @author Rogiel Josias Sulzbach
+ */
+public interface PeerWireMessageHeaderManager {
+ /**
+ * Return the message for an given id. If this handler cannot
+ * handle the message id, null must be returned.
+ *
+ * @param id
+ * the message id
+ * @return the message, null if id is unknown.
+ */
+ PeerWireReadableMessage getMessage(byte id);
+}
diff --git a/src/main/java/net/torrent/protocol/peerwire/message/header/PeerWireSpecificationMessageHeaderManager.java b/src/main/java/net/torrent/protocol/peerwire/message/header/PeerWireSpecificationMessageHeaderManager.java
new file mode 100644
index 0000000..e23e29c
--- /dev/null
+++ b/src/main/java/net/torrent/protocol/peerwire/message/header/PeerWireSpecificationMessageHeaderManager.java
@@ -0,0 +1,65 @@
+/*
+ * 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.header;
+
+import net.torrent.protocol.peerwire.codec.PeerWireReadableMessage;
+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.HaveMessage;
+import net.torrent.protocol.peerwire.message.InterestedMessage;
+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;
+
+/**
+ * The default PeerWire message handler
+ *
+ * @author Rogiel Josias Sulzbach
+ */
+public final class PeerWireSpecificationMessageHeaderManager implements
+ PeerWireMessageHeaderManager {
+ public static final PeerWireSpecificationMessageHeaderManager SHARED_INSTANCE = new PeerWireSpecificationMessageHeaderManager();
+
+ @Override
+ public PeerWireReadableMessage getMessage(byte id) {
+ switch (id) {
+ case BitfieldMessage.MESSAGE_ID:
+ return new BitfieldMessage();
+ case CancelMessage.MESSAGE_ID:
+ return new CancelMessage();
+ case ChokeMessage.MESSAGE_ID:
+ return new ChokeMessage();
+ case HaveMessage.MESSAGE_ID:
+ return new HaveMessage();
+ case InterestedMessage.MESSAGE_ID:
+ return new InterestedMessage();
+ case NotInterestedMessage.MESSAGE_ID:
+ return new NotInterestedMessage();
+ case PieceMessage.MESSAGE_ID:
+ return new PieceMessage();
+ case PortMessage.MESSAGE_ID:
+ return new PortMessage();
+ case RequestMessage.MESSAGE_ID:
+ return new RequestMessage();
+ case UnchokeMessage.MESSAGE_ID:
+ return new UnchokeMessage();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/net/torrent/torrent/context/TorrentContext.java b/src/main/java/net/torrent/torrent/context/TorrentContext.java
index 8d86ac1..3ef2fbf 100644
--- a/src/main/java/net/torrent/torrent/context/TorrentContext.java
+++ b/src/main/java/net/torrent/torrent/context/TorrentContext.java
@@ -35,10 +35,10 @@ public class TorrentContext {
private final TorrentBitfield bitfield = new TorrentBitfield(this);
/**
- * The capabilities
+ * The capabilities supported in this context
*/
private final TorrentPeerCapabilities capabilites = new TorrentPeerCapabilities(
- TorrentPeerCapability.DHT, TorrentPeerCapability.FAST_PEERS);
+ TorrentPeerCapability.DHT);
private final Set