mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 14:19:53 +01:00
Interrupt messaging session if contact or transport is removed.
This commit is contained in:
@@ -6,14 +6,13 @@ public interface MessagingSession {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the session. This method returns when there are no more packets to
|
* Runs the session. This method returns when there are no more packets to
|
||||||
* send or when the {@link #interrupt()} method has been called.
|
* send or receive, or when the {@link #interrupt()} method has been called.
|
||||||
*/
|
*/
|
||||||
void run() throws IOException;
|
void run() throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interrupts the session, causing the {@link #run()} method to return at
|
* Interrupts the session, causing the {@link #run()} method to return at
|
||||||
* the next opportunity or throw an {@link java.io.IOException IOException}
|
* the next opportunity.
|
||||||
* if it cannot return cleanly.
|
|
||||||
*/
|
*/
|
||||||
void interrupt();
|
void interrupt();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import org.briarproject.api.TransportId;
|
|||||||
|
|
||||||
public interface MessagingSessionFactory {
|
public interface MessagingSessionFactory {
|
||||||
|
|
||||||
MessagingSession createIncomingSession(ContactId c, InputStream in);
|
MessagingSession createIncomingSession(ContactId c, TransportId t,
|
||||||
|
InputStream in);
|
||||||
|
|
||||||
MessagingSession createOutgoingSession(ContactId c, TransportId t,
|
MessagingSession createOutgoingSession(ContactId c, TransportId t,
|
||||||
long maxLatency, OutputStream out, boolean duplex);
|
long maxLatency, boolean duplex, OutputStream out);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,13 +46,15 @@ import org.briarproject.api.messaging.TransportUpdate;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An outgoing {@link org.briarproject.api.messaging.MessagingSession
|
* An outgoing {@link org.briarproject.api.messaging.MessagingSession
|
||||||
* MessagingSession} that keeps its output stream open and reacts to events
|
* MessagingSession} suitable for duplex transports. The session offers
|
||||||
* that make packets available to send.
|
* messages before sending them, keeps its output stream open when there are no
|
||||||
|
* more packets to send, and reacts to events that make packets available to
|
||||||
|
* send.
|
||||||
*/
|
*/
|
||||||
class ReactiveOutgoingSession implements MessagingSession, EventListener {
|
class DuplexOutgoingSession implements MessagingSession, EventListener {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ReactiveOutgoingSession.class.getName());
|
Logger.getLogger(DuplexOutgoingSession.class.getName());
|
||||||
|
|
||||||
private static final ThrowingRunnable<IOException> CLOSE =
|
private static final ThrowingRunnable<IOException> CLOSE =
|
||||||
new ThrowingRunnable<IOException>() {
|
new ThrowingRunnable<IOException>() {
|
||||||
@@ -62,35 +64,33 @@ class ReactiveOutgoingSession implements MessagingSession, EventListener {
|
|||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
private final PacketWriterFactory packetWriterFactory;
|
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final long maxLatency;
|
private final long maxLatency;
|
||||||
private final OutputStream out;
|
private final OutputStream out;
|
||||||
|
private final PacketWriter packetWriter;
|
||||||
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
|
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
|
||||||
|
|
||||||
private volatile PacketWriter packetWriter = null;
|
|
||||||
private volatile boolean interrupted = false;
|
private volatile boolean interrupted = false;
|
||||||
|
|
||||||
ReactiveOutgoingSession(DatabaseComponent db, Executor dbExecutor,
|
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
|
||||||
EventBus eventBus, PacketWriterFactory packetWriterFactory,
|
EventBus eventBus, PacketWriterFactory packetWriterFactory,
|
||||||
ContactId contactId, TransportId transportId, long maxLatency,
|
ContactId contactId, TransportId transportId, long maxLatency,
|
||||||
OutputStream out) {
|
OutputStream out) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.packetWriterFactory = packetWriterFactory;
|
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
this.maxLatency = maxLatency;
|
this.maxLatency = maxLatency;
|
||||||
this.out = out;
|
this.out = out;
|
||||||
|
packetWriter = packetWriterFactory.createPacketWriter(out);
|
||||||
writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>();
|
writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() throws IOException {
|
public void run() throws IOException {
|
||||||
eventBus.addListener(this);
|
eventBus.addListener(this);
|
||||||
try {
|
try {
|
||||||
packetWriter = packetWriterFactory.createPacketWriter(out);
|
|
||||||
// Start a query for each type of packet, in order of urgency
|
// Start a query for each type of packet, in order of urgency
|
||||||
dbExecutor.execute(new GenerateTransportAcks());
|
dbExecutor.execute(new GenerateTransportAcks());
|
||||||
dbExecutor.execute(new GenerateTransportUpdates());
|
dbExecutor.execute(new GenerateTransportUpdates());
|
||||||
@@ -110,6 +110,7 @@ class ReactiveOutgoingSession implements MessagingSession, EventListener {
|
|||||||
task.run();
|
task.run();
|
||||||
if(writerTasks.isEmpty()) out.flush();
|
if(writerTasks.isEmpty()) out.flush();
|
||||||
}
|
}
|
||||||
|
out.flush();
|
||||||
out.close();
|
out.close();
|
||||||
} catch(InterruptedException e) {
|
} catch(InterruptedException e) {
|
||||||
LOG.info("Interrupted while waiting for a packet to write");
|
LOG.info("Interrupted while waiting for a packet to write");
|
||||||
@@ -128,10 +129,7 @@ class ReactiveOutgoingSession implements MessagingSession, EventListener {
|
|||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if(e instanceof ContactRemovedEvent) {
|
if(e instanceof ContactRemovedEvent) {
|
||||||
ContactRemovedEvent c = (ContactRemovedEvent) e;
|
ContactRemovedEvent c = (ContactRemovedEvent) e;
|
||||||
if(contactId.equals(c.getContactId())) {
|
if(c.getContactId().equals(contactId)) interrupt();
|
||||||
LOG.info("Contact removed, closing");
|
|
||||||
interrupt();
|
|
||||||
}
|
|
||||||
} else if(e instanceof MessageAddedEvent) {
|
} else if(e instanceof MessageAddedEvent) {
|
||||||
dbExecutor.execute(new GenerateOffer());
|
dbExecutor.execute(new GenerateOffer());
|
||||||
} else if(e instanceof MessageExpiredEvent) {
|
} else if(e instanceof MessageExpiredEvent) {
|
||||||
@@ -173,10 +171,7 @@ class ReactiveOutgoingSession implements MessagingSession, EventListener {
|
|||||||
dbExecutor.execute(new GenerateTransportAcks());
|
dbExecutor.execute(new GenerateTransportAcks());
|
||||||
} else if(e instanceof TransportRemovedEvent) {
|
} else if(e instanceof TransportRemovedEvent) {
|
||||||
TransportRemovedEvent t = (TransportRemovedEvent) e;
|
TransportRemovedEvent t = (TransportRemovedEvent) e;
|
||||||
if(t.getTransportId().equals(transportId)) {
|
if(t.getTransportId().equals(transportId)) interrupt();
|
||||||
LOG.info("Transport removed, closing");
|
|
||||||
interrupt();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10,8 +10,14 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
import org.briarproject.api.FormatException;
|
import org.briarproject.api.FormatException;
|
||||||
|
import org.briarproject.api.TransportId;
|
||||||
import org.briarproject.api.db.DatabaseComponent;
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
|
import org.briarproject.api.event.ContactRemovedEvent;
|
||||||
|
import org.briarproject.api.event.Event;
|
||||||
|
import org.briarproject.api.event.EventBus;
|
||||||
|
import org.briarproject.api.event.EventListener;
|
||||||
|
import org.briarproject.api.event.TransportRemovedEvent;
|
||||||
import org.briarproject.api.messaging.Ack;
|
import org.briarproject.api.messaging.Ack;
|
||||||
import org.briarproject.api.messaging.Message;
|
import org.briarproject.api.messaging.Message;
|
||||||
import org.briarproject.api.messaging.MessageVerifier;
|
import org.briarproject.api.messaging.MessageVerifier;
|
||||||
@@ -30,66 +36,75 @@ import org.briarproject.api.messaging.UnverifiedMessage;
|
|||||||
* An incoming {@link org.briarproject.api.messaging.MessagingSession
|
* An incoming {@link org.briarproject.api.messaging.MessagingSession
|
||||||
* MessagingSession}.
|
* MessagingSession}.
|
||||||
*/
|
*/
|
||||||
class IncomingSession implements MessagingSession {
|
class IncomingSession implements MessagingSession, EventListener {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(IncomingSession.class.getName());
|
Logger.getLogger(IncomingSession.class.getName());
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final Executor dbExecutor, cryptoExecutor;
|
private final Executor dbExecutor, cryptoExecutor;
|
||||||
|
private final EventBus eventBus;
|
||||||
private final MessageVerifier messageVerifier;
|
private final MessageVerifier messageVerifier;
|
||||||
private final PacketReaderFactory packetReaderFactory;
|
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
|
private final TransportId transportId;
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
|
private final PacketReader packetReader;
|
||||||
|
|
||||||
private volatile boolean interrupted = false;
|
private volatile boolean interrupted = false;
|
||||||
|
|
||||||
IncomingSession(DatabaseComponent db, Executor dbExecutor,
|
IncomingSession(DatabaseComponent db, Executor dbExecutor,
|
||||||
Executor cryptoExecutor, MessageVerifier messageVerifier,
|
Executor cryptoExecutor, EventBus eventBus,
|
||||||
|
MessageVerifier messageVerifier,
|
||||||
PacketReaderFactory packetReaderFactory, ContactId contactId,
|
PacketReaderFactory packetReaderFactory, ContactId contactId,
|
||||||
InputStream in) {
|
TransportId transportId, InputStream in) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.cryptoExecutor = cryptoExecutor;
|
this.cryptoExecutor = cryptoExecutor;
|
||||||
|
this.eventBus = eventBus;
|
||||||
this.messageVerifier = messageVerifier;
|
this.messageVerifier = messageVerifier;
|
||||||
this.packetReaderFactory = packetReaderFactory;
|
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
|
this.transportId = transportId;
|
||||||
this.in = in;
|
this.in = in;
|
||||||
|
packetReader = packetReaderFactory.createPacketReader(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() throws IOException {
|
public void run() throws IOException {
|
||||||
PacketReader packetReader = packetReaderFactory.createPacketReader(in);
|
eventBus.addListener(this);
|
||||||
// Read packets until interrupted or EOF
|
try {
|
||||||
while(!interrupted && !packetReader.eof()) {
|
// Read packets until interrupted or EOF
|
||||||
if(packetReader.hasAck()) {
|
while(!interrupted && !packetReader.eof()) {
|
||||||
Ack a = packetReader.readAck();
|
if(packetReader.hasAck()) {
|
||||||
dbExecutor.execute(new ReceiveAck(a));
|
Ack a = packetReader.readAck();
|
||||||
} else if(packetReader.hasMessage()) {
|
dbExecutor.execute(new ReceiveAck(a));
|
||||||
UnverifiedMessage m = packetReader.readMessage();
|
} else if(packetReader.hasMessage()) {
|
||||||
cryptoExecutor.execute(new VerifyMessage(m));
|
UnverifiedMessage m = packetReader.readMessage();
|
||||||
} else if(packetReader.hasRetentionAck()) {
|
cryptoExecutor.execute(new VerifyMessage(m));
|
||||||
RetentionAck a = packetReader.readRetentionAck();
|
} else if(packetReader.hasRetentionAck()) {
|
||||||
dbExecutor.execute(new ReceiveRetentionAck(a));
|
RetentionAck a = packetReader.readRetentionAck();
|
||||||
} else if(packetReader.hasRetentionUpdate()) {
|
dbExecutor.execute(new ReceiveRetentionAck(a));
|
||||||
RetentionUpdate u = packetReader.readRetentionUpdate();
|
} else if(packetReader.hasRetentionUpdate()) {
|
||||||
dbExecutor.execute(new ReceiveRetentionUpdate(u));
|
RetentionUpdate u = packetReader.readRetentionUpdate();
|
||||||
} else if(packetReader.hasSubscriptionAck()) {
|
dbExecutor.execute(new ReceiveRetentionUpdate(u));
|
||||||
SubscriptionAck a = packetReader.readSubscriptionAck();
|
} else if(packetReader.hasSubscriptionAck()) {
|
||||||
dbExecutor.execute(new ReceiveSubscriptionAck(a));
|
SubscriptionAck a = packetReader.readSubscriptionAck();
|
||||||
} else if(packetReader.hasSubscriptionUpdate()) {
|
dbExecutor.execute(new ReceiveSubscriptionAck(a));
|
||||||
SubscriptionUpdate u = packetReader.readSubscriptionUpdate();
|
} else if(packetReader.hasSubscriptionUpdate()) {
|
||||||
dbExecutor.execute(new ReceiveSubscriptionUpdate(u));
|
SubscriptionUpdate u = packetReader.readSubscriptionUpdate();
|
||||||
} else if(packetReader.hasTransportAck()) {
|
dbExecutor.execute(new ReceiveSubscriptionUpdate(u));
|
||||||
TransportAck a = packetReader.readTransportAck();
|
} else if(packetReader.hasTransportAck()) {
|
||||||
dbExecutor.execute(new ReceiveTransportAck(a));
|
TransportAck a = packetReader.readTransportAck();
|
||||||
} else if(packetReader.hasTransportUpdate()) {
|
dbExecutor.execute(new ReceiveTransportAck(a));
|
||||||
TransportUpdate u = packetReader.readTransportUpdate();
|
} else if(packetReader.hasTransportUpdate()) {
|
||||||
dbExecutor.execute(new ReceiveTransportUpdate(u));
|
TransportUpdate u = packetReader.readTransportUpdate();
|
||||||
} else {
|
dbExecutor.execute(new ReceiveTransportUpdate(u));
|
||||||
throw new FormatException();
|
} else {
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
in.close();
|
||||||
|
} finally {
|
||||||
|
eventBus.removeListener(this);
|
||||||
}
|
}
|
||||||
in.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void interrupt() {
|
public void interrupt() {
|
||||||
@@ -98,6 +113,16 @@ class IncomingSession implements MessagingSession {
|
|||||||
interrupted = true;
|
interrupted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if(e instanceof ContactRemovedEvent) {
|
||||||
|
ContactRemovedEvent c = (ContactRemovedEvent) e;
|
||||||
|
if(c.getContactId().equals(contactId)) interrupt();
|
||||||
|
} else if(e instanceof TransportRemovedEvent) {
|
||||||
|
TransportRemovedEvent t = (TransportRemovedEvent) e;
|
||||||
|
if(t.getTransportId().equals(transportId)) interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class ReceiveAck implements Runnable {
|
private class ReceiveAck implements Runnable {
|
||||||
|
|
||||||
private final Ack ack;
|
private final Ack ack;
|
||||||
|
|||||||
@@ -43,16 +43,17 @@ class MessagingSessionFactoryImpl implements MessagingSessionFactory {
|
|||||||
this.packetWriterFactory = packetWriterFactory;
|
this.packetWriterFactory = packetWriterFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessagingSession createIncomingSession(ContactId c, InputStream in) {
|
public MessagingSession createIncomingSession(ContactId c, TransportId t,
|
||||||
return new IncomingSession(db, dbExecutor, cryptoExecutor,
|
InputStream in) {
|
||||||
messageVerifier, packetReaderFactory, c, in);
|
return new IncomingSession(db, dbExecutor, cryptoExecutor, eventBus,
|
||||||
|
messageVerifier, packetReaderFactory, c, t, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessagingSession createOutgoingSession(ContactId c, TransportId t,
|
public MessagingSession createOutgoingSession(ContactId c, TransportId t,
|
||||||
long maxLatency, OutputStream out, boolean duplex) {
|
long maxLatency, boolean duplex, OutputStream out) {
|
||||||
if(duplex) return new ReactiveOutgoingSession(db, dbExecutor, eventBus,
|
if(duplex) return new DuplexOutgoingSession(db, dbExecutor, eventBus,
|
||||||
|
packetWriterFactory, c, t, maxLatency, out);
|
||||||
|
else return new SimplexOutgoingSession(db, dbExecutor, eventBus,
|
||||||
packetWriterFactory, c, t, maxLatency, out);
|
packetWriterFactory, c, t, maxLatency, out);
|
||||||
else return new SinglePassOutgoingSession(db, dbExecutor,
|
|
||||||
packetWriterFactory, c, maxLatency, out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,14 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
|
import org.briarproject.api.TransportId;
|
||||||
import org.briarproject.api.db.DatabaseComponent;
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
|
import org.briarproject.api.event.ContactRemovedEvent;
|
||||||
|
import org.briarproject.api.event.Event;
|
||||||
|
import org.briarproject.api.event.EventBus;
|
||||||
|
import org.briarproject.api.event.EventListener;
|
||||||
|
import org.briarproject.api.event.TransportRemovedEvent;
|
||||||
import org.briarproject.api.messaging.Ack;
|
import org.briarproject.api.messaging.Ack;
|
||||||
import org.briarproject.api.messaging.MessagingSession;
|
import org.briarproject.api.messaging.MessagingSession;
|
||||||
import org.briarproject.api.messaging.PacketWriter;
|
import org.briarproject.api.messaging.PacketWriter;
|
||||||
@@ -29,13 +35,14 @@ import org.briarproject.api.messaging.TransportUpdate;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An outgoing {@link org.briarproject.api.messaging.MessagingSession
|
* An outgoing {@link org.briarproject.api.messaging.MessagingSession
|
||||||
* MessagingSession} that closes its output stream when no more packets are
|
* MessagingSession} suitable for simplex transports. The session sends
|
||||||
* available to send.
|
* messages without offering them, and closes its output stream when there are
|
||||||
|
* no more packets to send.
|
||||||
*/
|
*/
|
||||||
class SinglePassOutgoingSession implements MessagingSession {
|
class SimplexOutgoingSession implements MessagingSession, EventListener {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(SinglePassOutgoingSession.class.getName());
|
Logger.getLogger(SimplexOutgoingSession.class.getName());
|
||||||
|
|
||||||
private static final ThrowingRunnable<IOException> CLOSE =
|
private static final ThrowingRunnable<IOException> CLOSE =
|
||||||
new ThrowingRunnable<IOException>() {
|
new ThrowingRunnable<IOException>() {
|
||||||
@@ -44,52 +51,60 @@ class SinglePassOutgoingSession implements MessagingSession {
|
|||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final PacketWriterFactory packetWriterFactory;
|
private final EventBus eventBus;
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
|
private final TransportId transportId;
|
||||||
private final long maxLatency;
|
private final long maxLatency;
|
||||||
private final OutputStream out;
|
private final OutputStream out;
|
||||||
|
private final PacketWriter packetWriter;
|
||||||
private final AtomicInteger outstandingQueries;
|
private final AtomicInteger outstandingQueries;
|
||||||
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
|
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
|
||||||
|
|
||||||
private volatile PacketWriter packetWriter = null;
|
|
||||||
private volatile boolean interrupted = false;
|
private volatile boolean interrupted = false;
|
||||||
|
|
||||||
SinglePassOutgoingSession(DatabaseComponent db, Executor dbExecutor,
|
SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
|
||||||
PacketWriterFactory packetWriterFactory, ContactId contactId,
|
EventBus eventBus, PacketWriterFactory packetWriterFactory,
|
||||||
long maxLatency, OutputStream out) {
|
ContactId contactId, TransportId transportId, long maxLatency,
|
||||||
|
OutputStream out) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.packetWriterFactory = packetWriterFactory;
|
this.eventBus = eventBus;
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
|
this.transportId = transportId;
|
||||||
this.maxLatency = maxLatency;
|
this.maxLatency = maxLatency;
|
||||||
this.out = out;
|
this.out = out;
|
||||||
|
packetWriter = packetWriterFactory.createPacketWriter(out);
|
||||||
outstandingQueries = new AtomicInteger(8); // One per type of packet
|
outstandingQueries = new AtomicInteger(8); // One per type of packet
|
||||||
writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>();
|
writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() throws IOException {
|
public void run() throws IOException {
|
||||||
packetWriter = packetWriterFactory.createPacketWriter(out);
|
eventBus.addListener(this);
|
||||||
// Start a query for each type of packet, in order of urgency
|
|
||||||
dbExecutor.execute(new GenerateTransportAcks());
|
|
||||||
dbExecutor.execute(new GenerateTransportUpdates());
|
|
||||||
dbExecutor.execute(new GenerateSubscriptionAck());
|
|
||||||
dbExecutor.execute(new GenerateSubscriptionUpdate());
|
|
||||||
dbExecutor.execute(new GenerateRetentionAck());
|
|
||||||
dbExecutor.execute(new GenerateRetentionUpdate());
|
|
||||||
dbExecutor.execute(new GenerateAck());
|
|
||||||
dbExecutor.execute(new GenerateBatch());
|
|
||||||
// Write packets until interrupted or there are no more packets to write
|
|
||||||
try {
|
try {
|
||||||
while(!interrupted) {
|
// Start a query for each type of packet, in order of urgency
|
||||||
ThrowingRunnable<IOException> task = writerTasks.take();
|
dbExecutor.execute(new GenerateTransportAcks());
|
||||||
if(task == CLOSE) break;
|
dbExecutor.execute(new GenerateTransportUpdates());
|
||||||
task.run();
|
dbExecutor.execute(new GenerateSubscriptionAck());
|
||||||
|
dbExecutor.execute(new GenerateSubscriptionUpdate());
|
||||||
|
dbExecutor.execute(new GenerateRetentionAck());
|
||||||
|
dbExecutor.execute(new GenerateRetentionUpdate());
|
||||||
|
dbExecutor.execute(new GenerateAck());
|
||||||
|
dbExecutor.execute(new GenerateBatch());
|
||||||
|
// Write packets until interrupted or no more packets to write
|
||||||
|
try {
|
||||||
|
while(!interrupted) {
|
||||||
|
ThrowingRunnable<IOException> task = writerTasks.take();
|
||||||
|
if(task == CLOSE) break;
|
||||||
|
task.run();
|
||||||
|
}
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
LOG.info("Interrupted while waiting for a packet to write");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
out.flush();
|
} finally {
|
||||||
out.close();
|
eventBus.removeListener(this);
|
||||||
} catch(InterruptedException e) {
|
|
||||||
LOG.info("Interrupted while waiting for a packet to write");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +117,16 @@ class SinglePassOutgoingSession implements MessagingSession {
|
|||||||
if(outstandingQueries.decrementAndGet() == 0) writerTasks.add(CLOSE);
|
if(outstandingQueries.decrementAndGet() == 0) writerTasks.add(CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if(e instanceof ContactRemovedEvent) {
|
||||||
|
ContactRemovedEvent c = (ContactRemovedEvent) e;
|
||||||
|
if(c.getContactId().equals(contactId)) interrupt();
|
||||||
|
} else if(e instanceof TransportRemovedEvent) {
|
||||||
|
TransportRemovedEvent t = (TransportRemovedEvent) e;
|
||||||
|
if(t.getTransportId().equals(transportId)) interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This task runs on the database thread
|
// This task runs on the database thread
|
||||||
private class GenerateAck implements Runnable {
|
private class GenerateAck implements Runnable {
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
|||||||
StreamReader streamReader = streamReaderFactory.createStreamReader(in,
|
StreamReader streamReader = streamReaderFactory.createStreamReader(in,
|
||||||
r.getMaxFrameLength(), ctx);
|
r.getMaxFrameLength(), ctx);
|
||||||
return messagingSessionFactory.createIncomingSession(ctx.getContactId(),
|
return messagingSessionFactory.createIncomingSession(ctx.getContactId(),
|
||||||
streamReader.getInputStream());
|
ctx.getTransportId(), streamReader.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessagingSession createOutgoingSession(StreamContext ctx,
|
private MessagingSession createOutgoingSession(StreamContext ctx,
|
||||||
@@ -110,7 +110,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
|||||||
w.getMaxFrameLength(), ctx);
|
w.getMaxFrameLength(), ctx);
|
||||||
return messagingSessionFactory.createOutgoingSession(ctx.getContactId(),
|
return messagingSessionFactory.createOutgoingSession(ctx.getContactId(),
|
||||||
ctx.getTransportId(), w.getMaxLatency(),
|
ctx.getTransportId(), w.getMaxLatency(),
|
||||||
streamWriter.getOutputStream(), duplex);
|
duplex, streamWriter.getOutputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DispatchIncomingSimplexConnection implements Runnable {
|
private class DispatchIncomingSimplexConnection implements Runnable {
|
||||||
|
|||||||
@@ -112,7 +112,7 @@
|
|||||||
<test name='org.briarproject.messaging.ConsumersTest'/>
|
<test name='org.briarproject.messaging.ConsumersTest'/>
|
||||||
<test name='org.briarproject.messaging.PacketReaderImplTest'/>
|
<test name='org.briarproject.messaging.PacketReaderImplTest'/>
|
||||||
<test name='org.briarproject.messaging.SimplexMessagingIntegrationTest'/>
|
<test name='org.briarproject.messaging.SimplexMessagingIntegrationTest'/>
|
||||||
<test name='org.briarproject.messaging.SinglePassOutgoingSessionTest'/>
|
<test name='org.briarproject.messaging.SimplexOutgoingSessionTest'/>
|
||||||
<test name='org.briarproject.plugins.ConnectionRegistryImplTest'/>
|
<test name='org.briarproject.plugins.ConnectionRegistryImplTest'/>
|
||||||
<test name='org.briarproject.plugins.PluginManagerImplTest'/>
|
<test name='org.briarproject.plugins.PluginManagerImplTest'/>
|
||||||
<test name='org.briarproject.plugins.file.LinuxRemovableDriveFinderTest'/>
|
<test name='org.briarproject.plugins.file.LinuxRemovableDriveFinderTest'/>
|
||||||
|
|||||||
@@ -149,11 +149,13 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
|||||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out,
|
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out,
|
||||||
MAX_FRAME_LENGTH, ctx);
|
MAX_FRAME_LENGTH, ctx);
|
||||||
// Create an outgoing messaging session
|
// Create an outgoing messaging session
|
||||||
|
EventBus eventBus = alice.getInstance(EventBus.class);
|
||||||
PacketWriterFactory packetWriterFactory =
|
PacketWriterFactory packetWriterFactory =
|
||||||
alice.getInstance(PacketWriterFactory.class);
|
alice.getInstance(PacketWriterFactory.class);
|
||||||
MessagingSession session = new SinglePassOutgoingSession(db,
|
MessagingSession session = new SimplexOutgoingSession(db,
|
||||||
new ImmediateExecutor(), packetWriterFactory, contactId,
|
new ImmediateExecutor(), eventBus, packetWriterFactory,
|
||||||
Long.MAX_VALUE, streamWriter.getOutputStream());
|
contactId, transportId, Long.MAX_VALUE,
|
||||||
|
streamWriter.getOutputStream());
|
||||||
// Write whatever needs to be written
|
// Write whatever needs to be written
|
||||||
session.run();
|
session.run();
|
||||||
// Clean up
|
// Clean up
|
||||||
@@ -207,13 +209,14 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
|||||||
StreamReader streamReader = streamReaderFactory.createStreamReader(in,
|
StreamReader streamReader = streamReaderFactory.createStreamReader(in,
|
||||||
MAX_FRAME_LENGTH, ctx);
|
MAX_FRAME_LENGTH, ctx);
|
||||||
// Create an incoming messaging session
|
// Create an incoming messaging session
|
||||||
|
EventBus eventBus = bob.getInstance(EventBus.class);
|
||||||
MessageVerifier messageVerifier =
|
MessageVerifier messageVerifier =
|
||||||
bob.getInstance(MessageVerifier.class);
|
bob.getInstance(MessageVerifier.class);
|
||||||
PacketReaderFactory packetReaderFactory =
|
PacketReaderFactory packetReaderFactory =
|
||||||
bob.getInstance(PacketReaderFactory.class);
|
bob.getInstance(PacketReaderFactory.class);
|
||||||
MessagingSession session = new IncomingSession(db,
|
MessagingSession session = new IncomingSession(db,
|
||||||
new ImmediateExecutor(), new ImmediateExecutor(),
|
new ImmediateExecutor(), new ImmediateExecutor(), eventBus,
|
||||||
messageVerifier, packetReaderFactory, contactId,
|
messageVerifier, packetReaderFactory, contactId, transportId,
|
||||||
streamReader.getInputStream());
|
streamReader.getInputStream());
|
||||||
// No messages should have been added yet
|
// No messages should have been added yet
|
||||||
assertFalse(listener.messageAdded);
|
assertFalse(listener.messageAdded);
|
||||||
|
|||||||
@@ -4,22 +4,18 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import org.briarproject.BriarTestCase;
|
import org.briarproject.BriarTestCase;
|
||||||
import org.briarproject.TestLifecycleModule;
|
|
||||||
import org.briarproject.TestSystemModule;
|
|
||||||
import org.briarproject.TestUtils;
|
import org.briarproject.TestUtils;
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
|
import org.briarproject.api.TransportId;
|
||||||
import org.briarproject.api.UniqueId;
|
import org.briarproject.api.UniqueId;
|
||||||
import org.briarproject.api.db.DatabaseComponent;
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
import org.briarproject.api.db.DatabaseExecutor;
|
import org.briarproject.api.event.EventBus;
|
||||||
import org.briarproject.api.messaging.Ack;
|
import org.briarproject.api.messaging.Ack;
|
||||||
import org.briarproject.api.messaging.MessageId;
|
import org.briarproject.api.messaging.MessageId;
|
||||||
import org.briarproject.api.messaging.MessagingSession;
|
|
||||||
import org.briarproject.api.messaging.PacketWriterFactory;
|
import org.briarproject.api.messaging.PacketWriterFactory;
|
||||||
import org.briarproject.crypto.CryptoModule;
|
import org.briarproject.plugins.ImmediateExecutor;
|
||||||
import org.briarproject.event.EventModule;
|
|
||||||
import org.briarproject.serial.SerialModule;
|
import org.briarproject.serial.SerialModule;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.Mockery;
|
import org.jmock.Mockery;
|
||||||
@@ -30,36 +26,36 @@ import com.google.inject.Guice;
|
|||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
|
|
||||||
public class SinglePassOutgoingSessionTest extends BriarTestCase {
|
public class SimplexOutgoingSessionTest extends BriarTestCase {
|
||||||
|
|
||||||
// FIXME: This is an integration test, not a unit test
|
// FIXME: This is an integration test, not a unit test
|
||||||
|
|
||||||
private final Mockery context;
|
private final Mockery context;
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
|
private final EventBus eventBus;
|
||||||
private final PacketWriterFactory packetWriterFactory;
|
private final PacketWriterFactory packetWriterFactory;
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
|
private final TransportId transportId;
|
||||||
private final MessageId messageId;
|
private final MessageId messageId;
|
||||||
private final byte[] secret;
|
private final byte[] secret;
|
||||||
|
|
||||||
public SinglePassOutgoingSessionTest() {
|
public SimplexOutgoingSessionTest() {
|
||||||
context = new Mockery();
|
context = new Mockery();
|
||||||
db = context.mock(DatabaseComponent.class);
|
db = context.mock(DatabaseComponent.class);
|
||||||
dbExecutor = Executors.newSingleThreadExecutor();
|
dbExecutor = new ImmediateExecutor();
|
||||||
Module testModule = new AbstractModule() {
|
Module testModule = new AbstractModule() {
|
||||||
@Override
|
@Override
|
||||||
public void configure() {
|
public void configure() {
|
||||||
bind(DatabaseComponent.class).toInstance(db);
|
bind(PacketWriterFactory.class).to(
|
||||||
bind(Executor.class).annotatedWith(
|
PacketWriterFactoryImpl.class);
|
||||||
DatabaseExecutor.class).toInstance(dbExecutor);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Injector i = Guice.createInjector(testModule,
|
Injector i = Guice.createInjector(testModule, new SerialModule());
|
||||||
new TestLifecycleModule(), new TestSystemModule(),
|
eventBus = context.mock(EventBus.class);
|
||||||
new CryptoModule(), new EventModule(), new MessagingModule(),
|
|
||||||
new SerialModule());
|
|
||||||
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
|
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
|
||||||
contactId = new ContactId(234);
|
contactId = new ContactId(234);
|
||||||
|
transportId = new TransportId("id");
|
||||||
messageId = new MessageId(TestUtils.getRandomId());
|
messageId = new MessageId(TestUtils.getRandomId());
|
||||||
secret = new byte[32];
|
secret = new byte[32];
|
||||||
new Random().nextBytes(secret);
|
new Random().nextBytes(secret);
|
||||||
@@ -68,9 +64,12 @@ public class SinglePassOutgoingSessionTest extends BriarTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testNothingToSend() throws Exception {
|
public void testNothingToSend() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
MessagingSession session = new SinglePassOutgoingSession(db, dbExecutor,
|
final SimplexOutgoingSession session = new SimplexOutgoingSession(db,
|
||||||
packetWriterFactory, contactId, Long.MAX_VALUE, out);
|
dbExecutor, eventBus, packetWriterFactory, contactId,
|
||||||
|
transportId, Long.MAX_VALUE, out);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
|
// Add listener
|
||||||
|
oneOf(eventBus).addListener(session);
|
||||||
// No transport acks to send
|
// No transport acks to send
|
||||||
oneOf(db).generateTransportAcks(contactId);
|
oneOf(db).generateTransportAcks(contactId);
|
||||||
will(returnValue(null));
|
will(returnValue(null));
|
||||||
@@ -99,6 +98,8 @@ public class SinglePassOutgoingSessionTest extends BriarTestCase {
|
|||||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)),
|
oneOf(db).generateBatch(with(contactId), with(any(int.class)),
|
||||||
with(any(long.class)));
|
with(any(long.class)));
|
||||||
will(returnValue(null));
|
will(returnValue(null));
|
||||||
|
// Remove listener
|
||||||
|
oneOf(eventBus).removeListener(session);
|
||||||
}});
|
}});
|
||||||
session.run();
|
session.run();
|
||||||
// Nothing should have been written
|
// Nothing should have been written
|
||||||
@@ -109,10 +110,13 @@ public class SinglePassOutgoingSessionTest extends BriarTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testSomethingToSend() throws Exception {
|
public void testSomethingToSend() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
MessagingSession session = new SinglePassOutgoingSession(db, dbExecutor,
|
final SimplexOutgoingSession session = new SimplexOutgoingSession(db,
|
||||||
packetWriterFactory, contactId, Long.MAX_VALUE, out);
|
dbExecutor, eventBus, packetWriterFactory, contactId,
|
||||||
|
transportId, Long.MAX_VALUE, out);
|
||||||
final byte[] raw = new byte[1234];
|
final byte[] raw = new byte[1234];
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
|
// Add listener
|
||||||
|
oneOf(eventBus).addListener(session);
|
||||||
// No transport acks to send
|
// No transport acks to send
|
||||||
oneOf(db).generateTransportAcks(contactId);
|
oneOf(db).generateTransportAcks(contactId);
|
||||||
will(returnValue(null));
|
will(returnValue(null));
|
||||||
@@ -148,6 +152,8 @@ public class SinglePassOutgoingSessionTest extends BriarTestCase {
|
|||||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)),
|
oneOf(db).generateBatch(with(contactId), with(any(int.class)),
|
||||||
with(any(long.class)));
|
with(any(long.class)));
|
||||||
will(returnValue(null));
|
will(returnValue(null));
|
||||||
|
// Remove listener
|
||||||
|
oneOf(eventBus).removeListener(session);
|
||||||
}});
|
}});
|
||||||
session.run();
|
session.run();
|
||||||
// Something should have been written
|
// Something should have been written
|
||||||
Reference in New Issue
Block a user