diff --git a/test/net/sf/briar/transport/ConnectionReaderImplTest.java b/test/net/sf/briar/transport/ConnectionReaderImplTest.java index d701ed999ea451fc4362b90652f54153f53b54e4..7725d6bf12bdc957020f2a3d91ca3ed24b312400 100644 --- a/test/net/sf/briar/transport/ConnectionReaderImplTest.java +++ b/test/net/sf/briar/transport/ConnectionReaderImplTest.java @@ -3,39 +3,19 @@ package net.sf.briar.transport; import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH; import java.io.ByteArrayInputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; import java.util.Arrays; -import javax.crypto.Mac; - -import junit.framework.TestCase; import net.sf.briar.TestUtils; import net.sf.briar.api.FormatException; -import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.transport.ConnectionReader; -import net.sf.briar.crypto.CryptoModule; -import net.sf.briar.util.ByteUtils; import org.apache.commons.io.output.ByteArrayOutputStream; import org.junit.Test; -import com.google.inject.Guice; -import com.google.inject.Injector; - -public class ConnectionReaderImplTest extends TestCase { - - private final Mac mac; - private final int headerLength = 8, macLength; +public class ConnectionReaderImplTest extends TransportTest { public ConnectionReaderImplTest() throws Exception { super(); - Injector i = Guice.createInjector(new CryptoModule()); - CryptoComponent crypto = i.getInstance(CryptoComponent.class); - mac = crypto.getMac(); - mac.init(crypto.generateSecretKey()); - macLength = mac.getMacLength(); } @Test @@ -75,7 +55,6 @@ public class ConnectionReaderImplTest extends TestCase { @Test public void testMaxLength() throws Exception { - int maxPayloadLength = MAX_FRAME_LENGTH - headerLength - macLength; // First frame: max payload length byte[] frame = new byte[MAX_FRAME_LENGTH]; writeHeader(frame, 0L, maxPayloadLength, 0); @@ -106,7 +85,6 @@ public class ConnectionReaderImplTest extends TestCase { @Test public void testMaxLengthWithPadding() throws Exception { - int maxPayloadLength = MAX_FRAME_LENGTH - headerLength - macLength; int paddingLength = 10; // First frame: max payload length, including padding byte[] frame = new byte[MAX_FRAME_LENGTH]; @@ -204,35 +182,4 @@ public class ConnectionReaderImplTest extends TestCase { fail(); } catch(FormatException expected) {} } - - private void writeHeader(byte[] b, long frame, int payload, int padding) { - ByteUtils.writeUint32(frame, b, 0); - ByteUtils.writeUint16(payload, b, 4); - ByteUtils.writeUint16(padding, b, 6); - } - - /** A ConnectionDecrypter that performs no decryption. */ - private static class NullConnectionDecrypter - implements ConnectionDecrypter { - - private final InputStream in; - - private NullConnectionDecrypter(InputStream in) { - this.in = in; - } - - public InputStream getInputStream() { - return in; - } - - public void readMac(byte[] mac) throws IOException { - int offset = 0; - while(offset < mac.length) { - int read = in.read(mac, offset, mac.length - offset); - if(read == -1) break; - offset += read; - } - if(offset < mac.length) throw new EOFException(); - } - } } diff --git a/test/net/sf/briar/transport/ConnectionWriterImplTest.java b/test/net/sf/briar/transport/ConnectionWriterImplTest.java index 50dbd389dcb83f211a0b4c0464f1a4516ce2a8ef..4640ab10f698e8298a19742720e93fe11233d2cf 100644 --- a/test/net/sf/briar/transport/ConnectionWriterImplTest.java +++ b/test/net/sf/briar/transport/ConnectionWriterImplTest.java @@ -3,35 +3,17 @@ package net.sf.briar.transport; import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH; import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; -import javax.crypto.Mac; - -import junit.framework.TestCase; -import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.transport.ConnectionWriter; -import net.sf.briar.crypto.CryptoModule; -import net.sf.briar.util.ByteUtils; import org.junit.Test; -import com.google.inject.Guice; -import com.google.inject.Injector; - -public class ConnectionWriterImplTest extends TestCase { - - private final Mac mac; - private final int headerLength = 8, macLength; +public class ConnectionWriterImplTest extends TransportTest { public ConnectionWriterImplTest() throws Exception { super(); - Injector i = Guice.createInjector(new CryptoModule()); - CryptoComponent crypto = i.getInstance(CryptoComponent.class); - mac = crypto.getMac(); - mac.init(crypto.generateSecretKey()); - macLength = mac.getMacLength(); } @Test @@ -64,7 +46,6 @@ public class ConnectionWriterImplTest extends TestCase { @Test public void testFrameIsWrittenAtMaxLength() throws Exception { - int maxPayloadLength = MAX_FRAME_LENGTH - headerLength - macLength; ByteArrayOutputStream out = new ByteArrayOutputStream(); ConnectionEncrypter e = new NullConnectionEncrypter(out); ConnectionWriter w = new ConnectionWriterImpl(e, mac); @@ -109,29 +90,4 @@ public class ConnectionWriterImplTest extends TestCase { byte[] actual = out.toByteArray(); assertTrue(Arrays.equals(expected, actual)); } - - private void writeHeader(byte[] b, long frame, int payload, int padding) { - ByteUtils.writeUint32(frame, b, 0); - ByteUtils.writeUint16(payload, b, 4); - ByteUtils.writeUint16(padding, b, 6); - } - - /** A ConnectionEncrypter that performs no encryption. */ - private static class NullConnectionEncrypter - implements ConnectionEncrypter { - - private final OutputStream out; - - private NullConnectionEncrypter(OutputStream out) { - this.out = out; - } - - public OutputStream getOutputStream() { - return out; - } - - public void writeMac(byte[] mac) throws IOException { - out.write(mac); - } - } } diff --git a/test/net/sf/briar/transport/NullConnectionDecrypter.java b/test/net/sf/briar/transport/NullConnectionDecrypter.java new file mode 100644 index 0000000000000000000000000000000000000000..0c6bf77f6e50fa3304f6e72faa9223800bae6e07 --- /dev/null +++ b/test/net/sf/briar/transport/NullConnectionDecrypter.java @@ -0,0 +1,29 @@ +package net.sf.briar.transport; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +/** A ConnectionDecrypter that performs no decryption. */ +class NullConnectionDecrypter implements ConnectionDecrypter { + + private final InputStream in; + + NullConnectionDecrypter(InputStream in) { + this.in = in; + } + + public InputStream getInputStream() { + return in; + } + + public void readMac(byte[] mac) throws IOException { + int offset = 0; + while(offset < mac.length) { + int read = in.read(mac, offset, mac.length - offset); + if(read == -1) break; + offset += read; + } + if(offset < mac.length) throw new EOFException(); + } +} diff --git a/test/net/sf/briar/transport/NullConnectionEncrypter.java b/test/net/sf/briar/transport/NullConnectionEncrypter.java new file mode 100644 index 0000000000000000000000000000000000000000..c9f492d69bd7e99c346b4bdb0afc49c8d0f16a1d --- /dev/null +++ b/test/net/sf/briar/transport/NullConnectionEncrypter.java @@ -0,0 +1,22 @@ +package net.sf.briar.transport; + +import java.io.IOException; +import java.io.OutputStream; + +/** A ConnectionEncrypter that performs no encryption. */ +class NullConnectionEncrypter implements ConnectionEncrypter { + + private final OutputStream out; + + NullConnectionEncrypter(OutputStream out) { + this.out = out; + } + + public OutputStream getOutputStream() { + return out; + } + + public void writeMac(byte[] mac) throws IOException { + out.write(mac); + } +} diff --git a/test/net/sf/briar/transport/PaddedConnectionWriterTest.java b/test/net/sf/briar/transport/PaddedConnectionWriterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2d2a0f22bdc17a34626b3664237ef4dd2602b06f --- /dev/null +++ b/test/net/sf/briar/transport/PaddedConnectionWriterTest.java @@ -0,0 +1,164 @@ +package net.sf.briar.transport; + +import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import net.sf.briar.api.transport.ConnectionWriter; +import net.sf.briar.util.ByteUtils; + +import org.junit.Test; + +public class PaddedConnectionWriterTest extends TransportTest { + + public PaddedConnectionWriterTest() throws Exception { + super(); + } + + @Test + public void testWriteByteDoesNotBlockUntilBufferIsFull() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ConnectionEncrypter e = new NullConnectionEncrypter(out); + ConnectionWriter w = new PaddedConnectionWriter(e, mac); + final OutputStream out1 = w.getOutputStream(); + final CountDownLatch latch = new CountDownLatch(1); + final AtomicBoolean finished = new AtomicBoolean(false); + final AtomicBoolean failed = new AtomicBoolean(false); + new Thread() { + @Override + public void run() { + try { + for(int i = 0; i < maxPayloadLength; i++) out1.write(0); + finished.set(true); + } catch(IOException e) { + failed.set(true); + } + latch.countDown(); + } + }.start(); + // The wait should not time out + assertTrue(latch.await(1, TimeUnit.SECONDS)); + assertTrue(finished.get()); + assertFalse(failed.get()); + // Nothing should have been written + assertEquals(0, out.size()); + } + + @Test + public void testWriteByteBlocksWhenBufferIsFull() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ConnectionEncrypter e = new NullConnectionEncrypter(out); + PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac); + final OutputStream out1 = w.getOutputStream(); + final CountDownLatch latch = new CountDownLatch(1); + final AtomicBoolean finished = new AtomicBoolean(false); + final AtomicBoolean failed = new AtomicBoolean(false); + new Thread() { + @Override + public void run() { + try { + for(int i = 0; i < maxPayloadLength + 1; i++) out1.write(0); + finished.set(true); + } catch(IOException e) { + failed.set(true); + } + latch.countDown(); + } + }.start(); + // The wait should time out + assertFalse(latch.await(1, TimeUnit.SECONDS)); + assertFalse(finished.get()); + assertFalse(failed.get()); + // Calling writeFullFrame() should allow the writer to proceed + w.writeFullFrame(); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + assertTrue(finished.get()); + assertFalse(failed.get()); + // A full frame should have been written + assertEquals(MAX_FRAME_LENGTH, out.size()); + } + + @Test + public void testWriteArrayDoesNotBlockUntilBufferIsFull() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ConnectionEncrypter e = new NullConnectionEncrypter(out); + ConnectionWriter w = new PaddedConnectionWriter(e, mac); + final OutputStream out1 = w.getOutputStream(); + final CountDownLatch latch = new CountDownLatch(1); + final AtomicBoolean finished = new AtomicBoolean(false); + final AtomicBoolean failed = new AtomicBoolean(false); + new Thread() { + @Override + public void run() { + try { + out1.write(new byte[maxPayloadLength]); + finished.set(true); + } catch(IOException e) { + failed.set(true); + } + latch.countDown(); + } + }.start(); + // The wait should not time out + assertTrue(latch.await(1, TimeUnit.SECONDS)); + assertTrue(finished.get()); + assertFalse(failed.get()); + // Nothing should have been written + assertEquals(0, out.size()); + } + + @Test + public void testWriteArrayBlocksWhenBufferIsFull() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ConnectionEncrypter e = new NullConnectionEncrypter(out); + PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac); + final OutputStream out1 = w.getOutputStream(); + final CountDownLatch latch = new CountDownLatch(1); + final AtomicBoolean finished = new AtomicBoolean(false); + final AtomicBoolean failed = new AtomicBoolean(false); + new Thread() { + @Override + public void run() { + try { + out1.write(new byte[maxPayloadLength + 1]); + finished.set(true); + } catch(IOException e) { + failed.set(true); + } + latch.countDown(); + } + }.start(); + // The wait should time out + assertFalse(latch.await(1, TimeUnit.SECONDS)); + assertFalse(finished.get()); + assertFalse(failed.get()); + // Calling writeFullFrame() should allow the writer to proceed + w.writeFullFrame(); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + assertTrue(finished.get()); + assertFalse(failed.get()); + // A full frame should have been written + assertEquals(MAX_FRAME_LENGTH, out.size()); + } + + @Test + public void testWriteFullFrameInsertsPadding() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ConnectionEncrypter e = new NullConnectionEncrypter(out); + PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac); + w.getOutputStream().write(0); + w.writeFullFrame(); + // A full frame should have been written + assertEquals(MAX_FRAME_LENGTH, out.size()); + // The frame should have a payload length of 1 and padding for the rest + byte[] frame = out.toByteArray(); + assertEquals(0L, ByteUtils.readUint32(frame, 0)); // Frame number + assertEquals(1, ByteUtils.readUint16(frame, 4)); // Payload length + assertEquals(maxPayloadLength - 1, ByteUtils.readUint16(frame, 6)); + } +} diff --git a/test/net/sf/briar/transport/TransportTest.java b/test/net/sf/briar/transport/TransportTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5ba5fcbaea7a231dd26623e17b3ae4b8f1b3d0af --- /dev/null +++ b/test/net/sf/briar/transport/TransportTest.java @@ -0,0 +1,35 @@ +package net.sf.briar.transport; + +import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH; + +import javax.crypto.Mac; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.crypto.CryptoModule; +import net.sf.briar.util.ByteUtils; +import junit.framework.TestCase; + +public abstract class TransportTest extends TestCase { + + protected final Mac mac; + protected final int headerLength = 8, macLength, maxPayloadLength; + + public TransportTest() throws Exception { + super(); + Injector i = Guice.createInjector(new CryptoModule()); + CryptoComponent crypto = i.getInstance(CryptoComponent.class); + mac = crypto.getMac(); + mac.init(crypto.generateSecretKey()); + macLength = mac.getMacLength(); + maxPayloadLength = MAX_FRAME_LENGTH - headerLength - macLength; + } + + static void writeHeader(byte[] b, long frame, int payload, int padding) { + ByteUtils.writeUint32(frame, b, 0); + ByteUtils.writeUint16(payload, b, 4); + ByteUtils.writeUint16(padding, b, 6); + } +}