bt.magnet.UtMetadataMessageHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bt-core Show documentation
Show all versions of bt-core Show documentation
BitTorrent Client Library (Core)
package bt.magnet;
import bt.bencoding.BEParser;
import bt.bencoding.model.BEInteger;
import bt.bencoding.model.BEMap;
import bt.bencoding.model.BEObject;
import bt.protocol.DecodingContext;
import bt.protocol.EncodingContext;
import bt.protocol.handler.MessageHandler;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
public class UtMetadataMessageHandler implements MessageHandler {
private final Collection> supportedTypes = Collections.singleton(UtMetadata.class);
@Override
public boolean encode(EncodingContext context, UtMetadata message, ByteBuffer buffer) {
boolean encoded = false;
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
writeMessage(message, bos);
byte[] payload = bos.toByteArray();
if (buffer.remaining() >= payload.length) {
buffer.put(payload);
encoded = true;
}
} catch (IOException e) {
// can't happen
}
return encoded;
}
private void writeMessage(UtMetadata message, OutputStream out) throws IOException {
BEMap m = new BEMap(null, new HashMap>() {{
put(UtMetadata.messageTypeField(), new BEInteger(null, BigInteger.valueOf(message.getType().id())));
put(UtMetadata.pieceIndexField(), new BEInteger(null, BigInteger.valueOf(message.getPieceIndex())));
if (message.getData().isPresent()) {
put(UtMetadata.totalSizeField(), new BEInteger(null, BigInteger.valueOf(message.getTotalSize().get())));
}
}});
m.writeTo(out);
if (message.getData().isPresent()) {
out.write(message.getData().get());
}
}
@Override
public int decode(DecodingContext context, ByteBuffer buffer) {
byte[] payload = new byte[buffer.remaining()];
buffer.get(payload);
try (BEParser parser = new BEParser(payload)) {
BEMap m = parser.readMap();
int length = m.getContent().length;
UtMetadata.Type messageType = getMessageType(m);
int pieceIndex = getPieceIndex(m);
switch (messageType) {
case REQUEST: {
context.setMessage(UtMetadata.request(pieceIndex));
return length;
}
case DATA: {
byte[] data = Arrays.copyOfRange(payload, length, payload.length);
context.setMessage(UtMetadata.data(pieceIndex, getTotalSize(m), data));
return payload.length;
}
case REJECT: {
context.setMessage(UtMetadata.reject(pieceIndex));
return length;
}
default: {
throw new IllegalStateException("Unknown message type: " + messageType.name());
}
}
}
}
private UtMetadata.Type getMessageType(BEMap m) {
BEInteger type = (BEInteger) m.getValue().get(UtMetadata.messageTypeField());
int typeId = type.getValue().intValueExact();
return UtMetadata.Type.forId(typeId);
}
private int getPieceIndex(BEMap m) {
return getIntAttribute(UtMetadata.pieceIndexField(), m);
}
private int getTotalSize(BEMap m) {
return getIntAttribute(UtMetadata.totalSizeField(), m);
}
private int getIntAttribute(String name, BEMap m) {
BEInteger value = ((BEInteger) m.getValue().get(name));
if (value == null) {
throw new IllegalStateException("Message attribute is missing: " + name);
}
return value.getValue().intValueExact();
}
@Override
public Collection> getSupportedTypes() {
return supportedTypes;
}
@Override
public Class extends UtMetadata> readMessageType(ByteBuffer buffer) {
return UtMetadata.class;
}
}