The response to a BMP Offer is now an Ack and/or a Request.

The Request packet now contains a list of message IDs, rather than a
bitmap referring to the list of messages IDs in the Offer. This allows
the Request to be understood out of context, e.g. if the Offer and
Request are sent over separate connections or a connection is replayed.
This commit is contained in:
akwizgran
2013-11-19 22:13:26 +00:00
parent 2e472c1d16
commit 1a351535be
12 changed files with 128 additions and 302 deletions

View File

@@ -9,7 +9,6 @@ import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Random;
@@ -138,9 +137,7 @@ public class ProtocolIntegrationTest extends BriarTestCase {
writer.writeOffer(new Offer(messageIds));
BitSet requested = new BitSet(2);
requested.set(1);
writer.writeRequest(new Request(requested, 2));
writer.writeRequest(new Request(messageIds));
SubscriptionUpdate su = new SubscriptionUpdate(Arrays.asList(group), 1);
writer.writeSubscriptionUpdate(su);
@@ -187,11 +184,7 @@ public class ProtocolIntegrationTest extends BriarTestCase {
// Read the request
assertTrue(reader.hasRequest());
Request req = reader.readRequest();
BitSet requested = req.getBitmap();
assertFalse(requested.get(0));
assertTrue(requested.get(1));
// If there are any padding bits, they should all be zero
assertEquals(1, requested.cardinality());
assertEquals(messageIds, req.getMessageIds());
// Read the subscription update
assertTrue(reader.hasSubscriptionUpdate());

View File

@@ -22,6 +22,7 @@ import net.sf.briar.api.LocalAuthor;
import net.sf.briar.api.TransportConfig;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.db.AckAndRequest;
import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.NoSuchContactException;
import net.sf.briar.api.db.NoSuchSubscriptionException;
@@ -1077,7 +1078,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId);
will(returnValue(false)); // Not visible - request message # 0
oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId1);
will(returnValue(true)); // Visible - do not request message # 1
will(returnValue(true)); // Visible - ack message # 1
oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId2);
will(returnValue(false)); // Not visible - request message # 2
}});
@@ -1085,9 +1086,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
shutdown);
Offer o = new Offer(Arrays.asList(messageId, messageId1, messageId2));
Request r = db.receiveOffer(contactId, o);
assertEquals(expectedRequest, r.getBitmap());
assertEquals(3, r.getLength());
AckAndRequest ar = db.receiveOffer(contactId, o);
Ack a = ar.getAck();
assertNotNull(a);
assertEquals(Arrays.asList(messageId1), a.getMessageIds());
Request r = ar.getRequest();
assertNotNull(r);
assertEquals(Arrays.asList(messageId, messageId2), r.getMessageIds());
context.assertIsSatisfied();
}

View File

@@ -7,12 +7,10 @@ import static net.sf.briar.api.messaging.Types.REQUEST;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.BitSet;
import net.sf.briar.BriarTestCase;
import net.sf.briar.TestUtils;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.messaging.Request;
import net.sf.briar.api.serial.ReaderFactory;
import net.sf.briar.api.serial.SerialComponent;
import net.sf.briar.api.serial.Writer;
@@ -127,39 +125,15 @@ public class PacketReaderImplTest extends BriarTestCase {
}
@Test
public void testBitmapDecoding() throws Exception {
// Test sizes from 0 to 1000 bits
for(int i = 0; i < 1000; i++) {
// Create a BitSet of size i with one in ten bits set (on average)
BitSet requested = new BitSet(i);
for(int j = 0; j < i; j++) if(Math.random() < 0.1) requested.set(j);
// Encode the BitSet as a bitmap
int bytes = i % 8 == 0 ? i / 8 : i / 8 + 1;
byte[] bitmap = new byte[bytes];
for(int j = 0; j < i; j++) {
if(requested.get(j)) {
int offset = j / 8;
byte bit = (byte) (128 >> j % 8);
bitmap[offset] |= bit;
}
}
// Create a serialised request containing the bitmap
byte[] b = createRequest(bitmap);
// Deserialise the request
ByteArrayInputStream in = new ByteArrayInputStream(b);
PacketReaderImpl reader = new PacketReaderImpl(readerFactory,
null, null, in);
Request request = reader.readRequest();
BitSet decoded = request.getBitmap();
// Check that the decoded BitSet matches the original - we can't
// use equals() because of padding, but the first i bits should
// match and the cardinalities should be equal, indicating that no
// padding bits are set
for(int j = 0; j < i; j++) {
assertEquals(requested.get(j), decoded.get(j));
}
assertEquals(requested.cardinality(), decoded.cardinality());
}
public void testEmptyRequest() throws Exception {
byte[] b = createEmptyRequest();
ByteArrayInputStream in = new ByteArrayInputStream(b);
PacketReaderImpl reader = new PacketReaderImpl(readerFactory, null,
null, in);
try {
reader.readRequest();
fail();
} catch(FormatException expected) {}
}
private byte[] createAck(boolean tooBig) throws Exception {
@@ -222,26 +196,26 @@ public class PacketReaderImplTest extends BriarTestCase {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out);
w.writeStructStart(REQUEST);
// Allow one byte for the STRUCT tag, one byte for the struct ID,
// one byte for the padding length as a uint7, one byte for the BYTES
// tag, five bytes for the length of the byte array as an int32, and
// one byte for the END tag
int size = MAX_PACKET_LENGTH - 10;
if(tooBig) size++;
assertTrue(size > Short.MAX_VALUE);
w.writeUint7((byte) 0);
w.writeBytes(new byte[size]);
w.writeListStart();
while(out.size() + serial.getSerialisedUniqueIdLength()
+ serial.getSerialisedListEndLength()
+ serial.getSerialisedStructEndLength()
< MAX_PACKET_LENGTH) {
w.writeBytes(TestUtils.getRandomId());
}
if(tooBig) w.writeBytes(TestUtils.getRandomId());
w.writeListEnd();
w.writeStructEnd();
assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH);
return out.toByteArray();
}
private byte[] createRequest(byte[] bitmap) throws Exception {
private byte[] createEmptyRequest() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out);
w.writeStructStart(REQUEST);
w.writeUint7((byte) 0);
w.writeBytes(bitmap);
w.writeListStart();
w.writeListEnd();
w.writeStructEnd();
return out.toByteArray();
}

