diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/Priority.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/Priority.java new file mode 100644 index 000000000..44a2169b7 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/Priority.java @@ -0,0 +1,23 @@ +package org.briarproject.bramble.api.sync; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.concurrent.Immutable; + +/** + * A record containing a nonce for choosing between redundant sessions. + */ +@Immutable +@NotNullByDefault +public class Priority { + + private final byte[] nonce; + + public Priority(byte[] nonce) { + this.nonce = nonce; + } + + public byte[] getNonce() { + return nonce; + } +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/RecordTypes.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/RecordTypes.java index 168b4c9ef..7fdbe0af7 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/RecordTypes.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/RecordTypes.java @@ -10,4 +10,5 @@ public interface RecordTypes { byte OFFER = 2; byte REQUEST = 3; byte VERSIONS = 4; + byte PRIORITY = 5; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncConstants.java index c430ac05f..2e9241bed 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncConstants.java @@ -49,4 +49,10 @@ public interface SyncConstants { * simultaneously. */ int MAX_SUPPORTED_VERSIONS = 10; + + /** + * The length of the priority nonce used for choosing between redundant + * connections. + */ + int PRIORITY_NONCE_BYTES = 16; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncRecordReader.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncRecordReader.java index 55650e407..c0f9b947a 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncRecordReader.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncRecordReader.java @@ -28,4 +28,8 @@ public interface SyncRecordReader { boolean hasVersions() throws IOException; Versions readVersions() throws IOException; + + boolean hasPriority() throws IOException; + + Priority readPriority() throws IOException; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncRecordWriter.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncRecordWriter.java index bdeca0cf8..75d4b8401 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncRecordWriter.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/SyncRecordWriter.java @@ -17,5 +17,7 @@ public interface SyncRecordWriter { void writeVersions(Versions v) throws IOException; + void writePriority(Priority p) throws IOException; + void flush() throws IOException; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordReaderImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordReaderImpl.java index 7ea0f6a41..8d8857460 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordReaderImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordReaderImpl.java @@ -11,6 +11,7 @@ import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.Offer; +import org.briarproject.bramble.api.sync.Priority; import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.SyncRecordReader; import org.briarproject.bramble.api.sync.Versions; @@ -26,10 +27,12 @@ import javax.annotation.concurrent.NotThreadSafe; import static org.briarproject.bramble.api.sync.RecordTypes.ACK; import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE; import static org.briarproject.bramble.api.sync.RecordTypes.OFFER; +import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY; import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST; import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS; import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; +import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES; import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION; @NotThreadSafe @@ -48,7 +51,7 @@ class SyncRecordReaderImpl implements SyncRecordReader { private static boolean isKnownRecordType(byte type) { return type == ACK || type == MESSAGE || type == OFFER || - type == REQUEST || type == VERSIONS; + type == REQUEST || type == VERSIONS || type == PRIORITY; } private final MessageFactory messageFactory; @@ -174,4 +177,23 @@ class SyncRecordReaderImpl implements SyncRecordReader { nextRecord = null; return supported; } + + @Override + public boolean hasPriority() throws IOException { + return !eof() && getNextRecordType() == PRIORITY; + } + + @Override + public Priority readPriority() throws IOException { + if (!hasPriority()) throw new FormatException(); + return new Priority(readNonce()); + } + + private byte[] readNonce() throws IOException { + if (nextRecord == null) throw new AssertionError(); + byte[] payload = nextRecord.getPayload(); + if (payload.length != PRIORITY_NONCE_BYTES) throw new FormatException(); + nextRecord = null; + return payload; + } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterImpl.java index 118cc51cd..d2b4e73f1 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterImpl.java @@ -8,6 +8,7 @@ import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.Offer; +import org.briarproject.bramble.api.sync.Priority; import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.SyncRecordWriter; import org.briarproject.bramble.api.sync.Versions; @@ -20,6 +21,7 @@ import javax.annotation.concurrent.NotThreadSafe; import static org.briarproject.bramble.api.sync.RecordTypes.ACK; import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE; import static org.briarproject.bramble.api.sync.RecordTypes.OFFER; +import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY; import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST; import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS; import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION; @@ -73,6 +75,12 @@ class SyncRecordWriterImpl implements SyncRecordWriter { writeRecord(VERSIONS); } + @Override + public void writePriority(Priority p) throws IOException { + writer.writeRecord( + new Record(PROTOCOL_VERSION, PRIORITY, p.getNonce())); + } + @Override public void flush() throws IOException { writer.flush(); diff --git a/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncRecordReaderImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncRecordReaderImplTest.java index 667c2b557..740451837 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncRecordReaderImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncRecordReaderImplTest.java @@ -8,6 +8,7 @@ import org.briarproject.bramble.api.record.RecordReader; import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.Offer; +import org.briarproject.bramble.api.sync.Priority; import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.SyncRecordReader; import org.briarproject.bramble.api.sync.Versions; @@ -24,11 +25,14 @@ import javax.annotation.Nullable; import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES; import static org.briarproject.bramble.api.sync.RecordTypes.ACK; import static org.briarproject.bramble.api.sync.RecordTypes.OFFER; +import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY; import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST; import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS; +import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES; import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION; +import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -119,6 +123,31 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase { reader.readVersions(); } + @Test(expected = FormatException.class) + public void testFormatExceptionIfPriorityNonceIsTooSmall() + throws Exception { + expectReadRecord(createPriority(PRIORITY_NONCE_BYTES - 1)); + + reader.readPriority(); + } + + @Test(expected = FormatException.class) + public void testFormatExceptionIfPriorityNonceIsTooLarge() + throws Exception { + expectReadRecord(createPriority(PRIORITY_NONCE_BYTES + 1)); + + reader.readPriority(); + } + + @Test + public void testNoFormatExceptionIfPriorityNonceIsCorrectSize() + throws Exception { + expectReadRecord(createPriority(PRIORITY_NONCE_BYTES)); + + Priority priority = reader.readPriority(); + assertEquals(PRIORITY_NONCE_BYTES, priority.getNonce().length); + } + @Test public void testEofReturnsTrueWhenAtEndOfStream() throws Exception { expectReadRecord(createAck()); @@ -173,6 +202,11 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase { return new Record(PROTOCOL_VERSION, VERSIONS, payload); } + private Record createPriority(int nonceBytes) { + byte[] payload = getRandomBytes(nonceBytes); + return new Record(PROTOCOL_VERSION, PRIORITY, payload); + } + private byte[] createPayload() throws Exception { ByteArrayOutputStream payload = new ByteArrayOutputStream(); while (payload.size() + UniqueId.LENGTH <= MAX_RECORD_PAYLOAD_BYTES) {