mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Protocol refactoring. Each bundle now consists of a signed header and zero or more signed batches. There is no overall signature on the bundle, since the bundle's contents may need to be processed before the entire bundle has been read. The protocol does not prevent an adversary from removing batches from a bundle, reordering batches, moving them from one bundle to another, etc. However, since each batch is signed and acknowledged independently, no such guarantees are required. Bundle IDs will go away when the retransmission mechanism is changed.
This commit is contained in:
@@ -10,6 +10,7 @@ import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.Status;
|
||||
import net.sf.briar.api.protocol.AuthorId;
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
|
||||
@@ -27,6 +28,7 @@ DatabaseCleaner.Callback {
|
||||
|
||||
protected final Database<Txn> db;
|
||||
protected final DatabaseCleaner cleaner;
|
||||
protected final Provider<HeaderBuilder> headerBuilderProvider;
|
||||
protected final Provider<BatchBuilder> batchBuilderProvider;
|
||||
|
||||
private final Object spaceLock = new Object();
|
||||
@@ -36,9 +38,11 @@ DatabaseCleaner.Callback {
|
||||
private volatile boolean writesAllowed = true;
|
||||
|
||||
DatabaseComponentImpl(Database<Txn> db, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
this.db = db;
|
||||
this.cleaner = cleaner;
|
||||
this.headerBuilderProvider = headerBuilderProvider;
|
||||
this.batchBuilderProvider = batchBuilderProvider;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.logging.Level;
|
||||
@@ -20,6 +21,8 @@ import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.Bundle;
|
||||
import net.sf.briar.api.protocol.BundleBuilder;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
|
||||
@@ -55,8 +58,9 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
|
||||
@Inject
|
||||
ReadWriteLockDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
super(db, cleaner, batchBuilderProvider);
|
||||
super(db, cleaner, headerBuilderProvider, batchBuilderProvider);
|
||||
}
|
||||
|
||||
public void close() throws DbException {
|
||||
@@ -187,23 +191,22 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
|
||||
public Bundle generateBundle(ContactId c, BundleBuilder b)
|
||||
throws DbException {
|
||||
throws DbException, IOException, SignatureException {
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
||||
// Ack all batches received from c
|
||||
HeaderBuilder h;
|
||||
// Add acks
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
h = headerBuilderProvider.get();
|
||||
messageStatusLock.writeLock().lock();
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
int numAcks = 0;
|
||||
for(BatchId ack : db.removeBatchesToAck(txn, c)) {
|
||||
b.addAck(ack);
|
||||
numAcks++;
|
||||
}
|
||||
Set<BatchId> acks = db.removeBatchesToAck(txn, c);
|
||||
h.addAcks(acks);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + numAcks + " acks");
|
||||
LOG.fine("Added " + acks.size() + " acks");
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -215,7 +218,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
} finally {
|
||||
contactLock.readLock().unlock();
|
||||
}
|
||||
// Add a list of subscriptions
|
||||
// Add subscriptions
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
@@ -223,13 +226,10 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
int numSubs = 0;
|
||||
for(GroupId g : db.getSubscriptions(txn)) {
|
||||
b.addSubscription(g);
|
||||
numSubs++;
|
||||
}
|
||||
Set<GroupId> subs = db.getSubscriptions(txn);
|
||||
h.addSubscriptions(subs);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + numSubs + " subscriptions");
|
||||
LOG.fine("Added " + subs.size() + " subscriptions");
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -249,14 +249,10 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
int numTransports = 0;
|
||||
Map<String, String> transports = db.getTransports(txn);
|
||||
for(Entry<String, String> e : transports.entrySet()) {
|
||||
b.addTransport(e.getKey(), e.getValue());
|
||||
numTransports++;
|
||||
}
|
||||
h.addTransports(transports);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + numTransports + " transports");
|
||||
LOG.fine("Added " + transports.size() + " transports");
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -268,8 +264,12 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
} finally {
|
||||
contactLock.readLock().unlock();
|
||||
}
|
||||
// Add as many messages as possible to the bundle
|
||||
// Sign the header and add it to the bundle
|
||||
Header header = h.build();
|
||||
long capacity = b.getCapacity();
|
||||
capacity -= header.getSize();
|
||||
b.addHeader(header);
|
||||
// Add as many messages as possible to the bundle
|
||||
while(true) {
|
||||
Batch batch = fillBatch(c, capacity);
|
||||
if(batch == null) break; // No more messages to send
|
||||
@@ -278,7 +278,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
capacity -= size;
|
||||
// If the batch is less than half full, stop trying - there may be
|
||||
// more messages trickling in but we can't wait forever
|
||||
if(size * 2 < Batch.CAPACITY) break;
|
||||
if(size * 2 < Batch.MAX_SIZE) break;
|
||||
}
|
||||
Bundle bundle = b.build();
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
@@ -287,20 +287,20 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
private Batch fillBatch(ContactId c, long capacity) throws DbException {
|
||||
private Batch fillBatch(ContactId c, long capacity) throws DbException,
|
||||
SignatureException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageLock.readLock().lock();
|
||||
try {
|
||||
Set<MessageId> sent;
|
||||
BatchBuilder b;
|
||||
Batch batch;
|
||||
messageStatusLock.readLock().lock();
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
capacity = Math.min(capacity, Batch.CAPACITY);
|
||||
capacity = Math.min(capacity, Batch.MAX_SIZE);
|
||||
Iterator<MessageId> it =
|
||||
db.getSendableMessages(txn, c, capacity).iterator();
|
||||
if(!it.hasNext()) {
|
||||
@@ -308,7 +308,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
return null; // No more messages to send
|
||||
}
|
||||
sent = new HashSet<MessageId>();
|
||||
b = batchBuilderProvider.get();
|
||||
BatchBuilder b = batchBuilderProvider.get();
|
||||
while(it.hasNext()) {
|
||||
MessageId m = it.next();
|
||||
b.addMessage(db.getMessage(txn, m));
|
||||
@@ -319,6 +319,9 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
} catch(SignatureException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
messageStatusLock.readLock().unlock();
|
||||
@@ -438,21 +441,23 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
}
|
||||
|
||||
public void receiveBundle(ContactId c, Bundle b) throws DbException {
|
||||
public void receiveBundle(ContactId c, Bundle b) throws DbException,
|
||||
IOException, SignatureException {
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received bundle from " + c + ", "
|
||||
+ b.getSize() + " bytes");
|
||||
Header h;
|
||||
// Mark all messages in acked batches as seen
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
h = b.getHeader();
|
||||
messageLock.readLock().lock();
|
||||
try {
|
||||
messageStatusLock.writeLock().lock();
|
||||
try {
|
||||
int acks = 0;
|
||||
for(BatchId ack : b.getAcks()) {
|
||||
acks++;
|
||||
Set<BatchId> acks = h.getAcks();
|
||||
for(BatchId ack : acks) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
db.removeAckedBatch(txn, c, ack);
|
||||
@@ -463,7 +468,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
}
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received " + acks + " acks");
|
||||
LOG.fine("Received " + acks.size() + " acks");
|
||||
} finally {
|
||||
messageStatusLock.writeLock().unlock();
|
||||
}
|
||||
@@ -481,14 +486,12 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
// FIXME: Replace clearSubs and addSub with setSubs
|
||||
db.clearSubscriptions(txn, c);
|
||||
int subs = 0;
|
||||
for(GroupId g : b.getSubscriptions()) {
|
||||
subs++;
|
||||
db.addSubscription(txn, c, g);
|
||||
}
|
||||
Set<GroupId> subs = h.getSubscriptions();
|
||||
for(GroupId sub : subs) db.addSubscription(txn, c, sub);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received " + subs + " subscriptions");
|
||||
LOG.fine("Received " + subs.size() + " subscriptions");
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -508,7 +511,11 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
db.setTransports(txn, c, b.getTransports());
|
||||
Map<String, String> transports = h.getTransports();
|
||||
db.setTransports(txn, c, transports);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received " + transports.size()
|
||||
+ " transports");
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -522,7 +529,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
// Store the messages
|
||||
int batches = 0;
|
||||
for(Batch batch : b.getBatches()) {
|
||||
for(Batch batch = b.getNextBatch(); batch != null; batch = b.getNextBatch()) {
|
||||
batches++;
|
||||
waitForPermissionToWrite();
|
||||
contactLock.readLock().lock();
|
||||
@@ -579,7 +586,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
lost = db.addReceivedBundle(txn, c, b.getId());
|
||||
lost = db.addReceivedBundle(txn, c, h.getId());
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -19,6 +20,8 @@ import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.Bundle;
|
||||
import net.sf.briar.api.protocol.BundleBuilder;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
|
||||
@@ -48,8 +51,9 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
|
||||
@Inject
|
||||
SynchronizedDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
super(db, cleaner, batchBuilderProvider);
|
||||
super(db, cleaner, headerBuilderProvider, batchBuilderProvider);
|
||||
}
|
||||
|
||||
public void close() throws DbException {
|
||||
@@ -140,21 +144,20 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
|
||||
public Bundle generateBundle(ContactId c, BundleBuilder b)
|
||||
throws DbException {
|
||||
throws DbException, IOException, SignatureException {
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
||||
// Ack all batches received from c
|
||||
HeaderBuilder h;
|
||||
// Add acks
|
||||
synchronized(contactLock) {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
h = headerBuilderProvider.get();
|
||||
synchronized(messageStatusLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
int numAcks = 0;
|
||||
for(BatchId ack : db.removeBatchesToAck(txn, c)) {
|
||||
b.addAck(ack);
|
||||
numAcks++;
|
||||
}
|
||||
Set<BatchId> acks = db.removeBatchesToAck(txn, c);
|
||||
h.addAcks(acks);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + numAcks + " acks");
|
||||
LOG.fine("Added " + acks.size() + " acks");
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -162,19 +165,16 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add a list of subscriptions
|
||||
// Add subscriptions
|
||||
synchronized(contactLock) {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
synchronized(subscriptionLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
int numSubs = 0;
|
||||
for(GroupId g : db.getSubscriptions(txn)) {
|
||||
b.addSubscription(g);
|
||||
numSubs++;
|
||||
}
|
||||
Set<GroupId> subs = db.getSubscriptions(txn);
|
||||
h.addSubscriptions(subs);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + numSubs + " subscriptions");
|
||||
LOG.fine("Added " + subs.size() + " subscriptions");
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -188,14 +188,10 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
synchronized(transportLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
int numTransports = 0;
|
||||
Map<String, String> transports = db.getTransports(txn);
|
||||
for(Entry<String, String> e : transports.entrySet()) {
|
||||
b.addTransport(e.getKey(), e.getValue());
|
||||
numTransports++;
|
||||
}
|
||||
h.addTransports(transports);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + numTransports + " transports");
|
||||
LOG.fine("Added " + transports.size() + " transports");
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -203,8 +199,12 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add as many messages as possible to the bundle
|
||||
// Sign the header and add it to the bundle
|
||||
Header header = h.build();
|
||||
long capacity = b.getCapacity();
|
||||
capacity -= header.getSize();
|
||||
b.addHeader(header);
|
||||
// Add as many messages as possible to the bundle
|
||||
while(true) {
|
||||
Batch batch = fillBatch(c, capacity);
|
||||
if(batch == null) break; // No more messages to send
|
||||
@@ -213,7 +213,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
capacity -= size;
|
||||
// If the batch is less than half full, stop trying - there may be
|
||||
// more messages trickling in but we can't wait forever
|
||||
if(size * 2 < Batch.CAPACITY) break;
|
||||
if(size * 2 < Batch.MAX_SIZE) break;
|
||||
}
|
||||
Bundle bundle = b.build();
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
@@ -222,14 +222,15 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
private Batch fillBatch(ContactId c, long capacity) throws DbException {
|
||||
private Batch fillBatch(ContactId c, long capacity) throws DbException,
|
||||
SignatureException {
|
||||
synchronized(contactLock) {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
synchronized(messageLock) {
|
||||
synchronized(messageStatusLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
capacity = Math.min(capacity, Batch.CAPACITY);
|
||||
capacity = Math.min(capacity, Batch.MAX_SIZE);
|
||||
Iterator<MessageId> it =
|
||||
db.getSendableMessages(txn, c, capacity).iterator();
|
||||
if(!it.hasNext()) {
|
||||
@@ -252,6 +253,9 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
} catch(SignatureException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -331,18 +335,20 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
}
|
||||
|
||||
public void receiveBundle(ContactId c, Bundle b) throws DbException {
|
||||
public void receiveBundle(ContactId c, Bundle b) throws DbException,
|
||||
IOException, SignatureException {
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received bundle from " + c + ", "
|
||||
+ b.getSize() + " bytes");
|
||||
Header h;
|
||||
// Mark all messages in acked batches as seen
|
||||
synchronized(contactLock) {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
h = b.getHeader();
|
||||
synchronized(messageLock) {
|
||||
synchronized(messageStatusLock) {
|
||||
int acks = 0;
|
||||
for(BatchId ack : b.getAcks()) {
|
||||
acks++;
|
||||
Set<BatchId> acks = h.getAcks();
|
||||
for(BatchId ack : acks) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
db.removeAckedBatch(txn, c, ack);
|
||||
@@ -353,7 +359,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
}
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received " + acks + " acks");
|
||||
LOG.fine("Received " + acks.size() + " acks");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,14 +369,12 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
synchronized(subscriptionLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
// FIXME: Replace clearSubs and addSub with setSubs
|
||||
db.clearSubscriptions(txn, c);
|
||||
int subs = 0;
|
||||
for(GroupId g : b.getSubscriptions()) {
|
||||
subs++;
|
||||
db.addSubscription(txn, c, g);
|
||||
}
|
||||
Set<GroupId> subs = h.getSubscriptions();
|
||||
for(GroupId sub : subs) db.addSubscription(txn, c, sub);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received " + subs + " subscriptions");
|
||||
LOG.fine("Received " + subs.size() + " subscriptions");
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -384,7 +388,11 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
synchronized(transportLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
db.setTransports(txn, c, b.getTransports());
|
||||
Map<String, String> transports = h.getTransports();
|
||||
db.setTransports(txn, c, transports);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received " + transports.size()
|
||||
+ " transports");
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -394,7 +402,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
// Store the messages
|
||||
int batches = 0;
|
||||
for(Batch batch : b.getBatches()) {
|
||||
for(Batch batch = b.getNextBatch(); batch != null; batch = b.getNextBatch()) {
|
||||
batches++;
|
||||
waitForPermissionToWrite();
|
||||
synchronized(contactLock) {
|
||||
@@ -436,7 +444,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
synchronized(messageStatusLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
lost = db.addReceivedBundle(txn, c, b.getId());
|
||||
lost = db.addReceivedBundle(txn, c, h.getId());
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
@@ -11,15 +9,16 @@ import net.sf.briar.api.protocol.Message;
|
||||
/** A simple in-memory implementation of a batch. */
|
||||
class BatchImpl implements Batch {
|
||||
|
||||
private final List<Message> messages = new ArrayList<Message>();
|
||||
private BatchId id = null;
|
||||
private long size = 0L;
|
||||
private final BatchId id;
|
||||
private final long size;
|
||||
private final List<Message> messages;
|
||||
private final byte[] signature;
|
||||
|
||||
public void seal() {
|
||||
// FIXME: Calculate batch ID
|
||||
byte[] b = new byte[BatchId.LENGTH];
|
||||
new Random().nextBytes(b);
|
||||
id = new BatchId(b);
|
||||
BatchImpl(BatchId id, long size, List<Message> messages, byte[] signature) {
|
||||
this.id = id;
|
||||
this.size = size;
|
||||
this.messages = messages;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public BatchId getId() {
|
||||
@@ -34,8 +33,7 @@ class BatchImpl implements Batch {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public void addMessage(Message m) {
|
||||
messages.add(m);
|
||||
size += m.getSize();
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
|
||||
94
components/net/sf/briar/protocol/BundleReader.java
Normal file
94
components/net/sf/briar/protocol/BundleReader.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.Bundle;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageParser;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
import net.sf.briar.api.serial.FormatException;
|
||||
import net.sf.briar.api.serial.Raw;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
|
||||
/** A bundle that deserialises its contents on demand using a reader. */
|
||||
abstract class BundleReader implements Bundle {
|
||||
|
||||
private static enum State { START, FIRST_BATCH, MORE_BATCHES, END };
|
||||
|
||||
private final Reader r;
|
||||
private final MessageParser messageParser;
|
||||
private final Provider<HeaderBuilder> headerBuilderProvider;
|
||||
private final Provider<BatchBuilder> batchBuilderProvider;
|
||||
private State state = State.START;
|
||||
|
||||
BundleReader(Reader r, MessageParser messageParser,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
this.r = r;
|
||||
this.messageParser = messageParser;
|
||||
this.headerBuilderProvider = headerBuilderProvider;
|
||||
this.batchBuilderProvider = batchBuilderProvider;
|
||||
}
|
||||
|
||||
public Header getHeader() throws IOException, SignatureException {
|
||||
if(state != State.START) throw new IllegalStateException();
|
||||
r.setReadLimit(Header.MAX_SIZE);
|
||||
Set<BatchId> acks = new HashSet<BatchId>();
|
||||
for(Raw raw : r.readList(Raw.class)) {
|
||||
byte[] b = raw.getBytes();
|
||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||
acks.add(new BatchId(b));
|
||||
}
|
||||
Set<GroupId> subs = new HashSet<GroupId>();
|
||||
for(Raw raw : r.readList(Raw.class)) {
|
||||
byte[] b = raw.getBytes();
|
||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||
subs.add(new GroupId(b));
|
||||
}
|
||||
Map<String, String> transports = r.readMap(String.class, String.class);
|
||||
byte[] sig = r.readRaw();
|
||||
state = State.FIRST_BATCH;
|
||||
HeaderBuilder h = headerBuilderProvider.get();
|
||||
h.addAcks(acks);
|
||||
h.addSubscriptions(subs);
|
||||
h.addTransports(transports);
|
||||
h.setSignature(sig);
|
||||
return h.build();
|
||||
}
|
||||
|
||||
public Batch getNextBatch() throws IOException, SignatureException {
|
||||
if(state == State.FIRST_BATCH) {
|
||||
r.readListStart();
|
||||
state = State.MORE_BATCHES;
|
||||
}
|
||||
if(state != State.MORE_BATCHES) throw new IllegalStateException();
|
||||
if(r.hasListEnd()) {
|
||||
r.readListEnd();
|
||||
state = State.END;
|
||||
return null;
|
||||
}
|
||||
r.setReadLimit(Batch.MAX_SIZE);
|
||||
List<Raw> messages = r.readList(Raw.class);
|
||||
BatchBuilder b = batchBuilderProvider.get();
|
||||
for(Raw r : messages) {
|
||||
Message m = messageParser.parseMessage(r.getBytes());
|
||||
b.addMessage(m);
|
||||
}
|
||||
byte[] sig = r.readRaw();
|
||||
b.setSignature(sig);
|
||||
return b.build();
|
||||
}
|
||||
}
|
||||
66
components/net/sf/briar/protocol/BundleWriter.java
Normal file
66
components/net/sf/briar/protocol/BundleWriter.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.BundleBuilder;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
|
||||
/** A bundle builder that serialises its contents using a writer. */
|
||||
abstract class BundleWriter implements BundleBuilder {
|
||||
|
||||
private static enum State { START, FIRST_BATCH, MORE_BATCHES, END };
|
||||
|
||||
private final Writer w;
|
||||
private final long capacity;
|
||||
private State state = State.START;
|
||||
|
||||
BundleWriter(Writer w, long capacity) {
|
||||
this.w = w;
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
public long getCapacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public void addHeader(Header h) throws IOException {
|
||||
if(state != State.START) throw new IllegalStateException();
|
||||
w.writeListStart();
|
||||
for(BatchId ack : h.getAcks()) w.writeRaw(ack);
|
||||
w.writeListEnd();
|
||||
w.writeListStart();
|
||||
for(GroupId sub : h.getSubscriptions()) w.writeRaw(sub);
|
||||
w.writeListEnd();
|
||||
w.writeMap(h.getTransports());
|
||||
w.writeRaw(h.getSignature());
|
||||
state = State.FIRST_BATCH;
|
||||
}
|
||||
|
||||
public void addBatch(Batch b) throws IOException {
|
||||
if(state == State.FIRST_BATCH) {
|
||||
w.writeListStart();
|
||||
state = State.MORE_BATCHES;
|
||||
}
|
||||
if(state != State.MORE_BATCHES) throw new IllegalStateException();
|
||||
w.writeListStart();
|
||||
for(Message m : b.getMessages()) w.writeRaw(m.getBody());
|
||||
w.writeListEnd();
|
||||
w.writeRaw(b.getSignature());
|
||||
}
|
||||
|
||||
void close() throws IOException {
|
||||
if(state == State.FIRST_BATCH) {
|
||||
w.writeListStart();
|
||||
state = State.MORE_BATCHES;
|
||||
}
|
||||
if(state != State.MORE_BATCHES) throw new IllegalStateException();
|
||||
w.writeListEnd();
|
||||
w.close();
|
||||
state = State.END;
|
||||
}
|
||||
}
|
||||
30
components/net/sf/briar/protocol/FileBundle.java
Normal file
30
components/net/sf/briar/protocol/FileBundle.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.MessageParser;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
|
||||
class FileBundle extends BundleReader {
|
||||
|
||||
private final File file;
|
||||
|
||||
FileBundle(File file, ReaderFactory readerFactory,
|
||||
MessageParser messageParser,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) throws IOException {
|
||||
super(readerFactory.createReader(new FileInputStream(file)),
|
||||
messageParser, headerBuilderProvider, batchBuilderProvider);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public long getSize() throws IOException {
|
||||
return file.length();
|
||||
}
|
||||
}
|
||||
41
components/net/sf/briar/protocol/FileBundleBuilder.java
Normal file
41
components/net/sf/briar/protocol/FileBundleBuilder.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.Bundle;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.MessageParser;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
|
||||
public class FileBundleBuilder extends BundleWriter {
|
||||
|
||||
private final File file;
|
||||
private final ReaderFactory readerFactory;
|
||||
private final MessageParser messageParser;
|
||||
private final Provider<HeaderBuilder> headerBuilderProvider;
|
||||
private final Provider<BatchBuilder> batchBuilderProvider;
|
||||
|
||||
FileBundleBuilder(File file, long capacity, WriterFactory writerFactory,
|
||||
ReaderFactory readerFactory, MessageParser messageParser,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) throws IOException {
|
||||
super(writerFactory.createWriter(new FileOutputStream(file)), capacity);
|
||||
this.file = file;
|
||||
this.readerFactory = readerFactory;
|
||||
this.messageParser = messageParser;
|
||||
this.headerBuilderProvider = headerBuilderProvider;
|
||||
this.batchBuilderProvider = batchBuilderProvider;
|
||||
}
|
||||
|
||||
public Bundle build() throws IOException {
|
||||
super.close();
|
||||
return new FileBundle(file, readerFactory, messageParser,
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
}
|
||||
}
|
||||
55
components/net/sf/briar/protocol/HeaderImpl.java
Normal file
55
components/net/sf/briar/protocol/HeaderImpl.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.BundleId;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
|
||||
/** A simple in-memory implementation of a header. */
|
||||
class HeaderImpl implements Header {
|
||||
|
||||
private final BundleId id;
|
||||
private final long size;
|
||||
private final Set<BatchId> acks;
|
||||
private final Set<GroupId> subscriptions;
|
||||
private final Map<String, String> transports;
|
||||
private final byte[] signature;
|
||||
|
||||
HeaderImpl(BundleId id, long size, Set<BatchId> acks,
|
||||
Set<GroupId> subscriptions, Map<String, String> transports,
|
||||
byte[] signature) {
|
||||
this.id = id;
|
||||
this.size = size;
|
||||
this.acks = acks;
|
||||
this.subscriptions = subscriptions;
|
||||
this.transports = transports;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public BundleId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Set<BatchId> getAcks() {
|
||||
return acks;
|
||||
}
|
||||
|
||||
public Set<GroupId> getSubscriptions() {
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
public Map<String, String> getTransports() {
|
||||
return transports;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
|
||||
/** A simple in-memory implementation of a message. */
|
||||
public class MessageImpl implements Message {
|
||||
|
||||
private final MessageId id, parent;
|
||||
@@ -53,7 +54,7 @@ public class MessageImpl implements Message {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof MessageImpl && id.equals(((MessageImpl)o).id);
|
||||
return o instanceof Message && id.equals(((Message)o).getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
public class ProtocolModule extends AbstractModule {
|
||||
|
||||
@@ -12,9 +10,4 @@ public class ProtocolModule extends AbstractModule {
|
||||
protected void configure() {
|
||||
bind(Message.class).to(MessageImpl.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
Batch createBatch() {
|
||||
return new BatchImpl();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user