From 4aff0c4f8828a316db53cf6ad8357bdc3fc81d9c Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Tue, 27 Sep 2011 19:21:44 +0100
Subject: [PATCH] Refactored transport component and renamed WritersModule.

The goal of the refactoring was to clean up the dependencies of
IncomingBatchConnection and OutgoingBatchConnection.
---
 .../briar/api/transport/ConnectionWriter.java |  8 +---
 .../transport/ConnectionWriterFactory.java    |  5 ++-
 .../transport/batch/BatchTransportReader.java |  2 +-
 .../transport/batch/BatchTransportWriter.java |  4 +-
 ...Module.java => ProtocolWritersModule.java} |  2 +-
 .../briar/transport/ConnectionEncrypter.java  |  7 +---
 .../transport/ConnectionEncrypterImpl.java    | 14 ++++---
 .../ConnectionWriterFactoryImpl.java          |  6 +--
 .../briar/transport/ConnectionWriterImpl.java |  6 +--
 .../batch/IncomingBatchConnection.java        | 21 ++--------
 .../batch/OutgoingBatchConnection.java        | 29 ++++----------
 test/net/sf/briar/FileReadWriteTest.java      |  8 ++--
 .../briar/protocol/ProtocolReadWriteTest.java |  5 ++-
 .../ConnectionEncrypterImplTest.java          | 15 +++++---
 .../transport/ConnectionWriterImplTest.java   | 28 --------------
 .../briar/transport/ConnectionWriterTest.java | 23 +++++------
 .../briar/transport/FrameReadWriteTest.java   |  4 +-
 .../transport/NullConnectionEncrypter.java    | 35 ++++++++++++++---
 .../transport/PaddedConnectionWriterTest.java | 38 +++----------------
 .../batch/TestBatchTransportReader.java       |  2 +-
 .../batch/TestBatchTransportWriter.java       |  4 +-
 21 files changed, 105 insertions(+), 161 deletions(-)
 rename components/net/sf/briar/protocol/writers/{WritersModule.java => ProtocolWritersModule.java} (81%)

diff --git a/api/net/sf/briar/api/transport/ConnectionWriter.java b/api/net/sf/briar/api/transport/ConnectionWriter.java
index cfc623bc38..ae3b5ab162 100644
--- a/api/net/sf/briar/api/transport/ConnectionWriter.java
+++ b/api/net/sf/briar/api/transport/ConnectionWriter.java
@@ -11,10 +11,6 @@ public interface ConnectionWriter {
 	 */
 	OutputStream getOutputStream();
 
-	/**
-	 * Returns the number of bytes that can be written to this writer without
-	 * outputting more than the given number of bytes, including encryption and
-	 * authentication overhead.
-	 */
-	long getCapacity(long capacity);
+	/** Returns the maximum number of bytes that can be written. */
+	long getCapacity();
 }
diff --git a/api/net/sf/briar/api/transport/ConnectionWriterFactory.java b/api/net/sf/briar/api/transport/ConnectionWriterFactory.java
index 696616dfb3..a8d58e8085 100644
--- a/api/net/sf/briar/api/transport/ConnectionWriterFactory.java
+++ b/api/net/sf/briar/api/transport/ConnectionWriterFactory.java
@@ -4,6 +4,7 @@ import java.io.OutputStream;
 
 public interface ConnectionWriterFactory {
 
-	ConnectionWriter createConnectionWriter(OutputStream out, boolean initiator,
-			int transportId, long connection, byte[] secret);
+	ConnectionWriter createConnectionWriter(OutputStream out, 
+			long capacity, boolean initiator, int transportId, long connection,
+			byte[] secret);
 }