View File

@@ -1,93 +0,0 @@
package net.sf.briar.messaging;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.BitSet;
import net.sf.briar.BriarTestCase;
import net.sf.briar.TestDatabaseModule;
import net.sf.briar.TestLifecycleModule;
import net.sf.briar.api.messaging.PacketWriter;
import net.sf.briar.api.messaging.Request;
import net.sf.briar.api.serial.SerialComponent;
import net.sf.briar.api.serial.WriterFactory;
import net.sf.briar.clock.ClockModule;
import net.sf.briar.crypto.CryptoModule;
import net.sf.briar.db.DatabaseModule;
import net.sf.briar.messaging.duplex.DuplexMessagingModule;
import net.sf.briar.messaging.simplex.SimplexMessagingModule;
import net.sf.briar.serial.SerialModule;
import net.sf.briar.transport.TransportModule;
import net.sf.briar.util.StringUtils;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class PacketWriterImplTest extends BriarTestCase {
// FIXME: This is an integration test, not a unit test
private final SerialComponent serial;
private final WriterFactory writerFactory;
public PacketWriterImplTest() {
Injector i = Guice.createInjector(new TestDatabaseModule(),
new TestLifecycleModule(), new ClockModule(),
new CryptoModule(), new DatabaseModule(), new MessagingModule(),
new DuplexMessagingModule(), new SimplexMessagingModule(),
new SerialModule(), new TransportModule());
serial = i.getInstance(SerialComponent.class);
writerFactory = i.getInstance(WriterFactory.class);
}
@Test
public void testWriteBitmapNoPadding() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PacketWriter w = new PacketWriterImpl(serial, writerFactory, out,
true);
BitSet b = new BitSet();
// 11011001 = 0xD9
b.set(0);
b.set(1);
b.set(3);
b.set(4);
b.set(7);
// 01011001 = 0x59
b.set(9);
b.set(11);
b.set(12);
b.set(15);
w.writeRequest(new Request(b, 16));
// STRUCT tag, struct ID 5, 0 as uint7, BYTES tag, length 2 as uint7,
// bitmap 0xD959, END tag
byte[] output = out.toByteArray();
assertEquals("F3" + "05" + "00" + "F6" + "02" + "D959" + "F2",
StringUtils.toHexString(output));
}
@Test
public void testWriteBitmapWithPadding() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PacketWriter w = new PacketWriterImpl(serial, writerFactory, out,
true);
BitSet b = new BitSet();
// 01011001 = 0x59
b.set(1);
b.set(3);
b.set(4);
b.set(7);
// 11011xxx = 0xD8, after padding
b.set(8);
b.set(9);
b.set(11);
b.set(12);
w.writeRequest(new Request(b, 13));
// STRUCT tag, struct ID 5, 3 as uint7, BYTES tag, length 2 as uint7,
// bitmap 0x59D8, END tag
byte[] output = out.toByteArray();
assertEquals("F3" + "05" + "03" + "F6" + "02" + "59D8" + "F2",
StringUtils.toHexString(output));
}
}