Improved tests and fixed some

This commit is contained in:
Christian Basler 2016-06-16 19:47:59 +02:00
parent ed4fd1002b
commit 0fadb40c6c
16 changed files with 234 additions and 105 deletions

View File

@ -25,7 +25,7 @@ artifacts {
dependencies { dependencies {
compile 'org.slf4j:slf4j-api:1.7.12' compile 'org.slf4j:slf4j-api:1.7.12'
testCompile 'junit:junit:4.11' testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:hamcrest-library:1.3' testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile 'org.mockito:mockito-core:1.10.19' testCompile 'org.mockito:mockito-core:1.10.19'
testCompile project(':cryptography-bc') testCompile project(':cryptography-bc')

View File

@ -24,7 +24,6 @@ import ch.dissem.bitmessage.utils.UnixTime;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Random;
/** /**
* The 'version' command advertises this node's latest supported protocol version upon initiation. * The 'version' command advertises this node's latest supported protocol version upon initiation.

View File

@ -40,7 +40,7 @@ import static ch.dissem.bitmessage.utils.Singleton.cryptography;
* Creates {@link NetworkMessage} objects from {@link InputStream InputStreams} * Creates {@link NetworkMessage} objects from {@link InputStream InputStreams}
*/ */
public class Factory { public class Factory {
public static final Logger LOG = LoggerFactory.getLogger(Factory.class); private static final Logger LOG = LoggerFactory.getLogger(Factory.class);
public static NetworkMessage getNetworkMessage(int version, InputStream stream) throws SocketTimeoutException { public static NetworkMessage getNetworkMessage(int version, InputStream stream) throws SocketTimeoutException {
try { try {

View File

@ -52,23 +52,21 @@ public class V3MessageReader {
state = ReaderState.HEADER; state = ReaderState.HEADER;
case HEADER: case HEADER:
if (buffer.remaining() < 20) { if (buffer.remaining() < 20) {
buffer.compact();
return; return;
} }
command = getCommand(buffer); command = getCommand(buffer);
length = (int) Decode.uint32(buffer); length = (int) Decode.uint32(buffer);
if (length > MAX_PAYLOAD_SIZE) { if (length > MAX_PAYLOAD_SIZE) {
throw new NodeException("Payload of " + length + " bytes received, no more than 1600003 was expected."); throw new NodeException("Payload of " + length + " bytes received, no more than " +
MAX_PAYLOAD_SIZE + " was expected.");
} }
checksum = new byte[4]; checksum = new byte[4];
buffer.get(checksum); buffer.get(checksum);
state = ReaderState.DATA; state = ReaderState.DATA;
if (buffer.remaining() < length) {
// We need to compact the buffer to make sure the message fits even if it's really big.
buffer.compact();
}
case DATA: case DATA:
if (buffer.remaining() < length) return; if (buffer.remaining() < length) {
return;
}
if (!testChecksum(buffer)) { if (!testChecksum(buffer)) {
throw new NodeException("Checksum failed for message '" + command + "'"); throw new NodeException("Checksum failed for message '" + command + "'");
} }
@ -95,34 +93,35 @@ public class V3MessageReader {
private boolean findMagicBytes(ByteBuffer buffer) { private boolean findMagicBytes(ByteBuffer buffer) {
int i = 0; int i = 0;
while (buffer.hasRemaining()) { while (buffer.hasRemaining()) {
if (buffer.get() == MAGIC_BYTES[i]) { if (i == 0) {
buffer.mark(); buffer.mark();
}
if (buffer.get() == MAGIC_BYTES[i]) {
i++; i++;
if (i == MAGIC_BYTES.length) return true; if (i == MAGIC_BYTES.length) {
return true;
}
} else { } else {
i = 0; i = 0;
} }
} }
if (i > 0) { if (i > 0) {
buffer.reset(); buffer.reset();
buffer.compact();
} else {
buffer.clear();
} }
return false; return false;
} }
private static String getCommand(ByteBuffer buffer) { private static String getCommand(ByteBuffer buffer) {
int start = buffer.position(); int start = buffer.position();
int i = 0; int l = 0;
while (i < 12 && buffer.get() != 0) i++; while (l < 12 && buffer.get() != 0) l++;
int end = start + i; int i = l + 1;
while (i < 12) { while (i < 12) {
if (buffer.get() != 0) throw new NodeException("'\\0' padding expected for command"); if (buffer.get() != 0) throw new NodeException("'\\0' padding expected for command");
i++; i++;
} }
try { try {
return new String(buffer.array(), start, end, "ASCII"); return new String(buffer.array(), start, l, "ASCII");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new ApplicationException(e); throw new ApplicationException(e);
} }

View File

@ -43,7 +43,7 @@ import static ch.dissem.bitmessage.utils.Numbers.max;
* Implements everything that isn't directly dependent on either Spongy- or Bouncycastle. * Implements everything that isn't directly dependent on either Spongy- or Bouncycastle.
*/ */
public abstract class AbstractCryptography implements Cryptography, InternalContext.ContextHolder { public abstract class AbstractCryptography implements Cryptography, InternalContext.ContextHolder {
public static final Logger LOG = LoggerFactory.getLogger(Cryptography.class); protected static final Logger LOG = LoggerFactory.getLogger(Cryptography.class);
private static final SecureRandom RANDOM = new SecureRandom(); private static final SecureRandom RANDOM = new SecureRandom();
private static final BigInteger TWO = BigInteger.valueOf(2); private static final BigInteger TWO = BigInteger.valueOf(2);
private static final BigInteger TWO_POW_64 = TWO.pow(64); private static final BigInteger TWO_POW_64 = TWO.pow(64);

View File

@ -13,6 +13,6 @@ uploadArchives {
dependencies { dependencies {
compile project(':core') compile project(':core')
compile 'org.bouncycastle:bcprov-jdk15on:1.52' compile 'org.bouncycastle:bcprov-jdk15on:1.52'
testCompile 'junit:junit:4.11' testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19' testCompile 'org.mockito:mockito-core:1.10.19'
} }

View File

@ -13,5 +13,5 @@ uploadArchives {
dependencies { dependencies {
compile project(':core') compile project(':core')
compile 'com.madgag.spongycastle:prov:1.52.0.0' compile 'com.madgag.spongycastle:prov:1.52.0.0'
testCompile 'junit:junit:4.11' testCompile 'junit:junit:4.12'
} }

View File

@ -32,6 +32,6 @@ dependencies {
compile 'args4j:args4j:2.32' compile 'args4j:args4j:2.32'
compile 'com.h2database:h2:1.4.190' compile 'com.h2database:h2:1.4.190'
compile 'org.apache.commons:commons-lang3:3.4' compile 'org.apache.commons:commons-lang3:3.4'
testCompile 'junit:junit:4.11' testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19' testCompile 'org.mockito:mockito-core:1.10.19'
} }

View File

@ -88,7 +88,7 @@ public class SystemTest {
bob.shutdown(); bob.shutdown();
} }
@Test @Test(timeout = 60_000)
public void ensureAliceCanSendMessageToBob() throws Exception { public void ensureAliceCanSendMessageToBob() throws Exception {
String originalMessage = UUID.randomUUID().toString(); String originalMessage = UUID.randomUUID().toString();
alice.send(aliceIdentity, new BitmessageAddress(bobIdentity.getAddress()), "Subject", originalMessage); alice.send(aliceIdentity, new BitmessageAddress(bobIdentity.getAddress()), "Subject", originalMessage);
@ -102,7 +102,7 @@ public class SystemTest {
.markAsAcknowledged(any()); .markAsAcknowledged(any());
} }
@Test @Test(timeout = 30_000)
public void ensureBobCanReceiveBroadcastFromAlice() throws Exception { public void ensureBobCanReceiveBroadcastFromAlice() throws Exception {
String originalMessage = UUID.randomUUID().toString(); String originalMessage = UUID.randomUUID().toString();
bob.addSubscribtion(new BitmessageAddress(aliceIdentity.getAddress())); bob.addSubscribtion(new BitmessageAddress(aliceIdentity.getAddress()));

View File

@ -28,7 +28,7 @@ uploadArchives {
dependencies { dependencies {
compile project(':core') compile project(':core')
testCompile 'junit:junit:4.11' testCompile 'junit:junit:4.12'
testCompile 'org.slf4j:slf4j-simple:1.7.12' testCompile 'org.slf4j:slf4j-simple:1.7.12'
testCompile 'org.mockito:mockito-core:1.10.19' testCompile 'org.mockito:mockito-core:1.10.19'
testCompile project(path: ':core', configuration: 'testArtifacts') testCompile project(path: ':core', configuration: 'testArtifacts')

View File

@ -12,7 +12,7 @@ uploadArchives {
dependencies { dependencies {
compile project(':core') compile project(':core')
testCompile 'junit:junit:4.11' testCompile 'junit:junit:4.12'
testCompile 'org.slf4j:slf4j-simple:1.7.12' testCompile 'org.slf4j:slf4j-simple:1.7.12'
testCompile 'org.mockito:mockito-core:1.10.19' testCompile 'org.mockito:mockito-core:1.10.19'
testCompile project(path: ':core', configuration: 'testArtifacts') testCompile project(path: ':core', configuration: 'testArtifacts')

View File

@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentLinkedDeque;
import static ch.dissem.bitmessage.InternalContext.NETWORK_EXTRA_BYTES; import static ch.dissem.bitmessage.InternalContext.NETWORK_EXTRA_BYTES;
import static ch.dissem.bitmessage.InternalContext.NETWORK_NONCE_TRIALS_PER_BYTE; import static ch.dissem.bitmessage.InternalContext.NETWORK_NONCE_TRIALS_PER_BYTE;
import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SERVER;
import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SYNC; import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SYNC;
import static ch.dissem.bitmessage.networking.AbstractConnection.State.*; import static ch.dissem.bitmessage.networking.AbstractConnection.State.*;
import static ch.dissem.bitmessage.utils.Singleton.cryptography; import static ch.dissem.bitmessage.utils.Singleton.cryptography;
@ -62,6 +63,8 @@ public abstract class AbstractConnection {
protected long peerNonce; protected long peerNonce;
protected int version; protected int version;
protected long[] streams; protected long[] streams;
private boolean verackSent;
private boolean verackReceived;
public AbstractConnection(InternalContext context, Mode mode, public AbstractConnection(InternalContext context, Mode mode,
NetworkAddress node, NetworkAddress node,
@ -198,7 +201,7 @@ public abstract class AbstractConnection {
return ivCache.containsKey(iv); return ivCache.containsKey(iv);
} }
protected void cleanupIvCache() { private void cleanupIvCache() {
Long fiveMinutesAgo = UnixTime.now(-5 * MINUTE); Long fiveMinutesAgo = UnixTime.now(-5 * MINUTE);
for (Map.Entry<InventoryVector, Long> entry : ivCache.entrySet()) { for (Map.Entry<InventoryVector, Long> entry : ivCache.entrySet()) {
if (entry.getValue() < fiveMinutesAgo) { if (entry.getValue() < fiveMinutesAgo) {
@ -213,16 +216,10 @@ public abstract class AbstractConnection {
handleVersion((Version) payload); handleVersion((Version) payload);
break; break;
case VERACK: case VERACK:
switch (mode) { if (verackSent) {
case SERVER: activateConnection();
activateConnection();
break;
case CLIENT:
case SYNC:
default:
// NO OP
break;
} }
verackReceived = true;
break; break;
case CUSTOM: case CUSTOM:
MessagePayload response = ctx.getCustomCommandHandler().handle((CustomMessage) payload); MessagePayload response = ctx.getCustomCommandHandler().handle((CustomMessage) payload);
@ -237,7 +234,7 @@ public abstract class AbstractConnection {
} }
} }
protected void activateConnection() { private void activateConnection() {
LOG.info("Successfully established connection with node " + node); LOG.info("Successfully established connection with node " + node);
state = ACTIVE; state = ACTIVE;
node.setTime(UnixTime.now()); node.setTime(UnixTime.now());
@ -272,17 +269,13 @@ public abstract class AbstractConnection {
this.version = version.getVersion(); this.version = version.getVersion();
this.streams = version.getStreams(); this.streams = version.getStreams();
verackSent = true;
send(new VerAck()); send(new VerAck());
switch (mode) { if (mode == SERVER) {
case SERVER: send(new Version.Builder().defaults(ctx.getClientNonce()).addrFrom(host).addrRecv(node).build());
send(new Version.Builder().defaults(ctx.getClientNonce()).addrFrom(host).addrRecv(node).build()); }
break; if (verackReceived) {
case CLIENT: activateConnection();
case SYNC:
activateConnection();
break;
default:
// NO OP
} }
} else { } else {
LOG.info("Received unsupported version " + version.getVersion() + ", disconnecting."); LOG.info("Received unsupported version " + version.getVersion() + ", disconnecting.");

View File

@ -19,6 +19,7 @@ package ch.dissem.bitmessage.networking.nio;
import ch.dissem.bitmessage.InternalContext; import ch.dissem.bitmessage.InternalContext;
import ch.dissem.bitmessage.entity.MessagePayload; import ch.dissem.bitmessage.entity.MessagePayload;
import ch.dissem.bitmessage.entity.NetworkMessage; import ch.dissem.bitmessage.entity.NetworkMessage;
import ch.dissem.bitmessage.entity.Version;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
import ch.dissem.bitmessage.factory.V3MessageReader; import ch.dissem.bitmessage.factory.V3MessageReader;
@ -26,9 +27,12 @@ import ch.dissem.bitmessage.networking.AbstractConnection;
import ch.dissem.bitmessage.ports.NetworkHandler; import ch.dissem.bitmessage.ports.NetworkHandler;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedDeque; import java.util.Queue;
import java.util.Set;
import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.CLIENT;
import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SYNC;
import static ch.dissem.bitmessage.ports.NetworkHandler.MAX_MESSAGE_SIZE; import static ch.dissem.bitmessage.ports.NetworkHandler.MAX_MESSAGE_SIZE;
/** /**
@ -43,6 +47,10 @@ public class ConnectionInfo extends AbstractConnection {
NetworkAddress node, NetworkHandler.MessageListener listener, NetworkAddress node, NetworkHandler.MessageListener listener,
Set<InventoryVector> commonRequestedObjects) { Set<InventoryVector> commonRequestedObjects) {
super(context, mode, node, listener, commonRequestedObjects, false); super(context, mode, node, listener, commonRequestedObjects, false);
out.flip();
if (mode == CLIENT || mode == SYNC) {
send(new Version.Builder().defaults(peerNonce).addrFrom(host).addrRecv(node).build());
}
} }
public State getState() { public State getState() {
@ -77,12 +85,8 @@ public class ConnectionInfo extends AbstractConnection {
} }
} }
public List<NetworkMessage> getMessages() {
return reader.getMessages();
}
@Override @Override
protected void send(MessagePayload payload) { protected void send(MessagePayload payload) {
sendingQueue.addFirst(payload); sendingQueue.add(payload);
} }
} }

View File

@ -37,11 +37,14 @@ import java.net.InetSocketAddress;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
import java.util.*; import java.util.*;
import java.util.concurrent.Future; import java.util.concurrent.*;
import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.CLIENT;
import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SERVER; import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SERVER;
import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SYNC;
import static ch.dissem.bitmessage.networking.AbstractConnection.State.ACTIVE; import static ch.dissem.bitmessage.networking.AbstractConnection.State.ACTIVE;
import static ch.dissem.bitmessage.utils.DebugUtils.inc; import static ch.dissem.bitmessage.utils.DebugUtils.inc;
import static ch.dissem.bitmessage.utils.ThreadFactoryBuilder.pool;
import static java.nio.channels.SelectionKey.OP_READ; import static java.nio.channels.SelectionKey.OP_READ;
import static java.nio.channels.SelectionKey.OP_WRITE; import static java.nio.channels.SelectionKey.OP_WRITE;
@ -51,13 +54,40 @@ import static java.nio.channels.SelectionKey.OP_WRITE;
public class NioNetworkHandler implements NetworkHandler, InternalContext.ContextHolder { public class NioNetworkHandler implements NetworkHandler, InternalContext.ContextHolder {
private static final Logger LOG = LoggerFactory.getLogger(NioNetworkHandler.class); private static final Logger LOG = LoggerFactory.getLogger(NioNetworkHandler.class);
private final ExecutorService pool = Executors.newCachedThreadPool(
pool("network")
.lowPrio()
.daemon()
.build());
private InternalContext ctx; private InternalContext ctx;
private Selector selector; private Selector selector;
private ServerSocketChannel serverChannel; private ServerSocketChannel serverChannel;
@Override @Override
public Future<?> synchronize(InetAddress server, int port, MessageListener listener, long timeoutInSeconds) { public Future<Void> synchronize(final InetAddress server, final int port, final MessageListener listener, long timeoutInSeconds) {
return null; return pool.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
Set<InventoryVector> requestedObjects = new HashSet<>();
try (SocketChannel channel = SocketChannel.open(new InetSocketAddress(server, port))) {
channel.finishConnect();
channel.configureBlocking(false);
ConnectionInfo connection = new ConnectionInfo(ctx, SYNC,
new NetworkAddress.Builder().ip(server).port(port).stream(1).build(),
listener, new HashSet<InventoryVector>());
while (channel.isConnected() &&
(connection.getState() != ACTIVE
|| connection.getSendingQueue().isEmpty()
|| requestedObjects.isEmpty())) {
write(requestedObjects, channel, connection);
read(channel, connection);
Thread.sleep(10);
}
}
return null;
}
});
} }
@Override @Override
@ -66,7 +96,9 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex
channel.configureBlocking(true); channel.configureBlocking(true);
ByteBuffer buffer = ByteBuffer.allocate(MAX_MESSAGE_SIZE); ByteBuffer buffer = ByteBuffer.allocate(MAX_MESSAGE_SIZE);
new NetworkMessage(request).write(buffer); new NetworkMessage(request).write(buffer);
channel.write(buffer); while (buffer.hasRemaining()) {
channel.write(buffer);
}
buffer.clear(); buffer.clear();
V3MessageReader reader = new V3MessageReader(); V3MessageReader reader = new V3MessageReader();
@ -106,34 +138,74 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex
throw new ApplicationException(e); throw new ApplicationException(e);
} }
final Set<InventoryVector> requestedObjects = new HashSet<>(); final Set<InventoryVector> requestedObjects = new HashSet<>();
new Thread(new Runnable() { start("connection listener", new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
serverChannel = ServerSocketChannel.open(); serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(ctx.getPort())); serverChannel.socket().bind(new InetSocketAddress(ctx.getPort()));
while (selector.isOpen() && serverChannel.isOpen()) {
SocketChannel accepted = serverChannel.accept(); try {
accepted.configureBlocking(false); SocketChannel accepted = serverChannel.accept();
// FIXME: apparently it isn't good practice to generally listen for OP_WRITE accepted.configureBlocking(false);
accepted.register(selector, OP_READ | OP_WRITE).attach( accepted.register(selector, OP_READ | OP_WRITE,
new ConnectionInfo(ctx, SERVER, new ConnectionInfo(ctx, SERVER,
new NetworkAddress.Builder().address(accepted.getRemoteAddress()).stream(1).build(), new NetworkAddress.Builder().address(accepted.getRemoteAddress()).stream(1).build(),
listener, listener,
requestedObjects requestedObjects
)); ));
} catch (AsynchronousCloseException ignore) {
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
}
} catch (ClosedSelectorException | AsynchronousCloseException ignore) { } catch (ClosedSelectorException | AsynchronousCloseException ignore) {
} catch (IOException e) { } catch (IOException e) {
throw new ApplicationException(e); throw new ApplicationException(e);
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
} }
} }
}, "Server").start(); });
new Thread(new Runnable() {
start("connection starter", new Runnable() {
@Override
public void run() {
while (selector.isOpen()) {
List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses(
2, ctx.getStreams());
for (NetworkAddress address : addresses) {
try {
SocketChannel channel = SocketChannel.open(
new InetSocketAddress(address.toInetAddress(), address.getPort()));
channel.configureBlocking(false);
channel.register(selector, OP_READ | OP_WRITE,
new ConnectionInfo(ctx, CLIENT,
address,
listener,
requestedObjects
));
} catch (AsynchronousCloseException ignore) {
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
}
try {
Thread.sleep(30_000);
} catch (InterruptedException e) {
return;
}
}
}
});
start("processor", new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
while (selector.isOpen()) { while (selector.isOpen()) {
// TODO: establish outgoing connections selector.select(1000);
Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator(); Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) { while (keyIterator.hasNext()) {
@ -141,22 +213,16 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex
if (key.attachment() instanceof ConnectionInfo) { if (key.attachment() instanceof ConnectionInfo) {
SocketChannel channel = (SocketChannel) key.channel(); SocketChannel channel = (SocketChannel) key.channel();
ConnectionInfo connection = (ConnectionInfo) key.attachment(); ConnectionInfo connection = (ConnectionInfo) key.attachment();
if (key.isWritable()) { if (key.isWritable()) {
if (connection.getOutBuffer().hasRemaining()) { write(requestedObjects, channel, connection);
channel.write(connection.getOutBuffer());
}
while (!connection.getOutBuffer().hasRemaining() && !connection.getSendingQueue().isEmpty()) {
MessagePayload payload = connection.getSendingQueue().poll();
if (payload instanceof GetData) {
requestedObjects.addAll(((GetData) payload).getInventory());
}
new NetworkMessage(payload).write(connection.getOutBuffer());
}
} }
if (key.isReadable()) { if (key.isReadable()) {
channel.read(connection.getInBuffer()); read(channel, connection);
connection.updateReader(); }
if (connection.getSendingQueue().isEmpty()) {
key.interestOps(OP_READ);
} else {
key.interestOps(OP_READ | OP_WRITE);
} }
} }
keyIterator.remove(); keyIterator.remove();
@ -168,13 +234,52 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex
throw new ApplicationException(e); throw new ApplicationException(e);
} }
} }
}, "Connections").start(); });
}
private static void write(Set<InventoryVector> requestedObjects, SocketChannel channel, ConnectionInfo connection)
throws IOException {
if (!connection.getSendingQueue().isEmpty()) {
ByteBuffer buffer = connection.getOutBuffer();
if (buffer.hasRemaining()) {
channel.write(buffer);
}
while (!buffer.hasRemaining()
&& !connection.getSendingQueue().isEmpty()) {
buffer.clear();
MessagePayload payload = connection.getSendingQueue().poll();
if (payload instanceof GetData) {
requestedObjects.addAll(((GetData) payload).getInventory());
}
new NetworkMessage(payload).write(buffer);
buffer.flip();
if (buffer.hasRemaining()) {
channel.write(buffer);
}
}
}
}
private static void read(SocketChannel channel, ConnectionInfo connection) throws IOException {
ByteBuffer buffer = connection.getInBuffer();
while (channel.read(buffer) > 0) {
buffer.flip();
connection.updateReader();
buffer.compact();
}
}
private void start(String threadName, Runnable runnable) {
Thread thread = new Thread(runnable, threadName);
thread.setDaemon(true);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
} }
@Override @Override
public void stop() { public void stop() {
try { try {
serverChannel.close(); serverChannel.socket().close();
for (SelectionKey key : selector.keys()) { for (SelectionKey key : selector.keys()) {
key.channel().close(); key.channel().close();
} }

View File

@ -22,14 +22,23 @@ import ch.dissem.bitmessage.entity.CustomMessage;
import ch.dissem.bitmessage.entity.MessagePayload; import ch.dissem.bitmessage.entity.MessagePayload;
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
import ch.dissem.bitmessage.exception.NodeException; import ch.dissem.bitmessage.exception.NodeException;
import ch.dissem.bitmessage.networking.nio.NioNetworkHandler;
import ch.dissem.bitmessage.ports.*; import ch.dissem.bitmessage.ports.*;
import ch.dissem.bitmessage.utils.Property; import ch.dissem.bitmessage.utils.Property;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.DisableOnDebug;
import org.junit.rules.TestRule;
import org.junit.rules.Timeout;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import static ch.dissem.bitmessage.utils.Singleton.cryptography; import static ch.dissem.bitmessage.utils.Singleton.cryptography;
@ -42,6 +51,7 @@ import static org.mockito.Mockito.mock;
/** /**
* FIXME: there really should be sensible tests for the network handler * FIXME: there really should be sensible tests for the network handler
*/ */
@RunWith(Parameterized.class)
public class NetworkHandlerTest { public class NetworkHandlerTest {
private static final Logger LOG = LoggerFactory.getLogger(NetworkHandlerTest.class); private static final Logger LOG = LoggerFactory.getLogger(NetworkHandlerTest.class);
private static NetworkAddress peerAddress = new NetworkAddress.Builder().ipv4(127, 0, 0, 1).port(6001).build(); private static NetworkAddress peerAddress = new NetworkAddress.Builder().ipv4(127, 0, 0, 1).port(6001).build();
@ -51,7 +61,27 @@ public class NetworkHandlerTest {
private BitmessageContext peer; private BitmessageContext peer;
private BitmessageContext node; private BitmessageContext node;
private NetworkHandler networkHandler;
private final NetworkHandler peerNetworkHandler;
private final NetworkHandler nodeNetworkHandler;
@Rule
public final TestRule timeout = new DisableOnDebug(Timeout.seconds(5));
public NetworkHandlerTest(NetworkHandler peer, NetworkHandler node) {
this.peerNetworkHandler = peer;
this.nodeNetworkHandler = node;
}
@Parameterized.Parameters
public static List<Object[]> parameters() {
return Arrays.asList(new Object[][]{
{new DefaultNetworkHandler(), new DefaultNetworkHandler()},
{new DefaultNetworkHandler(), new NioNetworkHandler()},
{new NioNetworkHandler(), new DefaultNetworkHandler()},
{new NioNetworkHandler(), new NioNetworkHandler()}
});
}
@Before @Before
public void setUp() { public void setUp() {
@ -63,7 +93,7 @@ public class NetworkHandlerTest {
.powRepo(mock(ProofOfWorkRepository.class)) .powRepo(mock(ProofOfWorkRepository.class))
.port(peerAddress.getPort()) .port(peerAddress.getPort())
.nodeRegistry(new TestNodeRegistry()) .nodeRegistry(new TestNodeRegistry())
.networkHandler(new DefaultNetworkHandler()) .networkHandler(peerNetworkHandler)
.cryptography(new BouncyCryptography()) .cryptography(new BouncyCryptography())
.listener(mock(BitmessageContext.Listener.class)) .listener(mock(BitmessageContext.Listener.class))
.customCommandHandler(new CustomCommandHandler() { .customCommandHandler(new CustomCommandHandler() {
@ -90,7 +120,6 @@ public class NetworkHandlerTest {
peer.startup(); peer.startup();
nodeInventory = new TestInventory(); nodeInventory = new TestInventory();
networkHandler = new DefaultNetworkHandler();
node = new BitmessageContext.Builder() node = new BitmessageContext.Builder()
.addressRepo(mock(AddressRepository.class)) .addressRepo(mock(AddressRepository.class))
.inventory(nodeInventory) .inventory(nodeInventory)
@ -98,7 +127,7 @@ public class NetworkHandlerTest {
.powRepo(mock(ProofOfWorkRepository.class)) .powRepo(mock(ProofOfWorkRepository.class))
.port(6002) .port(6002)
.nodeRegistry(new TestNodeRegistry(peerAddress)) .nodeRegistry(new TestNodeRegistry(peerAddress))
.networkHandler(networkHandler) .networkHandler(nodeNetworkHandler)
.cryptography(new BouncyCryptography()) .cryptography(new BouncyCryptography())
.listener(mock(BitmessageContext.Listener.class)) .listener(mock(BitmessageContext.Listener.class))
.build(); .build();
@ -108,7 +137,7 @@ public class NetworkHandlerTest {
public void cleanUp() { public void cleanUp() {
shutdown(peer); shutdown(peer);
shutdown(node); shutdown(node);
shutdown(networkHandler); shutdown(nodeNetworkHandler);
} }
private static void shutdown(BitmessageContext ctx) { private static void shutdown(BitmessageContext ctx) {
@ -140,7 +169,7 @@ public class NetworkHandlerTest {
} while (networkHandler.isRunning()); } while (networkHandler.isRunning());
} }
@Test(timeout = 5_000) @Test
public void ensureNodesAreConnecting() throws Exception { public void ensureNodesAreConnecting() throws Exception {
node.startup(); node.startup();
Property status; Property status;
@ -151,14 +180,14 @@ public class NetworkHandlerTest {
assertEquals(1, status.getProperty("outgoing").getValue()); assertEquals(1, status.getProperty("outgoing").getValue());
} }
@Test(timeout = 5_000) @Test
public void ensureCustomMessageIsSentAndResponseRetrieved() throws Exception { public void ensureCustomMessageIsSentAndResponseRetrieved() throws Exception {
byte[] data = cryptography().randomBytes(8); byte[] data = cryptography().randomBytes(8);
data[0] = (byte) 1; data[0] = (byte) 1;
CustomMessage request = new CustomMessage("test request", data); CustomMessage request = new CustomMessage("test request", data);
node.startup(); node.startup();
CustomMessage response = networkHandler.send(peerAddress.toInetAddress(), peerAddress.getPort(), request); CustomMessage response = nodeNetworkHandler.send(peerAddress.toInetAddress(), peerAddress.getPort(), request);
assertThat(response, notNullValue()); assertThat(response, notNullValue());
assertThat(response.getCustomCommand(), is("test response")); assertThat(response.getCustomCommand(), is("test response"));
@ -172,14 +201,14 @@ public class NetworkHandlerTest {
CustomMessage request = new CustomMessage("test request", data); CustomMessage request = new CustomMessage("test request", data);
node.startup(); node.startup();
CustomMessage response = networkHandler.send(peerAddress.toInetAddress(), peerAddress.getPort(), request); CustomMessage response = nodeNetworkHandler.send(peerAddress.toInetAddress(), peerAddress.getPort(), request);
assertThat(response, notNullValue()); assertThat(response, notNullValue());
assertThat(response.getCustomCommand(), is("test response")); assertThat(response.getCustomCommand(), is("test response"));
assertThat(response.getData(), is(request.getData())); assertThat(response.getData(), is(request.getData()));
} }
@Test(timeout = 5_000) @Test
public void ensureObjectsAreSynchronizedIfBothHaveObjects() throws Exception { public void ensureObjectsAreSynchronizedIfBothHaveObjects() throws Exception {
peerInventory.init( peerInventory.init(
"V4Pubkey.payload", "V4Pubkey.payload",
@ -191,7 +220,7 @@ public class NetworkHandlerTest {
"V4Pubkey.payload" "V4Pubkey.payload"
); );
Future<?> future = networkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), Future<?> future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(),
mock(NetworkHandler.MessageListener.class), mock(NetworkHandler.MessageListener.class),
10); 10);
future.get(); future.get();
@ -199,7 +228,7 @@ public class NetworkHandlerTest {
assertInventorySize(3, peerInventory); assertInventorySize(3, peerInventory);
} }
@Test(timeout = 5_000) @Test
public void ensureObjectsAreSynchronizedIfOnlyPeerHasObjects() throws Exception { public void ensureObjectsAreSynchronizedIfOnlyPeerHasObjects() throws Exception {
peerInventory.init( peerInventory.init(
"V4Pubkey.payload", "V4Pubkey.payload",
@ -208,7 +237,7 @@ public class NetworkHandlerTest {
nodeInventory.init(); nodeInventory.init();
Future<?> future = networkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), Future<?> future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(),
mock(NetworkHandler.MessageListener.class), mock(NetworkHandler.MessageListener.class),
10); 10);
future.get(); future.get();
@ -216,7 +245,7 @@ public class NetworkHandlerTest {
assertInventorySize(2, peerInventory); assertInventorySize(2, peerInventory);
} }
@Test(timeout = 5_000) @Test
public void ensureObjectsAreSynchronizedIfOnlyNodeHasObjects() throws Exception { public void ensureObjectsAreSynchronizedIfOnlyNodeHasObjects() throws Exception {
peerInventory.init(); peerInventory.init();
@ -224,7 +253,7 @@ public class NetworkHandlerTest {
"V1Msg.payload" "V1Msg.payload"
); );
Future<?> future = networkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), Future<?> future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(),
mock(NetworkHandler.MessageListener.class), mock(NetworkHandler.MessageListener.class),
10); 10);
future.get(); future.get();

View File

@ -13,7 +13,7 @@ uploadArchives {
dependencies { dependencies {
compile project(':core') compile project(':core')
compile 'org.ini4j:ini4j:0.5.4' compile 'org.ini4j:ini4j:0.5.4'
testCompile 'junit:junit:4.11' testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19' testCompile 'org.mockito:mockito-core:1.10.19'
testCompile project(':cryptography-bc') testCompile project(':cryptography-bc')
} }