diff --git a/api/net/sf/briar/api/transport/batch/BatchTransportReader.java b/api/net/sf/briar/api/transport/batch/BatchTransportReader.java
index 758a34c02c..486f441d49 100644
--- a/api/net/sf/briar/api/transport/batch/BatchTransportReader.java
+++ b/api/net/sf/briar/api/transport/batch/BatchTransportReader.java
@@ -10,7 +10,7 @@ import java.io.InputStream;
 public interface BatchTransportReader {
 
 	/** Returns an input stream for reading from the transport. */
-	InputStream getInputStream() throws IOException;
+	InputStream getInputStream();
 
 	/**
 	 * Closes the reader and disposes of any associated state. This method must
diff --git a/api/net/sf/briar/api/transport/batch/BatchTransportWriter.java b/api/net/sf/briar/api/transport/batch/BatchTransportWriter.java
index 7745803dff..10f4b9f70d 100644
--- a/api/net/sf/briar/api/transport/batch/BatchTransportWriter.java
+++ b/api/net/sf/briar/api/transport/batch/BatchTransportWriter.java
@@ -10,10 +10,10 @@ import java.io.OutputStream;
 public interface BatchTransportWriter {
 
 	/** Returns the maximum number of bytes that can be written. */
-	long getCapacity() throws IOException;
+	long getCapacity();
 
 	/** Returns an output stream for writing to the transport. */
-	OutputStream getOutputStream() throws IOException;
+	OutputStream getOutputStream();
 
 	/**
 	 * Closes the writer and disposes of any associated state. This method must
diff --git a/components/net/sf/briar/protocol/writers/WritersModule.java b/components/net/sf/briar/protocol/writers/ProtocolWritersModule.java
similarity index 81%
rename from components/net/sf/briar/protocol/writers/WritersModule.java
rename to components/net/sf/briar/protocol/writers/ProtocolWritersModule.java
index af8546ec6b..7e83b36b05 100644
--- a/components/net/sf/briar/protocol/writers/WritersModule.java
+++ b/components/net/sf/briar/protocol/writers/ProtocolWritersModule.java
@@ -4,7 +4,7 @@ import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
 
 import com.google.inject.AbstractModule;
 
-public class WritersModule extends AbstractModule {
+public class ProtocolWritersModule extends AbstractModule {
 
 	@Override
 	protected void configure() {
diff --git a/components/net/sf/briar/transport/ConnectionEncrypter.java b/components/net/sf/briar/transport/ConnectionEncrypter.java
index 8f25f27c3d..474c46fa9b 100644
--- a/components/net/sf/briar/transport/ConnectionEncrypter.java
+++ b/components/net/sf/briar/transport/ConnectionEncrypter.java
@@ -12,9 +12,6 @@ interface ConnectionEncrypter {
 	/** Encrypts and writes the MAC for the current frame. */
 	void writeMac(byte[] mac) throws IOException;
 
-	/**
-	 * Returns the number of bytes that can be encrypted without outputting
-	 * more than the given number of bytes, including encryption overhead.
-	 */
-	long getCapacity(long capacity);
+	/** Returns the maximum number of bytes that can be written. */
+	long getCapacity();
 }
diff --git a/components/net/sf/briar/transport/ConnectionEncrypterImpl.java b/components/net/sf/briar/transport/ConnectionEncrypterImpl.java
index 419445dcbd..1d8921236e 100644
--- a/components/net/sf/briar/transport/ConnectionEncrypterImpl.java
+++ b/components/net/sf/briar/transport/ConnectionEncrypterImpl.java
@@ -22,10 +22,10 @@ implements ConnectionEncrypter {
 	private final SecretKey frameKey;
 	private final byte[] iv;
 
-	private long frame = 0L;
+	private long capacity, frame = 0L;
 	private boolean ivWritten = false, betweenFrames = false;
 
-	ConnectionEncrypterImpl(OutputStream out, boolean initiator,
+	ConnectionEncrypterImpl(OutputStream out, long capacity, boolean initiator,
 			int transportId, long connection, Cipher ivCipher,
 			Cipher frameCipher, SecretKey ivKey, SecretKey frameKey) {
 		super(out);
@@ -40,6 +40,7 @@ implements ConnectionEncrypter {
 		}
 		if(ivCipher.getOutputSize(IV_LENGTH) != IV_LENGTH)
 			throw new IllegalArgumentException();
+		this.capacity = capacity;
 	}
 
 	public OutputStream getOutputStream() {
@@ -55,12 +56,12 @@ implements ConnectionEncrypter {
 		} catch(IllegalBlockSizeException badCipher) {
 			throw new RuntimeException(badCipher);
 		}
+		capacity -= mac.length;
 		betweenFrames = true;
 	}
 
-	public long getCapacity(long capacity) {
-		if(capacity < 0L) throw new IllegalArgumentException();
-		return ivWritten ? capacity : Math.max(0L, capacity - IV_LENGTH);
+	public long getCapacity() {
+		return capacity;
 	}
 
 	@Override
@@ -69,6 +70,7 @@ implements ConnectionEncrypter {
 		if(betweenFrames) initialiseCipher();
 		byte[] ciphertext = frameCipher.update(new byte[] {(byte) b});
 		if(ciphertext != null) out.write(ciphertext);
+		capacity--;
 	}
 
 	@Override
@@ -82,6 +84,7 @@ implements ConnectionEncrypter {
 		if(betweenFrames) initialiseCipher();
 		byte[] ciphertext = frameCipher.update(b, off, len);
 		if(ciphertext != null) out.write(ciphertext);
+		capacity -= len;
 	}
 
 	private void writeIv() throws IOException {
@@ -94,6 +97,7 @@ implements ConnectionEncrypter {
 		} catch(IllegalBlockSizeException badCipher) {
 			throw new RuntimeException(badCipher);
 		}
+		capacity -= iv.length;
 		ivWritten = true;
 		betweenFrames = true;
 	}
diff --git a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
index 42ed959005..50b835d5c0 100644
--- a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
@@ -23,7 +23,7 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
 	}
 
 	public ConnectionWriter createConnectionWriter(OutputStream out,
-			boolean initiator, int transportId, long connection,
+			long capacity, boolean initiator, int transportId, long connection,
 			byte[] secret) {
 		SecretKey macKey = crypto.deriveOutgoingMacKey(secret);
 		SecretKey ivKey = crypto.deriveOutgoingIvKey(secret);
@@ -37,8 +37,8 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
 			throw new IllegalArgumentException(badKey);
 		}
 		ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
-				initiator, transportId, connection, ivCipher, frameCipher,
-				ivKey, frameKey);
+				capacity, initiator, transportId, connection, ivCipher,
+				frameCipher, ivKey, frameKey);
 		return new ConnectionWriterImpl(encrypter, mac);
 	}
 }
