diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java
index 50cd9db82..d34965073 100644
--- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java
+++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java
@@ -3,8 +3,11 @@ package org.briarproject.plugins.droidtooth;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.briarproject.api.plugins.Plugin;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import android.bluetooth.BluetoothSocket;
@@ -13,30 +16,69 @@ class DroidtoothTransportConnection implements DuplexTransportConnection {
private final Plugin plugin;
private final BluetoothSocket socket;
+ private final Reader reader;
+ private final Writer writer;
+ private final AtomicBoolean halfClosed, closed;
DroidtoothTransportConnection(Plugin plugin, BluetoothSocket socket) {
this.plugin = plugin;
this.socket = socket;
+ reader = new Reader();
+ writer = new Writer();
+ halfClosed = new AtomicBoolean(false);
+ closed = new AtomicBoolean(false);
}
- public int getMaxFrameLength() {
- return plugin.getMaxFrameLength();
+ public TransportConnectionReader getReader() {
+ return reader;
}
- public long getMaxLatency() {
- return plugin.getMaxLatency();
+ public TransportConnectionWriter getWriter() {
+ return writer;
}
- public InputStream getInputStream() throws IOException {
- return socket.getInputStream();
+ private class Reader implements TransportConnectionReader {
+
+ public int getMaxFrameLength() {
+ return plugin.getMaxFrameLength();
+ }
+
+ public long getMaxLatency() {
+ return plugin.getMaxLatency();
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return socket.getInputStream();
+ }
+
+ public void dispose(boolean exception, boolean recognised)
+ throws IOException {
+ if(halfClosed.getAndSet(true) || exception)
+ if(!closed.getAndSet(true)) socket.close();
+ }
}
- public OutputStream getOutputStream() throws IOException {
- return socket.getOutputStream();
- }
+ private class Writer implements TransportConnectionWriter {
- public void dispose(boolean exception, boolean recognised)
- throws IOException {
- socket.close();
+ public int getMaxFrameLength() {
+ return plugin.getMaxFrameLength();
+ }
+
+ public long getMaxLatency() {
+ return plugin.getMaxLatency();
+ }
+
+ public long getCapacity() {
+ return Long.MAX_VALUE;
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ return socket.getOutputStream();
+ }
+
+ public void dispose(boolean exception) throws IOException {
+ if(halfClosed.getAndSet(true) || exception)
+ if(!closed.getAndSet(true)) socket.close();
+ }
}
}
diff --git a/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java b/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java
index 14c490d18..ab0969eae 100644
--- a/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java
+++ b/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java
@@ -4,38 +4,80 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.briarproject.api.plugins.Plugin;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
class TorTransportConnection implements DuplexTransportConnection {
private final Plugin plugin;
private final Socket socket;
+ private final Reader reader;
+ private final Writer writer;
+ private final AtomicBoolean halfClosed, closed;
TorTransportConnection(Plugin plugin, Socket socket) {
this.plugin = plugin;
this.socket = socket;
+ reader = new Reader();
+ writer = new Writer();
+ halfClosed = new AtomicBoolean(false);
+ closed = new AtomicBoolean(false);
}
- public int getMaxFrameLength() {
- return plugin.getMaxFrameLength();
+ public TransportConnectionReader getReader() {
+ return reader;
}
- public long getMaxLatency() {
- return plugin.getMaxLatency();
+ public TransportConnectionWriter getWriter() {
+ return writer;
}
- public InputStream getInputStream() throws IOException {
- return socket.getInputStream();
+ private class Reader implements TransportConnectionReader {
+
+ public int getMaxFrameLength() {
+ return plugin.getMaxFrameLength();
+ }
+
+ public long getMaxLatency() {
+ return plugin.getMaxLatency();
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return socket.getInputStream();
+ }
+
+ public void dispose(boolean exception, boolean recognised)
+ throws IOException {
+ if(halfClosed.getAndSet(true) || exception)
+ if(!closed.getAndSet(true)) socket.close();
+ }
}
- public OutputStream getOutputStream() throws IOException {
- return socket.getOutputStream();
- }
+ private class Writer implements TransportConnectionWriter {
- public void dispose(boolean exception, boolean recognised)
- throws IOException {
- socket.close();
+ public int getMaxFrameLength() {
+ return plugin.getMaxFrameLength();
+ }
+
+ public long getMaxLatency() {
+ return plugin.getMaxLatency();
+ }
+
+ public long getCapacity() {
+ return Long.MAX_VALUE;
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ return socket.getOutputStream();
+ }
+
+ public void dispose(boolean exception) throws IOException {
+ if(halfClosed.getAndSet(true) || exception)
+ if(!closed.getAndSet(true)) socket.close();
+ }
}
}
diff --git a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
index b946f5760..ba9c86224 100644
--- a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
+++ b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
@@ -72,13 +72,10 @@ public interface CryptoComponent {
/**
* Derives a frame key from the given temporary secret and stream number.
- * @param alice indicates whether the key is for a connection initiated by
+ * @param alice indicates whether the key is for a stream initiated by
* Alice or Bob.
- * @param initiator indicates whether the key is for the initiator's or the
- * responder's side of the connection.
*/
- SecretKey deriveFrameKey(byte[] secret, long streamNumber, boolean alice,
- boolean initiator);
+ SecretKey deriveFrameKey(byte[] secret, long streamNumber, boolean alice);
/** Returns a cipher for encrypting and authenticating frames. */
AuthenticatedCipher getFrameCipher();
diff --git a/briar-api/src/org/briarproject/api/messaging/MessagingSession.java b/briar-api/src/org/briarproject/api/messaging/MessagingSession.java
new file mode 100644
index 000000000..786922c81
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/messaging/MessagingSession.java
@@ -0,0 +1,19 @@
+package org.briarproject.api.messaging;
+
+import java.io.IOException;
+
+public interface MessagingSession {
+
+ /**
+ * Runs the session. This method returns when there are no more packets to
+ * send or when the {@link #interrupt()} method has been called.
+ */
+ void run() throws IOException;
+
+ /**
+ * Interrupts the session, causing the {@link #run()} method to return at
+ * the next opportunity or throw an {@link java.io.IOException IOException}
+ * if it cannot return cleanly.
+ */
+ void interrupt();
+}
diff --git a/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java b/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java
new file mode 100644
index 000000000..a014fe92f
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java
@@ -0,0 +1,14 @@
+package org.briarproject.api.messaging;
+
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
+import org.briarproject.api.transport.StreamContext;
+
+public interface MessagingSessionFactory {
+
+ MessagingSession createIncomingSession(StreamContext ctx,
+ TransportConnectionReader r);
+
+ MessagingSession createOutgoingSession(StreamContext ctx,
+ TransportConnectionWriter w, boolean duplex);
+}
diff --git a/briar-api/src/org/briarproject/api/messaging/duplex/DuplexConnectionFactory.java b/briar-api/src/org/briarproject/api/messaging/duplex/DuplexConnectionFactory.java
deleted file mode 100644
index 977df2176..000000000
--- a/briar-api/src/org/briarproject/api/messaging/duplex/DuplexConnectionFactory.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.briarproject.api.messaging.duplex;
-
-import org.briarproject.api.ContactId;
-import org.briarproject.api.TransportId;
-import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
-import org.briarproject.api.transport.StreamContext;
-
-public interface DuplexConnectionFactory {
-
- void createIncomingConnection(StreamContext ctx,
- DuplexTransportConnection d);
-
- void createOutgoingConnection(ContactId c, TransportId t,
- DuplexTransportConnection d);
-}
diff --git a/briar-api/src/org/briarproject/api/messaging/simplex/SimplexConnectionFactory.java b/briar-api/src/org/briarproject/api/messaging/simplex/SimplexConnectionFactory.java
deleted file mode 100644
index 61f75a084..000000000
--- a/briar-api/src/org/briarproject/api/messaging/simplex/SimplexConnectionFactory.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.briarproject.api.messaging.simplex;
-
-import org.briarproject.api.ContactId;
-import org.briarproject.api.TransportId;
-import org.briarproject.api.plugins.simplex.SimplexTransportReader;
-import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
-import org.briarproject.api.transport.StreamContext;
-
-public interface SimplexConnectionFactory {
-
- void createIncomingConnection(StreamContext ctx,
- SimplexTransportReader r);
-
- void createOutgoingConnection(ContactId c, TransportId t,
- SimplexTransportWriter w);
-}
diff --git a/briar-api/src/org/briarproject/api/plugins/TransportConnectionReader.java b/briar-api/src/org/briarproject/api/plugins/TransportConnectionReader.java
new file mode 100644
index 000000000..71bf0cc03
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/plugins/TransportConnectionReader.java
@@ -0,0 +1,32 @@
+package org.briarproject.api.plugins;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * An interface for reading data from a transport connection. The reader is not
+ * responsible for decrypting or authenticating the data.
+ */
+public interface TransportConnectionReader {
+
+ /** Returns the maximum frame length of the transport in bytes. */
+ int getMaxFrameLength();
+
+ /** Returns the maximum latency of the transport in milliseconds. */
+ long getMaxLatency();
+
+ /** Returns an input stream for reading from the transport connection. */
+ InputStream getInputStream() throws IOException;
+
+ /**
+ * Marks this side of the transport connection closed. If the transport is
+ * simplex, the connection is closed. If the transport is duplex, the
+ * connection is closed if exception is true or the other side of
+ * the connection has been marked as closed.
+ * @param exception true if the connection is being closed because of an
+ * exception. This may affect how resources are disposed of.
+ * @param recognised true if the pseudo-random tag was recognised. This may
+ * affect how resources are disposed of.
+ */
+ void dispose(boolean exception, boolean recognised) throws IOException;
+}
diff --git a/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java b/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java
new file mode 100644
index 000000000..554e5de8a
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java
@@ -0,0 +1,33 @@
+package org.briarproject.api.plugins;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An interface for writing data to a transport connection. The writer is not
+ * responsible for authenticating or encrypting the data.
+ */
+public interface TransportConnectionWriter {
+
+ /** Returns the maximum frame length of the transport in bytes. */
+ int getMaxFrameLength();
+
+ /** Returns the maximum latency of the transport in milliseconds. */
+ long getMaxLatency();
+
+ /** Returns the capacity of the transport connection in bytes. */
+ long getCapacity();
+
+ /** Returns an output stream for writing to the transport connection. */
+ OutputStream getOutputStream() throws IOException;
+
+ /**
+ * Marks this side of the transport connection closed. If the transport is
+ * simplex, the connection is closed. If the transport is duplex, the
+ * connection is closed if exception is true or the other side of
+ * the connection has been marked as closed.
+ * @param exception true if the connection is being closed because of an
+ * exception. This may affect how resources are disposed of.
+ */
+ void dispose(boolean exception) throws IOException;
+}
diff --git a/briar-api/src/org/briarproject/api/plugins/duplex/DuplexTransportConnection.java b/briar-api/src/org/briarproject/api/plugins/duplex/DuplexTransportConnection.java
index 7193c0849..8440fe4ad 100644
--- a/briar-api/src/org/briarproject/api/plugins/duplex/DuplexTransportConnection.java
+++ b/briar-api/src/org/briarproject/api/plugins/duplex/DuplexTransportConnection.java
@@ -1,8 +1,7 @@
package org.briarproject.api.plugins.duplex;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
/**
* An interface for reading and writing data over a duplex transport. The
@@ -11,23 +10,11 @@ import java.io.OutputStream;
*/
public interface DuplexTransportConnection {
- /** Returns the maximum frame length of the transport in bytes. */
- int getMaxFrameLength();
+ /** Returns a {@link org.briarproject.api.plugins.TransportConnectionReader
+ * TransportConnectionReader} for reading from the connection. */
+ TransportConnectionReader getReader();
- /** Returns the maximum latency of the transport in milliseconds. */
- long getMaxLatency();
-
- /** Returns an input stream for reading from the connection. */
- InputStream getInputStream() throws IOException;
-
- /** Returns an output stream for writing to the connection. */
- OutputStream getOutputStream() throws IOException;
-
- /**
- * Closes the connection and disposes of any associated resources. The
- * first argument indicates whether the connection is being closed because
- * of an exception and the second argument indicates whether the connection
- * was recognised, which may affect how resources are disposed of.
- */
- void dispose(boolean exception, boolean recognised) throws IOException;
+ /** Returns a {@link org.briarproject.api.plugins.TransportConnectionWriter
+ * TransportConnectionWriter} for writing to the connection. */
+ TransportConnectionWriter getWriter();
}
diff --git a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPlugin.java b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPlugin.java
index c6334becc..9e4a1defd 100644
--- a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPlugin.java
+++ b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPlugin.java
@@ -2,6 +2,8 @@ package org.briarproject.api.plugins.simplex;
import org.briarproject.api.ContactId;
import org.briarproject.api.plugins.Plugin;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
/** An interface for transport plugins that support simplex communication. */
public interface SimplexPlugin extends Plugin {
@@ -11,12 +13,12 @@ public interface SimplexPlugin extends Plugin {
* current transport and configuration properties. Returns null if a reader
* could not be created.
*/
- SimplexTransportReader createReader(ContactId c);
+ TransportConnectionReader createReader(ContactId c);
/**
* Attempts to create and return a writer for the given contact using the
* current transport and configuration properties. Returns null if a writer
* could not be created.
*/
- SimplexTransportWriter createWriter(ContactId c);
+ TransportConnectionWriter createWriter(ContactId c);
}
diff --git a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPluginCallback.java b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPluginCallback.java
index d4cf81611..1f597c70d 100644
--- a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPluginCallback.java
+++ b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPluginCallback.java
@@ -2,6 +2,8 @@ package org.briarproject.api.plugins.simplex;
import org.briarproject.api.ContactId;
import org.briarproject.api.plugins.PluginCallback;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
/**
* An interface for handling readers and writers created by a simplex transport
@@ -9,7 +11,7 @@ import org.briarproject.api.plugins.PluginCallback;
*/
public interface SimplexPluginCallback extends PluginCallback {
- void readerCreated(SimplexTransportReader r);
+ void readerCreated(TransportConnectionReader r);
- void writerCreated(ContactId c, SimplexTransportWriter w);
+ void writerCreated(ContactId c, TransportConnectionWriter w);
}
diff --git a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportReader.java b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportReader.java
deleted file mode 100644
index 387a7a03b..000000000
--- a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportReader.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.briarproject.api.plugins.simplex;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * An interface for reading data from a simplex transport. The reader is not
- * responsible for decrypting or authenticating the data before returning it.
- */
-public interface SimplexTransportReader {
-
- /** Returns the maximum frame length of the transport in bytes. */
- int getMaxFrameLength();
-
- /** Returns an input stream for reading from the transport. */
- InputStream getInputStream() throws IOException;
-
- /**
- * Closes the reader and disposes of any associated resources. The first
- * argument indicates whether the reader is being closed because of an
- * exception and the second argument indicates whether the connection was
- * recognised, which may affect how resources are disposed of.
- */
- void dispose(boolean exception, boolean recognised) throws IOException;
-}
diff --git a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportWriter.java b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportWriter.java
deleted file mode 100644
index 0b50a7898..000000000
--- a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportWriter.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.briarproject.api.plugins.simplex;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * An interface for writing data to a simplex transport. The writer is not
- * responsible for authenticating or encrypting the data before writing it.
- */
-public interface SimplexTransportWriter {
-
- /** Returns the capacity of the transport in bytes. */
- long getCapacity();
-
- /** Returns the maximum frame length of the transport in bytes. */
- int getMaxFrameLength();
-
- /** Returns the maximum latency of the transport in milliseconds. */
- long getMaxLatency();
-
- /** Returns an output stream for writing to the transport. */
- OutputStream getOutputStream() throws IOException;
-
- /**
- * Closes the writer and disposes of any associated resources. The
- * argument indicates whether the writer is being closed because of an
- * exception, which may affect how resources are disposed of.
- */
- void dispose(boolean exception) throws IOException;
-}
diff --git a/briar-api/src/org/briarproject/api/transport/ConnectionDispatcher.java b/briar-api/src/org/briarproject/api/transport/ConnectionDispatcher.java
index 27f41ce3f..6b2058c38 100644
--- a/briar-api/src/org/briarproject/api/transport/ConnectionDispatcher.java
+++ b/briar-api/src/org/briarproject/api/transport/ConnectionDispatcher.java
@@ -2,18 +2,18 @@ package org.briarproject.api.transport;
import org.briarproject.api.ContactId;
import org.briarproject.api.TransportId;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
-import org.briarproject.api.plugins.simplex.SimplexTransportReader;
-import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
public interface ConnectionDispatcher {
- void dispatchIncomingConnection(TransportId t, SimplexTransportReader r);
+ void dispatchIncomingConnection(TransportId t, TransportConnectionReader r);
void dispatchIncomingConnection(TransportId t, DuplexTransportConnection d);
void dispatchOutgoingConnection(ContactId c, TransportId t,
- SimplexTransportWriter w);
+ TransportConnectionWriter w);
void dispatchOutgoingConnection(ContactId c, TransportId t,
DuplexTransportConnection d);
diff --git a/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java b/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java
index 8c43c30eb..14459ec60 100644
--- a/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java
+++ b/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java
@@ -6,7 +6,7 @@ public interface StreamReaderFactory {
/** Creates a {@link StreamReader} for a transport connection. */
StreamReader createStreamReader(InputStream in, int maxFrameLength,
- StreamContext ctx, boolean incoming, boolean initiator);
+ StreamContext ctx);
/** Creates a {@link StreamReader} for an invitation connection. */
StreamReader createInvitationStreamReader(InputStream in,
diff --git a/briar-api/src/org/briarproject/api/transport/StreamWriter.java b/briar-api/src/org/briarproject/api/transport/StreamWriter.java
index e5f50ecce..2684742db 100644
--- a/briar-api/src/org/briarproject/api/transport/StreamWriter.java
+++ b/briar-api/src/org/briarproject/api/transport/StreamWriter.java
@@ -10,10 +10,4 @@ public interface StreamWriter {
* be written.
*/
OutputStream getOutputStream();
-
- /**
- * Returns the maximum number of bytes that can be written to the output
- * stream.
- */
- long getRemainingCapacity();
}
diff --git a/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java b/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java
index f3b8e1c2f..f038a7522 100644
--- a/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java
+++ b/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java
@@ -6,8 +6,7 @@ public interface StreamWriterFactory {
/** Creates a {@link StreamWriter} for a transport connection. */
StreamWriter createStreamWriter(OutputStream out, int maxFrameLength,
- long capacity, StreamContext ctx, boolean incoming,
- boolean initiator);
+ StreamContext ctx);
/** Creates a {@link StreamWriter} for an invitation connection. */
StreamWriter createInvitationStreamWriter(OutputStream out,
diff --git a/briar-api/src/org/briarproject/api/transport/TagRecogniser.java b/briar-api/src/org/briarproject/api/transport/TagRecogniser.java
index a6823a2e9..b73d09697 100644
--- a/briar-api/src/org/briarproject/api/transport/TagRecogniser.java
+++ b/briar-api/src/org/briarproject/api/transport/TagRecogniser.java
@@ -4,12 +4,13 @@ import org.briarproject.api.ContactId;
import org.briarproject.api.TransportId;
import org.briarproject.api.db.DbException;
-/** Maintains the table of expected tags for recognising incoming streams. */
+/** Keeps track of expected tags and uses them to recognise incoming streams. */
public interface TagRecogniser {
/**
- * Returns a {@link StreamContext} for reading from the stream with the
- * given tag if the tag was expected, or null if the tag was unexpected.
+ * Looks up the given tag and returns a {@link StreamContext} for reading
+ * from the stream if the tag was expected, or null if the tag was
+ * unexpected.
*/
StreamContext recogniseTag(TransportId t, byte[] tag) throws DbException;
diff --git a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
index dc4dbba95..d616a50b7 100644
--- a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
+++ b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
@@ -76,14 +76,10 @@ class CryptoComponentImpl implements CryptoComponent {
// Labels for key derivation
private static final byte[] A_TAG = { 'A', '_', 'T', 'A', 'G', '\0' };
private static final byte[] B_TAG = { 'B', '_', 'T', 'A', 'G', '\0' };
- private static final byte[] A_FRAME_A =
- { 'A', '_', 'F', 'R', 'A', 'M', 'E', '_', 'A', '\0' };
- private static final byte[] A_FRAME_B =
- { 'A', '_', 'F', 'R', 'A', 'M', 'E', '_', 'B', '\0' };
- private static final byte[] B_FRAME_A =
- { 'B', '_', 'F', 'R', 'A', 'M', 'E', '_', 'A', '\0' };
- private static final byte[] B_FRAME_B =
- { 'B', '_', 'F', 'R', 'A', 'M', 'E', '_', 'B', '\0' };
+ private static final byte[] A_FRAME =
+ { 'A', '_', 'F', 'R', 'A', 'M', 'E', '\0' };
+ private static final byte[] B_FRAME =
+ { 'B', '_', 'F', 'R', 'A', 'M', 'E', '\0' };
// Blank secret for argument validation
private static final byte[] BLANK_SECRET = new byte[CIPHER_KEY_BYTES];
@@ -288,20 +284,15 @@ class CryptoComponentImpl implements CryptoComponent {
}
public SecretKey deriveFrameKey(byte[] secret, long streamNumber,
- boolean alice, boolean initiator) {
+ boolean alice) {
if(secret.length != CIPHER_KEY_BYTES)
throw new IllegalArgumentException();
if(Arrays.equals(secret, BLANK_SECRET))
throw new IllegalArgumentException();
if(streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
- if(alice) {
- if(initiator) return deriveKey(secret, A_FRAME_A, streamNumber);
- else return deriveKey(secret, A_FRAME_B, streamNumber);
- } else {
- if(initiator) return deriveKey(secret, B_FRAME_A, streamNumber);
- else return deriveKey(secret, B_FRAME_B, streamNumber);
- }
+ if(alice) return deriveKey(secret, A_FRAME, streamNumber);
+ else return deriveKey(secret, B_FRAME, streamNumber);
}
private SecretKey deriveKey(byte[] secret, byte[] label, long context) {
diff --git a/briar-core/src/org/briarproject/invitation/AliceConnector.java b/briar-core/src/org/briarproject/invitation/AliceConnector.java
index 455e1a6bd..955dc1588 100644
--- a/briar-core/src/org/briarproject/invitation/AliceConnector.java
+++ b/briar-core/src/org/briarproject/invitation/AliceConnector.java
@@ -75,8 +75,8 @@ class AliceConnector extends Connector {
Writer w;
byte[] secret;
try {
- in = conn.getInputStream();
- out = conn.getOutputStream();
+ in = conn.getReader().getInputStream();
+ out = conn.getWriter().getOutputStream();
r = readerFactory.createReader(in);
w = writerFactory.createWriter(out);
// Alice goes first
@@ -130,7 +130,7 @@ class AliceConnector extends Connector {
// Confirmation succeeded - upgrade to a secure connection
if(LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation succeeded");
- int maxFrameLength = conn.getMaxFrameLength();
+ int maxFrameLength = conn.getReader().getMaxFrameLength();
StreamReader streamReader =
streamReaderFactory.createInvitationStreamReader(in,
maxFrameLength, secret, false);
diff --git a/briar-core/src/org/briarproject/invitation/BobConnector.java b/briar-core/src/org/briarproject/invitation/BobConnector.java
index 584f5b81f..4da2407eb 100644
--- a/briar-core/src/org/briarproject/invitation/BobConnector.java
+++ b/briar-core/src/org/briarproject/invitation/BobConnector.java
@@ -69,8 +69,8 @@ class BobConnector extends Connector {
Writer w;
byte[] secret;
try {
- in = conn.getInputStream();
- out = conn.getOutputStream();
+ in = conn.getReader().getInputStream();
+ out = conn.getWriter().getOutputStream();
r = readerFactory.createReader(in);
w = writerFactory.createWriter(out);
// Alice goes first
@@ -130,7 +130,7 @@ class BobConnector extends Connector {
// Confirmation succeeded - upgrade to a secure connection
if(LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation succeeded");
- int maxFrameLength = conn.getMaxFrameLength();
+ int maxFrameLength = conn.getReader().getMaxFrameLength();
StreamReader streamReader =
streamReaderFactory.createInvitationStreamReader(in,
maxFrameLength, secret, true);
diff --git a/briar-core/src/org/briarproject/invitation/Connector.java b/briar-core/src/org/briarproject/invitation/Connector.java
index 36b84860f..32347a130 100644
--- a/briar-core/src/org/briarproject/invitation/Connector.java
+++ b/briar-core/src/org/briarproject/invitation/Connector.java
@@ -311,7 +311,8 @@ abstract class Connector extends Thread {
boolean exception) {
try {
LOG.info("Closing connection");
- conn.dispose(exception, true);
+ conn.getReader().dispose(exception, true);
+ conn.getWriter().dispose(exception);
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
diff --git a/briar-core/src/org/briarproject/messaging/simplex/IncomingSimplexConnection.java b/briar-core/src/org/briarproject/messaging/IncomingSession.java
similarity index 62%
rename from briar-core/src/org/briarproject/messaging/simplex/IncomingSimplexConnection.java
rename to briar-core/src/org/briarproject/messaging/IncomingSession.java
index 72162747c..37547d3cd 100644
--- a/briar-core/src/org/briarproject/messaging/simplex/IncomingSimplexConnection.java
+++ b/briar-core/src/org/briarproject/messaging/IncomingSession.java
@@ -1,4 +1,4 @@
-package org.briarproject.messaging.simplex;
+package org.briarproject.messaging;
import static java.util.logging.Level.WARNING;
@@ -10,12 +10,12 @@ import java.util.logging.Logger;
import org.briarproject.api.ContactId;
import org.briarproject.api.FormatException;
-import org.briarproject.api.TransportId;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.messaging.Ack;
import org.briarproject.api.messaging.Message;
import org.briarproject.api.messaging.MessageVerifier;
+import org.briarproject.api.messaging.MessagingSession;
import org.briarproject.api.messaging.PacketReader;
import org.briarproject.api.messaging.PacketReaderFactory;
import org.briarproject.api.messaging.RetentionAck;
@@ -25,103 +25,91 @@ import org.briarproject.api.messaging.SubscriptionUpdate;
import org.briarproject.api.messaging.TransportAck;
import org.briarproject.api.messaging.TransportUpdate;
import org.briarproject.api.messaging.UnverifiedMessage;
-import org.briarproject.api.plugins.simplex.SimplexTransportReader;
-import org.briarproject.api.transport.ConnectionRegistry;
+import org.briarproject.api.plugins.TransportConnectionReader;
import org.briarproject.api.transport.StreamContext;
import org.briarproject.api.transport.StreamReader;
import org.briarproject.api.transport.StreamReaderFactory;
-import org.briarproject.util.ByteUtils;
-class IncomingSimplexConnection {
+/**
+ * An incoming {@link org.briarproject.api.messaging.MessagingSession
+ * MessagingSession}.
+ */
+class IncomingSession implements MessagingSession {
private static final Logger LOG =
- Logger.getLogger(IncomingSimplexConnection.class.getName());
+ Logger.getLogger(IncomingSession.class.getName());
+ private final DatabaseComponent db;
private final Executor dbExecutor, cryptoExecutor;
private final MessageVerifier messageVerifier;
- private final DatabaseComponent db;
- private final ConnectionRegistry connRegistry;
- private final StreamReaderFactory connReaderFactory;
+ private final StreamReaderFactory streamReaderFactory;
private final PacketReaderFactory packetReaderFactory;
private final StreamContext ctx;
- private final SimplexTransportReader transport;
+ private final TransportConnectionReader transportReader;
private final ContactId contactId;
- private final TransportId transportId;
- IncomingSimplexConnection(Executor dbExecutor, Executor cryptoExecutor,
- MessageVerifier messageVerifier, DatabaseComponent db,
- ConnectionRegistry connRegistry,
- StreamReaderFactory connReaderFactory,
+ private volatile boolean interrupted = false;
+
+ IncomingSession(DatabaseComponent db, Executor dbExecutor,
+ Executor cryptoExecutor, MessageVerifier messageVerifier,
+ StreamReaderFactory streamReaderFactory,
PacketReaderFactory packetReaderFactory, StreamContext ctx,
- SimplexTransportReader transport) {
+ TransportConnectionReader transportReader) {
+ this.db = db;
this.dbExecutor = dbExecutor;
this.cryptoExecutor = cryptoExecutor;
this.messageVerifier = messageVerifier;
- this.db = db;
- this.connRegistry = connRegistry;
- this.connReaderFactory = connReaderFactory;
+ this.streamReaderFactory = streamReaderFactory;
this.packetReaderFactory = packetReaderFactory;
this.ctx = ctx;
- this.transport = transport;
+ this.transportReader = transportReader;
contactId = ctx.getContactId();
- transportId = ctx.getTransportId();
}
- void read() {
- connRegistry.registerConnection(contactId, transportId);
- try {
- InputStream in = transport.getInputStream();
- int maxFrameLength = transport.getMaxFrameLength();
- StreamReader conn = connReaderFactory.createStreamReader(in,
- maxFrameLength, ctx, true, true);
- in = conn.getInputStream();
- PacketReader reader = packetReaderFactory.createPacketReader(in);
- // Read packets until EOF
- while(!reader.eof()) {
- if(reader.hasAck()) {
- Ack a = reader.readAck();
- dbExecutor.execute(new ReceiveAck(a));
- } else if(reader.hasMessage()) {
- UnverifiedMessage m = reader.readMessage();
- cryptoExecutor.execute(new VerifyMessage(m));
- } else if(reader.hasRetentionAck()) {
- RetentionAck a = reader.readRetentionAck();
- dbExecutor.execute(new ReceiveRetentionAck(a));
- } else if(reader.hasRetentionUpdate()) {
- RetentionUpdate u = reader.readRetentionUpdate();
- dbExecutor.execute(new ReceiveRetentionUpdate(u));
- } else if(reader.hasSubscriptionAck()) {
- SubscriptionAck a = reader.readSubscriptionAck();
- dbExecutor.execute(new ReceiveSubscriptionAck(a));
- } else if(reader.hasSubscriptionUpdate()) {
- SubscriptionUpdate u = reader.readSubscriptionUpdate();
- dbExecutor.execute(new ReceiveSubscriptionUpdate(u));
- } else if(reader.hasTransportAck()) {
- TransportAck a = reader.readTransportAck();
- dbExecutor.execute(new ReceiveTransportAck(a));
- } else if(reader.hasTransportUpdate()) {
- TransportUpdate u = reader.readTransportUpdate();
- dbExecutor.execute(new ReceiveTransportUpdate(u));
- } else {
- throw new FormatException();
- }
+ public void run() throws IOException {
+ InputStream in = transportReader.getInputStream();
+ int maxFrameLength = transportReader.getMaxFrameLength();
+ StreamReader streamReader = streamReaderFactory.createStreamReader(in,
+ maxFrameLength, ctx);
+ in = streamReader.getInputStream();
+ PacketReader packetReader = packetReaderFactory.createPacketReader(in);
+ // Read packets until interrupted or EOF
+ while(!interrupted && !packetReader.eof()) {
+ if(packetReader.hasAck()) {
+ Ack a = packetReader.readAck();
+ dbExecutor.execute(new ReceiveAck(a));
+ } else if(packetReader.hasMessage()) {
+ UnverifiedMessage m = packetReader.readMessage();
+ cryptoExecutor.execute(new VerifyMessage(m));
+ } else if(packetReader.hasRetentionAck()) {
+ RetentionAck a = packetReader.readRetentionAck();
+ dbExecutor.execute(new ReceiveRetentionAck(a));
+ } else if(packetReader.hasRetentionUpdate()) {
+ RetentionUpdate u = packetReader.readRetentionUpdate();
+ dbExecutor.execute(new ReceiveRetentionUpdate(u));
+ } else if(packetReader.hasSubscriptionAck()) {
+ SubscriptionAck a = packetReader.readSubscriptionAck();
+ dbExecutor.execute(new ReceiveSubscriptionAck(a));
+ } else if(packetReader.hasSubscriptionUpdate()) {
+ SubscriptionUpdate u = packetReader.readSubscriptionUpdate();
+ dbExecutor.execute(new ReceiveSubscriptionUpdate(u));
+ } else if(packetReader.hasTransportAck()) {
+ TransportAck a = packetReader.readTransportAck();
+ dbExecutor.execute(new ReceiveTransportAck(a));
+ } else if(packetReader.hasTransportUpdate()) {
+ TransportUpdate u = packetReader.readTransportUpdate();
+ dbExecutor.execute(new ReceiveTransportUpdate(u));
+ } else {
+ throw new FormatException();
}
- dispose(false, true);
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- } finally {
- connRegistry.unregisterConnection(contactId, transportId);
}
+ in.close();
}
- private void dispose(boolean exception, boolean recognised) {
- ByteUtils.erase(ctx.getSecret());
- try {
- transport.dispose(exception, recognised);
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
+ public void interrupt() {
+ // This won't interrupt a blocking read, but the read will throw an
+ // exception when the transport connection is closed
+ interrupted = true;
}
private class ReceiveAck implements Runnable {
diff --git a/briar-core/src/org/briarproject/messaging/MessagingModule.java b/briar-core/src/org/briarproject/messaging/MessagingModule.java
index c2dde2202..5986266b8 100644
--- a/briar-core/src/org/briarproject/messaging/MessagingModule.java
+++ b/briar-core/src/org/briarproject/messaging/MessagingModule.java
@@ -1,5 +1,7 @@
package org.briarproject.messaging;
+import javax.inject.Singleton;
+
import org.briarproject.api.Author;
import org.briarproject.api.AuthorFactory;
import org.briarproject.api.crypto.CryptoComponent;
@@ -9,6 +11,7 @@ import org.briarproject.api.messaging.MessageFactory;
import org.briarproject.api.messaging.MessageVerifier;
import org.briarproject.api.messaging.PacketReaderFactory;
import org.briarproject.api.messaging.PacketWriterFactory;
+import org.briarproject.api.messaging.MessagingSessionFactory;
import org.briarproject.api.messaging.SubscriptionUpdate;
import org.briarproject.api.messaging.UnverifiedMessage;
import org.briarproject.api.serial.StructReader;
@@ -18,6 +21,7 @@ import com.google.inject.Provides;
public class MessagingModule extends AbstractModule {
+ @Override
protected void configure() {
bind(AuthorFactory.class).to(AuthorFactoryImpl.class);
bind(GroupFactory.class).to(GroupFactoryImpl.class);
@@ -25,6 +29,8 @@ public class MessagingModule extends AbstractModule {
bind(MessageVerifier.class).to(MessageVerifierImpl.class);
bind(PacketReaderFactory.class).to(PacketReaderFactoryImpl.class);
bind(PacketWriterFactory.class).to(PacketWriterFactoryImpl.class);
+ bind(MessagingSessionFactory.class).to(
+ MessagingSessionFactoryImpl.class).in(Singleton.class);
}
@Provides
diff --git a/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java b/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java
new file mode 100644
index 000000000..f8b3792a4
--- /dev/null
+++ b/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java
@@ -0,0 +1,67 @@
+package org.briarproject.messaging;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+import org.briarproject.api.crypto.CryptoExecutor;
+import org.briarproject.api.db.DatabaseComponent;
+import org.briarproject.api.db.DatabaseExecutor;
+import org.briarproject.api.event.EventBus;
+import org.briarproject.api.messaging.MessageVerifier;
+import org.briarproject.api.messaging.MessagingSession;
+import org.briarproject.api.messaging.PacketReaderFactory;
+import org.briarproject.api.messaging.PacketWriterFactory;
+import org.briarproject.api.messaging.MessagingSessionFactory;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
+import org.briarproject.api.transport.StreamContext;
+import org.briarproject.api.transport.StreamReaderFactory;
+import org.briarproject.api.transport.StreamWriterFactory;
+
+class MessagingSessionFactoryImpl implements MessagingSessionFactory {
+
+ private final DatabaseComponent db;
+ private final Executor dbExecutor, cryptoExecutor;
+ private final MessageVerifier messageVerifier;
+ private final EventBus eventBus;
+ private final StreamReaderFactory streamReaderFactory;
+ private final StreamWriterFactory streamWriterFactory;
+ private final PacketReaderFactory packetReaderFactory;
+ private final PacketWriterFactory packetWriterFactory;
+
+ @Inject
+ MessagingSessionFactoryImpl(DatabaseComponent db,
+ @DatabaseExecutor Executor dbExecutor,
+ @CryptoExecutor Executor cryptoExecutor,
+ MessageVerifier messageVerifier, EventBus eventBus,
+ StreamReaderFactory streamReaderFactory,
+ StreamWriterFactory streamWriterFactory,
+ PacketReaderFactory packetReaderFactory,
+ PacketWriterFactory packetWriterFactory) {
+ this.db = db;
+ this.dbExecutor = dbExecutor;
+ this.cryptoExecutor = cryptoExecutor;
+ this.messageVerifier = messageVerifier;
+ this.eventBus = eventBus;
+ this.streamReaderFactory = streamReaderFactory;
+ this.streamWriterFactory = streamWriterFactory;
+ this.packetReaderFactory = packetReaderFactory;
+ this.packetWriterFactory = packetWriterFactory;
+ }
+
+ public MessagingSession createIncomingSession(StreamContext ctx,
+ TransportConnectionReader r) {
+ return new IncomingSession(db, dbExecutor, cryptoExecutor,
+ messageVerifier, streamReaderFactory, packetReaderFactory,
+ ctx, r);
+ }
+
+ public MessagingSession createOutgoingSession(StreamContext ctx,
+ TransportConnectionWriter w, boolean duplex) {
+ if(duplex) return new ReactiveOutgoingSession(db, dbExecutor, eventBus,
+ streamWriterFactory, packetWriterFactory, ctx, w);
+ else return new SinglePassOutgoingSession(db, dbExecutor,
+ streamWriterFactory, packetWriterFactory, ctx, w);
+ }
+}
diff --git a/briar-core/src/org/briarproject/messaging/ReactiveOutgoingSession.java b/briar-core/src/org/briarproject/messaging/ReactiveOutgoingSession.java
new file mode 100644
index 000000000..c3eae5105
--- /dev/null
+++ b/briar-core/src/org/briarproject/messaging/ReactiveOutgoingSession.java
@@ -0,0 +1,518 @@
+package org.briarproject.messaging;
+
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.logging.Logger;
+
+import org.briarproject.api.ContactId;
+import org.briarproject.api.db.DatabaseComponent;
+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.LocalSubscriptionsUpdatedEvent;
+import org.briarproject.api.event.LocalTransportsUpdatedEvent;
+import org.briarproject.api.event.MessageAddedEvent;
+import org.briarproject.api.event.MessageExpiredEvent;
+import org.briarproject.api.event.MessageRequestedEvent;
+import org.briarproject.api.event.MessageToAckEvent;
+import org.briarproject.api.event.MessageToRequestEvent;
+import org.briarproject.api.event.RemoteRetentionTimeUpdatedEvent;
+import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
+import org.briarproject.api.event.RemoteTransportsUpdatedEvent;
+import org.briarproject.api.event.TransportRemovedEvent;
+import org.briarproject.api.messaging.Ack;
+import org.briarproject.api.messaging.MessagingSession;
+import org.briarproject.api.messaging.Offer;
+import org.briarproject.api.messaging.PacketWriter;
+import org.briarproject.api.messaging.PacketWriterFactory;
+import org.briarproject.api.messaging.Request;
+import org.briarproject.api.messaging.RetentionAck;
+import org.briarproject.api.messaging.RetentionUpdate;
+import org.briarproject.api.messaging.SubscriptionAck;
+import org.briarproject.api.messaging.SubscriptionUpdate;
+import org.briarproject.api.messaging.TransportAck;
+import org.briarproject.api.messaging.TransportUpdate;
+import org.briarproject.api.plugins.TransportConnectionWriter;
+import org.briarproject.api.transport.StreamContext;
+import org.briarproject.api.transport.StreamWriter;
+import org.briarproject.api.transport.StreamWriterFactory;
+
+/**
+ * An outgoing {@link org.briarproject.api.messaging.MessagingSession
+ * MessagingSession} that keeps its output stream open and reacts to events
+ * that make packets available to send.
+ */
+class ReactiveOutgoingSession implements MessagingSession, EventListener {
+
+ private static final Logger LOG =
+ Logger.getLogger(ReactiveOutgoingSession.class.getName());
+
+ private static final ThrowingRunnable CLOSE =
+ new ThrowingRunnable() {
+ public void run() {}
+ };
+
+ private final DatabaseComponent db;
+ private final Executor dbExecutor;
+ private final EventBus eventBus;
+ private final StreamWriterFactory streamWriterFactory;
+ private final PacketWriterFactory packetWriterFactory;
+ private final StreamContext ctx;
+ private final TransportConnectionWriter transportWriter;
+ private final ContactId contactId;
+ private final long maxLatency;
+ private final BlockingQueue> writerTasks;
+
+ private volatile PacketWriter packetWriter = null;
+ private volatile boolean interrupted = false;
+
+ ReactiveOutgoingSession(DatabaseComponent db, Executor dbExecutor,
+ EventBus eventBus, StreamWriterFactory streamWriterFactory,
+ PacketWriterFactory packetWriterFactory, StreamContext ctx,
+ TransportConnectionWriter transportWriter) {
+ this.db = db;
+ this.dbExecutor = dbExecutor;
+ this.eventBus = eventBus;
+ this.streamWriterFactory = streamWriterFactory;
+ this.packetWriterFactory = packetWriterFactory;
+ this.ctx = ctx;
+ this.transportWriter = transportWriter;
+ contactId = ctx.getContactId();
+ maxLatency = transportWriter.getMaxLatency();
+ writerTasks = new LinkedBlockingQueue>();
+ }
+
+ public void run() throws IOException {
+ eventBus.addListener(this);
+ try {
+ OutputStream out = transportWriter.getOutputStream();
+ int maxFrameLength = transportWriter.getMaxFrameLength();
+ StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
+ out, maxFrameLength, ctx);
+ out = streamWriter.getOutputStream();
+ packetWriter = packetWriterFactory.createPacketWriter(out, true);
+ // 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());
+ dbExecutor.execute(new GenerateOffer());
+ dbExecutor.execute(new GenerateRequest());
+ // Write packets until interrupted
+ try {
+ while(!interrupted) {
+ ThrowingRunnable 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();
+ }
+ } finally {
+ eventBus.removeListener(this);
+ }
+ }
+
+ public void interrupt() {
+ interrupted = true;
+ writerTasks.add(CLOSE);
+ }
+
+ public void eventOccurred(Event e) {
+ if(e instanceof ContactRemovedEvent) {
+ ContactRemovedEvent c = (ContactRemovedEvent) e;
+ if(contactId.equals(c.getContactId())) {
+ LOG.info("Contact removed, closing");
+ interrupt();
+ }
+ } else if(e instanceof MessageAddedEvent) {
+ dbExecutor.execute(new GenerateOffer());
+ } else if(e instanceof MessageExpiredEvent) {
+ dbExecutor.execute(new GenerateRetentionUpdate());
+ } else if(e instanceof LocalSubscriptionsUpdatedEvent) {
+ LocalSubscriptionsUpdatedEvent l =
+ (LocalSubscriptionsUpdatedEvent) e;
+ if(l.getAffectedContacts().contains(contactId)) {
+ dbExecutor.execute(new GenerateSubscriptionUpdate());
+ dbExecutor.execute(new GenerateOffer());
+ }
+ } else if(e instanceof LocalTransportsUpdatedEvent) {
+ dbExecutor.execute(new GenerateTransportUpdates());
+ } else if(e instanceof MessageRequestedEvent) {
+ if(((MessageRequestedEvent) e).getContactId().equals(contactId))
+ dbExecutor.execute(new GenerateBatch());
+ } else if(e instanceof MessageToAckEvent) {
+ if(((MessageToAckEvent) e).getContactId().equals(contactId))
+ dbExecutor.execute(new GenerateAck());
+ } else if(e instanceof MessageToRequestEvent) {
+ if(((MessageToRequestEvent) e).getContactId().equals(contactId))
+ dbExecutor.execute(new GenerateRequest());
+ } else if(e instanceof RemoteRetentionTimeUpdatedEvent) {
+ dbExecutor.execute(new GenerateRetentionAck());
+ } else if(e instanceof RemoteSubscriptionsUpdatedEvent) {
+ dbExecutor.execute(new GenerateSubscriptionAck());
+ dbExecutor.execute(new GenerateOffer());
+ } else if(e instanceof RemoteTransportsUpdatedEvent) {
+ dbExecutor.execute(new GenerateTransportAcks());
+ } else if(e instanceof TransportRemovedEvent) {
+ TransportRemovedEvent t = (TransportRemovedEvent) e;
+ if(ctx.getTransportId().equals(t.getTransportId())) {
+ LOG.info("Transport removed, closing");
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateAck implements Runnable {
+
+ public void run() {
+ int maxMessages = packetWriter.getMaxMessagesForAck(Long.MAX_VALUE);
+ try {
+ Ack a = db.generateAck(contactId, maxMessages);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated ack: " + (a != null));
+ if(a != null) writerTasks.add(new WriteAck(a));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteAck implements ThrowingRunnable {
+
+ private final Ack ack;
+
+ private WriteAck(Ack ack) {
+ this.ack = ack;
+ }
+
+ public void run() throws IOException {
+ packetWriter.writeAck(ack);
+ LOG.info("Sent ack");
+ dbExecutor.execute(new GenerateAck());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateBatch implements Runnable {
+
+ public void run() {
+ try {
+ Collection b = db.generateRequestedBatch(contactId,
+ MAX_PACKET_LENGTH, maxLatency);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated batch: " + (b != null));
+ if(b != null) writerTasks.add(new WriteBatch(b));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteBatch implements ThrowingRunnable {
+
+ private final Collection batch;
+
+ private WriteBatch(Collection batch) {
+ this.batch = batch;
+ }
+
+ public void run() throws IOException {
+ for(byte[] raw : batch) packetWriter.writeMessage(raw);
+ LOG.info("Sent batch");
+ dbExecutor.execute(new GenerateBatch());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateOffer implements Runnable {
+
+ public void run() {
+ int maxMessages = packetWriter.getMaxMessagesForOffer(
+ Long.MAX_VALUE);
+ try {
+ Offer o = db.generateOffer(contactId, maxMessages, maxLatency);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated offer: " + (o != null));
+ if(o != null) writerTasks.add(new WriteOffer(o));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteOffer implements ThrowingRunnable {
+
+ private final Offer offer;
+
+ private WriteOffer(Offer offer) {
+ this.offer = offer;
+ }
+
+ public void run() throws IOException {
+ packetWriter.writeOffer(offer);
+ LOG.info("Sent offer");
+ dbExecutor.execute(new GenerateOffer());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateRequest implements Runnable {
+
+ public void run() {
+ int maxMessages = packetWriter.getMaxMessagesForRequest(
+ Long.MAX_VALUE);
+ try {
+ Request r = db.generateRequest(contactId, maxMessages);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated request: " + (r != null));
+ if(r != null) writerTasks.add(new WriteRequest(r));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteRequest implements ThrowingRunnable {
+
+ private final Request request;
+
+ private WriteRequest(Request request) {
+ this.request = request;
+ }
+
+ public void run() throws IOException {
+ packetWriter.writeRequest(request);
+ LOG.info("Sent request");
+ dbExecutor.execute(new GenerateRequest());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateRetentionAck implements Runnable {
+
+ public void run() {
+ try {
+ RetentionAck a = db.generateRetentionAck(contactId);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated retention ack: " + (a != null));
+ if(a != null) writerTasks.add(new WriteRetentionAck(a));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This tasks runs on the writer thread
+ private class WriteRetentionAck implements ThrowingRunnable {
+
+ private final RetentionAck ack;
+
+ private WriteRetentionAck(RetentionAck ack) {
+ this.ack = ack;
+ }
+
+
+ public void run() throws IOException {
+ packetWriter.writeRetentionAck(ack);
+ LOG.info("Sent retention ack");
+ dbExecutor.execute(new GenerateRetentionAck());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateRetentionUpdate implements Runnable {
+
+ public void run() {
+ try {
+ RetentionUpdate u =
+ db.generateRetentionUpdate(contactId, maxLatency);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated retention update: " + (u != null));
+ if(u != null) writerTasks.add(new WriteRetentionUpdate(u));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteRetentionUpdate
+ implements ThrowingRunnable {
+
+ private final RetentionUpdate update;
+
+ private WriteRetentionUpdate(RetentionUpdate update) {
+ this.update = update;
+ }
+
+ public void run() throws IOException {
+ packetWriter.writeRetentionUpdate(update);
+ LOG.info("Sent retention update");
+ dbExecutor.execute(new GenerateRetentionUpdate());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateSubscriptionAck implements Runnable {
+
+ public void run() {
+ try {
+ SubscriptionAck a = db.generateSubscriptionAck(contactId);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated subscription ack: " + (a != null));
+ if(a != null) writerTasks.add(new WriteSubscriptionAck(a));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This tasks runs on the writer thread
+ private class WriteSubscriptionAck
+ implements ThrowingRunnable {
+
+ private final SubscriptionAck ack;
+
+ private WriteSubscriptionAck(SubscriptionAck ack) {
+ this.ack = ack;
+ }
+
+ public void run() throws IOException {
+ packetWriter.writeSubscriptionAck(ack);
+ LOG.info("Sent subscription ack");
+ dbExecutor.execute(new GenerateSubscriptionAck());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateSubscriptionUpdate implements Runnable {
+
+ public void run() {
+ try {
+ SubscriptionUpdate u =
+ db.generateSubscriptionUpdate(contactId, maxLatency);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated subscription update: " + (u != null));
+ if(u != null) writerTasks.add(new WriteSubscriptionUpdate(u));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteSubscriptionUpdate
+ implements ThrowingRunnable {
+
+ private final SubscriptionUpdate update;
+
+ private WriteSubscriptionUpdate(SubscriptionUpdate update) {
+ this.update = update;
+ }
+
+ public void run() throws IOException {
+ packetWriter.writeSubscriptionUpdate(update);
+ LOG.info("Sent subscription update");
+ dbExecutor.execute(new GenerateSubscriptionUpdate());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateTransportAcks implements Runnable {
+
+ public void run() {
+ try {
+ Collection acks =
+ db.generateTransportAcks(contactId);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated transport acks: " + (acks != null));
+ if(acks != null) writerTasks.add(new WriteTransportAcks(acks));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This tasks runs on the writer thread
+ private class WriteTransportAcks implements ThrowingRunnable {
+
+ private final Collection acks;
+
+ private WriteTransportAcks(Collection acks) {
+ this.acks = acks;
+ }
+
+ public void run() throws IOException {
+ for(TransportAck a : acks) packetWriter.writeTransportAck(a);
+ LOG.info("Sent transport acks");
+ dbExecutor.execute(new GenerateTransportAcks());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateTransportUpdates implements Runnable {
+
+ public void run() {
+ try {
+ Collection t =
+ db.generateTransportUpdates(contactId, maxLatency);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated transport updates: " + (t != null));
+ if(t != null) writerTasks.add(new WriteTransportUpdates(t));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteTransportUpdates
+ implements ThrowingRunnable {
+
+ private final Collection updates;
+
+ private WriteTransportUpdates(Collection updates) {
+ this.updates = updates;
+ }
+
+ public void run() throws IOException {
+ for(TransportUpdate u : updates)
+ packetWriter.writeTransportUpdate(u);
+ LOG.info("Sent transport updates");
+ dbExecutor.execute(new GenerateTransportUpdates());
+ }
+ }
+}
diff --git a/briar-core/src/org/briarproject/messaging/SinglePassOutgoingSession.java b/briar-core/src/org/briarproject/messaging/SinglePassOutgoingSession.java
new file mode 100644
index 000000000..c09829a92
--- /dev/null
+++ b/briar-core/src/org/briarproject/messaging/SinglePassOutgoingSession.java
@@ -0,0 +1,395 @@
+package org.briarproject.messaging;
+
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import org.briarproject.api.ContactId;
+import org.briarproject.api.db.DatabaseComponent;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.messaging.Ack;
+import org.briarproject.api.messaging.MessagingSession;
+import org.briarproject.api.messaging.PacketWriter;
+import org.briarproject.api.messaging.PacketWriterFactory;
+import org.briarproject.api.messaging.RetentionAck;
+import org.briarproject.api.messaging.RetentionUpdate;
+import org.briarproject.api.messaging.SubscriptionAck;
+import org.briarproject.api.messaging.SubscriptionUpdate;
+import org.briarproject.api.messaging.TransportAck;
+import org.briarproject.api.messaging.TransportUpdate;
+import org.briarproject.api.plugins.TransportConnectionWriter;
+import org.briarproject.api.transport.StreamContext;
+import org.briarproject.api.transport.StreamWriter;
+import org.briarproject.api.transport.StreamWriterFactory;
+
+/**
+ * An outgoing {@link org.briarproject.api.messaging.MessagingSession
+ * MessagingSession} that closes its output stream when no more packets are
+ * available to send.
+ */
+class SinglePassOutgoingSession implements MessagingSession {
+
+ private static final Logger LOG =
+ Logger.getLogger(SinglePassOutgoingSession.class.getName());
+
+ private static final ThrowingRunnable CLOSE =
+ new ThrowingRunnable() {
+ public void run() {}
+ };
+
+ private final DatabaseComponent db;
+ private final Executor dbExecutor;
+ private final StreamWriterFactory streamWriterFactory;
+ private final PacketWriterFactory packetWriterFactory;
+ private final StreamContext ctx;
+ private final TransportConnectionWriter transportWriter;
+ private final ContactId contactId;
+ private final long maxLatency;
+ private final AtomicInteger outstandingQueries;
+ private final BlockingQueue> writerTasks;
+
+ private volatile StreamWriter streamWriter = null;
+ private volatile PacketWriter packetWriter = null;
+ private volatile boolean interrupted = false;
+
+ SinglePassOutgoingSession(DatabaseComponent db, Executor dbExecutor,
+ StreamWriterFactory streamWriterFactory,
+ PacketWriterFactory packetWriterFactory, StreamContext ctx,
+ TransportConnectionWriter transportWriter) {
+ this.db = db;
+ this.dbExecutor = dbExecutor;
+ this.streamWriterFactory = streamWriterFactory;
+ this.packetWriterFactory = packetWriterFactory;
+ this.ctx = ctx;
+ this.transportWriter = transportWriter;
+ contactId = ctx.getContactId();
+ maxLatency = transportWriter.getMaxLatency();
+ outstandingQueries = new AtomicInteger(8); // One per type of packet
+ writerTasks = new LinkedBlockingQueue>();
+ }
+
+ public void run() throws IOException {
+ OutputStream out = transportWriter.getOutputStream();
+ int maxFrameLength = transportWriter.getMaxFrameLength();
+ streamWriter = streamWriterFactory.createStreamWriter(out,
+ maxFrameLength, ctx);
+ out = streamWriter.getOutputStream();
+ packetWriter = packetWriterFactory.createPacketWriter(out, false);
+ // 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 {
+ while(!interrupted) {
+ ThrowingRunnable 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();
+ }
+ }
+
+ public void interrupt() {
+ interrupted = true;
+ writerTasks.add(CLOSE);
+ }
+
+ private void decrementOutstandingQueries() {
+ if(outstandingQueries.decrementAndGet() == 0) writerTasks.add(CLOSE);
+ }
+
+ // This task runs on the database thread
+ private class GenerateAck implements Runnable {
+
+ public void run() {
+ int maxMessages = packetWriter.getMaxMessagesForAck(Long.MAX_VALUE);
+ try {
+ Ack a = db.generateAck(contactId, maxMessages);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated ack: " + (a != null));
+ if(a == null) decrementOutstandingQueries();
+ else writerTasks.add(new WriteAck(a));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteAck implements ThrowingRunnable {
+
+ private final Ack ack;
+
+ private WriteAck(Ack ack) {
+ this.ack = ack;
+ }
+
+ public void run() throws IOException {
+ packetWriter.writeAck(ack);
+ LOG.info("Sent ack");
+ dbExecutor.execute(new GenerateAck());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateBatch implements Runnable {
+
+ public void run() {
+ try {
+ Collection b = db.generateBatch(contactId,
+ MAX_PACKET_LENGTH, maxLatency);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated batch: " + (b != null));
+ if(b == null) decrementOutstandingQueries();
+ else writerTasks.add(new WriteBatch(b));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteBatch implements ThrowingRunnable {
+
+ private final Collection batch;
+
+ private WriteBatch(Collection batch) {
+ this.batch = batch;
+ }
+
+ public void run() throws IOException {
+ for(byte[] raw : batch) packetWriter.writeMessage(raw);
+ LOG.info("Sent batch");
+ dbExecutor.execute(new GenerateBatch());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateRetentionAck implements Runnable {
+
+ public void run() {
+ try {
+ RetentionAck a = db.generateRetentionAck(contactId);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated retention ack: " + (a != null));
+ if(a == null) decrementOutstandingQueries();
+ else writerTasks.add(new WriteRetentionAck(a));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This tasks runs on the writer thread
+ private class WriteRetentionAck implements ThrowingRunnable {
+
+ private final RetentionAck ack;
+
+ private WriteRetentionAck(RetentionAck ack) {
+ this.ack = ack;
+ }
+
+
+ public void run() throws IOException {
+ packetWriter.writeRetentionAck(ack);
+ LOG.info("Sent retention ack");
+ dbExecutor.execute(new GenerateRetentionAck());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateRetentionUpdate implements Runnable {
+
+ public void run() {
+ try {
+ RetentionUpdate u =
+ db.generateRetentionUpdate(contactId, maxLatency);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated retention update: " + (u != null));
+ if(u == null) decrementOutstandingQueries();
+ else writerTasks.add(new WriteRetentionUpdate(u));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteRetentionUpdate
+ implements ThrowingRunnable {
+
+ private final RetentionUpdate update;
+
+ private WriteRetentionUpdate(RetentionUpdate update) {
+ this.update = update;
+ }
+
+ public void run() throws IOException {
+ packetWriter.writeRetentionUpdate(update);
+ LOG.info("Sent retention update");
+ dbExecutor.execute(new GenerateRetentionUpdate());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateSubscriptionAck implements Runnable {
+
+ public void run() {
+ try {
+ SubscriptionAck a = db.generateSubscriptionAck(contactId);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated subscription ack: " + (a != null));
+ if(a == null) decrementOutstandingQueries();
+ else writerTasks.add(new WriteSubscriptionAck(a));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This tasks runs on the writer thread
+ private class WriteSubscriptionAck
+ implements ThrowingRunnable {
+
+ private final SubscriptionAck ack;
+
+ private WriteSubscriptionAck(SubscriptionAck ack) {
+ this.ack = ack;
+ }
+
+ public void run() throws IOException {
+ packetWriter.writeSubscriptionAck(ack);
+ LOG.info("Sent subscription ack");
+ dbExecutor.execute(new GenerateSubscriptionAck());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateSubscriptionUpdate implements Runnable {
+
+ public void run() {
+ try {
+ SubscriptionUpdate u =
+ db.generateSubscriptionUpdate(contactId, maxLatency);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated subscription update: " + (u != null));
+ if(u == null) decrementOutstandingQueries();
+ else writerTasks.add(new WriteSubscriptionUpdate(u));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteSubscriptionUpdate
+ implements ThrowingRunnable {
+
+ private final SubscriptionUpdate update;
+
+ private WriteSubscriptionUpdate(SubscriptionUpdate update) {
+ this.update = update;
+ }
+
+ public void run() throws IOException {
+ packetWriter.writeSubscriptionUpdate(update);
+ LOG.info("Sent subscription update");
+ dbExecutor.execute(new GenerateSubscriptionUpdate());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateTransportAcks implements Runnable {
+
+ public void run() {
+ try {
+ Collection acks =
+ db.generateTransportAcks(contactId);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated transport acks: " + (acks != null));
+ if(acks == null) decrementOutstandingQueries();
+ else writerTasks.add(new WriteTransportAcks(acks));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This tasks runs on the writer thread
+ private class WriteTransportAcks implements ThrowingRunnable {
+
+ private final Collection acks;
+
+ private WriteTransportAcks(Collection acks) {
+ this.acks = acks;
+ }
+
+ public void run() throws IOException {
+ for(TransportAck a : acks) packetWriter.writeTransportAck(a);
+ LOG.info("Sent transport acks");
+ dbExecutor.execute(new GenerateTransportAcks());
+ }
+ }
+
+ // This task runs on the database thread
+ private class GenerateTransportUpdates implements Runnable {
+
+ public void run() {
+ try {
+ Collection t =
+ db.generateTransportUpdates(contactId, maxLatency);
+ if(LOG.isLoggable(INFO))
+ LOG.info("Generated transport updates: " + (t != null));
+ if(t == null) decrementOutstandingQueries();
+ else writerTasks.add(new WriteTransportUpdates(t));
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ interrupt();
+ }
+ }
+ }
+
+ // This task runs on the writer thread
+ private class WriteTransportUpdates
+ implements ThrowingRunnable {
+
+ private final Collection updates;
+
+ private WriteTransportUpdates(Collection updates) {
+ this.updates = updates;
+ }
+
+ public void run() throws IOException {
+ for(TransportUpdate u : updates)
+ packetWriter.writeTransportUpdate(u);
+ LOG.info("Sent transport updates");
+ dbExecutor.execute(new GenerateTransportUpdates());
+ }
+ }
+}
diff --git a/briar-core/src/org/briarproject/messaging/ThrowingRunnable.java b/briar-core/src/org/briarproject/messaging/ThrowingRunnable.java
new file mode 100644
index 000000000..334581daa
--- /dev/null
+++ b/briar-core/src/org/briarproject/messaging/ThrowingRunnable.java
@@ -0,0 +1,6 @@
+package org.briarproject.messaging;
+
+interface ThrowingRunnable {
+
+ public void run() throws T;
+}
diff --git a/briar-core/src/org/briarproject/messaging/duplex/DuplexConnection.java b/briar-core/src/org/briarproject/messaging/duplex/DuplexConnection.java
deleted file mode 100644
index 0ba89f29f..000000000
--- a/briar-core/src/org/briarproject/messaging/duplex/DuplexConnection.java
+++ /dev/null
@@ -1,871 +0,0 @@
-package org.briarproject.messaging.duplex;
-
-import static java.util.logging.Level.INFO;
-import static java.util.logging.Level.WARNING;
-import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.util.Collection;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Executor;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Logger;
-
-import org.briarproject.api.ContactId;
-import org.briarproject.api.FormatException;
-import org.briarproject.api.TransportId;
-import org.briarproject.api.db.DatabaseComponent;
-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.LocalSubscriptionsUpdatedEvent;
-import org.briarproject.api.event.LocalTransportsUpdatedEvent;
-import org.briarproject.api.event.MessageAddedEvent;
-import org.briarproject.api.event.MessageExpiredEvent;
-import org.briarproject.api.event.MessageRequestedEvent;
-import org.briarproject.api.event.MessageToAckEvent;
-import org.briarproject.api.event.MessageToRequestEvent;
-import org.briarproject.api.event.RemoteRetentionTimeUpdatedEvent;
-import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
-import org.briarproject.api.event.RemoteTransportsUpdatedEvent;
-import org.briarproject.api.messaging.Ack;
-import org.briarproject.api.messaging.Message;
-import org.briarproject.api.messaging.MessageVerifier;
-import org.briarproject.api.messaging.Offer;
-import org.briarproject.api.messaging.PacketReader;
-import org.briarproject.api.messaging.PacketReaderFactory;
-import org.briarproject.api.messaging.PacketWriter;
-import org.briarproject.api.messaging.PacketWriterFactory;
-import org.briarproject.api.messaging.Request;
-import org.briarproject.api.messaging.RetentionAck;
-import org.briarproject.api.messaging.RetentionUpdate;
-import org.briarproject.api.messaging.SubscriptionAck;
-import org.briarproject.api.messaging.SubscriptionUpdate;
-import org.briarproject.api.messaging.TransportAck;
-import org.briarproject.api.messaging.TransportUpdate;
-import org.briarproject.api.messaging.UnverifiedMessage;
-import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
-import org.briarproject.api.transport.ConnectionRegistry;
-import org.briarproject.api.transport.StreamContext;
-import org.briarproject.api.transport.StreamReader;
-import org.briarproject.api.transport.StreamReaderFactory;
-import org.briarproject.api.transport.StreamWriter;
-import org.briarproject.api.transport.StreamWriterFactory;
-import org.briarproject.util.ByteUtils;
-
-abstract class DuplexConnection implements EventListener {
-
- private static final Logger LOG =
- Logger.getLogger(DuplexConnection.class.getName());
-
- private static final Runnable CLOSE = new Runnable() {
- public void run() {}
- };
-
- private static final Runnable DIE = new Runnable() {
- public void run() {}
- };
-
- protected final DatabaseComponent db;
- protected final EventBus eventBus;
- protected final ConnectionRegistry connRegistry;
- protected final StreamReaderFactory connReaderFactory;
- protected final StreamWriterFactory connWriterFactory;
- protected final PacketReaderFactory packetReaderFactory;
- protected final PacketWriterFactory packetWriterFactory;
- protected final StreamContext ctx;
- protected final DuplexTransportConnection transport;
- protected final ContactId contactId;
- protected final TransportId transportId;
-
- private final Executor dbExecutor, cryptoExecutor;
- private final MessageVerifier messageVerifier;
- private final long maxLatency;
- private final AtomicBoolean disposed;
- private final BlockingQueue writerTasks;
-
- private volatile PacketWriter writer = null;
-
- DuplexConnection(Executor dbExecutor, Executor cryptoExecutor,
- MessageVerifier messageVerifier, DatabaseComponent db,
- EventBus eventBus, ConnectionRegistry connRegistry,
- StreamReaderFactory connReaderFactory,
- StreamWriterFactory connWriterFactory,
- PacketReaderFactory packetReaderFactory,
- PacketWriterFactory packetWriterFactory, StreamContext ctx,
- DuplexTransportConnection transport) {
- this.dbExecutor = dbExecutor;
- this.cryptoExecutor = cryptoExecutor;
- this.messageVerifier = messageVerifier;
- this.db = db;
- this.eventBus = eventBus;
- this.connRegistry = connRegistry;
- this.connReaderFactory = connReaderFactory;
- this.connWriterFactory = connWriterFactory;
- this.packetReaderFactory = packetReaderFactory;
- this.packetWriterFactory = packetWriterFactory;
- this.ctx = ctx;
- this.transport = transport;
- contactId = ctx.getContactId();
- transportId = ctx.getTransportId();
- maxLatency = transport.getMaxLatency();
- disposed = new AtomicBoolean(false);
- writerTasks = new LinkedBlockingQueue();
- }
-
- protected abstract StreamReader createStreamReader() throws IOException;
-
- protected abstract StreamWriter createStreamWriter() throws IOException;
-
- public void eventOccurred(Event e) {
- if(e instanceof ContactRemovedEvent) {
- ContactRemovedEvent c = (ContactRemovedEvent) e;
- if(contactId.equals(c.getContactId())) writerTasks.add(CLOSE);
- } else if(e instanceof MessageAddedEvent) {
- dbExecutor.execute(new GenerateOffer());
- } else if(e instanceof MessageExpiredEvent) {
- dbExecutor.execute(new GenerateRetentionUpdate());
- } else if(e instanceof LocalSubscriptionsUpdatedEvent) {
- LocalSubscriptionsUpdatedEvent l =
- (LocalSubscriptionsUpdatedEvent) e;
- if(l.getAffectedContacts().contains(contactId)) {
- dbExecutor.execute(new GenerateSubscriptionUpdate());
- dbExecutor.execute(new GenerateOffer());
- }
- } else if(e instanceof LocalTransportsUpdatedEvent) {
- dbExecutor.execute(new GenerateTransportUpdates());
- } else if(e instanceof MessageRequestedEvent) {
- if(((MessageRequestedEvent) e).getContactId().equals(contactId))
- dbExecutor.execute(new GenerateBatch());
- } else if(e instanceof MessageToAckEvent) {
- if(((MessageToAckEvent) e).getContactId().equals(contactId))
- dbExecutor.execute(new GenerateAck());
- } else if(e instanceof MessageToRequestEvent) {
- if(((MessageToRequestEvent) e).getContactId().equals(contactId))
- dbExecutor.execute(new GenerateRequest());
- } else if(e instanceof RemoteRetentionTimeUpdatedEvent) {
- dbExecutor.execute(new GenerateRetentionAck());
- } else if(e instanceof RemoteSubscriptionsUpdatedEvent) {
- dbExecutor.execute(new GenerateSubscriptionAck());
- dbExecutor.execute(new GenerateOffer());
- } else if(e instanceof RemoteTransportsUpdatedEvent) {
- dbExecutor.execute(new GenerateTransportAcks());
- }
- }
-
- void read() {
- try {
- InputStream in = createStreamReader().getInputStream();
- PacketReader reader = packetReaderFactory.createPacketReader(in);
- LOG.info("Starting to read");
- while(!reader.eof()) {
- if(reader.hasAck()) {
- Ack a = reader.readAck();
- LOG.info("Received ack");
- dbExecutor.execute(new ReceiveAck(a));
- } else if(reader.hasMessage()) {
- UnverifiedMessage m = reader.readMessage();
- LOG.info("Received message");
- cryptoExecutor.execute(new VerifyMessage(m));
- } else if(reader.hasOffer()) {
- Offer o = reader.readOffer();
- LOG.info("Received offer");
- dbExecutor.execute(new ReceiveOffer(o));
- } else if(reader.hasRequest()) {
- Request r = reader.readRequest();
- LOG.info("Received request");
- dbExecutor.execute(new ReceiveRequest(r));
- } else if(reader.hasRetentionAck()) {
- RetentionAck a = reader.readRetentionAck();
- LOG.info("Received retention ack");
- dbExecutor.execute(new ReceiveRetentionAck(a));
- } else if(reader.hasRetentionUpdate()) {
- RetentionUpdate u = reader.readRetentionUpdate();
- LOG.info("Received retention update");
- dbExecutor.execute(new ReceiveRetentionUpdate(u));
- } else if(reader.hasSubscriptionAck()) {
- SubscriptionAck a = reader.readSubscriptionAck();
- LOG.info("Received subscription ack");
- dbExecutor.execute(new ReceiveSubscriptionAck(a));
- } else if(reader.hasSubscriptionUpdate()) {
- SubscriptionUpdate u = reader.readSubscriptionUpdate();
- LOG.info("Received subscription update");
- dbExecutor.execute(new ReceiveSubscriptionUpdate(u));
- } else if(reader.hasTransportAck()) {
- TransportAck a = reader.readTransportAck();
- LOG.info("Received transport ack");
- dbExecutor.execute(new ReceiveTransportAck(a));
- } else if(reader.hasTransportUpdate()) {
- TransportUpdate u = reader.readTransportUpdate();
- LOG.info("Received transport update");
- dbExecutor.execute(new ReceiveTransportUpdate(u));
- } else {
- throw new FormatException();
- }
- }
- LOG.info("Finished reading");
- writerTasks.add(CLOSE);
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- writerTasks.add(DIE);
- }
- }
-
- void write() {
- connRegistry.registerConnection(contactId, transportId);
- eventBus.addListener(this);
- try {
- OutputStream out = createStreamWriter().getOutputStream();
- writer = packetWriterFactory.createPacketWriter(out, true);
- LOG.info("Starting to write");
- // Ensure the tag is sent
- out.flush();
- // Send the initial packets
- 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());
- dbExecutor.execute(new GenerateOffer());
- dbExecutor.execute(new GenerateRequest());
- // Main loop
- Runnable task = null;
- while(true) {
- LOG.info("Waiting for something to write");
- task = writerTasks.take();
- if(task == CLOSE || task == DIE) break;
- task.run();
- }
- LOG.info("Finished writing");
- if(task == CLOSE) {
- writer.flush();
- writer.close();
- dispose(false, true);
- } else {
- dispose(true, true);
- }
- } catch(InterruptedException e) {
- LOG.warning("Interrupted while waiting for task");
- Thread.currentThread().interrupt();
- dispose(true, true);
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- }
- eventBus.removeListener(this);
- connRegistry.unregisterConnection(contactId, transportId);
- }
-
- private void dispose(boolean exception, boolean recognised) {
- if(disposed.getAndSet(true)) return;
- if(LOG.isLoggable(INFO))
- LOG.info("Disposing: " + exception + ", " + recognised);
- ByteUtils.erase(ctx.getSecret());
- try {
- transport.dispose(exception, recognised);
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
-
- // This task runs on the database thread
- private class ReceiveAck implements Runnable {
-
- private final Ack ack;
-
- private ReceiveAck(Ack ack) {
- this.ack = ack;
- }
-
- public void run() {
- try {
- db.receiveAck(contactId, ack);
- LOG.info("DB received ack");
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on a crypto thread
- private class VerifyMessage implements Runnable {
-
- private final UnverifiedMessage message;
-
- private VerifyMessage(UnverifiedMessage message) {
- this.message = message;
- }
-
- public void run() {
- try {
- Message m = messageVerifier.verifyMessage(message);
- LOG.info("Verified message");
- dbExecutor.execute(new ReceiveMessage(m));
- } catch(GeneralSecurityException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the database thread
- private class ReceiveMessage implements Runnable {
-
- private final Message message;
-
- private ReceiveMessage(Message message) {
- this.message = message;
- }
-
- public void run() {
- try {
- db.receiveMessage(contactId, message);
- LOG.info("DB received message");
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the database thread
- private class ReceiveOffer implements Runnable {
-
- private final Offer offer;
-
- private ReceiveOffer(Offer offer) {
- this.offer = offer;
- }
-
- public void run() {
- try {
- db.receiveOffer(contactId, offer);
- LOG.info("DB received offer");
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the database thread
- private class ReceiveRequest implements Runnable {
-
- private final Request request;
-
- private ReceiveRequest(Request request) {
- this.request = request;
- }
-
- public void run() {
- try {
- db.receiveRequest(contactId, request);
- LOG.info("DB received request");
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the database thread
- private class ReceiveRetentionAck implements Runnable {
-
- private final RetentionAck ack;
-
- private ReceiveRetentionAck(RetentionAck ack) {
- this.ack = ack;
- }
-
- public void run() {
- try {
- db.receiveRetentionAck(contactId, ack);
- LOG.info("DB received retention ack");
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the database thread
- private class ReceiveRetentionUpdate implements Runnable {
-
- private final RetentionUpdate update;
-
- private ReceiveRetentionUpdate(RetentionUpdate update) {
- this.update = update;
- }
-
- public void run() {
- try {
- db.receiveRetentionUpdate(contactId, update);
- LOG.info("DB received retention update");
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the database thread
- private class ReceiveSubscriptionAck implements Runnable {
-
- private final SubscriptionAck ack;
-
- private ReceiveSubscriptionAck(SubscriptionAck ack) {
- this.ack = ack;
- }
-
- public void run() {
- try {
- db.receiveSubscriptionAck(contactId, ack);
- LOG.info("DB received subscription ack");
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the database thread
- private class ReceiveSubscriptionUpdate implements Runnable {
-
- private final SubscriptionUpdate update;
-
- private ReceiveSubscriptionUpdate(SubscriptionUpdate update) {
- this.update = update;
- }
-
- public void run() {
- try {
- db.receiveSubscriptionUpdate(contactId, update);
- LOG.info("DB received subscription update");
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the database thread
- private class ReceiveTransportAck implements Runnable {
-
- private final TransportAck ack;
-
- private ReceiveTransportAck(TransportAck ack) {
- this.ack = ack;
- }
-
- public void run() {
- try {
- db.receiveTransportAck(contactId, ack);
- LOG.info("DB received transport ack");
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the database thread
- private class ReceiveTransportUpdate implements Runnable {
-
- private final TransportUpdate update;
-
- private ReceiveTransportUpdate(TransportUpdate update) {
- this.update = update;
- }
-
- public void run() {
- try {
- db.receiveTransportUpdate(contactId, update);
- LOG.info("DB received transport update");
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the database thread
- private class GenerateAck implements Runnable {
-
- public void run() {
- assert writer != null;
- int maxMessages = writer.getMaxMessagesForAck(Long.MAX_VALUE);
- try {
- Ack a = db.generateAck(contactId, maxMessages);
- if(LOG.isLoggable(INFO))
- LOG.info("Generated ack: " + (a != null));
- if(a != null) writerTasks.add(new WriteAck(a));
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the writer thread
- private class WriteAck implements Runnable {
-
- private final Ack ack;
-
- private WriteAck(Ack ack) {
- this.ack = ack;
- }
-
- public void run() {
- assert writer != null;
- try {
- writer.writeAck(ack);
- LOG.info("Sent ack");
- dbExecutor.execute(new GenerateAck());
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- }
- }
- }
-
- // This task runs on the database thread
- private class GenerateBatch implements Runnable {
-
- public void run() {
- assert writer != null;
- try {
- Collection b = db.generateRequestedBatch(contactId,
- MAX_PACKET_LENGTH, maxLatency);
- if(LOG.isLoggable(INFO))
- LOG.info("Generated batch: " + (b != null));
- if(b != null) writerTasks.add(new WriteBatch(b));
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the writer thread
- private class WriteBatch implements Runnable {
-
- private final Collection batch;
-
- private WriteBatch(Collection batch) {
- this.batch = batch;
- }
-
- public void run() {
- assert writer != null;
- try {
- for(byte[] raw : batch) writer.writeMessage(raw);
- LOG.info("Sent batch");
- dbExecutor.execute(new GenerateBatch());
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- }
- }
- }
-
- // This task runs on the database thread
- private class GenerateOffer implements Runnable {
-
- public void run() {
- assert writer != null;
- int maxMessages = writer.getMaxMessagesForOffer(Long.MAX_VALUE);
- try {
- Offer o = db.generateOffer(contactId, maxMessages, maxLatency);
- if(LOG.isLoggable(INFO))
- LOG.info("Generated offer: " + (o != null));
- if(o != null) writerTasks.add(new WriteOffer(o));
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the writer thread
- private class WriteOffer implements Runnable {
-
- private final Offer offer;
-
- private WriteOffer(Offer offer) {
- this.offer = offer;
- }
-
- public void run() {
- assert writer != null;
- try {
- writer.writeOffer(offer);
- LOG.info("Sent offer");
- dbExecutor.execute(new GenerateOffer());
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- }
- }
- }
-
- // This task runs on the database thread
- private class GenerateRequest implements Runnable {
-
- public void run() {
- assert writer != null;
- int maxMessages = writer.getMaxMessagesForRequest(Long.MAX_VALUE);
- try {
- Request r = db.generateRequest(contactId, maxMessages);
- if(LOG.isLoggable(INFO))
- LOG.info("Generated request: " + (r != null));
- if(r != null) writerTasks.add(new WriteRequest(r));
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the writer thread
- private class WriteRequest implements Runnable {
-
- private final Request request;
-
- private WriteRequest(Request request) {
- this.request = request;
- }
-
- public void run() {
- assert writer != null;
- try {
- writer.writeRequest(request);
- LOG.info("Sent request");
- dbExecutor.execute(new GenerateRequest());
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- }
- }
- }
-
- // This task runs on the database thread
- private class GenerateRetentionAck implements Runnable {
-
- public void run() {
- try {
- RetentionAck a = db.generateRetentionAck(contactId);
- if(LOG.isLoggable(INFO))
- LOG.info("Generated retention ack: " + (a != null));
- if(a != null) writerTasks.add(new WriteRetentionAck(a));
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This tasks runs on the writer thread
- private class WriteRetentionAck implements Runnable {
-
- private final RetentionAck ack;
-
- private WriteRetentionAck(RetentionAck ack) {
- this.ack = ack;
- }
-
- public void run() {
- assert writer != null;
- try {
- writer.writeRetentionAck(ack);
- LOG.info("Sent retention ack");
- dbExecutor.execute(new GenerateRetentionAck());
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- }
- }
- }
-
- // This task runs on the database thread
- private class GenerateRetentionUpdate implements Runnable {
-
- public void run() {
- try {
- RetentionUpdate u =
- db.generateRetentionUpdate(contactId, maxLatency);
- if(LOG.isLoggable(INFO))
- LOG.info("Generated retention update: " + (u != null));
- if(u != null) writerTasks.add(new WriteRetentionUpdate(u));
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the writer thread
- private class WriteRetentionUpdate implements Runnable {
-
- private final RetentionUpdate update;
-
- private WriteRetentionUpdate(RetentionUpdate update) {
- this.update = update;
- }
-
- public void run() {
- assert writer != null;
- try {
- writer.writeRetentionUpdate(update);
- LOG.info("Sent retention update");
- dbExecutor.execute(new GenerateRetentionUpdate());
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- }
- }
- }
-
- // This task runs on the database thread
- private class GenerateSubscriptionAck implements Runnable {
-
- public void run() {
- try {
- SubscriptionAck a = db.generateSubscriptionAck(contactId);
- if(LOG.isLoggable(INFO))
- LOG.info("Generated subscription ack: " + (a != null));
- if(a != null) writerTasks.add(new WriteSubscriptionAck(a));
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This tasks runs on the writer thread
- private class WriteSubscriptionAck implements Runnable {
-
- private final SubscriptionAck ack;
-
- private WriteSubscriptionAck(SubscriptionAck ack) {
- this.ack = ack;
- }
-
- public void run() {
- assert writer != null;
- try {
- writer.writeSubscriptionAck(ack);
- LOG.info("Sent subscription ack");
- dbExecutor.execute(new GenerateSubscriptionAck());
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- }
- }
- }
-
- // This task runs on the database thread
- private class GenerateSubscriptionUpdate implements Runnable {
-
- public void run() {
- try {
- SubscriptionUpdate u =
- db.generateSubscriptionUpdate(contactId, maxLatency);
- if(LOG.isLoggable(INFO))
- LOG.info("Generated subscription update: " + (u != null));
- if(u != null) writerTasks.add(new WriteSubscriptionUpdate(u));
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the writer thread
- private class WriteSubscriptionUpdate implements Runnable {
-
- private final SubscriptionUpdate update;
-
- private WriteSubscriptionUpdate(SubscriptionUpdate update) {
- this.update = update;
- }
-
- public void run() {
- assert writer != null;
- try {
- writer.writeSubscriptionUpdate(update);
- LOG.info("Sent subscription update");
- dbExecutor.execute(new GenerateSubscriptionUpdate());
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- }
- }
- }
-
- // This task runs on the database thread
- private class GenerateTransportAcks implements Runnable {
-
- public void run() {
- try {
- Collection acks =
- db.generateTransportAcks(contactId);
- if(LOG.isLoggable(INFO))
- LOG.info("Generated transport acks: " + (acks != null));
- if(acks != null) writerTasks.add(new WriteTransportAcks(acks));
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This tasks runs on the writer thread
- private class WriteTransportAcks implements Runnable {
-
- private final Collection acks;
-
- private WriteTransportAcks(Collection acks) {
- this.acks = acks;
- }
-
- public void run() {
- assert writer != null;
- try {
- for(TransportAck a : acks) writer.writeTransportAck(a);
- LOG.info("Sent transport acks");
- dbExecutor.execute(new GenerateTransportAcks());
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- }
- }
- }
-
- // This task runs on the database thread
- private class GenerateTransportUpdates implements Runnable {
-
- public void run() {
- try {
- Collection t =
- db.generateTransportUpdates(contactId, maxLatency);
- if(LOG.isLoggable(INFO))
- LOG.info("Generated transport updates: " + (t != null));
- if(t != null) writerTasks.add(new WriteTransportUpdates(t));
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
- }
-
- // This task runs on the writer thread
- private class WriteTransportUpdates implements Runnable {
-
- private final Collection updates;
-
- private WriteTransportUpdates(Collection updates) {
- this.updates = updates;
- }
-
- public void run() {
- assert writer != null;
- try {
- for(TransportUpdate u : updates) writer.writeTransportUpdate(u);
- LOG.info("Sent transport updates");
- dbExecutor.execute(new GenerateTransportUpdates());
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, true);
- }
- }
- }
-}
diff --git a/briar-core/src/org/briarproject/messaging/duplex/DuplexConnectionFactoryImpl.java b/briar-core/src/org/briarproject/messaging/duplex/DuplexConnectionFactoryImpl.java
deleted file mode 100644
index f8affa8dd..000000000
--- a/briar-core/src/org/briarproject/messaging/duplex/DuplexConnectionFactoryImpl.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package org.briarproject.messaging.duplex;
-
-import java.util.concurrent.Executor;
-import java.util.logging.Logger;
-
-import javax.inject.Inject;
-
-import org.briarproject.api.ContactId;
-import org.briarproject.api.TransportId;
-import org.briarproject.api.crypto.CryptoExecutor;
-import org.briarproject.api.crypto.KeyManager;
-import org.briarproject.api.db.DatabaseComponent;
-import org.briarproject.api.db.DatabaseExecutor;
-import org.briarproject.api.event.EventBus;
-import org.briarproject.api.messaging.MessageVerifier;
-import org.briarproject.api.messaging.PacketReaderFactory;
-import org.briarproject.api.messaging.PacketWriterFactory;
-import org.briarproject.api.messaging.duplex.DuplexConnectionFactory;
-import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
-import org.briarproject.api.transport.ConnectionRegistry;
-import org.briarproject.api.transport.StreamContext;
-import org.briarproject.api.transport.StreamReaderFactory;
-import org.briarproject.api.transport.StreamWriterFactory;
-
-class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
-
- private static final Logger LOG =
- Logger.getLogger(DuplexConnectionFactoryImpl.class.getName());
-
- private final Executor dbExecutor, cryptoExecutor;
- private final MessageVerifier messageVerifier;
- private final DatabaseComponent db;
- private final EventBus eventBus;
- private final KeyManager keyManager;
- private final ConnectionRegistry connRegistry;
- private final StreamReaderFactory connReaderFactory;
- private final StreamWriterFactory connWriterFactory;
- private final PacketReaderFactory packetReaderFactory;
- private final PacketWriterFactory packetWriterFactory;
-
- @Inject
- DuplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
- @CryptoExecutor Executor cryptoExecutor,
- MessageVerifier messageVerifier, DatabaseComponent db,
- EventBus eventBus, KeyManager keyManager,
- ConnectionRegistry connRegistry,
- StreamReaderFactory connReaderFactory,
- StreamWriterFactory connWriterFactory,
- PacketReaderFactory packetReaderFactory,
- PacketWriterFactory packetWriterFactory) {
- this.dbExecutor = dbExecutor;
- this.cryptoExecutor = cryptoExecutor;
- this.messageVerifier = messageVerifier;
- this.db = db;
- this.eventBus = eventBus;
- this.keyManager = keyManager;
- this.connRegistry = connRegistry;
- this.connReaderFactory = connReaderFactory;
- this.connWriterFactory = connWriterFactory;
- this.packetReaderFactory = packetReaderFactory;
- this.packetWriterFactory = packetWriterFactory;
- }
-
- public void createIncomingConnection(StreamContext ctx,
- DuplexTransportConnection transport) {
- final DuplexConnection conn = new IncomingDuplexConnection(dbExecutor,
- cryptoExecutor, messageVerifier, db, eventBus, connRegistry,
- connReaderFactory, connWriterFactory, packetReaderFactory,
- packetWriterFactory, ctx, transport);
- Runnable write = new Runnable() {
- public void run() {
- conn.write();
- }
- };
- new Thread(write, "DuplexConnectionWriter").start();
- Runnable read = new Runnable() {
- public void run() {
- conn.read();
- }
- };
- new Thread(read, "DuplexConnectionReader").start();
- }
-
- public void createOutgoingConnection(ContactId c, TransportId t,
- DuplexTransportConnection transport) {
- StreamContext ctx = keyManager.getStreamContext(c, t);
- if(ctx == null) {
- LOG.warning("Could not create outgoing stream context");
- return;
- }
- final DuplexConnection conn = new OutgoingDuplexConnection(dbExecutor,
- cryptoExecutor, messageVerifier, db, eventBus, connRegistry,
- connReaderFactory, connWriterFactory, packetReaderFactory,
- packetWriterFactory, ctx, transport);
- Runnable write = new Runnable() {
- public void run() {
- conn.write();
- }
- };
- new Thread(write, "DuplexConnectionWriter").start();
- Runnable read = new Runnable() {
- public void run() {
- conn.read();
- }
- };
- new Thread(read, "DuplexConnectionReader").start();
- }
-}
diff --git a/briar-core/src/org/briarproject/messaging/duplex/DuplexMessagingModule.java b/briar-core/src/org/briarproject/messaging/duplex/DuplexMessagingModule.java
deleted file mode 100644
index 7f3996f34..000000000
--- a/briar-core/src/org/briarproject/messaging/duplex/DuplexMessagingModule.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.briarproject.messaging.duplex;
-
-import javax.inject.Singleton;
-
-import org.briarproject.api.messaging.duplex.DuplexConnectionFactory;
-
-import com.google.inject.AbstractModule;
-
-public class DuplexMessagingModule extends AbstractModule {
-
- protected void configure() {
- bind(DuplexConnectionFactory.class).to(
- DuplexConnectionFactoryImpl.class).in(Singleton.class);
- }
-}
diff --git a/briar-core/src/org/briarproject/messaging/duplex/IncomingDuplexConnection.java b/briar-core/src/org/briarproject/messaging/duplex/IncomingDuplexConnection.java
deleted file mode 100644
index 52e4e9318..000000000
--- a/briar-core/src/org/briarproject/messaging/duplex/IncomingDuplexConnection.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.briarproject.messaging.duplex;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.concurrent.Executor;
-
-import org.briarproject.api.db.DatabaseComponent;
-import org.briarproject.api.event.EventBus;
-import org.briarproject.api.messaging.MessageVerifier;
-import org.briarproject.api.messaging.PacketReaderFactory;
-import org.briarproject.api.messaging.PacketWriterFactory;
-import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
-import org.briarproject.api.transport.ConnectionRegistry;
-import org.briarproject.api.transport.StreamContext;
-import org.briarproject.api.transport.StreamReader;
-import org.briarproject.api.transport.StreamReaderFactory;
-import org.briarproject.api.transport.StreamWriter;
-import org.briarproject.api.transport.StreamWriterFactory;
-
-class IncomingDuplexConnection extends DuplexConnection {
-
- IncomingDuplexConnection(Executor dbExecutor, Executor cryptoExecutor,
- MessageVerifier messageVerifier, DatabaseComponent db,
- EventBus eventBus, ConnectionRegistry connRegistry,
- StreamReaderFactory connReaderFactory,
- StreamWriterFactory connWriterFactory,
- PacketReaderFactory packetReaderFactory,
- PacketWriterFactory packetWriterFactory,
- StreamContext ctx, DuplexTransportConnection transport) {
- super(dbExecutor, cryptoExecutor, messageVerifier, db, eventBus,
- connRegistry, connReaderFactory, connWriterFactory,
- packetReaderFactory, packetWriterFactory, ctx, transport);
- }
-
- @Override
- protected StreamReader createStreamReader() throws IOException {
- InputStream in = transport.getInputStream();
- int maxFrameLength = transport.getMaxFrameLength();
- return connReaderFactory.createStreamReader(in, maxFrameLength,
- ctx, true, true);
- }
-
- @Override
- protected StreamWriter createStreamWriter() throws IOException {
- OutputStream out = transport.getOutputStream();
- int maxFrameLength = transport.getMaxFrameLength();
- return connWriterFactory.createStreamWriter(out, maxFrameLength,
- Long.MAX_VALUE, ctx, true, false);
- }
-}
diff --git a/briar-core/src/org/briarproject/messaging/duplex/OutgoingDuplexConnection.java b/briar-core/src/org/briarproject/messaging/duplex/OutgoingDuplexConnection.java
deleted file mode 100644
index 7eab0226d..000000000
--- a/briar-core/src/org/briarproject/messaging/duplex/OutgoingDuplexConnection.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.briarproject.messaging.duplex;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.concurrent.Executor;
-
-import org.briarproject.api.db.DatabaseComponent;
-import org.briarproject.api.event.EventBus;
-import org.briarproject.api.messaging.MessageVerifier;
-import org.briarproject.api.messaging.PacketReaderFactory;
-import org.briarproject.api.messaging.PacketWriterFactory;
-import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
-import org.briarproject.api.transport.ConnectionRegistry;
-import org.briarproject.api.transport.StreamContext;
-import org.briarproject.api.transport.StreamReader;
-import org.briarproject.api.transport.StreamReaderFactory;
-import org.briarproject.api.transport.StreamWriter;
-import org.briarproject.api.transport.StreamWriterFactory;
-
-class OutgoingDuplexConnection extends DuplexConnection {
-
- OutgoingDuplexConnection(Executor dbExecutor, Executor cryptoExecutor,
- MessageVerifier messageVerifier, DatabaseComponent db,
- EventBus eventBus, ConnectionRegistry connRegistry,
- StreamReaderFactory connReaderFactory,
- StreamWriterFactory connWriterFactory,
- PacketReaderFactory packetReaderFactory,
- PacketWriterFactory packetWriterFactory, StreamContext ctx,
- DuplexTransportConnection transport) {
- super(dbExecutor, cryptoExecutor, messageVerifier, db, eventBus,
- connRegistry, connReaderFactory, connWriterFactory,
- packetReaderFactory, packetWriterFactory, ctx, transport);
- }
-
- @Override
- protected StreamReader createStreamReader() throws IOException {
- InputStream in = transport.getInputStream();
- int maxFrameLength = transport.getMaxFrameLength();
- return connReaderFactory.createStreamReader(in, maxFrameLength,
- ctx, false, false);
- }
-
- @Override
- protected StreamWriter createStreamWriter() throws IOException {
- OutputStream out = transport.getOutputStream();
- int maxFrameLength = transport.getMaxFrameLength();
- return connWriterFactory.createStreamWriter(out, maxFrameLength,
- Long.MAX_VALUE, ctx, false, true);
- }
-}
diff --git a/briar-core/src/org/briarproject/messaging/simplex/OutgoingSimplexConnection.java b/briar-core/src/org/briarproject/messaging/simplex/OutgoingSimplexConnection.java
deleted file mode 100644
index 94fd128ff..000000000
--- a/briar-core/src/org/briarproject/messaging/simplex/OutgoingSimplexConnection.java
+++ /dev/null
@@ -1,187 +0,0 @@
-package org.briarproject.messaging.simplex;
-
-import static java.util.logging.Level.WARNING;
-import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Collection;
-import java.util.logging.Logger;
-
-import org.briarproject.api.ContactId;
-import org.briarproject.api.TransportId;
-import org.briarproject.api.db.DatabaseComponent;
-import org.briarproject.api.db.DbException;
-import org.briarproject.api.messaging.Ack;
-import org.briarproject.api.messaging.PacketWriter;
-import org.briarproject.api.messaging.PacketWriterFactory;
-import org.briarproject.api.messaging.RetentionAck;
-import org.briarproject.api.messaging.RetentionUpdate;
-import org.briarproject.api.messaging.SubscriptionAck;
-import org.briarproject.api.messaging.SubscriptionUpdate;
-import org.briarproject.api.messaging.TransportAck;
-import org.briarproject.api.messaging.TransportUpdate;
-import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
-import org.briarproject.api.transport.ConnectionRegistry;
-import org.briarproject.api.transport.StreamContext;
-import org.briarproject.api.transport.StreamWriter;
-import org.briarproject.api.transport.StreamWriterFactory;
-import org.briarproject.util.ByteUtils;
-
-class OutgoingSimplexConnection {
-
- private static final Logger LOG =
- Logger.getLogger(OutgoingSimplexConnection.class.getName());
-
- private final DatabaseComponent db;
- private final ConnectionRegistry connRegistry;
- private final StreamWriterFactory connWriterFactory;
- private final PacketWriterFactory packetWriterFactory;
- private final StreamContext ctx;
- private final SimplexTransportWriter transport;
- private final ContactId contactId;
- private final TransportId transportId;
- private final long maxLatency;
-
- OutgoingSimplexConnection(DatabaseComponent db,
- ConnectionRegistry connRegistry,
- StreamWriterFactory connWriterFactory,
- PacketWriterFactory packetWriterFactory, StreamContext ctx,
- SimplexTransportWriter transport) {
- this.db = db;
- this.connRegistry = connRegistry;
- this.connWriterFactory = connWriterFactory;
- this.packetWriterFactory = packetWriterFactory;
- this.ctx = ctx;
- this.transport = transport;
- contactId = ctx.getContactId();
- transportId = ctx.getTransportId();
- maxLatency = transport.getMaxLatency();
- }
-
- void write() {
- connRegistry.registerConnection(contactId, transportId);
- try {
- OutputStream out = transport.getOutputStream();
- long capacity = transport.getCapacity();
- int maxFrameLength = transport.getMaxFrameLength();
- StreamWriter conn = connWriterFactory.createStreamWriter(
- out, maxFrameLength, capacity, ctx, false, true);
- out = conn.getOutputStream();
- if(conn.getRemainingCapacity() < MAX_PACKET_LENGTH)
- throw new EOFException();
- PacketWriter writer = packetWriterFactory.createPacketWriter(out,
- false);
- // Send the initial packets: updates and acks
- boolean hasSpace = writeTransportAcks(conn, writer);
- if(hasSpace) hasSpace = writeTransportUpdates(conn, writer);
- if(hasSpace) hasSpace = writeSubscriptionAck(conn, writer);
- if(hasSpace) hasSpace = writeSubscriptionUpdate(conn, writer);
- if(hasSpace) hasSpace = writeRetentionAck(conn, writer);
- if(hasSpace) hasSpace = writeRetentionUpdate(conn, writer);
- // Write acks until you can't write acks no more
- capacity = conn.getRemainingCapacity();
- int maxMessages = writer.getMaxMessagesForAck(capacity);
- Ack a = db.generateAck(contactId, maxMessages);
- while(a != null) {
- writer.writeAck(a);
- capacity = conn.getRemainingCapacity();
- maxMessages = writer.getMaxMessagesForAck(capacity);
- a = db.generateAck(contactId, maxMessages);
- }
- // Write messages until you can't write messages no more
- capacity = conn.getRemainingCapacity();
- int maxLength = (int) Math.min(capacity, MAX_PACKET_LENGTH);
- Collection batch = db.generateBatch(contactId, maxLength,
- maxLatency);
- while(batch != null) {
- for(byte[] raw : batch) writer.writeMessage(raw);
- capacity = conn.getRemainingCapacity();
- maxLength = (int) Math.min(capacity, MAX_PACKET_LENGTH);
- batch = db.generateBatch(contactId, maxLength, maxLatency);
- }
- writer.flush();
- writer.close();
- dispose(false);
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true);
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true);
- }
- connRegistry.unregisterConnection(contactId, transportId);
- }
-
- private boolean writeTransportAcks(StreamWriter conn,
- PacketWriter writer) throws DbException, IOException {
- assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH;
- Collection acks = db.generateTransportAcks(contactId);
- if(acks == null) return true;
- for(TransportAck a : acks) {
- writer.writeTransportAck(a);
- if(conn.getRemainingCapacity() < MAX_PACKET_LENGTH) return false;
- }
- return true;
- }
-
- private boolean writeTransportUpdates(StreamWriter conn,
- PacketWriter writer) throws DbException, IOException {
- assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH;
- Collection updates =
- db.generateTransportUpdates(contactId, maxLatency);
- if(updates == null) return true;
- for(TransportUpdate u : updates) {
- writer.writeTransportUpdate(u);
- if(conn.getRemainingCapacity() < MAX_PACKET_LENGTH) return false;
- }
- return true;
- }
-
- private boolean writeSubscriptionAck(StreamWriter conn,
- PacketWriter writer) throws DbException, IOException {
- assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH;
- SubscriptionAck a = db.generateSubscriptionAck(contactId);
- if(a == null) return true;
- writer.writeSubscriptionAck(a);
- return conn.getRemainingCapacity() >= MAX_PACKET_LENGTH;
- }
-
- private boolean writeSubscriptionUpdate(StreamWriter conn,
- PacketWriter writer) throws DbException, IOException {
- assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH;
- SubscriptionUpdate u =
- db.generateSubscriptionUpdate(contactId, maxLatency);
- if(u == null) return true;
- writer.writeSubscriptionUpdate(u);
- return conn.getRemainingCapacity() >= MAX_PACKET_LENGTH;
- }
-
- private boolean writeRetentionAck(StreamWriter conn,
- PacketWriter writer) throws DbException, IOException {
- assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH;
- RetentionAck a = db.generateRetentionAck(contactId);
- if(a == null) return true;
- writer.writeRetentionAck(a);
- return conn.getRemainingCapacity() >= MAX_PACKET_LENGTH;
- }
-
- private boolean writeRetentionUpdate(StreamWriter conn,
- PacketWriter writer) throws DbException, IOException {
- assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH;
- RetentionUpdate u = db.generateRetentionUpdate(contactId, maxLatency);
- if(u == null) return true;
- writer.writeRetentionUpdate(u);
- return conn.getRemainingCapacity() >= MAX_PACKET_LENGTH;
- }
-
- private void dispose(boolean exception) {
- ByteUtils.erase(ctx.getSecret());
- try {
- transport.dispose(exception);
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- }
- }
-}
diff --git a/briar-core/src/org/briarproject/messaging/simplex/SimplexConnectionFactoryImpl.java b/briar-core/src/org/briarproject/messaging/simplex/SimplexConnectionFactoryImpl.java
deleted file mode 100644
index f202d061e..000000000
--- a/briar-core/src/org/briarproject/messaging/simplex/SimplexConnectionFactoryImpl.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.briarproject.messaging.simplex;
-
-import java.util.concurrent.Executor;
-import java.util.logging.Logger;
-
-import javax.inject.Inject;
-
-import org.briarproject.api.ContactId;
-import org.briarproject.api.TransportId;
-import org.briarproject.api.crypto.CryptoExecutor;
-import org.briarproject.api.crypto.KeyManager;
-import org.briarproject.api.db.DatabaseComponent;
-import org.briarproject.api.db.DatabaseExecutor;
-import org.briarproject.api.messaging.MessageVerifier;
-import org.briarproject.api.messaging.PacketReaderFactory;
-import org.briarproject.api.messaging.PacketWriterFactory;
-import org.briarproject.api.messaging.simplex.SimplexConnectionFactory;
-import org.briarproject.api.plugins.simplex.SimplexTransportReader;
-import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
-import org.briarproject.api.transport.ConnectionRegistry;
-import org.briarproject.api.transport.StreamContext;
-import org.briarproject.api.transport.StreamReaderFactory;
-import org.briarproject.api.transport.StreamWriterFactory;
-
-class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
-
- private static final Logger LOG =
- Logger.getLogger(SimplexConnectionFactoryImpl.class.getName());
-
- private final Executor dbExecutor, cryptoExecutor;
- private final MessageVerifier messageVerifier;
- private final DatabaseComponent db;
- private final KeyManager keyManager;
- private final ConnectionRegistry connRegistry;
- private final StreamReaderFactory connReaderFactory;
- private final StreamWriterFactory connWriterFactory;
- private final PacketReaderFactory packetReaderFactory;
- private final PacketWriterFactory packetWriterFactory;
-
- @Inject
- SimplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
- @CryptoExecutor Executor cryptoExecutor,
- MessageVerifier messageVerifier, DatabaseComponent db,
- KeyManager keyManager, ConnectionRegistry connRegistry,
- StreamReaderFactory connReaderFactory,
- StreamWriterFactory connWriterFactory,
- PacketReaderFactory packetReaderFactory,
- PacketWriterFactory packetWriterFactory) {
- this.dbExecutor = dbExecutor;
- this.cryptoExecutor = cryptoExecutor;
- this.messageVerifier = messageVerifier;
- this.db = db;
- this.keyManager = keyManager;
- this.connRegistry = connRegistry;
- this.connReaderFactory = connReaderFactory;
- this.connWriterFactory = connWriterFactory;
- this.packetReaderFactory = packetReaderFactory;
- this.packetWriterFactory = packetWriterFactory;
- }
-
- public void createIncomingConnection(StreamContext ctx,
- SimplexTransportReader r) {
- final IncomingSimplexConnection conn = new IncomingSimplexConnection(
- dbExecutor, cryptoExecutor, messageVerifier, db, connRegistry,
- connReaderFactory, packetReaderFactory, ctx, r);
- Runnable read = new Runnable() {
- public void run() {
- conn.read();
- }
- };
- new Thread(read, "SimplexConnectionReader").start();
- }
-
- public void createOutgoingConnection(ContactId c, TransportId t,
- SimplexTransportWriter w) {
- StreamContext ctx = keyManager.getStreamContext(c, t);
- if(ctx == null) {
- LOG.warning("Could not create outgoing connection context");
- return;
- }
- final OutgoingSimplexConnection conn = new OutgoingSimplexConnection(db,
- connRegistry, connWriterFactory, packetWriterFactory, ctx, w);
- Runnable write = new Runnable() {
- public void run() {
- conn.write();
- }
- };
- new Thread(write, "SimplexConnectionWriter").start();
- }
-}
diff --git a/briar-core/src/org/briarproject/messaging/simplex/SimplexMessagingModule.java b/briar-core/src/org/briarproject/messaging/simplex/SimplexMessagingModule.java
deleted file mode 100644
index 3bf9ffe85..000000000
--- a/briar-core/src/org/briarproject/messaging/simplex/SimplexMessagingModule.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.briarproject.messaging.simplex;
-
-import javax.inject.Singleton;
-
-import org.briarproject.api.messaging.simplex.SimplexConnectionFactory;
-
-import com.google.inject.AbstractModule;
-
-public class SimplexMessagingModule extends AbstractModule {
-
- protected void configure() {
- bind(SimplexConnectionFactory.class).to(
- SimplexConnectionFactoryImpl.class).in(Singleton.class);
- }
-}
diff --git a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java
index 1e14f9c72..745d5df05 100644
--- a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java
+++ b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java
@@ -27,6 +27,8 @@ import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginCallback;
import org.briarproject.api.plugins.PluginManager;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexPluginConfig;
@@ -36,8 +38,6 @@ import org.briarproject.api.plugins.simplex.SimplexPlugin;
import org.briarproject.api.plugins.simplex.SimplexPluginCallback;
import org.briarproject.api.plugins.simplex.SimplexPluginConfig;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
-import org.briarproject.api.plugins.simplex.SimplexTransportReader;
-import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
import org.briarproject.api.system.Clock;
import org.briarproject.api.transport.ConnectionDispatcher;
import org.briarproject.api.ui.UiCallback;
@@ -377,11 +377,11 @@ class PluginManagerImpl implements PluginManager {
super(id);
}
- public void readerCreated(SimplexTransportReader r) {
+ public void readerCreated(TransportConnectionReader r) {
dispatcher.dispatchIncomingConnection(id, r);
}
- public void writerCreated(ContactId c, SimplexTransportWriter w) {
+ public void writerCreated(ContactId c, TransportConnectionWriter w) {
dispatcher.dispatchOutgoingConnection(c, id, w);
}
}
diff --git a/briar-core/src/org/briarproject/plugins/PollerImpl.java b/briar-core/src/org/briarproject/plugins/PollerImpl.java
index a92cdf4cd..b560bfdd5 100644
--- a/briar-core/src/org/briarproject/plugins/PollerImpl.java
+++ b/briar-core/src/org/briarproject/plugins/PollerImpl.java
@@ -20,14 +20,14 @@ class PollerImpl implements Poller {
Logger.getLogger(PollerImpl.class.getName());
private final Executor ioExecutor;
- private final ConnectionRegistry connRegistry;
+ private final ConnectionRegistry connectionRegistry;
private final Timer timer;
@Inject
- PollerImpl(@IoExecutor Executor ioExecutor, ConnectionRegistry connRegistry,
- Timer timer) {
+ PollerImpl(@IoExecutor Executor ioExecutor,
+ ConnectionRegistry connectionRegistry, Timer timer) {
this.ioExecutor = ioExecutor;
- this.connRegistry = connRegistry;
+ this.connectionRegistry = connectionRegistry;
this.timer = timer;
}
@@ -53,7 +53,7 @@ class PollerImpl implements Poller {
public void run() {
if(LOG.isLoggable(INFO))
LOG.info("Polling " + p.getClass().getSimpleName());
- p.poll(connRegistry.getConnectedContacts(p.getId()));
+ p.poll(connectionRegistry.getConnectedContacts(p.getId()));
}
});
}
diff --git a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java
index 030203736..1af4215ba 100644
--- a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java
+++ b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java
@@ -14,10 +14,10 @@ import java.util.concurrent.Executor;
import java.util.logging.Logger;
import org.briarproject.api.ContactId;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.simplex.SimplexPlugin;
import org.briarproject.api.plugins.simplex.SimplexPluginCallback;
-import org.briarproject.api.plugins.simplex.SimplexTransportReader;
-import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
import org.briarproject.api.system.FileUtils;
public abstract class FilePlugin implements SimplexPlugin {
@@ -60,11 +60,11 @@ public abstract class FilePlugin implements SimplexPlugin {
return running;
}
- public SimplexTransportReader createReader(ContactId c) {
+ public TransportConnectionReader createReader(ContactId c) {
return null;
}
- public SimplexTransportWriter createWriter(ContactId c) {
+ public TransportConnectionWriter createWriter(ContactId c) {
if(!running) return null;
return createWriter(createConnectionFilename());
}
@@ -81,7 +81,7 @@ public abstract class FilePlugin implements SimplexPlugin {
return filename.toLowerCase(Locale.US).matches("[a-z]{8}\\.dat");
}
- private SimplexTransportWriter createWriter(String filename) {
+ private TransportConnectionWriter createWriter(String filename) {
if(!running) return null;
File dir = chooseOutputDirectory();
if(dir == null || !dir.exists() || !dir.isDirectory()) return null;
diff --git a/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java b/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java
index 0a97e5673..316773ec0 100644
--- a/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java
+++ b/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java
@@ -7,9 +7,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;
-import org.briarproject.api.plugins.simplex.SimplexTransportReader;
+import org.briarproject.api.plugins.TransportConnectionReader;
-class FileTransportReader implements SimplexTransportReader {
+class FileTransportReader implements TransportConnectionReader {
private static final Logger LOG =
Logger.getLogger(FileTransportReader.class.getName());
@@ -28,6 +28,10 @@ class FileTransportReader implements SimplexTransportReader {
return plugin.getMaxFrameLength();
}
+ public long getMaxLatency() {
+ return plugin.getMaxLatency();
+ }
+
public InputStream getInputStream() {
return in;
}
diff --git a/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java b/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java
index d56dfb726..2ca55593f 100644
--- a/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java
+++ b/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java
@@ -7,9 +7,9 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Logger;
-import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
+import org.briarproject.api.plugins.TransportConnectionWriter;
-class FileTransportWriter implements SimplexTransportWriter {
+class FileTransportWriter implements TransportConnectionWriter {
private static final Logger LOG =
Logger.getLogger(FileTransportWriter.class.getName());
@@ -27,10 +27,6 @@ class FileTransportWriter implements SimplexTransportWriter {
this.plugin = plugin;
}
- public long getCapacity() {
- return capacity;
- }
-
public int getMaxFrameLength() {
return plugin.getMaxFrameLength();
}
@@ -39,6 +35,10 @@ class FileTransportWriter implements SimplexTransportWriter {
return plugin.getMaxLatency();
}
+ public long getCapacity() {
+ return capacity;
+ }
+
public OutputStream getOutputStream() {
return out;
}
diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java b/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java
index 6612f95fe..83506a211 100644
--- a/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java
+++ b/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java
@@ -4,38 +4,80 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.briarproject.api.plugins.Plugin;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
class TcpTransportConnection implements DuplexTransportConnection {
private final Plugin plugin;
private final Socket socket;
+ private final Reader reader;
+ private final Writer writer;
+ private final AtomicBoolean halfClosed, closed;
TcpTransportConnection(Plugin plugin, Socket socket) {
this.plugin = plugin;
this.socket = socket;
+ reader = new Reader();
+ writer = new Writer();
+ halfClosed = new AtomicBoolean(false);
+ closed = new AtomicBoolean(false);
}
- public int getMaxFrameLength() {
- return plugin.getMaxFrameLength();
+ public TransportConnectionReader getReader() {
+ return reader;
}
- public long getMaxLatency() {
- return plugin.getMaxLatency();
+ public TransportConnectionWriter getWriter() {
+ return writer;
}
- public InputStream getInputStream() throws IOException {
- return socket.getInputStream();
+ private class Reader implements TransportConnectionReader {
+
+ public int getMaxFrameLength() {
+ return plugin.getMaxFrameLength();
+ }
+
+ public long getMaxLatency() {
+ return plugin.getMaxLatency();
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return socket.getInputStream();
+ }
+
+ public void dispose(boolean exception, boolean recognised)
+ throws IOException {
+ if(halfClosed.getAndSet(true) || exception)
+ if(!closed.getAndSet(true)) socket.close();
+ }
}
- public OutputStream getOutputStream() throws IOException {
- return socket.getOutputStream();
- }
+ private class Writer implements TransportConnectionWriter {
- public void dispose(boolean exception, boolean recognised)
- throws IOException {
- socket.close();
+ public int getMaxFrameLength() {
+ return plugin.getMaxFrameLength();
+ }
+
+ public long getMaxLatency() {
+ return plugin.getMaxLatency();
+ }
+
+ public long getCapacity() {
+ return Long.MAX_VALUE;
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ return socket.getOutputStream();
+ }
+
+ public void dispose(boolean exception) throws IOException {
+ if(halfClosed.getAndSet(true) || exception)
+ if(!closed.getAndSet(true)) socket.close();
+ }
}
}
diff --git a/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java b/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java
index adda06a56..abae50bff 100644
--- a/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java
+++ b/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java
@@ -13,14 +13,16 @@ import javax.inject.Inject;
import org.briarproject.api.ContactId;
import org.briarproject.api.TransportId;
+import org.briarproject.api.crypto.KeyManager;
import org.briarproject.api.db.DbException;
import org.briarproject.api.lifecycle.IoExecutor;
-import org.briarproject.api.messaging.duplex.DuplexConnectionFactory;
-import org.briarproject.api.messaging.simplex.SimplexConnectionFactory;
+import org.briarproject.api.messaging.MessagingSession;
+import org.briarproject.api.messaging.MessagingSessionFactory;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
-import org.briarproject.api.plugins.simplex.SimplexTransportReader;
-import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
import org.briarproject.api.transport.ConnectionDispatcher;
+import org.briarproject.api.transport.ConnectionRegistry;
import org.briarproject.api.transport.StreamContext;
import org.briarproject.api.transport.TagRecogniser;
@@ -30,132 +32,280 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher {
Logger.getLogger(ConnectionDispatcherImpl.class.getName());
private final Executor ioExecutor;
+ private final KeyManager keyManager;
private final TagRecogniser tagRecogniser;
- private final SimplexConnectionFactory simplexConnFactory;
- private final DuplexConnectionFactory duplexConnFactory;
+ private final MessagingSessionFactory messagingSessionFactory;
+ private final ConnectionRegistry connectionRegistry;
@Inject
ConnectionDispatcherImpl(@IoExecutor Executor ioExecutor,
- TagRecogniser tagRecogniser,
- SimplexConnectionFactory simplexConnFactory,
- DuplexConnectionFactory duplexConnFactory) {
+ KeyManager keyManager, TagRecogniser tagRecogniser,
+ MessagingSessionFactory messagingSessionFactory,
+ ConnectionRegistry connectionRegistry) {
this.ioExecutor = ioExecutor;
+ this.keyManager = keyManager;
this.tagRecogniser = tagRecogniser;
- this.simplexConnFactory = simplexConnFactory;
- this.duplexConnFactory = duplexConnFactory;
+ this.messagingSessionFactory = messagingSessionFactory;
+ this.connectionRegistry = connectionRegistry;
}
public void dispatchIncomingConnection(TransportId t,
- SimplexTransportReader r) {
- ioExecutor.execute(new DispatchSimplexConnection(t, r));
+ TransportConnectionReader r) {
+ ioExecutor.execute(new DispatchIncomingSimplexConnection(t, r));
}
public void dispatchIncomingConnection(TransportId t,
DuplexTransportConnection d) {
- ioExecutor.execute(new DispatchDuplexConnection(t, d));
+ ioExecutor.execute(new DispatchIncomingDuplexConnection(t, d));
}
public void dispatchOutgoingConnection(ContactId c, TransportId t,
- SimplexTransportWriter w) {
- simplexConnFactory.createOutgoingConnection(c, t, w);
+ TransportConnectionWriter w) {
+ ioExecutor.execute(new DispatchOutgoingSimplexConnection(c, t, w));
}
public void dispatchOutgoingConnection(ContactId c, TransportId t,
DuplexTransportConnection d) {
- duplexConnFactory.createOutgoingConnection(c, t, d);
+ ioExecutor.execute(new DispatchOutgoingDuplexConnection(c, t, d));
}
- private byte[] readTag(InputStream in) throws IOException {
- byte[] b = new byte[TAG_LENGTH];
- int offset = 0;
- while(offset < b.length) {
- int read = in.read(b, offset, b.length - offset);
- if(read == -1) throw new EOFException();
- offset += read;
+ private StreamContext readAndRecogniseTag(TransportId t,
+ TransportConnectionReader r) {
+ // Read the tag
+ byte[] tag = new byte[TAG_LENGTH];
+ try {
+ InputStream in = r.getInputStream();
+ int offset = 0;
+ while(offset < tag.length) {
+ int read = in.read(tag, offset, tag.length - offset);
+ if(read == -1) throw new EOFException();
+ offset += read;
+ }
+ } catch(IOException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ dispose(r, true, false);
+ return null;
}
- return b;
+ // Recognise the tag
+ StreamContext ctx = null;
+ try {
+ ctx = tagRecogniser.recogniseTag(t, tag);
+ } catch(DbException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ dispose(r, true, false);
+ return null;
+ }
+ if(ctx == null) dispose(r, false, false);
+ return ctx;
}
- private class DispatchSimplexConnection implements Runnable {
+ private void runAndDispose(StreamContext ctx, TransportConnectionReader r) {
+ MessagingSession in =
+ messagingSessionFactory.createIncomingSession(ctx, r);
+ ContactId contactId = ctx.getContactId();
+ TransportId transportId = ctx.getTransportId();
+ connectionRegistry.registerConnection(contactId, transportId);
+ try {
+ in.run();
+ } catch(IOException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ dispose(r, true, true);
+ return;
+ } finally {
+ connectionRegistry.unregisterConnection(contactId, transportId);
+ }
+ dispose(r, false, true);
+ }
+
+ private void dispose(TransportConnectionReader r, boolean exception,
+ boolean recognised) {
+ try {
+ r.dispose(exception, recognised);
+ } catch(IOException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ }
+ }
+
+ private void runAndDispose(StreamContext ctx, TransportConnectionWriter w,
+ boolean duplex) {
+ MessagingSession out =
+ messagingSessionFactory.createOutgoingSession(ctx, w, duplex);
+ ContactId contactId = ctx.getContactId();
+ TransportId transportId = ctx.getTransportId();
+ connectionRegistry.registerConnection(contactId, transportId);
+ try {
+ out.run();
+ } catch(IOException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ dispose(w, true);
+ return;
+ } finally {
+ connectionRegistry.unregisterConnection(contactId, transportId);
+ }
+ dispose(w, false);
+ }
+
+ private void dispose(TransportConnectionWriter w, boolean exception) {
+ try {
+ w.dispose(exception);
+ } catch(IOException e) {
+ if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ }
+ }
+
+ private class DispatchIncomingSimplexConnection implements Runnable {
private final TransportId transportId;
- private final SimplexTransportReader transport;
+ private final TransportConnectionReader reader;
- private DispatchSimplexConnection(TransportId transportId,
- SimplexTransportReader transport) {
+ private DispatchIncomingSimplexConnection(TransportId transportId,
+ TransportConnectionReader reader) {
this.transportId = transportId;
- this.transport = transport;
+ this.reader = reader;
}
public void run() {
- try {
- byte[] tag = readTag(transport.getInputStream());
- StreamContext ctx = tagRecogniser.recogniseTag(transportId,
- tag);
- if(ctx == null) {
- transport.dispose(false, false);
- } else {
- simplexConnFactory.createIncomingConnection(ctx,
- transport);
- }
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- try {
- transport.dispose(true, false);
- } catch(IOException e1) {
- if(LOG.isLoggable(WARNING))
- LOG.log(WARNING, e1.toString(), e1);
- }
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- try {
- transport.dispose(true, false);
- } catch(IOException e1) {
- if(LOG.isLoggable(WARNING))
- LOG.log(WARNING, e1.toString(), e1);
- }
- }
+ // Read and recognise the tag
+ StreamContext ctx = readAndRecogniseTag(transportId, reader);
+ if(ctx == null) return;
+ // Run the incoming session
+ runAndDispose(ctx, reader);
}
}
- private class DispatchDuplexConnection implements Runnable {
+ private class DispatchOutgoingSimplexConnection implements Runnable {
+
+ private final ContactId contactId;
+ private final TransportId transportId;
+ private final TransportConnectionWriter writer;
+
+ private DispatchOutgoingSimplexConnection(ContactId contactId,
+ TransportId transportId, TransportConnectionWriter writer) {
+ this.contactId = contactId;
+ this.transportId = transportId;
+ this.writer = writer;
+ }
+
+ public void run() {
+ // Allocate a stream context
+ StreamContext ctx = keyManager.getStreamContext(contactId,
+ transportId);
+ if(ctx == null) {
+ dispose(writer, false);
+ return;
+ }
+ // Run the outgoing session
+ runAndDispose(ctx, writer, false);
+ }
+ }
+
+ private class DispatchIncomingDuplexConnection implements Runnable {
private final TransportId transportId;
- private final DuplexTransportConnection transport;
+ private final TransportConnectionReader reader;
+ private final TransportConnectionWriter writer;
- private DispatchDuplexConnection(TransportId transportId,
+ private DispatchIncomingDuplexConnection(TransportId transportId,
DuplexTransportConnection transport) {
this.transportId = transportId;
- this.transport = transport;
+ reader = transport.getReader();
+ writer = transport.getWriter();
}
public void run() {
- byte[] tag;
- try {
- tag = readTag(transport.getInputStream());
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, false);
- return;
- }
- StreamContext ctx = null;
- try {
- ctx = tagRecogniser.recogniseTag(transportId, tag);
- } catch(DbException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
- dispose(true, false);
- return;
- }
- if(ctx == null) dispose(false, false);
- else duplexConnFactory.createIncomingConnection(ctx, transport);
+ // Read and recognise the tag
+ StreamContext ctx = readAndRecogniseTag(transportId, reader);
+ if(ctx == null) return;
+ // Start the outgoing session on another thread
+ ioExecutor.execute(new DispatchIncomingDuplexConnectionSide2(
+ ctx.getContactId(), transportId, writer));
+ // Run the incoming session
+ runAndDispose(ctx, reader);
+ }
+ }
+
+ private class DispatchIncomingDuplexConnectionSide2 implements Runnable {
+
+ private final ContactId contactId;
+ private final TransportId transportId;
+ private final TransportConnectionWriter writer;
+
+ private DispatchIncomingDuplexConnectionSide2(ContactId contactId,
+ TransportId transportId, TransportConnectionWriter writer) {
+ this.contactId = contactId;
+ this.transportId = transportId;
+ this.writer = writer;
}
- private void dispose(boolean exception, boolean recognised) {
- try {
- transport.dispose(exception, recognised);
- } catch(IOException e) {
- if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+ public void run() {
+ // Allocate a stream context
+ StreamContext ctx = keyManager.getStreamContext(contactId,
+ transportId);
+ if(ctx == null) {
+ dispose(writer, false);
+ return;
}
+ // Run the outgoing session
+ runAndDispose(ctx, writer, true);
+ }
+ }
+
+ private class DispatchOutgoingDuplexConnection implements Runnable {
+
+ private final ContactId contactId;
+ private final TransportId transportId;
+ private final TransportConnectionReader reader;
+ private final TransportConnectionWriter writer;
+
+ private DispatchOutgoingDuplexConnection(ContactId contactId,
+ TransportId transportId, DuplexTransportConnection transport) {
+ this.contactId = contactId;
+ this.transportId = transportId;
+ reader = transport.getReader();
+ writer = transport.getWriter();
+ }
+
+ public void run() {
+ // Allocate a stream context
+ StreamContext ctx = keyManager.getStreamContext(contactId,
+ transportId);
+ if(ctx == null) {
+ dispose(writer, false);
+ return;
+ }
+ // Start the incoming session on another thread
+ ioExecutor.execute(new DispatchOutgoingDuplexConnectionSide2(
+ contactId, transportId, reader));
+ // Run the outgoing session
+ runAndDispose(ctx, writer, true);
+ }
+ }
+
+ private class DispatchOutgoingDuplexConnectionSide2 implements Runnable {
+
+ private final ContactId contactId;
+ private final TransportId transportId;
+ private final TransportConnectionReader reader;
+
+ private DispatchOutgoingDuplexConnectionSide2(ContactId contactId,
+ TransportId transportId, TransportConnectionReader reader) {
+ this.contactId = contactId;
+ this.transportId = transportId;
+ this.reader = reader;
+ }
+
+ public void run() {
+ // Read and recognise the tag
+ StreamContext ctx = readAndRecogniseTag(transportId, reader);
+ if(ctx == null) return;
+ // Check that the stream comes from the expected contact
+ if(!ctx.getContactId().equals(contactId)) {
+ LOG.warning("Wrong contact ID for duplex connection");
+ dispose(reader, true, true);
+ return;
+ }
+ // Run the incoming session
+ runAndDispose(ctx, reader);
}
}
}
\ No newline at end of file
diff --git a/briar-core/src/org/briarproject/transport/FrameWriter.java b/briar-core/src/org/briarproject/transport/FrameWriter.java
index 167ab3430..4f29b7999 100644
--- a/briar-core/src/org/briarproject/transport/FrameWriter.java
+++ b/briar-core/src/org/briarproject/transport/FrameWriter.java
@@ -8,9 +8,6 @@ interface FrameWriter {
void writeFrame(byte[] frame, int payloadLength, boolean finalFrame)
throws IOException;
- /** Flushes the stack. */
+ /** Flushes the stream. */
void flush() throws IOException;
-
- /** Returns the maximum number of bytes that can be written. */
- long getRemainingCapacity();
}
diff --git a/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java b/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java
index a2c003677..0d6d6ace9 100644
--- a/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java
+++ b/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java
@@ -19,22 +19,18 @@ class OutgoingEncryptionLayer implements FrameWriter {
private final AuthenticatedCipher frameCipher;
private final SecretKey frameKey;
private final byte[] tag, iv, aad, ciphertext;
- private final int frameLength, maxPayloadLength;
+ private final int frameLength;
- private long capacity, frameNumber;
+ private long frameNumber;
private boolean writeTag;
- /** Constructor for the initiator's side of a connection. */
- OutgoingEncryptionLayer(OutputStream out, long capacity,
- AuthenticatedCipher frameCipher, SecretKey frameKey,
- int frameLength, byte[] tag) {
+ OutgoingEncryptionLayer(OutputStream out, AuthenticatedCipher frameCipher,
+ SecretKey frameKey, int frameLength, byte[] tag) {
this.out = out;
- this.capacity = capacity;
this.frameCipher = frameCipher;
this.frameKey = frameKey;
this.frameLength = frameLength;
this.tag = tag;
- maxPayloadLength = frameLength - HEADER_LENGTH - MAC_LENGTH;
iv = new byte[IV_LENGTH];
aad = new byte[AAD_LENGTH];
ciphertext = new byte[frameLength];
@@ -42,24 +38,6 @@ class OutgoingEncryptionLayer implements FrameWriter {
writeTag = true;
}
- /** Constructor for the responder's side of a connection. */
- OutgoingEncryptionLayer(OutputStream out, long capacity,
- AuthenticatedCipher frameCipher, SecretKey frameKey,
- int frameLength) {
- this.out = out;
- this.capacity = capacity;
- this.frameCipher = frameCipher;
- this.frameKey = frameKey;
- this.frameLength = frameLength;
- tag = null;
- maxPayloadLength = frameLength - HEADER_LENGTH - MAC_LENGTH;
- iv = new byte[IV_LENGTH];
- aad = new byte[AAD_LENGTH];
- ciphertext = new byte[frameLength];
- frameNumber = 0;
- writeTag = false;
- }
-
public void writeFrame(byte[] frame, int payloadLength, boolean finalFrame)
throws IOException {
if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
@@ -71,7 +49,6 @@ class OutgoingEncryptionLayer implements FrameWriter {
frameKey.erase();
throw e;
}
- capacity -= tag.length;
writeTag = false;
}
// Encode the header
@@ -107,7 +84,6 @@ class OutgoingEncryptionLayer implements FrameWriter {
frameKey.erase();
throw e;
}
- capacity -= ciphertextLength;
frameNumber++;
}
@@ -120,33 +96,8 @@ class OutgoingEncryptionLayer implements FrameWriter {
frameKey.erase();
throw e;
}
- capacity -= tag.length;
writeTag = false;
}
out.flush();
}
-
- public long getRemainingCapacity() {
- // How many frame numbers can we use?
- long frameNumbers = MAX_32_BIT_UNSIGNED - frameNumber + 1;
- // How many full frames do we have space for?
- long bytes = writeTag ? capacity - tag.length : capacity;
- long fullFrames = bytes / frameLength;
- // Are we limited by frame numbers or space?
- if(frameNumbers > fullFrames) {
- // Can we send a partial frame after the full frames?
- int partialFrame = (int) (bytes - fullFrames * frameLength);
- if(partialFrame > HEADER_LENGTH + MAC_LENGTH) {
- // Send full frames and a partial frame, limited by space
- int partialPayload = partialFrame - HEADER_LENGTH - MAC_LENGTH;
- return maxPayloadLength * fullFrames + partialPayload;
- } else {
- // Send full frames only, limited by space
- return maxPayloadLength * fullFrames;
- }
- } else {
- // Send full frames only, limited by frame numbers
- return maxPayloadLength * frameNumbers;
- }
- }
}
\ No newline at end of file
diff --git a/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java b/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java
index d2997b366..d71bfc50a 100644
--- a/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java
+++ b/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java
@@ -20,24 +20,21 @@ class StreamReaderFactoryImpl implements StreamReaderFactory {
}
public StreamReader createStreamReader(InputStream in,
- int maxFrameLength, StreamContext ctx, boolean incoming,
- boolean initiator) {
+ int maxFrameLength, StreamContext ctx) {
byte[] secret = ctx.getSecret();
long streamNumber = ctx.getStreamNumber();
- boolean weAreAlice = ctx.getAlice();
- boolean initiatorIsAlice = incoming ? !weAreAlice : weAreAlice;
- SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber,
- initiatorIsAlice, initiator);
- FrameReader encryption = new IncomingEncryptionLayer(in,
+ boolean alice = !ctx.getAlice();
+ SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice);
+ FrameReader frameReader = new IncomingEncryptionLayer(in,
crypto.getFrameCipher(), frameKey, maxFrameLength);
- return new StreamReaderImpl(encryption, maxFrameLength);
+ return new StreamReaderImpl(frameReader, maxFrameLength);
}
public StreamReader createInvitationStreamReader(InputStream in,
int maxFrameLength, byte[] secret, boolean alice) {
- SecretKey frameKey = crypto.deriveFrameKey(secret, 0, true, alice);
- FrameReader encryption = new IncomingEncryptionLayer(in,
+ SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice);
+ FrameReader frameReader = new IncomingEncryptionLayer(in,
crypto.getFrameCipher(), frameKey, maxFrameLength);
- return new StreamReaderImpl(encryption, maxFrameLength);
+ return new StreamReaderImpl(frameReader, maxFrameLength);
}
}
diff --git a/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java b/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java
index 1935fea1b..80185d74b 100644
--- a/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java
+++ b/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java
@@ -22,35 +22,29 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
}
public StreamWriter createStreamWriter(OutputStream out,
- int maxFrameLength, long capacity, StreamContext ctx,
- boolean incoming, boolean initiator) {
+ int maxFrameLength, StreamContext ctx) {
byte[] secret = ctx.getSecret();
long streamNumber = ctx.getStreamNumber();
- boolean weAreAlice = ctx.getAlice();
- boolean initiatorIsAlice = incoming ? !weAreAlice : weAreAlice;
- SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber,
- initiatorIsAlice, initiator);
- FrameWriter encryption;
- if(initiator) {
- byte[] tag = new byte[TAG_LENGTH];
- SecretKey tagKey = crypto.deriveTagKey(secret, initiatorIsAlice);
- crypto.encodeTag(tag, tagKey, streamNumber);
- tagKey.erase();
- encryption = new OutgoingEncryptionLayer(out, capacity,
- crypto.getFrameCipher(), frameKey, maxFrameLength, tag);
- } else {
- encryption = new OutgoingEncryptionLayer(out, capacity,
- crypto.getFrameCipher(), frameKey, maxFrameLength);
- }
- return new StreamWriterImpl(encryption, maxFrameLength);
+ boolean alice = ctx.getAlice();
+ byte[] tag = new byte[TAG_LENGTH];
+ SecretKey tagKey = crypto.deriveTagKey(secret, alice);
+ crypto.encodeTag(tag, tagKey, streamNumber);
+ tagKey.erase();
+ SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice);
+ FrameWriter frameWriter = new OutgoingEncryptionLayer(out,
+ crypto.getFrameCipher(), frameKey, maxFrameLength, tag);
+ return new StreamWriterImpl(frameWriter, maxFrameLength);
}
public StreamWriter createInvitationStreamWriter(OutputStream out,
int maxFrameLength, byte[] secret, boolean alice) {
- SecretKey frameKey = crypto.deriveFrameKey(secret, 0, true, alice);
- FrameWriter encryption = new OutgoingEncryptionLayer(out,
- Long.MAX_VALUE, crypto.getFrameCipher(), frameKey,
- maxFrameLength);
- return new StreamWriterImpl(encryption, maxFrameLength);
+ byte[] tag = new byte[TAG_LENGTH];
+ SecretKey tagKey = crypto.deriveTagKey(secret, alice);
+ crypto.encodeTag(tag, tagKey, 0);
+ tagKey.erase();
+ SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice);
+ FrameWriter frameWriter = new OutgoingEncryptionLayer(out,
+ crypto.getFrameCipher(), frameKey, maxFrameLength, tag);
+ return new StreamWriterImpl(frameWriter, maxFrameLength);
}
}
\ No newline at end of file
diff --git a/briar-core/src/org/briarproject/transport/StreamWriterImpl.java b/briar-core/src/org/briarproject/transport/StreamWriterImpl.java
index 82de19e1e..7a65a1219 100644
--- a/briar-core/src/org/briarproject/transport/StreamWriterImpl.java
+++ b/briar-core/src/org/briarproject/transport/StreamWriterImpl.java
@@ -33,10 +33,6 @@ class StreamWriterImpl extends OutputStream implements StreamWriter {
return this;
}
- public long getRemainingCapacity() {
- return out.getRemainingCapacity();
- }
-
@Override
public void close() throws IOException {
writeFrame(true);
diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java
index 4e99e439c..8a2c79672 100644
--- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java
+++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java
@@ -3,40 +3,82 @@ package org.briarproject.plugins.bluetooth;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.microedition.io.StreamConnection;
import org.briarproject.api.plugins.Plugin;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
class BluetoothTransportConnection implements DuplexTransportConnection {
private final Plugin plugin;
private final StreamConnection stream;
+ private final Reader reader;
+ private final Writer writer;
+ private final AtomicBoolean halfClosed, closed;
BluetoothTransportConnection(Plugin plugin, StreamConnection stream) {
this.plugin = plugin;
this.stream = stream;
+ reader = new Reader();
+ writer = new Writer();
+ halfClosed = new AtomicBoolean(false);
+ closed = new AtomicBoolean(false);
}
- public int getMaxFrameLength() {
- return plugin.getMaxFrameLength();
+ public TransportConnectionReader getReader() {
+ return reader;
}
- public long getMaxLatency() {
- return plugin.getMaxLatency();
+ public TransportConnectionWriter getWriter() {
+ return writer;
}
- public InputStream getInputStream() throws IOException {
- return stream.openInputStream();
+ private class Reader implements TransportConnectionReader {
+
+ public int getMaxFrameLength() {
+ return plugin.getMaxFrameLength();
+ }
+
+ public long getMaxLatency() {
+ return plugin.getMaxLatency();
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return stream.openInputStream();
+ }
+
+ public void dispose(boolean exception, boolean recognised)
+ throws IOException {
+ if(halfClosed.getAndSet(true) || exception)
+ if(!closed.getAndSet(true)) stream.close();
+ }
}
- public OutputStream getOutputStream() throws IOException {
- return stream.openOutputStream();
- }
+ private class Writer implements TransportConnectionWriter {
- public void dispose(boolean exception, boolean recognised)
- throws IOException {
- stream.close();
+ public int getMaxFrameLength() {
+ return plugin.getMaxFrameLength();
+ }
+
+ public long getMaxLatency() {
+ return plugin.getMaxLatency();
+ }
+
+ public long getCapacity() {
+ return Long.MAX_VALUE;
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ return stream.openOutputStream();
+ }
+
+ public void dispose(boolean exception) throws IOException {
+ if(halfClosed.getAndSet(true) || exception)
+ if(!closed.getAndSet(true)) stream.close();
+ }
}
}
diff --git a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java
index 22d45eb02..873fd5e39 100644
--- a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java
+++ b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java
@@ -14,12 +14,15 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import org.briarproject.api.ContactId;
import org.briarproject.api.TransportId;
import org.briarproject.api.TransportProperties;
import org.briarproject.api.crypto.PseudoRandom;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
@@ -102,6 +105,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
return running;
}
+ // FIXME: Don't poll this plugin
public boolean shouldPoll() {
return true;
}
@@ -164,7 +168,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
}
}
- private boolean resetModem() {
+ boolean resetModem() {
if(!running) return false;
for(String portName : serialPortList.getPortNames()) {
if(LOG.isLoggable(INFO))
@@ -227,25 +231,21 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
private class ModemTransportConnection
implements DuplexTransportConnection {
- private final CountDownLatch finished = new CountDownLatch(1);
+ private final AtomicBoolean halfClosed = new AtomicBoolean(false);
+ private final AtomicBoolean closed = new AtomicBoolean(false);
+ private final CountDownLatch disposalFinished = new CountDownLatch(1);
+ private final Reader reader = new Reader();
+ private final Writer writer = new Writer();
- public int getMaxFrameLength() {
- return maxFrameLength;
+ public TransportConnectionReader getReader() {
+ return reader;
}
- public long getMaxLatency() {
- return maxLatency;
+ public TransportConnectionWriter getWriter() {
+ return writer;
}
- public InputStream getInputStream() throws IOException {
- return modem.getInputStream();
- }
-
- public OutputStream getOutputStream() throws IOException {
- return modem.getOutputStream();
- }
-
- public void dispose(boolean exception, boolean recognised) {
+ private void hangUp(boolean exception) {
LOG.info("Call disconnected");
try {
modem.hangUp();
@@ -254,11 +254,55 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
exception = true;
}
if(exception) resetModem();
- finished.countDown();
+ disposalFinished.countDown();
}
private void waitForDisposal() throws InterruptedException {
- finished.await();
+ disposalFinished.await();
+ }
+
+ private class Reader implements TransportConnectionReader {
+
+ public int getMaxFrameLength() {
+ return maxFrameLength;
+ }
+
+ public long getMaxLatency() {
+ return maxLatency;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return modem.getInputStream();
+ }
+
+ public void dispose(boolean exception, boolean recognised) {
+ if(halfClosed.getAndSet(true) || exception)
+ if(!closed.getAndSet(true)) hangUp(exception);
+ }
+ }
+
+ private class Writer implements TransportConnectionWriter {
+
+ public int getMaxFrameLength() {
+ return maxFrameLength;
+ }
+
+ public long getMaxLatency() {
+ return maxLatency;
+ }
+
+ public long getCapacity() {
+ return Long.MAX_VALUE;
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ return modem.getOutputStream();
+ }
+
+ public void dispose(boolean exception) {
+ if(halfClosed.getAndSet(true) || exception)
+ if(!closed.getAndSet(true)) hangUp(exception);
+ }
}
}
}
diff --git a/briar-tests/build.xml b/briar-tests/build.xml
index e126d2dce..88c573a8b 100644
--- a/briar-tests/build.xml
+++ b/briar-tests/build.xml
@@ -111,8 +111,8 @@
-
-
+
+
diff --git a/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java b/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java
index 9e1b190ad..8cbc75bef 100644
--- a/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java
+++ b/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java
@@ -45,8 +45,6 @@ import org.briarproject.crypto.CryptoModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
import org.briarproject.messaging.MessagingModule;
-import org.briarproject.messaging.duplex.DuplexMessagingModule;
-import org.briarproject.messaging.simplex.SimplexMessagingModule;
import org.briarproject.reliability.ReliabilityModule;
import org.briarproject.serial.SerialModule;
import org.briarproject.transport.TransportModule;
@@ -81,7 +79,6 @@ public class ProtocolIntegrationTest extends BriarTestCase {
new TestLifecycleModule(), new TestSystemModule(),
new TestUiModule(), new CryptoModule(), new DatabaseModule(),
new EventModule(), new MessagingModule(),
- new DuplexMessagingModule(), new SimplexMessagingModule(),
new ReliabilityModule(), new SerialModule(),
new TransportModule());
streamReaderFactory = i.getInstance(StreamReaderFactory.class);
@@ -125,29 +122,29 @@ public class ProtocolIntegrationTest extends BriarTestCase {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamContext ctx = new StreamContext(contactId, transportId,
secret.clone(), 0, true);
- StreamWriter conn = streamWriterFactory.createStreamWriter(
- out, MAX_FRAME_LENGTH, Long.MAX_VALUE, ctx, false, true);
- OutputStream out1 = conn.getOutputStream();
- PacketWriter writer = packetWriterFactory.createPacketWriter(out1,
+ StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out,
+ MAX_FRAME_LENGTH, ctx);
+ OutputStream out1 = streamWriter.getOutputStream();
+ PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out1,
false);
- writer.writeAck(new Ack(messageIds));
+ packetWriter.writeAck(new Ack(messageIds));
- writer.writeMessage(message.getSerialised());
- writer.writeMessage(message1.getSerialised());
+ packetWriter.writeMessage(message.getSerialised());
+ packetWriter.writeMessage(message1.getSerialised());
- writer.writeOffer(new Offer(messageIds));
+ packetWriter.writeOffer(new Offer(messageIds));
- writer.writeRequest(new Request(messageIds));
+ packetWriter.writeRequest(new Request(messageIds));
SubscriptionUpdate su = new SubscriptionUpdate(Arrays.asList(group), 1);
- writer.writeSubscriptionUpdate(su);
+ packetWriter.writeSubscriptionUpdate(su);
TransportUpdate tu = new TransportUpdate(transportId,
transportProperties, 1);
- writer.writeTransportUpdate(tu);
+ packetWriter.writeTransportUpdate(tu);
- writer.flush();
+ packetWriter.flush();
return out.toByteArray();
}
@@ -158,44 +155,44 @@ public class ProtocolIntegrationTest extends BriarTestCase {
// FIXME: Check that the expected tag was received
StreamContext ctx = new StreamContext(contactId, transportId,
secret.clone(), 0, false);
- StreamReader conn = streamReaderFactory.createStreamReader(
- in, MAX_FRAME_LENGTH, ctx, true, true);
- InputStream in1 = conn.getInputStream();
- PacketReader reader = packetReaderFactory.createPacketReader(in1);
+ StreamReader streamReader = streamReaderFactory.createStreamReader(in,
+ MAX_FRAME_LENGTH, ctx);
+ InputStream in1 = streamReader.getInputStream();
+ PacketReader packetReader = packetReaderFactory.createPacketReader(in1);
// Read the ack
- assertTrue(reader.hasAck());
- Ack a = reader.readAck();
+ assertTrue(packetReader.hasAck());
+ Ack a = packetReader.readAck();
assertEquals(messageIds, a.getMessageIds());
// Read and verify the messages
- assertTrue(reader.hasMessage());
- UnverifiedMessage m = reader.readMessage();
+ assertTrue(packetReader.hasMessage());
+ UnverifiedMessage m = packetReader.readMessage();
checkMessageEquality(message, messageVerifier.verifyMessage(m));
- assertTrue(reader.hasMessage());
- m = reader.readMessage();
+ assertTrue(packetReader.hasMessage());
+ m = packetReader.readMessage();
checkMessageEquality(message1, messageVerifier.verifyMessage(m));
- assertFalse(reader.hasMessage());
+ assertFalse(packetReader.hasMessage());
// Read the offer
- assertTrue(reader.hasOffer());
- Offer o = reader.readOffer();
+ assertTrue(packetReader.hasOffer());
+ Offer o = packetReader.readOffer();
assertEquals(messageIds, o.getMessageIds());
// Read the request
- assertTrue(reader.hasRequest());
- Request req = reader.readRequest();
+ assertTrue(packetReader.hasRequest());
+ Request req = packetReader.readRequest();
assertEquals(messageIds, req.getMessageIds());
// Read the subscription update
- assertTrue(reader.hasSubscriptionUpdate());
- SubscriptionUpdate su = reader.readSubscriptionUpdate();
+ assertTrue(packetReader.hasSubscriptionUpdate());
+ SubscriptionUpdate su = packetReader.readSubscriptionUpdate();
assertEquals(Arrays.asList(group), su.getGroups());
assertEquals(1, su.getVersion());
// Read the transport update
- assertTrue(reader.hasTransportUpdate());
- TransportUpdate tu = reader.readTransportUpdate();
+ assertTrue(packetReader.hasTransportUpdate());
+ TransportUpdate tu = packetReader.readTransportUpdate();
assertEquals(transportId, tu.getId());
assertEquals(transportProperties, tu.getProperties());
assertEquals(1, tu.getVersion());
diff --git a/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java b/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java
index 4b27522e8..54c4a3c15 100644
--- a/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java
+++ b/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java
@@ -25,10 +25,8 @@ public class KeyDerivationTest extends BriarTestCase {
@Test
public void testKeysAreDistinct() {
List keys = new ArrayList();
- keys.add(crypto.deriveFrameKey(secret, 0, false, false));
- keys.add(crypto.deriveFrameKey(secret, 0, false, true));
- keys.add(crypto.deriveFrameKey(secret, 0, true, false));
- keys.add(crypto.deriveFrameKey(secret, 0, true, true));
+ keys.add(crypto.deriveFrameKey(secret, 0, true));
+ keys.add(crypto.deriveFrameKey(secret, 0, false));
keys.add(crypto.deriveTagKey(secret, true));
keys.add(crypto.deriveTagKey(secret, false));
for(int i = 0; i < 4; i++) {
diff --git a/briar-tests/src/org/briarproject/messaging/ConstantsTest.java b/briar-tests/src/org/briarproject/messaging/ConstantsTest.java
index 75e36a80f..1aee065e1 100644
--- a/briar-tests/src/org/briarproject/messaging/ConstantsTest.java
+++ b/briar-tests/src/org/briarproject/messaging/ConstantsTest.java
@@ -46,8 +46,6 @@ import org.briarproject.api.messaging.TransportUpdate;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
-import org.briarproject.messaging.duplex.DuplexMessagingModule;
-import org.briarproject.messaging.simplex.SimplexMessagingModule;
import org.briarproject.serial.SerialModule;
import org.briarproject.transport.TransportModule;
import org.junit.Test;
@@ -67,8 +65,7 @@ public class ConstantsTest extends BriarTestCase {
Injector i = Guice.createInjector(new TestDatabaseModule(),
new TestLifecycleModule(), new TestSystemModule(),
new CryptoModule(), new DatabaseModule(), new EventModule(),
- new MessagingModule(), new DuplexMessagingModule(),
- new SimplexMessagingModule(), new SerialModule(),
+ new MessagingModule(), new SerialModule(),
new TransportModule());
crypto = i.getInstance(CryptoComponent.class);
groupFactory = i.getInstance(GroupFactory.class);
diff --git a/briar-tests/src/org/briarproject/messaging/simplex/SimplexMessagingIntegrationTest.java b/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java
similarity index 84%
rename from briar-tests/src/org/briarproject/messaging/simplex/SimplexMessagingIntegrationTest.java
rename to briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java
index e26b4be88..834eba129 100644
--- a/briar-tests/src/org/briarproject/messaging/simplex/SimplexMessagingIntegrationTest.java
+++ b/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java
@@ -1,4 +1,4 @@
-package org.briarproject.messaging.simplex;
+package org.briarproject.messaging;
import static org.briarproject.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.messaging.MessagingConstants.GROUP_SALT_LENGTH;
@@ -30,9 +30,9 @@ import org.briarproject.api.messaging.GroupFactory;
import org.briarproject.api.messaging.Message;
import org.briarproject.api.messaging.MessageFactory;
import org.briarproject.api.messaging.MessageVerifier;
+import org.briarproject.api.messaging.MessagingSession;
import org.briarproject.api.messaging.PacketReaderFactory;
import org.briarproject.api.messaging.PacketWriterFactory;
-import org.briarproject.api.transport.ConnectionRegistry;
import org.briarproject.api.transport.Endpoint;
import org.briarproject.api.transport.StreamContext;
import org.briarproject.api.transport.StreamReaderFactory;
@@ -41,8 +41,6 @@ import org.briarproject.api.transport.TagRecogniser;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
-import org.briarproject.messaging.MessagingModule;
-import org.briarproject.messaging.duplex.DuplexMessagingModule;
import org.briarproject.plugins.ImmediateExecutor;
import org.briarproject.serial.SerialModule;
import org.briarproject.transport.TransportModule;
@@ -88,8 +86,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
return Guice.createInjector(new TestDatabaseModule(dir),
new TestLifecycleModule(), new TestSystemModule(),
new CryptoModule(), new DatabaseModule(), new EventModule(),
- new MessagingModule(), new DuplexMessagingModule(),
- new SimplexMessagingModule(), new SerialModule(),
+ new MessagingModule(), new SerialModule(),
new TransportModule());
}
@@ -140,29 +137,26 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
Message message = messageFactory.createAnonymousMessage(null, group,
contentType, timestamp, body);
db.addLocalMessage(message);
- // Create an outgoing simplex connection
+ // Create an outgoing messaging session
ByteArrayOutputStream out = new ByteArrayOutputStream();
- ConnectionRegistry connRegistry =
- alice.getInstance(ConnectionRegistry.class);
- StreamWriterFactory connWriterFactory =
+ StreamWriterFactory streamWriterFactory =
alice.getInstance(StreamWriterFactory.class);
PacketWriterFactory packetWriterFactory =
alice.getInstance(PacketWriterFactory.class);
- TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
- out, Long.MAX_VALUE, Long.MAX_VALUE);
+ TestTransportConnectionWriter transport =
+ new TestTransportConnectionWriter(out);
StreamContext ctx = km.getStreamContext(contactId, transportId);
assertNotNull(ctx);
- OutgoingSimplexConnection simplex = new OutgoingSimplexConnection(db,
- connRegistry, connWriterFactory, packetWriterFactory, ctx,
- transport);
+ MessagingSession session = new SinglePassOutgoingSession(db,
+ new ImmediateExecutor(), streamWriterFactory,
+ packetWriterFactory, ctx, transport);
// Write whatever needs to be written
- simplex.write();
- assertTrue(transport.getDisposed());
- assertFalse(transport.getException());
+ session.run();
+ transport.dispose(false);
// Clean up
km.stop();
db.close();
- // Return the contents of the simplex connection
+ // Return the contents of the stream
return out.toByteArray();
}
@@ -204,28 +198,24 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
assertEquals(tag.length, read);
StreamContext ctx = rec.recogniseTag(transportId, tag);
assertNotNull(ctx);
- // Create an incoming simplex connection
+ // Create an incoming messaging session
MessageVerifier messageVerifier =
bob.getInstance(MessageVerifier.class);
- ConnectionRegistry connRegistry =
- bob.getInstance(ConnectionRegistry.class);
- StreamReaderFactory connWriterFactory =
+ StreamReaderFactory streamReaderFactory =
bob.getInstance(StreamReaderFactory.class);
- PacketReaderFactory packetWriterFactory =
+ PacketReaderFactory packetReaderFactory =
bob.getInstance(PacketReaderFactory.class);
- TestSimplexTransportReader transport =
- new TestSimplexTransportReader(in);
- IncomingSimplexConnection simplex = new IncomingSimplexConnection(
+ TestTransportConnectionReader transport =
+ new TestTransportConnectionReader(in);
+ MessagingSession session = new IncomingSession(db,
new ImmediateExecutor(), new ImmediateExecutor(),
- messageVerifier, db, connRegistry, connWriterFactory,
- packetWriterFactory, ctx, transport);
+ messageVerifier, streamReaderFactory, packetReaderFactory,
+ ctx, transport);
// No messages should have been added yet
assertFalse(listener.messageAdded);
// Read whatever needs to be read
- simplex.read();
- assertTrue(transport.getDisposed());
- assertFalse(transport.getException());
- assertTrue(transport.getRecognised());
+ session.run();
+ transport.dispose(false, true);
// The private message from Alice should have been added
assertTrue(listener.messageAdded);
// Clean up
diff --git a/briar-tests/src/org/briarproject/messaging/simplex/OutgoingSimplexConnectionTest.java b/briar-tests/src/org/briarproject/messaging/SinglePassOutgoingSessionTest.java
similarity index 70%
rename from briar-tests/src/org/briarproject/messaging/simplex/OutgoingSimplexConnectionTest.java
rename to briar-tests/src/org/briarproject/messaging/SinglePassOutgoingSessionTest.java
index 3de41423c..27f03296f 100644
--- a/briar-tests/src/org/briarproject/messaging/simplex/OutgoingSimplexConnectionTest.java
+++ b/briar-tests/src/org/briarproject/messaging/SinglePassOutgoingSessionTest.java
@@ -1,9 +1,7 @@
-package org.briarproject.messaging.simplex;
+package org.briarproject.messaging;
-import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH;
-import static org.briarproject.api.transport.TransportConstants.MIN_STREAM_LENGTH;
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
import java.io.ByteArrayOutputStream;
@@ -23,14 +21,12 @@ import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DatabaseExecutor;
import org.briarproject.api.messaging.Ack;
import org.briarproject.api.messaging.MessageId;
+import org.briarproject.api.messaging.MessagingSession;
import org.briarproject.api.messaging.PacketWriterFactory;
-import org.briarproject.api.transport.ConnectionRegistry;
import org.briarproject.api.transport.StreamContext;
import org.briarproject.api.transport.StreamWriterFactory;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.event.EventModule;
-import org.briarproject.messaging.MessagingModule;
-import org.briarproject.messaging.duplex.DuplexMessagingModule;
import org.briarproject.serial.SerialModule;
import org.briarproject.transport.TransportModule;
import org.jmock.Expectations;
@@ -42,39 +38,37 @@ import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
-public class OutgoingSimplexConnectionTest extends BriarTestCase {
+public class SinglePassOutgoingSessionTest extends BriarTestCase {
// FIXME: This is an integration test, not a unit test
private final Mockery context;
private final DatabaseComponent db;
- private final ConnectionRegistry connRegistry;
- private final StreamWriterFactory connWriterFactory;
+ private final Executor dbExecutor;
+ private final StreamWriterFactory streamWriterFactory;
private final PacketWriterFactory packetWriterFactory;
private final ContactId contactId;
private final MessageId messageId;
private final TransportId transportId;
private final byte[] secret;
- public OutgoingSimplexConnectionTest() {
+ public SinglePassOutgoingSessionTest() {
context = new Mockery();
db = context.mock(DatabaseComponent.class);
+ dbExecutor = Executors.newSingleThreadExecutor();
Module testModule = new AbstractModule() {
@Override
public void configure() {
bind(DatabaseComponent.class).toInstance(db);
bind(Executor.class).annotatedWith(
- DatabaseExecutor.class).toInstance(
- Executors.newCachedThreadPool());
+ DatabaseExecutor.class).toInstance(dbExecutor);
}
};
Injector i = Guice.createInjector(testModule,
new TestLifecycleModule(), new TestSystemModule(),
new CryptoModule(), new EventModule(), new MessagingModule(),
- new DuplexMessagingModule(), new SimplexMessagingModule(),
new SerialModule(), new TransportModule());
- connRegistry = i.getInstance(ConnectionRegistry.class);
- connWriterFactory = i.getInstance(StreamWriterFactory.class);
+ streamWriterFactory = i.getInstance(StreamWriterFactory.class);
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
contactId = new ContactId(234);
messageId = new MessageId(TestUtils.getRandomId());
@@ -83,34 +77,15 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
new Random().nextBytes(secret);
}
- @Test
- public void testConnectionTooShort() throws Exception {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
- out, MAX_PACKET_LENGTH, Long.MAX_VALUE);
- StreamContext ctx = new StreamContext(contactId, transportId,
- secret, 0, true);
- OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
- connRegistry, connWriterFactory, packetWriterFactory, ctx,
- transport);
- connection.write();
- // Nothing should have been written
- assertEquals(0, out.size());
- // The transport should have been disposed with exception == true
- assertTrue(transport.getDisposed());
- assertTrue(transport.getException());
- }
-
@Test
public void testNothingToSend() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
- TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
- out, MIN_STREAM_LENGTH, Long.MAX_VALUE);
+ TestTransportConnectionWriter writer =
+ new TestTransportConnectionWriter(out);
StreamContext ctx = new StreamContext(contactId, transportId,
secret, 0, true);
- OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
- connRegistry, connWriterFactory, packetWriterFactory, ctx,
- transport);
+ MessagingSession session = new SinglePassOutgoingSession(db, dbExecutor,
+ streamWriterFactory, packetWriterFactory, ctx, writer);
context.checking(new Expectations() {{
// No transport acks to send
oneOf(db).generateTransportAcks(contactId);
@@ -141,25 +116,22 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
with(any(long.class)));
will(returnValue(null));
}});
- connection.write();
+ session.run();
// Only the tag and an empty final frame should have been written
assertEquals(TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH, out.size());
- // The transport should have been disposed with exception == false
- assertTrue(transport.getDisposed());
- assertFalse(transport.getException());
context.assertIsSatisfied();
}
@Test
public void testSomethingToSend() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
- TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
- out, MIN_STREAM_LENGTH, Long.MAX_VALUE);
+ TestTransportConnectionWriter writer =
+ new TestTransportConnectionWriter(out);
StreamContext ctx = new StreamContext(contactId, transportId,
secret, 0, true);
- OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
- connRegistry, connWriterFactory, packetWriterFactory, ctx,
- transport);
+ MessagingSession session = new SinglePassOutgoingSession(db, dbExecutor,
+ streamWriterFactory, packetWriterFactory,
+ ctx, writer);
final byte[] raw = new byte[1234];
context.checking(new Expectations() {{
// No transport acks to send
@@ -198,13 +170,10 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
with(any(long.class)));
will(returnValue(null));
}});
- connection.write();
+ session.run();
// Something should have been written
int overhead = TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH;
assertTrue(out.size() > overhead + UniqueId.LENGTH + raw.length);
- // The transport should have been disposed with exception == false
- assertTrue(transport.getDisposed());
- assertFalse(transport.getException());
context.assertIsSatisfied();
}
}
diff --git a/briar-tests/src/org/briarproject/messaging/TestTransportConnectionReader.java b/briar-tests/src/org/briarproject/messaging/TestTransportConnectionReader.java
new file mode 100644
index 000000000..246c0920d
--- /dev/null
+++ b/briar-tests/src/org/briarproject/messaging/TestTransportConnectionReader.java
@@ -0,0 +1,35 @@
+package org.briarproject.messaging;
+
+import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH;
+
+import java.io.InputStream;
+
+import org.briarproject.api.plugins.TransportConnectionReader;
+
+class TestTransportConnectionReader implements TransportConnectionReader {
+
+ private final InputStream in;
+
+ private boolean disposed = false;
+
+ TestTransportConnectionReader(InputStream in) {
+ this.in = in;
+ }
+
+ public int getMaxFrameLength() {
+ return MAX_FRAME_LENGTH;
+ }
+
+ public long getMaxLatency() {
+ return Long.MAX_VALUE;
+ }
+
+ public InputStream getInputStream() {
+ return in;
+ }
+
+ public void dispose(boolean exception, boolean recognised) {
+ assert !disposed;
+ disposed = true;
+ }
+}
\ No newline at end of file
diff --git a/briar-tests/src/org/briarproject/messaging/TestTransportConnectionWriter.java b/briar-tests/src/org/briarproject/messaging/TestTransportConnectionWriter.java
new file mode 100644
index 000000000..ab4e4077e
--- /dev/null
+++ b/briar-tests/src/org/briarproject/messaging/TestTransportConnectionWriter.java
@@ -0,0 +1,40 @@
+package org.briarproject.messaging;
+
+import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+
+import org.briarproject.api.plugins.TransportConnectionWriter;
+
+class TestTransportConnectionWriter implements TransportConnectionWriter {
+
+ private final ByteArrayOutputStream out;
+
+ private boolean disposed = false;
+
+ TestTransportConnectionWriter(ByteArrayOutputStream out) {
+ this.out = out;
+ }
+
+ public long getCapacity() {
+ return Long.MAX_VALUE;
+ }
+
+ public int getMaxFrameLength() {
+ return MAX_FRAME_LENGTH;
+ }
+
+ public long getMaxLatency() {
+ return Long.MAX_VALUE;
+ }
+
+ public OutputStream getOutputStream() {
+ return out;
+ }
+
+ public void dispose(boolean exception) {
+ assert !disposed;
+ disposed = true;
+ }
+}
\ No newline at end of file
diff --git a/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportReader.java b/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportReader.java
deleted file mode 100644
index 22829b8b8..000000000
--- a/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportReader.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package org.briarproject.messaging.simplex;
-
-import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH;
-
-import java.io.InputStream;
-
-import org.briarproject.api.plugins.simplex.SimplexTransportReader;
-
-class TestSimplexTransportReader implements SimplexTransportReader {
-
- private final InputStream in;
-
- private boolean disposed = false, exception = false, recognised = false;
-
- TestSimplexTransportReader(InputStream in) {
- this.in = in;
- }
-
- public int getMaxFrameLength() {
- return MAX_FRAME_LENGTH;
- }
-
- public InputStream getInputStream() {
- return in;
- }
-
- public void dispose(boolean exception, boolean recognised) {
- assert !disposed;
- disposed = true;
- this.exception = exception;
- this.recognised = recognised;
- }
-
- boolean getDisposed() {
- return disposed;
- }
-
- boolean getException() {
- return exception;
- }
-
- boolean getRecognised() {
- return recognised;
- }
-}
\ No newline at end of file
diff --git a/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportWriter.java b/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportWriter.java
deleted file mode 100644
index 39e4a7681..000000000
--- a/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportWriter.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package org.briarproject.messaging.simplex;
-
-import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH;
-
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
-
-import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
-
-class TestSimplexTransportWriter implements SimplexTransportWriter {
-
- private final ByteArrayOutputStream out;
- private final long capacity, maxLatency;
-
- private boolean disposed = false, exception = false;
-
- TestSimplexTransportWriter(ByteArrayOutputStream out, long capacity,
- long maxLatency) {
- this.out = out;
- this.capacity = capacity;
- this.maxLatency = maxLatency;
- }
-
- public long getCapacity() {
- return capacity;
- }
-
- public int getMaxFrameLength() {
- return MAX_FRAME_LENGTH;
- }
-
- public long getMaxLatency() {
- return maxLatency;
- }
-
- public OutputStream getOutputStream() {
- return out;
- }
-
- public void dispose(boolean exception) {
- assert !disposed;
- disposed = true;
- this.exception = exception;
- }
-
- boolean getDisposed() {
- return disposed;
- }
-
- boolean getException() {
- return exception;
- }
-}
\ No newline at end of file
diff --git a/briar-tests/src/org/briarproject/plugins/DuplexTest.java b/briar-tests/src/org/briarproject/plugins/DuplexTest.java
index 20de33cbe..e818e3bb2 100644
--- a/briar-tests/src/org/briarproject/plugins/DuplexTest.java
+++ b/briar-tests/src/org/briarproject/plugins/DuplexTest.java
@@ -7,6 +7,8 @@ import java.util.Scanner;
import org.briarproject.api.ContactId;
import org.briarproject.api.crypto.PseudoRandom;
+import org.briarproject.api.plugins.TransportConnectionReader;
+import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
@@ -21,12 +23,14 @@ abstract class DuplexTest {
protected void sendChallengeReceiveResponse(DuplexTransportConnection d) {
assert plugin != null;
+ TransportConnectionReader r = d.getReader();
+ TransportConnectionWriter w = d.getWriter();
try {
- PrintStream out = new PrintStream(d.getOutputStream());
+ PrintStream out = new PrintStream(w.getOutputStream());
out.println(CHALLENGE);
out.flush();
System.out.println("Sent challenge: " + CHALLENGE);
- Scanner in = new Scanner(d.getInputStream());
+ Scanner in = new Scanner(r.getInputStream());
if(in.hasNextLine()) {
String response = in.nextLine();
System.out.println("Received response: " + response);
@@ -38,11 +42,13 @@ abstract class DuplexTest {
} else {
System.out.println("No response");
}
- d.dispose(false, true);
+ r.dispose(false, true);
+ w.dispose(false);
} catch(IOException e) {
e.printStackTrace();
try {
- d.dispose(true, true);
+ r.dispose(true, true);
+ w.dispose(true);
} catch(IOException e1) {
e1.printStackTrace();
}
@@ -51,13 +57,16 @@ abstract class DuplexTest {
protected void receiveChallengeSendResponse(DuplexTransportConnection d) {
assert plugin != null;
+ TransportConnectionReader r = d.getReader();
+ TransportConnectionWriter w = d.getWriter();
try {
- Scanner in = new Scanner(d.getInputStream());
+ Scanner in = new Scanner(r.getInputStream());
if(in.hasNextLine()) {
String challenge = in.nextLine();
System.out.println("Received challenge: " + challenge);
if(CHALLENGE.equals(challenge)) {
- PrintStream out = new PrintStream(d.getOutputStream());
+
+ PrintStream out = new PrintStream(w.getOutputStream());
out.println(RESPONSE);
out.flush();
System.out.println("Sent response: " + RESPONSE);
@@ -67,11 +76,13 @@ abstract class DuplexTest {
} else {
System.out.println("No challenge");
}
- d.dispose(false, true);
+ r.dispose(false, true);
+ w.dispose(false);
} catch(IOException e) {
e.printStackTrace();
try {
- d.dispose(true, true);
+ r.dispose(true, true);
+ w.dispose(true);
} catch(IOException e1) {
e1.printStackTrace();
}
diff --git a/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java b/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java
index b79926218..b8e4ee88b 100644
--- a/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java
+++ b/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java
@@ -15,8 +15,8 @@ import org.briarproject.BriarTestCase;
import org.briarproject.TestFileUtils;
import org.briarproject.TestUtils;
import org.briarproject.api.ContactId;
+import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.simplex.SimplexPluginCallback;
-import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
import org.briarproject.api.system.FileUtils;
import org.briarproject.plugins.ImmediateExecutor;
import org.briarproject.plugins.file.RemovableDriveMonitor.Callback;
@@ -32,6 +32,7 @@ public class RemovableDrivePluginTest extends BriarTestCase {
private final ContactId contactId = new ContactId(234);
private final FileUtils fileUtils = new TestFileUtils();
+ @Override
@Before
public void setUp() {
testDir.mkdirs();
@@ -253,7 +254,7 @@ public class RemovableDrivePluginTest extends BriarTestCase {
fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0);
plugin.start();
- SimplexTransportWriter writer = plugin.createWriter(contactId);
+ TransportConnectionWriter writer = plugin.createWriter(contactId);
assertNotNull(writer);
// The output file should exist and should be empty
File[] files = drive1.listFiles();
@@ -352,6 +353,7 @@ public class RemovableDrivePluginTest extends BriarTestCase {
context.assertIsSatisfied();
}
+ @Override
@After
public void tearDown() {
TestUtils.deleteTestDirectory(testDir);
diff --git a/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java b/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java
index d008330a2..1abf9d244 100644
--- a/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java
+++ b/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java
@@ -278,7 +278,8 @@ public class ModemPluginTest extends BriarTestCase {
public Object invoke(Invocation invocation) throws Throwable {
DuplexTransportConnection conn =
(DuplexTransportConnection) invocation.getParameter(1);
- conn.dispose(false, true);
+ conn.getReader().dispose(false, true);
+ conn.getWriter().dispose(false);
invoked.countDown();
return null;
}
diff --git a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java
index fcbb30c52..f3a321bbc 100644
--- a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java
+++ b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java
@@ -148,7 +148,8 @@ public class LanTcpPluginTest extends BriarTestCase {
assertTrue(latch.await(5, SECONDS));
assertFalse(error.get());
// Clean up
- d.dispose(false, true);
+ d.getReader().dispose(false, true);
+ d.getWriter().dispose(false);
ss.close();
plugin.stop();
}
diff --git a/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java b/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java
index d3a7746a1..93f0a53f9 100644
--- a/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java
+++ b/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java
@@ -25,8 +25,6 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase {
// FIXME: This is an integration test, not a unit test
private static final int FRAME_LENGTH = 1024;
- private static final int MAX_PAYLOAD_LENGTH =
- FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH;
private final CryptoComponent crypto;
private final AuthenticatedCipher frameCipher;
@@ -56,7 +54,7 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase {
// Check that the actual tag and ciphertext match what's expected
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
- 10 * FRAME_LENGTH, frameCipher, frameKey, FRAME_LENGTH, tag);
+ frameCipher, frameKey, FRAME_LENGTH, tag);
o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], payloadLength, false);
byte[] actual = out.toByteArray();
assertEquals(TAG_LENGTH + FRAME_LENGTH, actual.length);
@@ -67,93 +65,14 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase {
}
@Test
- public void testInitiatorClosesConnectionWithoutWriting() throws Exception {
+ public void testCloseConnectionWithoutWriting() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Initiator's constructor
OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
- 10 * FRAME_LENGTH, frameCipher, crypto.generateSecretKey(),
- FRAME_LENGTH, tag);
+ frameCipher, crypto.generateSecretKey(), FRAME_LENGTH, tag);
// Write an empty final frame without having written any other frames
o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true);
// The tag and the empty frame should be written to the output stream
assertEquals(TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH, out.size());
}
-
- @Test
- public void testResponderClosesConnectionWithoutWriting() throws Exception {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- // Responder's constructor
- OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
- 10 * FRAME_LENGTH, frameCipher, crypto.generateSecretKey(),
- FRAME_LENGTH);
- // Write an empty final frame without having written any other frames
- o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true);
- // An empty final frame should be written to the output stream
- assertEquals(HEADER_LENGTH + MAC_LENGTH, out.size());
- }
-
- @Test
- public void testRemainingCapacityWithTag() throws Exception {
- int MAX_PAYLOAD_LENGTH = FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH;
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- // Initiator's constructor
- OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
- 10 * FRAME_LENGTH, frameCipher, crypto.generateSecretKey(),
- FRAME_LENGTH, tag);
- // There should be space for nine full frames and one partial frame
- byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH];
- assertEquals(10 * MAX_PAYLOAD_LENGTH - TAG_LENGTH,
- o.getRemainingCapacity());
- // Write nine frames, each containing a partial payload
- for(int i = 0; i < 9; i++) {
- o.writeFrame(frame, 123, false);
- assertEquals((9 - i) * MAX_PAYLOAD_LENGTH - TAG_LENGTH,
- o.getRemainingCapacity());
- }
- // Write the final frame, which will not be padded
- o.writeFrame(frame, 123, true);
- int finalFrameLength = HEADER_LENGTH + 123 + MAC_LENGTH;
- assertEquals(MAX_PAYLOAD_LENGTH - TAG_LENGTH - finalFrameLength,
- o.getRemainingCapacity());
- }
-
- @Test
- public void testRemainingCapacityWithoutTag() throws Exception {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- // Responder's constructor
- OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
- 10 * FRAME_LENGTH, frameCipher, crypto.generateSecretKey(),
- FRAME_LENGTH);
- // There should be space for ten full frames
- assertEquals(10 * MAX_PAYLOAD_LENGTH, o.getRemainingCapacity());
- // Write nine frames, each containing a partial payload
- byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH];
- for(int i = 0; i < 9; i++) {
- o.writeFrame(frame, 123, false);
- assertEquals((9 - i) * MAX_PAYLOAD_LENGTH,
- o.getRemainingCapacity());
- }
- // Write the final frame, which will not be padded
- o.writeFrame(frame, 123, true);
- int finalFrameLength = HEADER_LENGTH + 123 + MAC_LENGTH;
- assertEquals(MAX_PAYLOAD_LENGTH - finalFrameLength,
- o.getRemainingCapacity());
- }
-
- @Test
- public void testRemainingCapacityLimitedByFrameNumbers() throws Exception {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- // The connection has plenty of space so we're limited by frame numbers
- OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
- Long.MAX_VALUE, frameCipher, crypto.generateSecretKey(),
- FRAME_LENGTH);
- // There should be enough frame numbers for 2^32 frames
- assertEquals((1L << 32) * MAX_PAYLOAD_LENGTH, o.getRemainingCapacity());
- // Write a frame containing a partial payload
- byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH];
- o.writeFrame(frame, 123, false);
- // There should be enough frame numbers for 2^32 - 1 frames
- assertEquals(((1L << 32) - 1) * MAX_PAYLOAD_LENGTH,
- o.getRemainingCapacity());
- }
}
diff --git a/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java b/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java
index 742e20741..19153db75 100644
--- a/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java
+++ b/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java
@@ -1,12 +1,11 @@
package org.briarproject.transport;
-import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
-import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH;
-import static org.briarproject.api.transport.TransportConstants.MIN_STREAM_LENGTH;
+import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Random;
@@ -14,13 +13,9 @@ import java.util.Random;
import org.briarproject.BriarTestCase;
import org.briarproject.TestLifecycleModule;
import org.briarproject.TestSystemModule;
-import org.briarproject.api.ContactId;
-import org.briarproject.api.TransportId;
import org.briarproject.api.crypto.AuthenticatedCipher;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.SecretKey;
-import org.briarproject.api.transport.StreamContext;
-import org.briarproject.api.transport.StreamWriter;
import org.briarproject.api.transport.StreamWriterFactory;
import org.briarproject.crypto.CryptoModule;
import org.junit.Test;
@@ -35,13 +30,10 @@ public class TransportIntegrationTest extends BriarTestCase {
private final int FRAME_LENGTH = 2048;
private final CryptoComponent crypto;
- private final StreamWriterFactory streamWriterFactory;
- private final ContactId contactId;
- private final TransportId transportId;
private final AuthenticatedCipher frameCipher;
private final Random random;
private final byte[] secret;
- private final SecretKey frameKey;
+ private final SecretKey tagKey, frameKey;
public TransportIntegrationTest() {
Module testModule = new AbstractModule() {
@@ -54,15 +46,13 @@ public class TransportIntegrationTest extends BriarTestCase {
Injector i = Guice.createInjector(testModule, new CryptoModule(),
new TestLifecycleModule(), new TestSystemModule());
crypto = i.getInstance(CryptoComponent.class);
- streamWriterFactory = i.getInstance(StreamWriterFactory.class);
- contactId = new ContactId(234);
- transportId = new TransportId("id");
frameCipher = crypto.getFrameCipher();
random = new Random();
// Since we're sending frames to ourselves, we only need outgoing keys
secret = new byte[32];
random.nextBytes(secret);
- frameKey = crypto.deriveFrameKey(secret, 0, true, true);
+ tagKey = crypto.deriveTagKey(secret, true);
+ frameKey = crypto.deriveFrameKey(secret, 0, true);
}
@Test
@@ -76,6 +66,9 @@ public class TransportIntegrationTest extends BriarTestCase {
}
private void testWriteAndRead(boolean initiator) throws Exception {
+ // Encode the tag
+ byte[] tag = new byte[TAG_LENGTH];
+ crypto.encodeTag(tag, tagKey, 0);
// Generate two random frames
byte[] frame = new byte[1234];
random.nextBytes(frame);
@@ -83,87 +76,46 @@ public class TransportIntegrationTest extends BriarTestCase {
random.nextBytes(frame1);
// Copy the frame key - the copy will be erased
SecretKey frameCopy = frameKey.copy();
- // Write the frames
+ // Write the tag and the frames
ByteArrayOutputStream out = new ByteArrayOutputStream();
- FrameWriter encryptionOut = new OutgoingEncryptionLayer(out,
- Long.MAX_VALUE, frameCipher, frameCopy, FRAME_LENGTH);
- StreamWriterImpl writer = new StreamWriterImpl(encryptionOut,
+ FrameWriter frameWriter = new OutgoingEncryptionLayer(out,
+ frameCipher, frameCopy, FRAME_LENGTH, tag);
+ StreamWriterImpl streamWriter = new StreamWriterImpl(frameWriter,
FRAME_LENGTH);
- OutputStream out1 = writer.getOutputStream();
+ OutputStream out1 = streamWriter.getOutputStream();
out1.write(frame);
out1.flush();
out1.write(frame1);
out1.flush();
byte[] output = out.toByteArray();
- assertEquals(FRAME_LENGTH * 2, output.length);
- // Read the tag and the frames back
+ assertEquals(TAG_LENGTH + FRAME_LENGTH * 2, output.length);
+ // Read the tag back
ByteArrayInputStream in = new ByteArrayInputStream(output);
- FrameReader encryptionIn = new IncomingEncryptionLayer(in, frameCipher,
+ byte[] recoveredTag = new byte[tag.length];
+ read(in, recoveredTag);
+ assertArrayEquals(tag, recoveredTag);
+ // Read the frames back
+ FrameReader frameReader = new IncomingEncryptionLayer(in, frameCipher,
frameKey, FRAME_LENGTH);
- StreamReaderImpl reader = new StreamReaderImpl(encryptionIn,
+ StreamReaderImpl streamReader = new StreamReaderImpl(frameReader,
FRAME_LENGTH);
- InputStream in1 = reader.getInputStream();
- byte[] recovered = new byte[frame.length];
+ InputStream in1 = streamReader.getInputStream();
+ byte[] recoveredFrame = new byte[frame.length];
+ read(in1, recoveredFrame);
+ assertArrayEquals(frame, recoveredFrame);
+ byte[] recoveredFrame1 = new byte[frame1.length];
+ read(in1, recoveredFrame1);
+ assertArrayEquals(frame1, recoveredFrame1);
+ streamWriter.close();
+ streamReader.close();
+ }
+
+ private void read(InputStream in, byte[] dest) throws IOException {
int offset = 0;
- while(offset < recovered.length) {
- int read = in1.read(recovered, offset, recovered.length - offset);
+ while(offset < dest.length) {
+ int read = in.read(dest, offset, dest.length - offset);
if(read == -1) break;
offset += read;
}
- assertEquals(recovered.length, offset);
- assertArrayEquals(frame, recovered);
- byte[] recovered1 = new byte[frame1.length];
- offset = 0;
- while(offset < recovered1.length) {
- int read = in1.read(recovered1, offset, recovered1.length - offset);
- if(read == -1) break;
- offset += read;
- }
- assertEquals(recovered1.length, offset);
- assertArrayEquals(frame1, recovered1);
- writer.close();
- reader.close();
- }
-
- @Test
- public void testOverheadWithTag() throws Exception {
- ByteArrayOutputStream out =
- new ByteArrayOutputStream(MIN_STREAM_LENGTH);
- StreamContext ctx = new StreamContext(contactId, transportId,
- secret, 0, true);
- StreamWriter w = streamWriterFactory.createStreamWriter(out,
- MAX_FRAME_LENGTH, MIN_STREAM_LENGTH, ctx, false, true);
- // Check that the connection writer thinks there's room for a packet
- long capacity = w.getRemainingCapacity();
- assertTrue(capacity > MAX_PACKET_LENGTH);
- assertTrue(capacity < MIN_STREAM_LENGTH);
- // Check that there really is room for a packet
- byte[] payload = new byte[MAX_PACKET_LENGTH];
- w.getOutputStream().write(payload);
- w.getOutputStream().close();
- long used = out.size();
- assertTrue(used > MAX_PACKET_LENGTH);
- assertTrue(used <= MIN_STREAM_LENGTH);
- }
-
- @Test
- public void testOverheadWithoutTag() throws Exception {
- ByteArrayOutputStream out =
- new ByteArrayOutputStream(MIN_STREAM_LENGTH);
- StreamContext ctx = new StreamContext(contactId, transportId,
- secret, 0, true);
- StreamWriter w = streamWriterFactory.createStreamWriter(out,
- MAX_FRAME_LENGTH, MIN_STREAM_LENGTH, ctx, false, false);
- // Check that the connection writer thinks there's room for a packet
- long capacity = w.getRemainingCapacity();
- assertTrue(capacity > MAX_PACKET_LENGTH);
- assertTrue(capacity < MIN_STREAM_LENGTH);
- // Check that there really is room for a packet
- byte[] payload = new byte[MAX_PACKET_LENGTH];
- w.getOutputStream().write(payload);
- w.getOutputStream().close();
- long used = out.size();
- assertTrue(used > MAX_PACKET_LENGTH);
- assertTrue(used <= MIN_STREAM_LENGTH);
}
}