summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--PortingProgress.txt7
-rw-r--r--src/com/isode/stroke/base/ByteArray.java10
-rw-r--r--src/com/isode/stroke/base/SafeByteArray.java10
-rw-r--r--src/com/isode/stroke/filetransfer/ByteArrayReadBytestream.java10
-rw-r--r--src/com/isode/stroke/filetransfer/OutgoingJingleFileTransfer.java3
-rw-r--r--src/com/isode/stroke/filetransfer/SOCKS5BytestreamClientSession.java15
-rw-r--r--src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java17
-rw-r--r--src/com/isode/stroke/network/DummyNetworkEnvironment.java32
-rw-r--r--test/com/isode/stroke/filetransfer/IBBReceiveSessionTest.java238
-rw-r--r--test/com/isode/stroke/filetransfer/IBBSendSessionTest.java220
-rw-r--r--test/com/isode/stroke/filetransfer/IncomingJingleFileTransferTest.java224
-rw-r--r--test/com/isode/stroke/filetransfer/OutgoingJingleFileTransferTest.java269
-rw-r--r--test/com/isode/stroke/filetransfer/SOCKS5BytestreamClientSessionTest.java427
-rw-r--r--test/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSessionTest.java256
14 files changed, 1714 insertions, 24 deletions
diff --git a/PortingProgress.txt b/PortingProgress.txt
index 97c4ac9..d8dee4f 100644
--- a/PortingProgress.txt
+++ b/PortingProgress.txt
@@ -94,14 +94,15 @@ All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39.
-----
FileTransfer:
-All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39 except for:
-
-Tests not yet ported.
+All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39
Individual Comments:
Incoming and Outgoing Jingle File Transfer have been implemented
slightly differently to swiften due to multipe inheritance.
+IncomingJingleFileTransferTest -- test_AcceptFailingS5BFallsBackToIBB test commented
+out in Swiften code this test is not run as it does not work in Swiften.
+
-----
IDN
diff --git a/src/com/isode/stroke/base/ByteArray.java b/src/com/isode/stroke/base/ByteArray.java
index 997cc6e..f1181f5 100644
--- a/src/com/isode/stroke/base/ByteArray.java
+++ b/src/com/isode/stroke/base/ByteArray.java
@@ -3,7 +3,7 @@
* All rights reserved.
*/
/*
- * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * Copyright (c) 2010-2016, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.base;
@@ -61,6 +61,14 @@ public class ByteArray {
public ByteArray(ByteArray b) {
append(b);
}
+
+ /**
+ * Constructs a new {@link ByteArray} containing the user supplied byte.
+ * @param b a byte.
+ */
+ public ByteArray(byte b) {
+ append(b);
+ }
/*public ByteArray(char[] c, int n) {
for (int i = 0; i < n; i++) {
diff --git a/src/com/isode/stroke/base/SafeByteArray.java b/src/com/isode/stroke/base/SafeByteArray.java
index 437346f..f07cbe1 100644
--- a/src/com/isode/stroke/base/SafeByteArray.java
+++ b/src/com/isode/stroke/base/SafeByteArray.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2015, Isode Limited, London, England.
+ * Copyright (c) 2011-2016, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.base;
@@ -34,6 +34,14 @@ public class SafeByteArray extends ByteArray {
public SafeByteArray(byte[] c) {
super(c);
}
+
+ /**
+ * Constructs a new {@link SafeByteArray} containing the user supplied byte.
+ * @param b a byte.
+ */
+ public SafeByteArray(byte b) {
+ super(b);
+ }
/**
* Creates a new SafeByteArray object containing all
diff --git a/src/com/isode/stroke/filetransfer/ByteArrayReadBytestream.java b/src/com/isode/stroke/filetransfer/ByteArrayReadBytestream.java
index 41528fa..5b3542f 100644
--- a/src/com/isode/stroke/filetransfer/ByteArrayReadBytestream.java
+++ b/src/com/isode/stroke/filetransfer/ByteArrayReadBytestream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -11,6 +11,8 @@
package com.isode.stroke.filetransfer;
+import java.util.Arrays;
+
import com.isode.stroke.base.ByteArray;
public class ByteArrayReadBytestream extends ReadBytestream {
@@ -30,9 +32,9 @@ public class ByteArrayReadBytestream extends ReadBytestream {
if (position + readSize > data.getSize()) {
readSize = data.getSize() - position;
}
- String s = data.toString();
- s = s.substring(position, position+readSize);
- ByteArray result = new ByteArray(s);
+ byte[] rawBytes = data.getData();
+ byte[] resultRawBytes = Arrays.copyOfRange(rawBytes, position, position+readSize);
+ ByteArray result = new ByteArray(resultRawBytes);
onRead.emit(result);
position += readSize;
diff --git a/src/com/isode/stroke/filetransfer/OutgoingJingleFileTransfer.java b/src/com/isode/stroke/filetransfer/OutgoingJingleFileTransfer.java
index 10760a1..c5a1f11 100644
--- a/src/com/isode/stroke/filetransfer/OutgoingJingleFileTransfer.java
+++ b/src/com/isode/stroke/filetransfer/OutgoingJingleFileTransfer.java
@@ -4,7 +4,7 @@
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
/*
- * Copyright (c) 2013-2015 Isode Limited.
+ * Copyright (c) 2013-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -100,6 +100,7 @@ public class OutgoingJingleFileTransfer extends JingleFileTransfer implements Ou
this.idGenerator = idGenerator;
this.stream = stream;
this.fileInfo = fileInfo;
+ this.options = options;
this.session = session;
this.contentID = new JingleContentID(idGenerator.generateID(), JingleContentPayload.Creator.InitiatorCreator);
this.state = State.Initial;
diff --git a/src/com/isode/stroke/filetransfer/SOCKS5BytestreamClientSession.java b/src/com/isode/stroke/filetransfer/SOCKS5BytestreamClientSession.java
index 3e51b5f..18e4484 100644
--- a/src/com/isode/stroke/filetransfer/SOCKS5BytestreamClientSession.java
+++ b/src/com/isode/stroke/filetransfer/SOCKS5BytestreamClientSession.java
@@ -55,7 +55,7 @@ public class SOCKS5BytestreamClientSession extends SOCKS5AbstractBytestreamSessi
private State state;
- private ByteArray unprocessedData;
+ private final ByteArray unprocessedData = new ByteArray();
private ByteArray authenticateAddress;
private int chunkSize;
@@ -227,12 +227,9 @@ public class SOCKS5BytestreamClientSession extends SOCKS5AbstractBytestreamSessi
private void authenticate() {
logger_.fine("\n");
- SafeByteArray header = new SafeByteArray(new byte[]{0x05, 0x01, 0x00, 0x03});
- SafeByteArray message = header;
- String destinationlength = Integer.toString(destination.length());
- message.append(new SafeByteArray(destinationlength));
- authenticateAddress = new ByteArray(destination);
- message.append(authenticateAddress);
+ SafeByteArray message = new SafeByteArray(new byte[]{0x05, 0x01, 0x00, 0x03});
+ message.append((byte)destination.length());
+ message.append(destination);
message.append(new SafeByteArray(new byte[]{0x00, 0x00})); // 2 byte for port
connection.write(message);
state = State.Authenticating;
@@ -324,7 +321,9 @@ public class SOCKS5BytestreamClientSession extends SOCKS5AbstractBytestreamSessi
private void closeConnection() {
connectFinishedConnection.disconnect();
- dataWrittenConnection.disconnect();
+ if (dataWrittenConnection != null) {
+ dataWrittenConnection.disconnect();
+ }
dataReadConnection.disconnect();
disconnectedConnection.disconnect();
connection.disconnect();
diff --git a/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java b/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java
index 8452e36..8facca0 100644
--- a/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java
+++ b/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java
@@ -15,6 +15,7 @@ import com.isode.stroke.network.Connection;
import com.isode.stroke.network.HostAddressPort;
import com.isode.stroke.base.ByteArray;
import com.isode.stroke.base.SafeByteArray;
+import com.isode.stroke.base.StartStoppable;
import com.isode.stroke.signals.SignalConnection;
import com.isode.stroke.signals.Signal1;
import com.isode.stroke.signals.Signal;
@@ -23,11 +24,11 @@ import com.isode.stroke.signals.Slot1;
import java.util.logging.Logger;
-public class SOCKS5BytestreamServerSession extends SOCKS5AbstractBytestreamSession {
+public class SOCKS5BytestreamServerSession extends SOCKS5AbstractBytestreamSession implements StartStoppable {
private Connection connection;
private SOCKS5BytestreamRegistry bytestreams;
- private ByteArray unprocessedData;
+ private ByteArray unprocessedData = new ByteArray();
private State state;
private int chunkSize;
private String streamID = "";
@@ -131,8 +132,12 @@ public class SOCKS5BytestreamServerSession extends SOCKS5AbstractBytestreamSess
disconnectedConnection.disconnect();
dataReadConnection.disconnect();
- dataWrittenConnection.disconnect();
- dataAvailableConnection.disconnect();
+ if (dataWrittenConnection != null) {
+ dataWrittenConnection.disconnect();
+ }
+ if (dataAvailableConnection != null) {
+ dataAvailableConnection.disconnect();
+ }
readBytestream = null;
state = State.Finished;
if (error) {
@@ -180,10 +185,10 @@ public class SOCKS5BytestreamServerSession extends SOCKS5AbstractBytestreamSess
unprocessedData.clear();
streamID = requestID.toString();
boolean hasBytestream = bytestreams.hasBytestream(streamID);
- SafeByteArray result = new SafeByteArray("0x05");
+ SafeByteArray result = new SafeByteArray((byte)0x05);
result.append(hasBytestream ? (byte)0x0 : (byte)0x4);
result.append(new ByteArray(new byte[]{0x00, 0x03}));
- result.append(Integer.toString(requestID.getSize()));
+ result.append((byte)requestID.getSize());
result.append(requestID.append(new ByteArray(new byte[]{0x00, 0x00})));
if (!hasBytestream) {
logger_.fine("Readstream or Wrtiestream with ID " + streamID + " not found!\n");
diff --git a/src/com/isode/stroke/network/DummyNetworkEnvironment.java b/src/com/isode/stroke/network/DummyNetworkEnvironment.java
new file mode 100644
index 0000000..32524d9
--- /dev/null
+++ b/src/com/isode/stroke/network/DummyNetworkEnvironment.java
@@ -0,0 +1,32 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.network;
+
+import java.util.Vector;
+
+/**
+ * Dummy {@link NetworkEnvironment} for testing, returns an empty vector
+ * of {@link NetworkInterface}
+ */
+public class DummyNetworkEnvironment extends NetworkEnvironment {
+
+ /**
+ * Constructor
+ */
+ public DummyNetworkEnvironment() {
+ // Empty Constructor
+ }
+
+ @Override
+ public Vector<NetworkInterface> getNetworkInterfaces() {
+ return new Vector<NetworkInterface>();
+ }
+
+}
diff --git a/test/com/isode/stroke/filetransfer/IBBReceiveSessionTest.java b/test/com/isode/stroke/filetransfer/IBBReceiveSessionTest.java
new file mode 100644
index 0000000..bb6ac7a
--- /dev/null
+++ b/test/com/isode/stroke/filetransfer/IBBReceiveSessionTest.java
@@ -0,0 +1,238 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.filetransfer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.client.DummyStanzaChannel;
+import com.isode.stroke.elements.IBB;
+import com.isode.stroke.elements.IBB.Action;
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.IQ.Type;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.signals.Slot1;
+
+/**
+ * Tests for {@link IBBReceiveSession}
+ */
+public class IBBReceiveSessionTest {
+
+ private final DummyStanzaChannel stanzaChannel = new DummyStanzaChannel();
+ private final IQRouter iqRouter = new IQRouter(stanzaChannel);
+ private boolean finished = false;
+ private final ByteArrayWriteBytestream bytestream = new ByteArrayWriteBytestream();
+ private FileTransferError error = null;
+
+ @Test
+ public void testOpen() {
+ IBBReceiveSession testling = createSession("foo@bar.com/baz", "mysession");
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBBRequest(IBB.createIBBOpen("mysession", 0x10),
+ new JID("foo@bar.com/baz"), "id-open"));
+ assertTrue(stanzaChannel.isResultAtIndex(0, "id-open"));
+ assertFalse(finished);
+ testling.stop();
+ }
+
+ @Test
+ public void testReceiveData() {
+ IBBReceiveSession testling = createSession("foo@bar.com/baz", "mysession");
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBOpen("mysession", 0x10),
+ new JID("foo@bar.com/baz"),
+ "id-open"));
+
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBData("mysession", 0, new ByteArray("abc")),
+ new JID("foo@bar.com/baz"),
+ "id-a"));
+
+ assertTrue(stanzaChannel.isResultAtIndex(1, "id-a"));
+ assertEquals(new ByteArray("abc"),bytestream.getData());
+ assertFalse(finished);
+
+ testling.stop();
+ }
+
+ @Test
+ public void testReceiveMultipleData() {
+ IBBReceiveSession testling = createSession("foo@bar.com/baz", "mysession");
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBOpen("mysession", 0x10),
+ new JID("foo@bar.com/baz"),
+ "id-open"));
+
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBData("mysession", 0, new ByteArray("abc")),
+ new JID("foo@bar.com/baz"),
+ "id-a"));
+
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBData("mysession", 1, new ByteArray("def")),
+ new JID("foo@bar.com/baz"),
+ "id-b"));
+
+ assertTrue(stanzaChannel.isResultAtIndex(2, "id-b"));
+ assertEquals(new ByteArray("abcdef"),bytestream.getData());
+ assertFalse(finished);
+
+ testling.stop();
+ }
+
+ @Test
+ public void testReceiveDataForOtherSession() {
+ IBBReceiveSession testling = createSession("foo@bar.com/baz", "mysession");
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBOpen("mysession", 0x10),
+ new JID("foo@bar.com/baz"),
+ "id-open"));
+
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBData("othersession", 0, new ByteArray("abc")),
+ new JID("foo@bar.com/baz"),
+ "id-a"));
+
+ assertTrue(stanzaChannel.isErrorAtIndex(1, "id-a"));
+
+ testling.stop();
+ }
+
+ @Test
+ public void testReceiveDataOutOfOrder() {
+ IBBReceiveSession testling = createSession("foo@bar.com/baz", "mysession");
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBOpen("mysession", 0x10),
+ new JID("foo@bar.com/baz"),
+ "id-open"));
+
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBData("mysession", 0, new ByteArray("abc")),
+ new JID("foo@bar.com/baz"),
+ "id-a"));
+
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBData("mysession", 0, new ByteArray("def")),
+ new JID("foo@bar.com/baz"),
+ "id-b"));
+
+ assertTrue(stanzaChannel.isErrorAtIndex(2, "id-b"));
+ assertTrue(finished);
+ assertNotNull(error);
+
+ testling.stop();
+ }
+
+ @Test
+ public void testReceiveLastData() {
+ IBBReceiveSession testling = createSession("foo@bar.com/baz", "mysession", 6);
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBOpen("mysession", 0x10),
+ new JID("foo@bar.com/baz"),
+ "id-open"));
+
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBData("mysession", 0, new ByteArray("abc")),
+ new JID("foo@bar.com/baz"),
+ "id-a"));
+
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBData("mysession", 1, new ByteArray("def")),
+ new JID("foo@bar.com/baz"),
+ "id-b"));
+
+ assertTrue(stanzaChannel.isResultAtIndex(2, "id-b"));
+ assertEquals(new ByteArray("abcdef"),bytestream.getData());
+ assertTrue(finished);
+ assertNull(error);
+
+ testling.stop();
+ }
+
+ @Test
+ public void testReceiveClose() {
+ IBBReceiveSession testling = createSession("foo@bar.com/baz", "mysession", 6);
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBOpen("mysession", 0x10),
+ new JID("foo@bar.com/baz"),
+ "id-open"));
+
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBClose("mysession"),
+ new JID("foo@bar.com/baz"),
+ "id-close"));
+
+ assertTrue(finished);
+ assertNotNull(error);
+
+ testling.stop();
+ }
+
+ @Test
+ public void testStopWhileActive() {
+ IBBReceiveSession testling = createSession("foo@bar.com/baz", "mysession", 6);
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBOpen("mysession", 0x10),
+ new JID("foo@bar.com/baz"),
+ "id-open"));
+
+ testling.stop();
+
+ assertTrue(stanzaChannel.isRequestAtIndex(1, new JID("foo@bar.com/baz"),
+ Type.Set, new IBB()));
+ IBB ibb = stanzaChannel.sentStanzas.get(1).getPayload(new IBB());
+ assertEquals(Action.Close,ibb.getAction());
+ assertTrue(finished);
+ assertNull(error);
+ }
+
+ private IQ createIBBRequest(IBB ibb,JID from,String id) {
+ IQ request = IQ.createRequest(Type.Set, new JID("baz@fum.com/dum"), id, ibb);
+ request.setFrom(from);
+ return request;
+ }
+
+ private IBBReceiveSession createSession(String from,String id) {
+ return createSession(from, id, 0x1000);
+ }
+
+ private IBBReceiveSession createSession(String from,String id,int size) {
+ IBBReceiveSession session =
+ new IBBReceiveSession(id, new JID(from), new JID(), size, bytestream, iqRouter);
+ session.onFinished.connect(new Slot1<FileTransferError>() {
+
+ @Override
+ public void call(FileTransferError error) {
+ handleFinished(error);
+ }
+ });
+ return session;
+ }
+
+ private void handleFinished(FileTransferError error) {
+ finished = true;
+ this.error = error;
+ }
+
+}
diff --git a/test/com/isode/stroke/filetransfer/IBBSendSessionTest.java b/test/com/isode/stroke/filetransfer/IBBSendSessionTest.java
new file mode 100644
index 0000000..dd39d57
--- /dev/null
+++ b/test/com/isode/stroke/filetransfer/IBBSendSessionTest.java
@@ -0,0 +1,220 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.filetransfer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.client.DummyStanzaChannel;
+import com.isode.stroke.elements.IBB;
+import com.isode.stroke.elements.IBB.Action;
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.IQ.Type;
+import com.isode.stroke.elements.Stanza;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.signals.Slot1;
+
+/**
+ * Tests for {@link IBBSendSession}
+ */
+public class IBBSendSessionTest {
+
+ private final DummyStanzaChannel stanzaChannel = new DummyStanzaChannel();
+ private final IQRouter iqRouter = new IQRouter(stanzaChannel);
+ private boolean finished;
+ private FileTransferError error;
+ private final ByteArrayReadBytestream bytestream = new ByteArrayReadBytestream(new ByteArray("abcdefg"));
+
+ @Test
+ public void testStart() {
+ IBBSendSession testling = createSession("foo@bar.com/baz");
+ testling.setBlockSize(1234);
+ testling.start();
+ assertEquals(1,stanzaChannel.sentStanzas.size());
+ assertTrue(stanzaChannel.isRequestAtIndex(0, new JID("foo@bar.com/baz"), Type.Set, new IBB()));
+ IBB ibb = stanzaChannel.sentStanzas.get(0).getPayload(new IBB());
+ assertEquals(Action.Open,ibb.getAction());
+ assertEquals(1234,ibb.getBlockSize());
+ assertEquals("myid",ibb.getStreamID());
+ }
+
+ @Test
+ public void testStart_ResponseStartsSending() {
+ IBBSendSession testling = createSession("foo@bar.com/baz");
+ testling.setBlockSize(3);
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ assertEquals(2, stanzaChannel.sentStanzas.size());
+ assertTrue(stanzaChannel.isRequestAtIndex(1, new JID("foo@bar.com/baz"), Type.Set, new IBB()));
+ IBB ibb = stanzaChannel.sentStanzas.get(1).getPayload(new IBB());
+ assertEquals(Action.Data,ibb.getAction());
+ assertEquals(new ByteArray("abc"),ibb.getData());
+ assertEquals(0,ibb.getSequenceNumber());
+ assertEquals("myid",ibb.getStreamID());
+ }
+
+ @Test
+ public void testResponseContinuesSending() {
+ IBBSendSession testling = createSession("foo@bar.com/baz");
+ testling.setBlockSize(3);
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ assertEquals(3,stanzaChannel.sentStanzas.size());
+ assertTrue(stanzaChannel.isRequestAtIndex(2, new JID("foo@bar.com/baz"),
+ Type.Set, new IBB()));
+ IBB ibb = stanzaChannel.sentStanzas.get(2).getPayload(new IBB());
+ assertEquals(Action.Data,ibb.getAction());
+ assertEquals(new ByteArray("def"),ibb.getData());
+ assertEquals(1,ibb.getSequenceNumber());
+ assertEquals("myid",ibb.getStreamID());
+ }
+
+ @Test
+ public void testResponsdToAllFinishes() {
+ IBBSendSession testling = createSession("foo@bar.com/baz");
+ testling.setBlockSize(3);
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ assertTrue(finished);
+ assertNull(error);
+ }
+
+ @Test
+ public void testErrorResponseFinishesWithError() {
+ IBBSendSession testling = createSession("foo@bar.com/baz");
+ testling.setBlockSize(3);
+ testling.start();
+ Stanza sentStanza = stanzaChannel.sentStanzas.get(0);
+ IQ errorIQ =
+ IQ.createError(new JID("baz@fum.com/foo"), sentStanza.getTo(), sentStanza.getID());
+ stanzaChannel.onIQReceived.emit(errorIQ);
+ assertTrue(finished);
+ assertNotNull(error);
+ }
+
+ @Test
+ public void testStopDuringSessionCloses() {
+ IBBSendSession testling = createSession("foo@bar.com/baz");
+ testling.setBlockSize(3);
+ testling.start();
+ testling.stop();
+
+ assertEquals(2,stanzaChannel.sentStanzas.size());
+ assertTrue(stanzaChannel.isRequestAtIndex(1, new JID("foo@bar.com/baz"),
+ Type.Set, new IBB()));
+ IBB ibb = stanzaChannel.sentStanzas.get(1).getPayload(new IBB());
+ assertEquals(Action.Close,ibb.getAction());
+ assertEquals("myid",ibb.getStreamID());
+ assertTrue(finished);
+ assertNull(error);
+ }
+
+ @Test
+ public void testStopAfterFinishedDoesNotClose() {
+ IBBSendSession testling = createSession("foo@bar.com/baz");
+ testling.setBlockSize(16);
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ assertTrue(finished);
+ testling.stop();
+ assertEquals(2,stanzaChannel.sentStanzas.size());
+ }
+
+ @Test
+ public void testDataStreamPauseStopsSendingData() {
+ IBBSendSession testling = createSession("foo@bar.com/baz");
+ bytestream.setDataComplete(false);
+ testling.setBlockSize(3);
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ assertFalse(finished);
+ assertNull(error);
+ }
+
+ @Test
+ public void testDataStreamResumeAfterPauseSendsData() {
+ IBBSendSession testling = createSession("foo@bar.com/baz");
+ bytestream.setDataComplete(false);
+ testling.setBlockSize(3);
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ bytestream.addData(new ByteArray("xyz"));
+ assertEquals(5,stanzaChannel.sentStanzas.size());
+ }
+
+ @Test
+ public void testDataStreamResumeBeforePauseDoesNotSendData() {
+ IBBSendSession testling = createSession("foo@bar.com/baz");
+ bytestream.setDataComplete(false);
+ testling.setBlockSize(3);
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ bytestream.addData(new ByteArray("xyz"));
+ assertEquals(2,stanzaChannel.sentStanzas.size());
+ }
+
+ @Test
+ public void testDataStreamResumeAfterResumeDoesNotSendData() {
+ IBBSendSession testling = createSession("foo@bar.com/baz");
+ bytestream.setDataComplete(false);
+ testling.setBlockSize(3);
+ testling.start();
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ stanzaChannel.onIQReceived.emit(createIBResult());
+ bytestream.addData(new ByteArray("xyz"));
+ bytestream.addData(new ByteArray("xuv"));
+ assertEquals(5,stanzaChannel.sentStanzas.size());
+ }
+
+ private IQ createIBResult() {
+ Stanza lastStanza = stanzaChannel.sentStanzas.lastElement();
+ return IQ.createResult(new JID("baz@fum.com/dum"), lastStanza.getTo(),
+ lastStanza.getID(), new IBB());
+ }
+
+ private IBBSendSession createSession(String to) {
+ IBBSendSession session =
+ new IBBSendSession("myid", new JID(), new JID(to), bytestream, iqRouter);
+ session.onFinished.connect(new Slot1<FileTransferError>() {
+
+ @Override
+ public void call(FileTransferError error) {
+ handleFinished(error);
+ }
+
+ });
+ return session;
+ }
+
+ private void handleFinished(FileTransferError error) {
+ finished = true;
+ this.error = error;
+ }
+}
diff --git a/test/com/isode/stroke/filetransfer/IncomingJingleFileTransferTest.java b/test/com/isode/stroke/filetransfer/IncomingJingleFileTransferTest.java
new file mode 100644
index 0000000..87dbaa4
--- /dev/null
+++ b/test/com/isode/stroke/filetransfer/IncomingJingleFileTransferTest.java
@@ -0,0 +1,224 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.filetransfer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.base.IDGenerator;
+import com.isode.stroke.base.SimpleIDGenerator;
+import com.isode.stroke.client.DummyStanzaChannel;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.crypto.JavaCryptoProvider;
+import com.isode.stroke.elements.IBB;
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.IQ.Type;
+import com.isode.stroke.elements.JingleContentPayload;
+import com.isode.stroke.elements.JingleFileTransferDescription;
+import com.isode.stroke.elements.JingleFileTransferFileInfo;
+import com.isode.stroke.elements.JingleIBBTransportPayload;
+import com.isode.stroke.elements.JingleS5BTransportPayload;
+import com.isode.stroke.eventloop.DummyEventLoop;
+import com.isode.stroke.eventloop.EventLoop;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.jingle.FakeJingleSession;
+import com.isode.stroke.jingle.JingleContentID;
+import com.isode.stroke.network.DomainNameResolver;
+import com.isode.stroke.network.DummyConnectionFactory;
+import com.isode.stroke.network.DummyConnectionServerFactory;
+import com.isode.stroke.network.DummyNetworkEnvironment;
+import com.isode.stroke.network.DummyTimerFactory;
+import com.isode.stroke.network.NATTraverser;
+import com.isode.stroke.network.NetworkEnvironment;
+import com.isode.stroke.network.NullNATTraverser;
+import com.isode.stroke.network.StaticDomainNameResolver;
+import com.isode.stroke.queries.IQRouter;
+
+/**
+ * Tests for {@link IncomingJingleFileTransfer}
+ */
+public class IncomingJingleFileTransferTest {
+
+ private final CryptoProvider crypto = new JavaCryptoProvider();
+ private final EventLoop eventLoop = new DummyEventLoop();
+ private final DomainNameResolver resolver = new StaticDomainNameResolver(eventLoop);
+ private final FakeJingleSession session =
+ new FakeJingleSession(new JID("foo@bar.com/baz"),"mysession");
+ private final JingleContentPayload jingleContentPayload = new JingleContentPayload();
+ private final DummyStanzaChannel stanzaChannel = new DummyStanzaChannel();
+ private final DummyConnectionFactory connectionFactory =
+ new DummyConnectionFactory(eventLoop);
+ private final DummyConnectionServerFactory serverConnectionFactory =
+ new DummyConnectionServerFactory(eventLoop);
+ private final IQRouter iqRouter = new IQRouter(stanzaChannel);
+ private final SOCKS5BytestreamRegistry bytestreamRegistry = new SOCKS5BytestreamRegistry();
+ private final NetworkEnvironment networkEnvironment = new DummyNetworkEnvironment();
+ private final NATTraverser natTraverser = new NullNATTraverser(eventLoop);
+ private final SOCKS5BytestreamServerManager bytestreamServerManager =
+ new SOCKS5BytestreamServerManager(bytestreamRegistry, serverConnectionFactory,
+ networkEnvironment, natTraverser);
+ private final IDGenerator idGenerator = new SimpleIDGenerator();
+ private final DummyTimerFactory timerFactory = new DummyTimerFactory();
+ private final SOCKS5BytestreamProxiesManager bytestreamProxy =
+ new SOCKS5BytestreamProxiesManager(connectionFactory, timerFactory, resolver,
+ iqRouter, new JID("bar.com"));
+ private final FileTransferTransporterFactory ftTransporterFactory =
+ new DefaultFileTransferTransporterFactory(bytestreamRegistry, bytestreamServerManager,
+ bytestreamProxy, idGenerator, connectionFactory, timerFactory, crypto, iqRouter);
+
+
+ @Test
+ public void test_AcceptOnyIBBSendsSessionAccept() {
+ // Tests whether IncomingJingleFileTransfer would accept a IBB only file transfer.
+ // 1 Create your test incoming file transfer
+ JingleFileTransferDescription desc = new JingleFileTransferDescription();
+ desc.setFileInfo(new JingleFileTransferFileInfo("foo.tx", "", 10));
+ jingleContentPayload.addDescription(desc);
+ JingleIBBTransportPayload tpRef = new JingleIBBTransportPayload();
+ tpRef.setSessionID("mysession");
+ jingleContentPayload.addTransport(tpRef);
+
+ IncomingJingleFileTransfer fileTransfer = createTestling();
+
+ // 2 Do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+ ByteArrayWriteBytestream byteStream = new ByteArrayWriteBytestream();
+ fileTransfer.accept(byteStream);;
+
+ // 3 Check whether accept has been called
+ getCall(FakeJingleSession.AcceptCall.class,0);
+ }
+
+ @Test
+ public void test_OnlyIBBTransferReceiveWorks() {
+ // 1 Create your test incoming file transfer
+ JingleFileTransferDescription desc = new JingleFileTransferDescription();
+ desc.setFileInfo(new JingleFileTransferFileInfo("foo.tx", "", 10));
+ jingleContentPayload.addDescription(desc);
+ JingleIBBTransportPayload tpRef = new JingleIBBTransportPayload();
+ tpRef.setSessionID("mysession");
+ jingleContentPayload.addTransport(tpRef);
+
+ IncomingJingleFileTransfer fileTransfer = createTestling();
+
+ // 2 Do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+ ByteArrayWriteBytestream byteStream = new ByteArrayWriteBytestream();
+ fileTransfer.accept(byteStream);;
+
+ // 3 Check whether accept has been called
+ getCall(FakeJingleSession.AcceptCall.class,0);
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBOpen("myession", 10),
+ new JID("foo@bar.com/baz"), "id-open"));
+ stanzaChannel.onIQReceived.emit(createIBBRequest(
+ IBB.createIBBData("mysession", 0, new ByteArray("abc")),
+ new JID("foo@bar.com/baz"), "id-open"));
+ assertEquals(new ByteArray("abc"),byteStream.getData());
+ }
+
+// This test is not run in the Swiften code (If it is run it fails there too)
+//
+// public void test_AcceptFailingS5BFallsBackToIBB() {
+// // 1 Create your test incoming file transfer
+// addFileTransferDescription();
+//
+// // add SOCKS5BytestreamTransportPayload
+// JingleS5BTransportPayload payload = addJingleS5BPayload();
+//
+// IncomingJingleFileTransfer fileTransfer = createTestling();
+//
+// // 2 Do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+// ByteArrayWriteBytestream byteStream = new ByteArrayWriteBytestream();
+// fileTransfer.accept(byteStream);;
+//
+// // Candidates are gathered
+//
+// // Check whether accept has been called
+// FakeJingleSession.AcceptCall acceptCall = getCall(FakeJingleSession.AcceptCall.class, 0);
+// assertEquals(payload.getSessionID(),acceptCall.payload.getSessionID());
+//
+// // Check for candiate error
+// FakeJingleSession.InfoTransportCall infoTransportCall = getCall(FakeJingleSession.InfoTransportCall.class,1);
+// JingleS5BTransportPayload s5bPayload = null;
+// if (infoTransportCall.payload instanceof JingleS5BTransportPayload) {
+// s5bPayload = (JingleS5BTransportPayload) infoTransportCall.payload;
+// }
+// assertNotNull(s5bPayload);
+// assertTrue(s5bPayload.hasCandidateError());
+//
+// // Indicate transport replace (Romeo)
+// session.handleTransportReplaceReceived(getContentID(), addJingleIBBPayload());
+//
+// FakeJingleSession.AcceptTransportCall acceptTranpsportCall = getCall(FakeJingleSession.AcceptTransportCall.class,2);
+//
+// // Send a bit of data
+// stanzaChannel.onIQReceived.emit(createIBBRequest(IBB.createIBBOpen("mysession", 10), new JID("foo@bar.com/baz"), "id-open"));
+// stanzaChannel.onIQReceived.emit(createIBBRequest(IBB.createIBBData("mysession", 0, new ByteArray("abc")), new JID("foo@bar.com/baz"), "id-a"));
+// assertEquals(new ByteArray("abc"),byteStream.getData());
+//
+// }
+
+ private IncomingJingleFileTransfer createTestling() {
+ JID ourJID = new JID("");
+ return new IncomingJingleFileTransfer(ourJID, session, jingleContentPayload,
+ ftTransporterFactory, timerFactory, crypto);
+ }
+
+ private IQ createIBBRequest(IBB payload,JID from,String id) {
+ IQ request = IQ.createRequest(Type.Set, new JID("foo@bar.com/baz"), id, payload);
+ request.setFrom(from);
+ return request;
+ }
+
+ private void addFileTransferDescription() {
+ JingleFileTransferDescription desc = new JingleFileTransferDescription();
+ desc.setFileInfo(new JingleFileTransferFileInfo("file.txt", "", 10));
+ jingleContentPayload.addDescription(desc);
+ }
+
+ private JingleS5BTransportPayload addJingleS5BPayload() {
+ JingleS5BTransportPayload payLoad = new JingleS5BTransportPayload();
+ payLoad.setSessionID("mysession");
+ jingleContentPayload.addTransport(payLoad);
+ return payLoad;
+ }
+
+ private JingleIBBTransportPayload addJingleIBBPayload() {
+ JingleIBBTransportPayload payLoad = new JingleIBBTransportPayload();
+ payLoad.setSessionID("mysession");
+ jingleContentPayload.addTransport(payLoad);
+ return payLoad;
+ }
+
+ private JingleContentID getContentID() {
+ return new JingleContentID(jingleContentPayload.getName(),
+ jingleContentPayload.getCreator());
+ }
+
+ private <T> T getCall(Class<T> target,int i) {
+ assertTrue("Index "+i+" is not less then session.calledCommands.size() = "
+ +session.calledCommands.size(),
+ i < session.calledCommands.size());
+ Object rawObject = session.calledCommands.get(i);
+ try {
+ return target.cast(rawObject);
+ }
+ catch (ClassCastException e) {
+ fail("Item could not be cast to type "+e.getMessage());
+ }
+ // Should not get here
+ return null;
+ }
+
+
+}
diff --git a/test/com/isode/stroke/filetransfer/OutgoingJingleFileTransferTest.java b/test/com/isode/stroke/filetransfer/OutgoingJingleFileTransferTest.java
new file mode 100644
index 0000000..4532efb
--- /dev/null
+++ b/test/com/isode/stroke/filetransfer/OutgoingJingleFileTransferTest.java
@@ -0,0 +1,269 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.filetransfer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.base.IDGenerator;
+import com.isode.stroke.client.DummyStanzaChannel;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.crypto.JavaCryptoProvider;
+import com.isode.stroke.elements.HashElement;
+import com.isode.stroke.elements.IBB;
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.JingleContentPayload;
+import com.isode.stroke.elements.JingleFileTransferDescription;
+import com.isode.stroke.elements.JingleFileTransferFileInfo;
+import com.isode.stroke.elements.JingleIBBTransportPayload;
+import com.isode.stroke.elements.JingleS5BTransportPayload;
+import com.isode.stroke.elements.JinglePayload.Reason;
+import com.isode.stroke.eventloop.DummyEventLoop;
+import com.isode.stroke.eventloop.EventLoop;
+import com.isode.stroke.filetransfer.FileTransfer.State;
+import com.isode.stroke.filetransfer.FileTransferError.Type;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.jingle.FakeJingleSession;
+import com.isode.stroke.jingle.JingleContentID;
+import com.isode.stroke.network.DomainNameResolver;
+import com.isode.stroke.network.DummyConnectionFactory;
+import com.isode.stroke.network.DummyConnectionServerFactory;
+import com.isode.stroke.network.DummyNetworkEnvironment;
+import com.isode.stroke.network.DummyTimerFactory;
+import com.isode.stroke.network.NATTraverser;
+import com.isode.stroke.network.NetworkEnvironment;
+import com.isode.stroke.network.NullNATTraverser;
+import com.isode.stroke.network.StaticDomainNameResolver;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.signals.Slot1;
+
+/**
+ * Tests for {@link OutgoingJingleFileTransfer}
+ *
+ */
+public class OutgoingJingleFileTransferTest {
+
+ private final CryptoProvider crypto = new JavaCryptoProvider();
+ private final FakeJingleSession fakeJingleSession =
+ new FakeJingleSession(new JID("foo@bar.com/baz"), "mysession");
+ private final JingleContentPayload jingleContentPayload = new JingleContentPayload();
+ private final DummyStanzaChannel stanzaChannel = new DummyStanzaChannel();
+ private final IQRouter iqRouter = new IQRouter(stanzaChannel);
+ private final EventLoop eventLoop = new DummyEventLoop();
+ private final DummyTimerFactory timeFactory = new DummyTimerFactory();
+ private final DummyConnectionFactory connectionFactory = new DummyConnectionFactory(eventLoop);
+ private final DummyConnectionServerFactory serverConnectionFactory = new DummyConnectionServerFactory(eventLoop);
+ private final SOCKS5BytestreamRegistry s5bRegistry = new SOCKS5BytestreamRegistry();
+ private final NetworkEnvironment networkEnviroment = new DummyNetworkEnvironment();
+ private final NATTraverser natTraverser = new NullNATTraverser(eventLoop);
+ private final SOCKS5BytestreamServerManager bytestreamServerManager =
+ new SOCKS5BytestreamServerManager(s5bRegistry, serverConnectionFactory, networkEnviroment, natTraverser);
+ private final ByteArray data = new ByteArray();
+ {
+ for (int n = 0; n < (1024 * 1024); ++n) {
+ data.append((byte)34);
+ }
+ }
+ private final ByteArrayReadBytestream stream = new ByteArrayReadBytestream(data);
+ private final IDGenerator idGen = new IDGenerator();
+ private final DomainNameResolver resolver = new StaticDomainNameResolver(eventLoop);
+ private final SOCKS5BytestreamProxiesManager s5bProxy =
+ new SOCKS5BytestreamProxiesManager(connectionFactory, timeFactory, resolver,
+ iqRouter, new JID("bar.com"));
+ private final FileTransferTransporterFactory ftTransporterFactory =
+ new DummyFileTransferTransporterFactory(s5bRegistry, bytestreamServerManager, s5bProxy, idGen, connectionFactory, timeFactory, crypto, iqRouter);
+
+ @Test
+ public void test_SendSessionInitiateOnStart() {
+ OutgoingJingleFileTransfer transfer = createTestling();
+ transfer.start();
+
+ FakeJingleSession.InitiateCall call = getCall(FakeJingleSession.InitiateCall.class,0);
+ JingleFileTransferDescription description = null;
+ if (call.description instanceof JingleFileTransferDescription) {
+ description = (JingleFileTransferDescription) call.description;
+ }
+ assertNotNull(description);
+ assertEquals(1048576,description.getFileInfo().getSize());
+
+ JingleS5BTransportPayload transport = null;
+ if (call.payload instanceof JingleS5BTransportPayload) {
+ transport = (JingleS5BTransportPayload) call.payload;
+ }
+ assertNotNull(transport);
+ }
+
+ @Test
+ public void test_FallbackToIBBAfterFailingS5b() {
+ OutgoingJingleFileTransfer transfer = createTestling();
+ transfer.start();
+
+ FakeJingleSession.InitiateCall call = getCall(FakeJingleSession.InitiateCall.class,0);
+
+ fakeJingleSession.handleSessionAcceptReceived(call.id, call.description, call.payload);
+
+ // Send candidate failure
+ JingleS5BTransportPayload candiateFailurePayload = new JingleS5BTransportPayload();
+ candiateFailurePayload.setCandidateError(true);
+ candiateFailurePayload.setSessionID(call.payload.getSessionID());
+ fakeJingleSession.handleTransportInfoReceived(call.id, candiateFailurePayload);
+
+ // no S5B candidates -> fall back to IBB
+ // call at position 1 is the candidate our candidate error
+ FakeJingleSession.ReplaceTransportCall replaceCall =
+ getCall(FakeJingleSession.ReplaceTransportCall.class,2);
+
+ // accept transport replace
+ fakeJingleSession.handleTransportAcceptReceived(replaceCall.id, replaceCall.payload);
+
+ IQ iqOpenStanza = stanzaChannel.getStanzaAtIndex(new IQ(), 0);
+ assertNotNull(iqOpenStanza);
+ IBB ibbOpen = iqOpenStanza.getPayload(new IBB());
+ assertNotNull(ibbOpen);
+ assertEquals(IBB.Action.Open,ibbOpen.getAction());
+
+ }
+
+ @Test
+ public void test_ReceiveSessionTerminateAfterSessionInitiate() {
+ OutgoingJingleFileTransfer transfer = createTestling();
+ transfer.start();
+
+ getCall(FakeJingleSession.InitiateCall.class,0);
+
+ final FTStatusHelper helper = new FTStatusHelper();
+ helper.finishedCalled = false;
+ transfer.onFinished.connect(new Slot1<FileTransferError>() {
+
+ @Override
+ public void call(FileTransferError error) {
+ helper.handleFileTransferFinished(error);
+ }
+
+ });
+ fakeJingleSession.handleSessionTerminateReceived(new Reason(Reason.Type.Busy));
+ assertTrue(helper.finishedCalled);
+ assertEquals(FileTransferError.Type.PeerError,helper.errorType);
+ }
+
+ @Test
+ public void test_DeclineEmitsFinishedStateCanceled() {
+ OutgoingJingleFileTransfer transfer = createTestling();
+ transfer.start();
+
+ getCall(FakeJingleSession.InitiateCall.class,0);
+
+ final FTStatusHelper helper = new FTStatusHelper();
+ helper.finishedCalled = false;
+ transfer.onFinished.connect(new Slot1<FileTransferError>() {
+
+ @Override
+ public void call(FileTransferError error) {
+ helper.handleFileTransferFinished(error);
+ }
+
+ });
+ transfer.onStateChanged.connect(new Slot1<FileTransfer.State>() {
+
+ @Override
+ public void call(State newState) {
+ helper.handleFileTransferStatusChanged(newState);
+ }
+
+ });
+ fakeJingleSession.handleSessionTerminateReceived(new Reason(Reason.Type.Decline));
+ assertTrue(helper.finishedCalled);
+ assertEquals(FileTransferError.Type.UnknownError, helper.errorType);
+ assertEquals(State.Type.Canceled,helper.state.type);
+ }
+
+ private static class FTStatusHelper {
+
+ public FTStatusHelper() {
+ // Empty Constructor
+ }
+
+ public void handleFileTransferFinished(FileTransferError error) {
+ finishedCalled = true;
+ if (error != null) {
+ errorType = error.getType();
+ }
+ }
+
+ public void handleFileTransferStatusChanged(State fileTransferState) {
+ state = fileTransferState;
+ }
+ private boolean finishedCalled = false;
+ private Type errorType = Type.UnknownError;
+ private State state = null;
+ }
+
+ private OutgoingJingleFileTransfer createTestling() {
+ JID to = new JID("test@foo.com/bla");
+ JingleFileTransferFileInfo fileInfo = new JingleFileTransferFileInfo();
+ fileInfo.setDescription("some file");
+ fileInfo.setName("test.bin");
+ fileInfo.addHash(new HashElement("sha-1", new ByteArray()));
+ fileInfo.setSize(1024 * 1024);
+ FileTransferOptions options = (new FileTransferOptions()).withAssistedAllowed(false).withDirectAllowed(false).withProxiedAllowed(false);
+ return new OutgoingJingleFileTransfer(to, fakeJingleSession, stream,
+ ftTransporterFactory, timeFactory, idGen, fileInfo, options, crypto);
+ }
+
+ private IQ createIBBRequest(IBB ibb,JID from,String id) {
+ IQ request = IQ.createRequest(IQ.Type.Set, new JID("foo@bar.com/baz"), id, ibb);
+ request.setFrom(from);
+ return request;
+ }
+
+ private void addFileTransferDescription() {
+ JingleFileTransferDescription desc = new JingleFileTransferDescription();
+ desc.setFileInfo(new JingleFileTransferFileInfo());
+ jingleContentPayload.addDescription(desc);
+ }
+
+ private JingleS5BTransportPayload addJingleS5BPayload() {
+ JingleS5BTransportPayload payLoad = new JingleS5BTransportPayload();
+ payLoad.setSessionID("mysession");
+ jingleContentPayload.addTransport(payLoad);
+ return payLoad;
+ }
+
+ private JingleIBBTransportPayload addJingleIBBPayload() {
+ JingleIBBTransportPayload payLoad = new JingleIBBTransportPayload();
+ payLoad.setSessionID("mysession");
+ jingleContentPayload.addTransport(payLoad);
+ return payLoad;
+ }
+
+ private JingleContentID getContentID() {
+ return new JingleContentID(jingleContentPayload.getName(),
+ jingleContentPayload.getCreator());
+ }
+
+ private <T> T getCall(Class<T> target,int i) {
+ assertTrue(i < fakeJingleSession.calledCommands.size());
+ Object rawObject = fakeJingleSession.calledCommands.get(i);
+ try {
+ return target.cast(rawObject);
+ }
+ catch (ClassCastException e) {
+ fail("Item could not be cast to type "+e.getMessage());
+ }
+ // Should not get here
+ return null;
+ }
+
+}
diff --git a/test/com/isode/stroke/filetransfer/SOCKS5BytestreamClientSessionTest.java b/test/com/isode/stroke/filetransfer/SOCKS5BytestreamClientSessionTest.java
new file mode 100644
index 0000000..ba696b7
--- /dev/null
+++ b/test/com/isode/stroke/filetransfer/SOCKS5BytestreamClientSessionTest.java
@@ -0,0 +1,427 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.filetransfer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.base.SafeByteArray;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.crypto.JavaCryptoProvider;
+import com.isode.stroke.eventloop.DummyEventLoop;
+import com.isode.stroke.eventloop.Event.Callback;
+import com.isode.stroke.eventloop.EventLoop;
+import com.isode.stroke.eventloop.EventOwner;
+import com.isode.stroke.network.Connection;
+import com.isode.stroke.network.DummyTimerFactory;
+import com.isode.stroke.network.HostAddress;
+import com.isode.stroke.network.HostAddressPort;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.signals.Slot1;
+
+/**
+ * Tests for {@link SOCKS5BytestreamClientSession}
+ */
+public class SOCKS5BytestreamClientSessionTest {
+
+ private static final Random rng = new Random();
+
+ private final HostAddressPort destinationAddressPort = new HostAddressPort(new HostAddress("127.0.0.1"), 8888);
+ private final CryptoProvider crypto = new JavaCryptoProvider();
+ private final String destination = "092a44d859d19c9eed676b551ee80025903351c2";
+ private final DummyEventLoop eventLoop = new DummyEventLoop();
+ private final DummyTimerFactory timerFactory = new DummyTimerFactory();
+ private final List<HostAddressPort> failingPorts = new ArrayList<HostAddressPort>();
+ private final MockeryConnection connection =
+ new MockeryConnection(failingPorts, true, eventLoop);
+
+ @Before
+ public void setUp() {
+ rng.setSeed(System.currentTimeMillis());
+ }
+
+ @Test
+ public void testForSessionReady() {
+ final TestHelper helper = new TestHelper();
+ connection.onDataSent.connect(new Slot1<SafeByteArray>() {
+
+ @Override
+ public void call(SafeByteArray data) {
+ helper.handleConnectionDataWritten(data);
+ }
+
+ });
+
+ SOCKS5BytestreamClientSession clientSession = new SOCKS5BytestreamClientSession(connection, destinationAddressPort, destination, timerFactory);
+ clientSession.onSessionReady.connect(new Slot1<Boolean>() {
+
+ @Override
+ public void call(Boolean hasError) {
+ helper.handleSessionRead(hasError.booleanValue());
+ }
+
+ });
+
+ clientSession.start();
+ eventLoop.processEvents();
+ assertEquals(new ByteArray(new byte[] {0x05,0x01,0x00}),helper.unprocessedInput);
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop.processEvents();
+ ByteArray expected = new ByteArray(new byte[] {0x05,0x01,0x00,0x03});
+ expected.append((byte)destination.length());
+ expected.append(destination);
+ expected.append((byte)0x00);
+ ByteArray results = getSubArray(helper.unprocessedInput, expected.getSize());
+ assertEquals(expected,results);
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestOK();
+ eventLoop.processEvents();
+ assertTrue(helper.sessionReadyCalled);
+ assertFalse(helper.sessionReadyError);
+ }
+
+ @Test
+ public void testErrorHandlingHello() {
+ final TestHelper helper = new TestHelper();
+ connection.onDataSent.connect(new Slot1<SafeByteArray>() {
+
+ @Override
+ public void call(SafeByteArray data) {
+ helper.handleConnectionDataWritten(data);
+ }
+
+ });
+
+ SOCKS5BytestreamClientSession clientSession = new SOCKS5BytestreamClientSession(connection, destinationAddressPort, destination, timerFactory);
+ clientSession.onSessionReady.connect(new Slot1<Boolean>() {
+
+ @Override
+ public void call(Boolean hasError) {
+ helper.handleSessionRead(hasError.booleanValue());
+ }
+
+ });
+
+ clientSession.start();
+ eventLoop.processEvents();
+ assertEquals(new ByteArray(new byte[] {0x05,0x01,0x00}),helper.unprocessedInput);
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloAuthFail();
+ eventLoop.processEvents();
+
+ assertTrue(helper.sessionReadyCalled);
+ assertTrue(helper.sessionReadyError);
+ assertTrue(connection.disconnectCalled);
+ }
+
+ @Test
+ public void testErrorHandlingRequest() {
+ final TestHelper helper = new TestHelper();
+ connection.onDataSent.connect(new Slot1<SafeByteArray>() {
+
+ @Override
+ public void call(SafeByteArray data) {
+ helper.handleConnectionDataWritten(data);
+ }
+
+ });
+
+ SOCKS5BytestreamClientSession clientSession = new SOCKS5BytestreamClientSession(connection, destinationAddressPort, destination, timerFactory);
+ clientSession.onSessionReady.connect(new Slot1<Boolean>() {
+
+ @Override
+ public void call(Boolean hasError) {
+ helper.handleSessionRead(hasError.booleanValue());
+ }
+
+ });
+
+ clientSession.start();
+ eventLoop.processEvents();
+ assertEquals(new ByteArray(new byte[] {0x05,0x01,0x00}),helper.unprocessedInput);
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop.processEvents();
+ ByteArray expected = new ByteArray(new byte[] {0x05,0x01,0x00,0x03});
+ expected.append((byte)destination.length());
+ expected.append(destination);
+ expected.append((byte)0x00);
+ ByteArray results = getSubArray(helper.unprocessedInput, expected.getSize());
+ assertEquals(expected,results);
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestFail();
+ eventLoop.processEvents();
+ assertTrue(helper.sessionReadyCalled);
+ assertTrue(helper.sessionReadyError);
+ assertTrue(connection.disconnectCalled);
+ }
+
+ @Test
+ public void testWriteBytestream() {
+ final TestHelper helper = new TestHelper();
+ connection.onDataSent.connect(new Slot1<SafeByteArray>() {
+
+ @Override
+ public void call(SafeByteArray data) {
+ helper.handleConnectionDataWritten(data);
+ }
+
+ });
+
+ SOCKS5BytestreamClientSession clientSession = new SOCKS5BytestreamClientSession(connection, destinationAddressPort, destination, timerFactory);
+ clientSession.onSessionReady.connect(new Slot1<Boolean>() {
+
+ @Override
+ public void call(Boolean hasError) {
+ helper.handleSessionRead(hasError.booleanValue());
+ }
+
+ });
+
+ clientSession.start();
+ eventLoop.processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop.processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestOK();
+ eventLoop.processEvents();
+ assertTrue(helper.sessionReadyCalled);
+ assertFalse(helper.sessionReadyError);
+
+ ByteArrayWriteBytestream output = new ByteArrayWriteBytestream();
+ clientSession.startReceiving(output);
+
+ ByteArray transferData = generateRandomByteArray(1024);
+ connection.onDataRead.emit(new SafeByteArray(transferData));
+ assertEquals(transferData,output.getData());
+ }
+
+ @Test
+ public void testReadBytestream() {
+ final TestHelper helper = new TestHelper();
+ connection.onDataSent.connect(new Slot1<SafeByteArray>() {
+
+ @Override
+ public void call(SafeByteArray data) {
+ helper.handleConnectionDataWritten(data);
+ }
+
+ });
+
+ SOCKS5BytestreamClientSession clientSession = new SOCKS5BytestreamClientSession(connection, destinationAddressPort, destination, timerFactory);
+ clientSession.onSessionReady.connect(new Slot1<Boolean>() {
+
+ @Override
+ public void call(Boolean hasError) {
+ helper.handleSessionRead(hasError.booleanValue());
+ }
+
+ });
+
+ clientSession.start();
+ eventLoop.processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop.processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestOK();
+ eventLoop.processEvents();
+ assertTrue(helper.sessionReadyCalled);
+ assertFalse(helper.sessionReadyError);
+
+ helper.unprocessedInput.clear();
+ ByteArray transferData = generateRandomByteArray(1024);
+ ByteArrayReadBytestream input = new ByteArrayReadBytestream(transferData);
+ clientSession.startSending(input);
+ eventLoop.processEvents();
+
+ assertEquals(transferData,helper.unprocessedInput);
+ }
+
+ private static ByteArray generateRandomByteArray(int len) {
+ byte[] randomBytes = new byte[len];
+ rng.nextBytes(randomBytes);
+ return new ByteArray(randomBytes);
+ }
+
+ private void serverRespondHelloOK() {
+ connection.onDataRead.emit(new SafeByteArray(new byte[] {0x05,0x00}));
+ }
+
+ private void serverRespondHelloAuthFail() {
+ connection.onDataRead.emit(new SafeByteArray(new byte[] {0x05,(byte) 0xFF}));
+ }
+
+ private void serverRespondRequestOK() {
+ SafeByteArray dataToSend = new SafeByteArray(new byte[] {0x05,0x00,0x00,0x03});
+ dataToSend.append((byte)destination.length());
+ dataToSend.append(destination);
+ dataToSend.append((byte)0x00);
+ connection.onDataRead.emit(dataToSend);
+ }
+
+ private void serverRespondRequestFail() {
+ SafeByteArray correctData = new SafeByteArray(new byte[] {0x05,0x00,0x00,0x03});
+ correctData.append((byte)destination.length());
+ correctData.append(destination);
+ correctData.append((byte)0x00);
+ SafeByteArray dataToSend;
+ do {
+ ByteArray rndArray = generateRandomByteArray(correctData.getSize());
+ dataToSend = new SafeByteArray(rndArray);
+ } while (dataToSend.equals(correctData));
+ connection.onDataRead.emit(dataToSend);
+ }
+
+ /**
+ * Gets the sub {@link ByteArray} consisting of the first n bytes of
+ * a given {@link ByteArray}
+ * @param array A {@link ByteArray} should not be {@code null} and should
+ * be at least n characters long.
+ * @param n the number of bytes of the {@link ByteArray} to return as a new
+ * {@link ByteArray}
+ * @return The first n characters of the given {@link ByteArray} as a new
+ * {@link ByteArray}. Will not be {@code null}
+ */
+ private ByteArray getSubArray(ByteArray array,int n) {
+ byte[] arrayData = array.getData();
+ byte[] newArrayData = Arrays.copyOfRange(arrayData, 0, n);
+ return new ByteArray(newArrayData);
+ }
+
+ private static final class TestHelper {
+
+ private ByteArray unprocessedInput = new ByteArray();
+ private boolean sessionReadyCalled = false;
+ private boolean sessionReadyError = false;
+
+ public TestHelper() {
+ // Empty Constructor
+ }
+
+ public void handleConnectionDataWritten(SafeByteArray data) {
+ unprocessedInput.append(data);
+ }
+
+ public void handleSessionRead(boolean error) {
+ sessionReadyCalled = true;
+ sessionReadyError = error;
+ }
+
+ }
+
+
+ private static final class MockeryConnection extends Connection implements EventOwner {
+
+ private EventLoop eventLoop;
+ private HostAddressPort hostAddressPort;
+ private final List<HostAddressPort> failingPorts;
+ private boolean isResponsive;
+ private boolean disconnectCalled;
+
+ private final Signal1<SafeByteArray> onDataSent = new Signal1<SafeByteArray>();
+
+ public MockeryConnection(Collection<HostAddressPort> failingPorts,
+ boolean isResponsive,EventLoop eventLoop) {
+ this.eventLoop = eventLoop;
+ this.failingPorts = new ArrayList<HostAddressPort>(failingPorts);
+ this.isResponsive = isResponsive;
+ this.disconnectCalled = false;
+ }
+
+ @Override
+ public void listen() {
+ fail();
+ }
+
+ /* (non-Javadoc)
+ * @see com.isode.stroke.network.Connection#connect(com.isode.stroke.network.HostAddressPort)
+ */
+ @Override
+ public void connect(HostAddressPort address) {
+ hostAddressPort = address;
+ if (isResponsive) {
+ final boolean fail = failingPorts.contains(address);
+ eventLoop.postEvent(new Callback() {
+
+ @Override
+ public void run() {
+ onConnectFinished.emit(fail);
+ }
+
+ });
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.isode.stroke.network.Connection#disconnect()
+ */
+ @Override
+ public void disconnect() {
+ disconnectCalled = true;
+ }
+
+ /* (non-Javadoc)
+ * @see com.isode.stroke.network.Connection#write(com.isode.stroke.base.SafeByteArray)
+ */
+ @Override
+ public void write(SafeByteArray data) {
+ eventLoop.postEvent(new Callback() {
+
+ @Override
+ public void run() {
+ onDataWritten.emit();
+ }
+
+ });
+ onDataSent.emit(data);
+ }
+
+
+
+ /* (non-Javadoc)
+ * @see com.isode.stroke.network.Connection#getLocalAddress()
+ */
+ @Override
+ public HostAddressPort getLocalAddress() {
+ return new HostAddressPort();
+ }
+
+ public HostAddressPort getRemoteAddress() {
+ return new HostAddressPort();
+ }
+
+ }
+
+}
diff --git a/test/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSessionTest.java b/test/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSessionTest.java
new file mode 100644
index 0000000..d5dc3da
--- /dev/null
+++ b/test/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSessionTest.java
@@ -0,0 +1,256 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.filetransfer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+
+import java.util.Arrays;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.base.SafeByteArray;
+import com.isode.stroke.base.StartStopper;
+import com.isode.stroke.eventloop.DummyEventLoop;
+import com.isode.stroke.network.DummyConnection;
+import com.isode.stroke.signals.SignalConnection;
+import com.isode.stroke.signals.Slot1;
+
+/**
+ * Tests for {@link SOCKS5BytestreamServerSession}
+ */
+public class SOCKS5BytestreamServerSessionTest {
+
+ private final DummyEventLoop eventLoop = new DummyEventLoop();
+ private final SOCKS5BytestreamRegistry bytestreams = new SOCKS5BytestreamRegistry();
+ private final DummyConnection connection = new DummyConnection(eventLoop);
+ private final ByteArray receivedData = new ByteArray();
+ private int receivedDataChunks = 0;
+ private final ByteArrayReadBytestream stream1 =
+ new ByteArrayReadBytestream(new ByteArray("abcdefg"));
+ private boolean finished = false;
+ private FileTransferError error = null;
+ private SignalConnection onDataSentConnection;
+
+
+ @Before
+ public void setUp() {
+ onDataSentConnection = connection.onDataSent.connect(new Slot1<SafeByteArray>() {
+
+ @Override
+ public void call(SafeByteArray data) {
+ handleDataWritten(data);
+ }
+
+ });
+ }
+
+ @After
+ public void tearDown() {
+ onDataSentConnection.disconnect();
+ }
+
+ @Test
+ public void testAuthenticate() {
+ SOCKS5BytestreamServerSession testling = createSession();
+ StartStopper<SOCKS5BytestreamServerSession> stopper =
+ new StartStopper<SOCKS5BytestreamServerSession>(testling);
+ receive(new SafeByteArray(new byte[] {0x05,0x02,0x01,0x02}));
+ SafeByteArray expected = new SafeByteArray(new byte[] {0x05,0x00});
+ assertEquals(expected,receivedData);
+ }
+
+
+ @Test
+ public void testAuthenticate_Chunked() {
+ SOCKS5BytestreamServerSession testling = createSession();
+ StartStopper<SOCKS5BytestreamServerSession> stopper =
+ new StartStopper<SOCKS5BytestreamServerSession>(testling);
+ receive(new SafeByteArray(new byte[] {0x05,0x02,0x01}));
+ assertEquals(0,receivedData.getSize());
+ receive(new SafeByteArray(new byte[] {0x02}));
+ SafeByteArray expected = new SafeByteArray(new byte[] {0x05,0x00});
+ assertEquals(expected,receivedData);
+ }
+
+ @Test
+ public void testRequest() {
+ SOCKS5BytestreamServerSession testling = createSession();
+ StartStopper<SOCKS5BytestreamServerSession> stopper =
+ new StartStopper<SOCKS5BytestreamServerSession>(testling);
+ bytestreams.setHasBytestream("abcdef", true);
+ authenticate();
+
+ ByteArray hostname = new ByteArray("abcdef");
+ SafeByteArray data = new SafeByteArray();
+ data.append(new byte[] {0x05,0x01,0x00,0x03});
+ data.append((byte)hostname.getSize());
+ data.append(hostname);
+ data.append(new byte[] {0x00,0x00});
+ receive(data);
+ // Compare first 13 bytes of received data with what we expect
+ ByteArray expectedData =
+ new ByteArray(new byte[] {0x05,0x00,0x00,0x03,0x06,0x61,0x62,
+ 0x63,0x64,0x65,0x66,0x00,0x00});
+ assertEquals(expectedData,receivedData);
+
+ }
+
+ @Test
+ public void testRequest_UnknownBytestream() {
+ SOCKS5BytestreamServerSession testling = createSession();
+ StartStopper<SOCKS5BytestreamServerSession> stopper =
+ new StartStopper<SOCKS5BytestreamServerSession>(testling);
+ authenticate();
+ ByteArray hostname = new ByteArray("abcdef");
+ SafeByteArray data = new SafeByteArray();
+ data.append(new byte[]{0x05,0x01,0x00,0x03});
+ data.append((byte)hostname.getSize());
+ data.append(hostname);
+ data.append(new byte[] {0x00,0x00});
+ receive(data);
+
+ ByteArray expected =
+ new ByteArray(new byte[] {0x05,0x04,0x00,0x03,0x06,0x61,0x62,
+ 0x63,0x64,0x65,0x66,0x00,0x00});
+
+ assertEquals(expected,receivedData);
+ }
+
+ @Test
+ public void testReceiveData() {
+ SOCKS5BytestreamServerSession testling = createSession();
+ StartStopper<SOCKS5BytestreamServerSession> stopper =
+ new StartStopper<SOCKS5BytestreamServerSession>(testling);
+ bytestreams.setHasBytestream("abcdef", true);
+ authenticate();
+ request("abcdef");
+ eventLoop.processEvents();
+ testling.startSending(stream1);
+ skipHeader("abcdef");
+ eventLoop.processEvents();
+ assertEquals(new ByteArray("abcdefg"),receivedData);
+ assertEquals(2,receivedDataChunks);
+ }
+
+ @Test
+ public void testReceiveData_Chunked() {
+ SOCKS5BytestreamServerSession testling = createSession();
+ testling.setChunkSize(3);
+ StartStopper<SOCKS5BytestreamServerSession> stopper =
+ new StartStopper<SOCKS5BytestreamServerSession>(testling);
+ bytestreams.setHasBytestream("abcdef", true);
+ authenticate();
+ request("abcdef");
+ eventLoop.processEvents();
+ testling.startSending(stream1);
+ eventLoop.processEvents();
+ skipHeader("abcdef");
+ assertEquals(new ByteArray("abcdefg"),receivedData);
+ assertEquals(4,receivedDataChunks);
+ }
+
+ @Test
+ public void testDataStreamPauseStopsSendingData() {
+ SOCKS5BytestreamServerSession testling = createSession();
+ testling.setChunkSize(3);
+ stream1.setDataComplete(false);
+ StartStopper<SOCKS5BytestreamServerSession> stopper =
+ new StartStopper<SOCKS5BytestreamServerSession>(testling);
+ bytestreams.setHasBytestream("abcdef", true);
+ authenticate();
+ request("abcdef");
+ eventLoop.processEvents();
+ testling.startSending(stream1);
+ eventLoop.processEvents();
+ skipHeader("abcdef");
+ assertEquals(new ByteArray("abcdefg"),receivedData);
+ assertEquals(4,receivedDataChunks);
+ assertFalse(finished);
+ assertNull(error);
+ }
+
+ @Test
+ public void testDataStreamResumeAfterPauseSendsData() {
+ SOCKS5BytestreamServerSession testling = createSession();
+ testling.setChunkSize(3);
+ stream1.setDataComplete(false);
+ StartStopper<SOCKS5BytestreamServerSession> stopper =
+ new StartStopper<SOCKS5BytestreamServerSession>(testling);
+ bytestreams.setHasBytestream("abcdef", true);
+ authenticate();
+ request("abcdef");
+ eventLoop.processEvents();
+ testling.startSending(stream1);
+ eventLoop.processEvents();
+ skipHeader("abcdef");
+ stream1.addData(new ByteArray("xyz"));
+ eventLoop.processEvents();
+ assertEquals(new ByteArray("abcdefgxyz"),receivedData);
+ assertFalse(finished);
+ assertNull(error);
+ }
+
+ private void receive(SafeByteArray data) {
+ connection.receive(data);
+ eventLoop.processEvents();
+ }
+
+ private void authenticate() {
+ receive(new SafeByteArray(new byte[] {0x05,0x02,0x01,0x02}));
+ receivedData.clear();
+ receivedDataChunks = 0;
+ }
+
+ private void request(String hostname) {
+ SafeByteArray results = new SafeByteArray();
+ results.append(new byte[] {0x05,0x01,0x00,0x03});
+ results.append((byte) hostname.length());
+ results.append(hostname);
+ results.append(new byte[] {0x00,0x00});
+ receive(results);
+ }
+
+ private void skipHeader(String hostname) {
+ int headerSize = 7 + hostname.length();
+ byte[] currentReceivedData = receivedData.getData();
+ byte[] newContents = Arrays.copyOfRange(currentReceivedData, headerSize, currentReceivedData.length);
+ receivedData.clear();
+ receivedData.append(newContents);
+ }
+
+ private void handleDataWritten(SafeByteArray data) {
+ receivedData.append(data);
+ receivedDataChunks++;
+ }
+
+ private SOCKS5BytestreamServerSession createSession() {
+ SOCKS5BytestreamServerSession session = new SOCKS5BytestreamServerSession(connection, bytestreams);
+ session.onFinished.connect(new Slot1<FileTransferError>() {
+
+ @Override
+ public void call(FileTransferError error) {
+ handleFinished(error);
+ }
+
+ });
+ return session;
+ }
+
+ private void handleFinished(FileTransferError error) {
+ finished = true;
+ this.error = error;
+ }
+
+}