diff --git a/components/net/sf/briar/transport/ConnectionWriterImpl.java b/components/net/sf/briar/transport/ConnectionWriterImpl.java
index 61d6364471..a4bedfda63 100644
--- a/components/net/sf/briar/transport/ConnectionWriterImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWriterImpl.java
@@ -41,10 +41,8 @@ implements ConnectionWriter {
 		return this;
 	}
 
-	public long getCapacity(long capacity) {
-		if(capacity < 0L) throw new IllegalArgumentException();
-		// Subtract the encryption overhead
-		capacity = encrypter.getCapacity(capacity);
+	public long getCapacity() {
+		long capacity = encrypter.getCapacity();
 		// If there's any data buffered, subtract it and its auth overhead
 		int overheadPerFrame = header.length + mac.getMacLength();
 		if(buf.size() > 0) capacity -= buf.size() + overheadPerFrame;
diff --git a/components/net/sf/briar/transport/batch/IncomingBatchConnection.java b/components/net/sf/briar/transport/batch/IncomingBatchConnection.java
index cf16f44b54..20f6f5bd37 100644
--- a/components/net/sf/briar/transport/batch/IncomingBatchConnection.java
+++ b/components/net/sf/briar/transport/batch/IncomingBatchConnection.java
@@ -14,36 +14,23 @@ import net.sf.briar.api.protocol.ProtocolReaderFactory;
 import net.sf.briar.api.protocol.SubscriptionUpdate;
 import net.sf.briar.api.protocol.TransportUpdate;
 import net.sf.briar.api.transport.ConnectionReader;
-import net.sf.briar.api.transport.ConnectionReaderFactory;
-import net.sf.briar.api.transport.batch.BatchTransportReader;
 
 class IncomingBatchConnection {
 
-	private final BatchTransportReader trans;
-	private final ConnectionReaderFactory connFactory;
+	private final ConnectionReader conn;
 	private final DatabaseComponent db;
 	private final ProtocolReaderFactory protoFactory;
-	private final int transportId;
-	private final long connection;
 	private final ContactId contactId;
 
-	IncomingBatchConnection(BatchTransportReader trans,
-			ConnectionReaderFactory connFactory, DatabaseComponent db,
-			ProtocolReaderFactory protoFactory, int transportId,
-			long connection, ContactId contactId) {
-		this.trans = trans;
-		this.connFactory = connFactory;
+	IncomingBatchConnection(ConnectionReader conn, DatabaseComponent db,
+			ProtocolReaderFactory protoFactory, ContactId contactId) {
+		this.conn = conn;
 		this.db = db;
 		this.protoFactory = protoFactory;
-		this.transportId = transportId;
-		this.connection = connection;
 		this.contactId = contactId;
 	}
 
 	void read() throws DbException, IOException {
-		byte[] secret = db.getSharedSecret(contactId);
-		ConnectionReader conn = connFactory.createConnectionReader(
-				trans.getInputStream(), false, transportId, connection, secret);
 		InputStream in = conn.getInputStream();
 		ProtocolReader proto = protoFactory.createProtocolReader(in);
 		// Read packets until EOF
diff --git a/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java b/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java
index 6d4d17d072..e3cdb16aed 100644
--- a/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java
+++ b/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java
@@ -14,45 +14,32 @@ import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
 import net.sf.briar.api.protocol.writers.SubscriptionWriter;
 import net.sf.briar.api.protocol.writers.TransportWriter;
 import net.sf.briar.api.transport.ConnectionWriter;
-import net.sf.briar.api.transport.ConnectionWriterFactory;
-import net.sf.briar.api.transport.batch.BatchTransportWriter;
 
 class OutgoingBatchConnection {
 
-	private final BatchTransportWriter trans;
-	private final ConnectionWriterFactory connFactory;
+	private final ConnectionWriter conn;
 	private final DatabaseComponent db;
 	private final ProtocolWriterFactory protoFactory;
-	private final int transportId;
-	private final long connection;
 	private final ContactId contactId;
 
-	OutgoingBatchConnection(BatchTransportWriter trans,
-			ConnectionWriterFactory connFactory, DatabaseComponent db,
-			ProtocolWriterFactory protoFactory, int transportId,
-			long connection, ContactId contactId) {
-		this.trans = trans;
-		this.connFactory = connFactory;
+	OutgoingBatchConnection(ConnectionWriter conn, DatabaseComponent db,
+			ProtocolWriterFactory protoFactory, ContactId contactId) {
+		this.conn = conn;
 		this.db = db;
 		this.protoFactory = protoFactory;
-		this.transportId = transportId;
-		this.connection = connection;
 		this.contactId = contactId;
 	}
 
 	void write() throws DbException, IOException {
-		byte[] secret = db.getSharedSecret(contactId);
-		ConnectionWriter conn = connFactory.createConnectionWriter(
-				trans.getOutputStream(), true, transportId, connection, secret);
 		OutputStream out = conn.getOutputStream();
 		// There should be enough space for a packet
-		long capacity = conn.getCapacity(trans.getCapacity());
+		long capacity = conn.getCapacity();
 		if(capacity < MAX_PACKET_LENGTH) throw new IOException();
 		// Write a transport update
 		TransportWriter t = protoFactory.createTransportWriter(out);
 		db.generateTransportUpdate(contactId, t);
 		// If there's space, write a subscription update
-		capacity = conn.getCapacity(trans.getCapacity());
+		capacity = conn.getCapacity();
 		if(capacity >= MAX_PACKET_LENGTH) {
 			SubscriptionWriter s = protoFactory.createSubscriptionWriter(out);
 			db.generateSubscriptionUpdate(contactId, s);
@@ -60,14 +47,14 @@ class OutgoingBatchConnection {
 		// Write acks until you can't write acks no more
 		AckWriter a = protoFactory.createAckWriter(out);
 		do {
-			capacity = conn.getCapacity(trans.getCapacity());
+			capacity = conn.getCapacity();
 			int max = (int) Math.min(MAX_PACKET_LENGTH, capacity);
 			a.setMaxPacketLength(max);
 		} while(db.generateAck(contactId, a));
 		// Write batches until you can't write batches no more
 		BatchWriter b = protoFactory.createBatchWriter(out);
 		do {
-			capacity = conn.getCapacity(trans.getCapacity());
+			capacity = conn.getCapacity();
 			int max = (int) Math.min(MAX_PACKET_LENGTH, capacity);
 			b.setMaxPacketLength(max);
 		} while(db.generateBatch(contactId, b));
diff --git a/test/net/sf/briar/FileReadWriteTest.java b/test/net/sf/briar/FileReadWriteTest.java
index c50f1d4535..0b1e5878f1 100644
--- a/test/net/sf/briar/FileReadWriteTest.java
+++ b/test/net/sf/briar/FileReadWriteTest.java
@@ -46,7 +46,7 @@ import net.sf.briar.api.transport.ConnectionWriter;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
 import net.sf.briar.crypto.CryptoModule;
 import net.sf.briar.protocol.ProtocolModule;
-import net.sf.briar.protocol.writers.WritersModule;
+import net.sf.briar.protocol.writers.ProtocolWritersModule;
 import net.sf.briar.serial.SerialModule;
 import net.sf.briar.transport.TransportModule;
 
@@ -83,8 +83,8 @@ public class FileReadWriteTest extends TestCase {
 	public FileReadWriteTest() throws Exception {
 		super();
 		Injector i = Guice.createInjector(new CryptoModule(),
-				new ProtocolModule(), new SerialModule(), new TransportModule(),
-				new WritersModule());
+				new ProtocolModule(), new ProtocolWritersModule(),
+				new SerialModule(), new TransportModule());
 		connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class);
 		connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class);
 		protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class);
@@ -132,7 +132,7 @@ public class FileReadWriteTest extends TestCase {
 		OutputStream out = new FileOutputStream(file);
 		// Use Alice's secret for writing
 		ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out,
-				true, transportId, connection, aliceSecret);
+				Long.MAX_VALUE, true, transportId, connection, aliceSecret);
 		out = w.getOutputStream();
 
 		AckWriter a = protocolWriterFactory.createAckWriter(out);
diff --git a/test/net/sf/briar/protocol/ProtocolReadWriteTest.java b/test/net/sf/briar/protocol/ProtocolReadWriteTest.java
index ad5a7981c0..3ae4e99793 100644
--- a/test/net/sf/briar/protocol/ProtocolReadWriteTest.java
+++ b/test/net/sf/briar/protocol/ProtocolReadWriteTest.java
@@ -29,7 +29,7 @@ import net.sf.briar.api.protocol.writers.RequestWriter;
 import net.sf.briar.api.protocol.writers.SubscriptionWriter;
 import net.sf.briar.api.protocol.writers.TransportWriter;
 import net.sf.briar.crypto.CryptoModule;
-import net.sf.briar.protocol.writers.WritersModule;
+import net.sf.briar.protocol.writers.ProtocolWritersModule;
 import net.sf.briar.serial.SerialModule;
 
 import org.junit.Test;
@@ -53,7 +53,8 @@ public class ProtocolReadWriteTest extends TestCase {
 	public ProtocolReadWriteTest() throws Exception {
 		super();
 		Injector i = Guice.createInjector(new CryptoModule(),
-				new ProtocolModule(), new SerialModule(), new WritersModule());
+				new ProtocolModule(), new ProtocolWritersModule(),
+				new SerialModule());
 		readerFactory = i.getInstance(ProtocolReaderFactory.class);
 		writerFactory = i.getInstance(ProtocolWriterFactory.class);
 		batchId = new BatchId(TestUtils.getRandomId());
diff --git a/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java b/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java
index 9121b6b381..b266b8f2d9 100644
--- a/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java
@@ -40,12 +40,14 @@ public class ConnectionEncrypterImplTest extends TestCase {
 	@Test
 	public void testSingleByteFrame() throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		ConnectionEncrypter e = new ConnectionEncrypterImpl(out, true,
-				transportId, connection, ivCipher, frameCipher, ivKey,
+		ConnectionEncrypter e = new ConnectionEncrypterImpl(out, Long.MAX_VALUE,
+				true, transportId, connection, ivCipher, frameCipher, ivKey,
 				frameKey);
 		e.getOutputStream().write((byte) 0);
 		e.writeMac(new byte[MAC_LENGTH]);
-		assertEquals(IV_LENGTH + 1 + MAC_LENGTH, out.toByteArray().length);
+		byte[] ciphertext = out.toByteArray();
+		assertEquals(IV_LENGTH + 1 + MAC_LENGTH, ciphertext.length);
+		assertEquals(Long.MAX_VALUE - ciphertext.length, e.getCapacity());
 	}
 
 	@Test
@@ -93,9 +95,9 @@ public class ConnectionEncrypterImplTest extends TestCase {
 		byte[] expected = out.toByteArray();
 		// Use a ConnectionEncrypter to encrypt the plaintext
 		out.reset();
-		ConnectionEncrypter e = new ConnectionEncrypterImpl(out, initiator,
-				transportId, connection, ivCipher, frameCipher, ivKey,
-				frameKey);
+		ConnectionEncrypter e = new ConnectionEncrypterImpl(out, Long.MAX_VALUE,
+				initiator, transportId, connection, ivCipher, frameCipher,
+				ivKey, frameKey);
 		e.getOutputStream().write(plaintext);
 		e.writeMac(plaintextMac);
 		e.getOutputStream().write(plaintext1);
@@ -103,5 +105,6 @@ public class ConnectionEncrypterImplTest extends TestCase {
 		byte[] actual = out.toByteArray();
 		// Check that the actual ciphertext matches the expected ciphertext
 		assertTrue(Arrays.equals(expected, actual));
+		assertEquals(Long.MAX_VALUE - actual.length, e.getCapacity());
 	}
 }
diff --git a/test/net/sf/briar/transport/ConnectionWriterImplTest.java b/test/net/sf/briar/transport/ConnectionWriterImplTest.java
index 9d7794a797..985c962b0f 100644
--- a/test/net/sf/briar/transport/ConnectionWriterImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionWriterImplTest.java
@@ -100,32 +100,4 @@ public class ConnectionWriterImplTest extends TransportTest {
 		byte[] actual = out.toByteArray();
 		assertTrue(Arrays.equals(expected, actual));
 	}
-
-	@Test
-	public void testGetCapacity() throws Exception {
-		int overheadPerFrame = headerLength + macLength;
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		ConnectionEncrypter e = new NullConnectionEncrypter(out);
-		ConnectionWriterImpl w = new ConnectionWriterImpl(e, mac);
-		// Full frame
-		long capacity = w.getCapacity(MAX_FRAME_LENGTH);
-		assertEquals(MAX_FRAME_LENGTH - overheadPerFrame, capacity);
-		// Partial frame
-		capacity = w.getCapacity(overheadPerFrame + 1);
-		assertEquals(1, capacity);
-		// Full frame and partial frame
-		capacity = w.getCapacity(MAX_FRAME_LENGTH + 1);
-		assertEquals(MAX_FRAME_LENGTH + 1 - 2 * overheadPerFrame, capacity);
-		// Buffer some output
-		w.getOutputStream().write(0);
-		// Full frame minus buffered frame
-		capacity = w.getCapacity(MAX_FRAME_LENGTH);
-		assertEquals(MAX_FRAME_LENGTH - 1 - 2 * overheadPerFrame, capacity);
-		// Flush the buffer
-		w.flush();
-		assertEquals(1 + overheadPerFrame, out.size());
-		// Back to square one
-		capacity = w.getCapacity(MAX_FRAME_LENGTH);
-		assertEquals(MAX_FRAME_LENGTH - overheadPerFrame, capacity);
-	}
 }
diff --git a/test/net/sf/briar/transport/ConnectionWriterTest.java b/test/net/sf/briar/transport/ConnectionWriterTest.java
index aa1487edae..91b60d6133 100644
--- a/test/net/sf/briar/transport/ConnectionWriterTest.java
+++ b/test/net/sf/briar/transport/ConnectionWriterTest.java
@@ -1,12 +1,13 @@
 package net.sf.briar.transport;
 
+import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
+import static net.sf.briar.api.transport.TransportConstants.MIN_CONNECTION_LENGTH;
+
 import java.io.ByteArrayOutputStream;
 
 import junit.framework.TestCase;
-import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.transport.ConnectionWriter;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
-import net.sf.briar.api.transport.TransportConstants;
 import net.sf.briar.crypto.CryptoModule;
 
 import org.junit.Test;
@@ -30,20 +31,20 @@ public class ConnectionWriterTest extends TestCase {
 
 	@Test
 	public void testOverhead() throws Exception {
-		ByteArrayOutputStream out = new ByteArrayOutputStream(
-				TransportConstants.MIN_CONNECTION_LENGTH);
+		ByteArrayOutputStream out =
+			new ByteArrayOutputStream(MIN_CONNECTION_LENGTH);
 		ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out,
-				true, transportId, connection, secret);
+				MIN_CONNECTION_LENGTH, true, transportId, connection, secret);
 		// Check that the connection writer thinks there's room for a packet
-		long capacity = w.getCapacity(TransportConstants.MIN_CONNECTION_LENGTH);
-		assertTrue(capacity >= ProtocolConstants.MAX_PACKET_LENGTH);
-		assertTrue(capacity <= TransportConstants.MIN_CONNECTION_LENGTH);
+		long capacity = w.getCapacity();
+		assertTrue(capacity >= MAX_PACKET_LENGTH);
+		assertTrue(capacity <= MIN_CONNECTION_LENGTH);
 		// Check that there really is room for a packet
-		byte[] payload = new byte[ProtocolConstants.MAX_PACKET_LENGTH];
+		byte[] payload = new byte[MAX_PACKET_LENGTH];
 		w.getOutputStream().write(payload);
 		w.getOutputStream().flush();
 		long used = out.size();
-		assertTrue(used >= ProtocolConstants.MAX_PACKET_LENGTH);
-		assertTrue(used <= TransportConstants.MIN_CONNECTION_LENGTH);
+		assertTrue(used >= MAX_PACKET_LENGTH);
+		assertTrue(used <= MIN_CONNECTION_LENGTH);
 	}
 }
diff --git a/test/net/sf/briar/transport/FrameReadWriteTest.java b/test/net/sf/briar/transport/FrameReadWriteTest.java
index f676f838cc..4418ece226 100644
--- a/test/net/sf/briar/transport/FrameReadWriteTest.java
+++ b/test/net/sf/briar/transport/FrameReadWriteTest.java
@@ -74,8 +74,8 @@ public class FrameReadWriteTest extends TestCase {
 		// Write the frames
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
-				initiator, transportId, connection, ivCipher, frameCipher,
-				ivKey, frameKey);
+				Long.MAX_VALUE, initiator, transportId, connection, ivCipher,
+				frameCipher, ivKey, frameKey);
 		mac.init(macKey);
 		ConnectionWriter writer = new ConnectionWriterImpl(encrypter, mac);
 		OutputStream out1 = writer.getOutputStream();
diff --git a/test/net/sf/briar/transport/NullConnectionEncrypter.java b/test/net/sf/briar/transport/NullConnectionEncrypter.java
index 7ba7b9d7a5..de5744d68e 100644
--- a/test/net/sf/briar/transport/NullConnectionEncrypter.java
+++ b/test/net/sf/briar/transport/NullConnectionEncrypter.java
@@ -1,26 +1,51 @@
 package net.sf.briar.transport;
 
+import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 
 /** A ConnectionEncrypter that performs no encryption. */
-class NullConnectionEncrypter implements ConnectionEncrypter {
+class NullConnectionEncrypter extends FilterOutputStream
+implements ConnectionEncrypter {
 
-	private final OutputStream out;
+	private long capacity;
 
 	NullConnectionEncrypter(OutputStream out) {
-		this.out = out;
+		this(out, Long.MAX_VALUE);
+	}
+
+	NullConnectionEncrypter(OutputStream out, long capacity) {
+		super(out);
+		this.capacity = capacity;
 	}
 
 	public OutputStream getOutputStream() {
-		return out;
+		return this;
 	}
 
 	public void writeMac(byte[] mac) throws IOException {
 		out.write(mac);
+		capacity -= mac.length;
 	}
 
-	public long getCapacity(long capacity) {
+	public long getCapacity() {
 		return capacity;
 	}
+
+	@Override
+	public void write(int b) throws IOException {
+		out.write(b);
+		capacity--;
+	}
+
+	@Override
+	public void write(byte[] b) throws IOException {
+		write(b, 0, b.length);
+	}
+
+	@Override
+	public void write(byte[] b, int off, int len) throws IOException {
+		out.write(b, off, len);
+		capacity -= len;
+	}
 }
diff --git a/test/net/sf/briar/transport/PaddedConnectionWriterTest.java b/test/net/sf/briar/transport/PaddedConnectionWriterTest.java
index 90efc10499..11c5a4ee22 100644
--- a/test/net/sf/briar/transport/PaddedConnectionWriterTest.java
+++ b/test/net/sf/briar/transport/PaddedConnectionWriterTest.java
@@ -23,7 +23,7 @@ public class PaddedConnectionWriterTest extends TransportTest {
 	@Test
 	public void testWriteByteDoesNotBlockUntilBufferIsFull() throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		ConnectionEncrypter e = new NullConnectionEncrypter(out);
+		ConnectionEncrypter e = new NullConnectionEncrypter(out, Long.MAX_VALUE);
 		ConnectionWriter w = new PaddedConnectionWriter(e, mac);
 		final OutputStream out1 = w.getOutputStream();
 		final CountDownLatch latch = new CountDownLatch(1);
@@ -52,7 +52,7 @@ public class PaddedConnectionWriterTest extends TransportTest {
 	@Test
 	public void testWriteByteBlocksWhenBufferIsFull() throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		ConnectionEncrypter e = new NullConnectionEncrypter(out);
+		ConnectionEncrypter e = new NullConnectionEncrypter(out, Long.MAX_VALUE);
 		PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac);
 		final OutputStream out1 = w.getOutputStream();
 		final CountDownLatch latch = new CountDownLatch(1);
@@ -86,7 +86,7 @@ public class PaddedConnectionWriterTest extends TransportTest {
 	@Test
 	public void testWriteArrayDoesNotBlockUntilBufferIsFull() throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		ConnectionEncrypter e = new NullConnectionEncrypter(out);
+		ConnectionEncrypter e = new NullConnectionEncrypter(out, Long.MAX_VALUE);
 		ConnectionWriter w = new PaddedConnectionWriter(e, mac);
 		final OutputStream out1 = w.getOutputStream();
 		final CountDownLatch latch = new CountDownLatch(1);
@@ -115,7 +115,7 @@ public class PaddedConnectionWriterTest extends TransportTest {
 	@Test
 	public void testWriteArrayBlocksWhenBufferIsFull() throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		ConnectionEncrypter e = new NullConnectionEncrypter(out);
+		ConnectionEncrypter e = new NullConnectionEncrypter(out, Long.MAX_VALUE);
 		PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac);
 		final OutputStream out1 = w.getOutputStream();
 		final CountDownLatch latch = new CountDownLatch(1);
@@ -149,7 +149,7 @@ public class PaddedConnectionWriterTest extends TransportTest {
 	@Test
 	public void testWriteFullFrameInsertsPadding() throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		ConnectionEncrypter e = new NullConnectionEncrypter(out);
+		ConnectionEncrypter e = new NullConnectionEncrypter(out, Long.MAX_VALUE);
 		PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac);
 		w.getOutputStream().write(0);
 		w.writeFullFrame();
@@ -160,32 +160,4 @@ public class PaddedConnectionWriterTest extends TransportTest {
 		assertEquals(1, ByteUtils.readUint16(frame, 0)); // Payload length
 		assertEquals(maxPayloadLength - 1, ByteUtils.readUint16(frame, 2));
 	}
-
-	@Test
-	public void testGetCapacity() throws Exception {
-		int overheadPerFrame = headerLength + macLength;
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		ConnectionEncrypter e = new NullConnectionEncrypter(out);
-		PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac);
-		// Full frame
-		long capacity = w.getCapacity(MAX_FRAME_LENGTH);
-		assertEquals(MAX_FRAME_LENGTH - overheadPerFrame, capacity);
-		// Partial frame
-		capacity = w.getCapacity(overheadPerFrame + 1);
-		assertEquals(1, capacity);
-		// Full frame and partial frame
-		capacity = w.getCapacity(MAX_FRAME_LENGTH + 1);
-		assertEquals(MAX_FRAME_LENGTH + 1 - 2 * overheadPerFrame, capacity);
-		// Buffer some output
-		w.getOutputStream().write(0);
-		// Full frame minus buffered frame
-		capacity = w.getCapacity(MAX_FRAME_LENGTH);
-		assertEquals(MAX_FRAME_LENGTH - 1 - 2 * overheadPerFrame, capacity);
-		// Flush the buffer
-		w.writeFullFrame();
-		assertEquals(MAX_FRAME_LENGTH, out.size());
-		// Back to square one
-		capacity = w.getCapacity(MAX_FRAME_LENGTH);
-		assertEquals(MAX_FRAME_LENGTH - overheadPerFrame, capacity);
-	}
 }
diff --git a/test/net/sf/briar/transport/batch/TestBatchTransportReader.java b/test/net/sf/briar/transport/batch/TestBatchTransportReader.java
index 9dbd2f4a6d..0ecc5cf0de 100644
--- a/test/net/sf/briar/transport/batch/TestBatchTransportReader.java
+++ b/test/net/sf/briar/transport/batch/TestBatchTransportReader.java
@@ -13,7 +13,7 @@ implements BatchTransportReader {
 		super(in);
 	}
 
-	public InputStream getInputStream() throws IOException {
+	public InputStream getInputStream() {
 		return this;
 	}
 
diff --git a/test/net/sf/briar/transport/batch/TestBatchTransportWriter.java b/test/net/sf/briar/transport/batch/TestBatchTransportWriter.java
index 949b1331b0..c0ff7c54fe 100644
--- a/test/net/sf/briar/transport/batch/TestBatchTransportWriter.java
+++ b/test/net/sf/briar/transport/batch/TestBatchTransportWriter.java
@@ -16,11 +16,11 @@ implements BatchTransportWriter {
 		this.capacity = capacity;
 	}
 
-	public long getCapacity() throws IOException {
+	public long getCapacity() {
 		return capacity;
 	}
 
-	public OutputStream getOutputStream() throws IOException {
+	public OutputStream getOutputStream() {
 		return this;
 	}
 
-- 
GitLab