mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2025-09-10 18:59:41 +02:00
applied patches for extracted api for socks5 bytestreams and in-band bytestream
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/improve_bytestreams@11818 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
0540662db2
commit
8cb01900c9
72 changed files with 11761 additions and 1893 deletions
77
test-unit/org/jivesoftware/smackx/ibb/CloseListenerTest.java
Normal file
77
test-unit/org/jivesoftware/smackx/ibb/CloseListenerTest.java
Normal file
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ibb;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smackx.ibb.packet.Close;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
/**
|
||||
* Test for the CloseListener class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class CloseListenerTest {
|
||||
|
||||
String initiatorJID = "initiator@xmpp-server/Smack";
|
||||
String targetJID = "target@xmpp-server/Smack";
|
||||
|
||||
/**
|
||||
* If a close request to an unknown session is received it should be replied
|
||||
* with an <item-not-found/> error.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldReplyErrorIfSessionIsUnknown() throws Exception {
|
||||
|
||||
// mock connection
|
||||
Connection connection = mock(Connection.class);
|
||||
|
||||
// initialize InBandBytestreamManager to get the CloseListener
|
||||
InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
|
||||
// get the CloseListener from InBandByteStreamManager
|
||||
CloseListener closeListener = Whitebox.getInternalState(byteStreamManager,
|
||||
CloseListener.class);
|
||||
|
||||
Close close = new Close("unknownSessionId");
|
||||
close.setFrom(initiatorJID);
|
||||
close.setTo(targetJID);
|
||||
|
||||
closeListener.processPacket(close);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// capture reply to the In-Band Bytestream close request
|
||||
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
|
||||
verify(connection).sendPacket(argument.capture());
|
||||
|
||||
// assert that reply is the correct error packet
|
||||
assertEquals(initiatorJID, argument.getValue().getTo());
|
||||
assertEquals(IQ.Type.ERROR, argument.getValue().getType());
|
||||
assertEquals(XMPPError.Condition.item_not_found.toString(),
|
||||
argument.getValue().getError().getCondition());
|
||||
|
||||
}
|
||||
|
||||
}
|
79
test-unit/org/jivesoftware/smackx/ibb/DataListenerTest.java
Normal file
79
test-unit/org/jivesoftware/smackx/ibb/DataListenerTest.java
Normal file
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ibb;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smackx.ibb.packet.Data;
|
||||
import org.jivesoftware.smackx.ibb.packet.DataPacketExtension;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
/**
|
||||
* Test for the CloseListener class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class DataListenerTest {
|
||||
|
||||
String initiatorJID = "initiator@xmpp-server/Smack";
|
||||
String targetJID = "target@xmpp-server/Smack";
|
||||
|
||||
/**
|
||||
* If a data packet of an unknown session is received it should be replied
|
||||
* with an <item-not-found/> error.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldReplyErrorIfSessionIsUnknown() throws Exception {
|
||||
|
||||
// mock connection
|
||||
Connection connection = mock(Connection.class);
|
||||
|
||||
// initialize InBandBytestreamManager to get the DataListener
|
||||
InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
|
||||
// get the DataListener from InBandByteStreamManager
|
||||
DataListener dataListener = Whitebox.getInternalState(byteStreamManager,
|
||||
DataListener.class);
|
||||
|
||||
DataPacketExtension dpe = new DataPacketExtension("unknownSessionID", 0, "Data");
|
||||
Data data = new Data(dpe);
|
||||
data.setFrom(initiatorJID);
|
||||
data.setTo(targetJID);
|
||||
|
||||
dataListener.processPacket(data);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// capture reply to the In-Band Bytestream close request
|
||||
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
|
||||
verify(connection).sendPacket(argument.capture());
|
||||
|
||||
// assert that reply is the correct error packet
|
||||
assertEquals(initiatorJID, argument.getValue().getTo());
|
||||
assertEquals(IQ.Type.ERROR, argument.getValue().getType());
|
||||
assertEquals(XMPPError.Condition.item_not_found.toString(),
|
||||
argument.getValue().getError().getCondition());
|
||||
|
||||
}
|
||||
|
||||
}
|
57
test-unit/org/jivesoftware/smackx/ibb/IBBPacketUtils.java
Normal file
57
test-unit/org/jivesoftware/smackx/ibb/IBBPacketUtils.java
Normal file
|
@ -0,0 +1,57 @@
|
|||
package org.jivesoftware.smackx.ibb;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
|
||||
/**
|
||||
* Utility methods to create packets.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class IBBPacketUtils {
|
||||
|
||||
/**
|
||||
* Returns an error IQ.
|
||||
*
|
||||
* @param from the senders JID
|
||||
* @param to the recipients JID
|
||||
* @param xmppError the XMPP error
|
||||
* @return an error IQ
|
||||
*/
|
||||
public static IQ createErrorIQ(String from, String to, XMPPError xmppError) {
|
||||
IQ errorIQ = new IQ() {
|
||||
|
||||
public String getChildElementXML() {
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
errorIQ.setType(IQ.Type.ERROR);
|
||||
errorIQ.setFrom(from);
|
||||
errorIQ.setTo(to);
|
||||
errorIQ.setError(xmppError);
|
||||
return errorIQ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a result IQ.
|
||||
*
|
||||
* @param from the senders JID
|
||||
* @param to the recipients JID
|
||||
* @return a result IQ
|
||||
*/
|
||||
public static IQ createResultIQ(String from, String to) {
|
||||
IQ result = new IQ() {
|
||||
|
||||
public String getChildElementXML() {
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
result.setType(IQ.Type.RESULT);
|
||||
result.setFrom(from);
|
||||
result.setTo(to);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
21
test-unit/org/jivesoftware/smackx/ibb/IBBTestsSuite.java
Normal file
21
test-unit/org/jivesoftware/smackx/ibb/IBBTestsSuite.java
Normal file
|
@ -0,0 +1,21 @@
|
|||
package org.jivesoftware.smackx.ibb;
|
||||
|
||||
import org.jivesoftware.smackx.ibb.packet.CloseTest;
|
||||
import org.jivesoftware.smackx.ibb.packet.DataPacketExtensionTest;
|
||||
import org.jivesoftware.smackx.ibb.packet.DataTest;
|
||||
import org.jivesoftware.smackx.ibb.packet.OpenTest;
|
||||
import org.jivesoftware.smackx.ibb.provider.OpenIQProviderTest;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@Suite.SuiteClasses( { CloseTest.class, DataPacketExtensionTest.class, DataTest.class,
|
||||
OpenTest.class, OpenIQProviderTest.class, CloseListenerTest.class,
|
||||
DataListenerTest.class, InBandBytestreamManagerTest.class,
|
||||
InBandBytestreamRequestTest.class,
|
||||
InBandBytestreamSessionMessageTest.class,
|
||||
InBandBytestreamSessionTest.class, InitiationListenerTest.class })
|
||||
public class IBBTestsSuite {
|
||||
// the class remains completely empty,
|
||||
// being used only as a holder for the above annotations
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ibb;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smackx.ibb.InBandBytestreamManager.StanzaType;
|
||||
import org.jivesoftware.smackx.ibb.packet.Open;
|
||||
import org.jivesoftware.util.ConnectionUtils;
|
||||
import org.jivesoftware.util.Protocol;
|
||||
import org.jivesoftware.util.Verification;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test for InBandBytestreamManager.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class InBandBytestreamManagerTest {
|
||||
|
||||
// settings
|
||||
String initiatorJID = "initiator@xmpp-server/Smack";
|
||||
String targetJID = "target@xmpp-server/Smack";
|
||||
String xmppServer = "xmpp-server";
|
||||
String sessionID = "session_id";
|
||||
|
||||
// protocol verifier
|
||||
Protocol protocol;
|
||||
|
||||
// mocked XMPP connection
|
||||
Connection connection;
|
||||
|
||||
/**
|
||||
* Initialize fields used in the tests.
|
||||
*/
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// build protocol verifier
|
||||
protocol = new Protocol();
|
||||
|
||||
// create mocked XMPP connection
|
||||
connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID,
|
||||
xmppServer);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that
|
||||
* {@link InBandBytestreamManager#getByteStreamManager(Connection)} returns
|
||||
* one bytestream manager for every connection
|
||||
*/
|
||||
@Test
|
||||
public void shouldHaveOneManagerForEveryConnection() {
|
||||
|
||||
// mock two connections
|
||||
Connection connection1 = mock(Connection.class);
|
||||
Connection connection2 = mock(Connection.class);
|
||||
|
||||
// get bytestream manager for the first connection twice
|
||||
InBandBytestreamManager conn1ByteStreamManager1 = InBandBytestreamManager.getByteStreamManager(connection1);
|
||||
InBandBytestreamManager conn1ByteStreamManager2 = InBandBytestreamManager.getByteStreamManager(connection1);
|
||||
|
||||
// get bytestream manager for second connection
|
||||
InBandBytestreamManager conn2ByteStreamManager1 = InBandBytestreamManager.getByteStreamManager(connection2);
|
||||
|
||||
// assertions
|
||||
assertEquals(conn1ByteStreamManager1, conn1ByteStreamManager2);
|
||||
assertNotSame(conn1ByteStreamManager1, conn2ByteStreamManager1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoking {@link InBandBytestreamManager#establishSession(String)} should
|
||||
* throw an exception if the given target does not support in-band
|
||||
* bytestream.
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailIfTargetDoesNotSupportIBB() {
|
||||
InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
|
||||
try {
|
||||
XMPPError xmppError = new XMPPError(
|
||||
XMPPError.Condition.feature_not_implemented);
|
||||
IQ errorIQ = IBBPacketUtils.createErrorIQ(targetJID, initiatorJID, xmppError);
|
||||
protocol.addResponse(errorIQ);
|
||||
|
||||
// start In-Band Bytestream
|
||||
byteStreamManager.establishSession(targetJID);
|
||||
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
assertEquals(XMPPError.Condition.feature_not_implemented.toString(),
|
||||
e.getXMPPError().getCondition());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotAllowTooBigDefaultBlockSize() {
|
||||
InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
byteStreamManager.setDefaultBlockSize(1000000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCorrectlySetDefaultBlockSize() {
|
||||
InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
byteStreamManager.setDefaultBlockSize(1024);
|
||||
assertEquals(1024, byteStreamManager.getDefaultBlockSize());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotAllowTooBigMaximumBlockSize() {
|
||||
InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
byteStreamManager.setMaximumBlockSize(1000000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCorrectlySetMaximumBlockSize() {
|
||||
InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
byteStreamManager.setMaximumBlockSize(1024);
|
||||
assertEquals(1024, byteStreamManager.getMaximumBlockSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseConfiguredStanzaType() {
|
||||
InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
byteStreamManager.setStanza(StanzaType.MESSAGE);
|
||||
|
||||
protocol.addResponse(null, new Verification<Open, IQ>() {
|
||||
|
||||
public void verify(Open request, IQ response) {
|
||||
assertEquals(StanzaType.MESSAGE, request.getStanza());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
try {
|
||||
// start In-Band Bytestream
|
||||
byteStreamManager.establishSession(targetJID);
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
protocol.verifyAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnSession() throws Exception {
|
||||
InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
|
||||
IQ success = IBBPacketUtils.createResultIQ(targetJID, initiatorJID);
|
||||
protocol.addResponse(success, Verification.correspondingSenderReceiver,
|
||||
Verification.requestTypeSET);
|
||||
|
||||
// start In-Band Bytestream
|
||||
InBandBytestreamSession session = byteStreamManager.establishSession(targetJID);
|
||||
|
||||
assertNotNull(session);
|
||||
assertNotNull(session.getInputStream());
|
||||
assertNotNull(session.getOutputStream());
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package org.jivesoftware.smackx.ibb;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smackx.ibb.packet.Open;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
/**
|
||||
* Test for InBandBytestreamRequest.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class InBandBytestreamRequestTest {
|
||||
|
||||
String initiatorJID = "initiator@xmpp-server/Smack";
|
||||
String targetJID = "target@xmpp-server/Smack";
|
||||
String sessionID = "session_id";
|
||||
|
||||
Connection connection;
|
||||
InBandBytestreamManager byteStreamManager;
|
||||
Open initBytestream;
|
||||
|
||||
/**
|
||||
* Initialize fields used in the tests.
|
||||
*/
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// mock connection
|
||||
connection = mock(Connection.class);
|
||||
|
||||
// initialize InBandBytestreamManager to get the InitiationListener
|
||||
byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
|
||||
// create a In-Band Bytestream open packet
|
||||
initBytestream = new Open(sessionID, 4096);
|
||||
initBytestream.setFrom(initiatorJID);
|
||||
initBytestream.setTo(targetJID);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reject() method.
|
||||
*/
|
||||
@Test
|
||||
public void shouldReplyWithErrorIfRequestIsRejected() {
|
||||
InBandBytestreamRequest ibbRequest = new InBandBytestreamRequest(
|
||||
byteStreamManager, initBytestream);
|
||||
|
||||
// reject request
|
||||
ibbRequest.reject();
|
||||
|
||||
// capture reply to the In-Band Bytestream open request
|
||||
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
|
||||
verify(connection).sendPacket(argument.capture());
|
||||
|
||||
// assert that reply is the correct error packet
|
||||
assertEquals(initiatorJID, argument.getValue().getTo());
|
||||
assertEquals(IQ.Type.ERROR, argument.getValue().getType());
|
||||
assertEquals(XMPPError.Condition.no_acceptable.toString(),
|
||||
argument.getValue().getError().getCondition());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test accept() method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldReturnSessionIfRequestIsAccepted() throws Exception {
|
||||
InBandBytestreamRequest ibbRequest = new InBandBytestreamRequest(
|
||||
byteStreamManager, initBytestream);
|
||||
|
||||
// accept request
|
||||
InBandBytestreamSession session = ibbRequest.accept();
|
||||
|
||||
// capture reply to the In-Band Bytestream open request
|
||||
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
|
||||
verify(connection).sendPacket(argument.capture());
|
||||
|
||||
// assert that reply is the correct acknowledgment packet
|
||||
assertEquals(initiatorJID, argument.getValue().getTo());
|
||||
assertEquals(IQ.Type.RESULT, argument.getValue().getType());
|
||||
|
||||
assertNotNull(session);
|
||||
assertNotNull(session.getInputStream());
|
||||
assertNotNull(session.getOutputStream());
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,354 @@
|
|||
package org.jivesoftware.smackx.ibb;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Random;
|
||||
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.PacketListener;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smackx.ibb.InBandBytestreamManager.StanzaType;
|
||||
import org.jivesoftware.smackx.ibb.packet.DataPacketExtension;
|
||||
import org.jivesoftware.smackx.ibb.packet.Open;
|
||||
import org.jivesoftware.util.ConnectionUtils;
|
||||
import org.jivesoftware.util.Protocol;
|
||||
import org.jivesoftware.util.Verification;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
/**
|
||||
* Test for InBandBytestreamSession.
|
||||
* <p>
|
||||
* Tests sending data encapsulated in message stanzas.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class InBandBytestreamSessionMessageTest {
|
||||
|
||||
// settings
|
||||
String initiatorJID = "initiator@xmpp-server/Smack";
|
||||
String targetJID = "target@xmpp-server/Smack";
|
||||
String xmppServer = "xmpp-server";
|
||||
String sessionID = "session_id";
|
||||
|
||||
int blockSize = 10;
|
||||
|
||||
// protocol verifier
|
||||
Protocol protocol;
|
||||
|
||||
// mocked XMPP connection
|
||||
Connection connection;
|
||||
|
||||
InBandBytestreamManager byteStreamManager;
|
||||
|
||||
Open initBytestream;
|
||||
|
||||
Verification<Message, IQ> incrementingSequence;
|
||||
|
||||
/**
|
||||
* Initialize fields used in the tests.
|
||||
*/
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// build protocol verifier
|
||||
protocol = new Protocol();
|
||||
|
||||
// create mocked XMPP connection
|
||||
connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer);
|
||||
|
||||
// initialize InBandBytestreamManager to get the InitiationListener
|
||||
byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
|
||||
// create a In-Band Bytestream open packet with message stanza
|
||||
initBytestream = new Open(sessionID, blockSize, StanzaType.MESSAGE);
|
||||
initBytestream.setFrom(initiatorJID);
|
||||
initBytestream.setTo(targetJID);
|
||||
|
||||
incrementingSequence = new Verification<Message, IQ>() {
|
||||
|
||||
long lastSeq = 0;
|
||||
|
||||
public void verify(Message request, IQ response) {
|
||||
DataPacketExtension dpe = (DataPacketExtension) request.getExtension(
|
||||
DataPacketExtension.ELEMENT_NAME, InBandBytestreamManager.NAMESPACE);
|
||||
assertEquals(lastSeq++, dpe.getSeq());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the output stream write(byte[]) method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendThreeDataPackets1() throws Exception {
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
|
||||
// verify the data packets
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
|
||||
byte[] controlData = new byte[blockSize * 3];
|
||||
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
outputStream.write(controlData);
|
||||
outputStream.flush();
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the output stream write(byte) method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendThreeDataPackets2() throws Exception {
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
|
||||
// verify the data packets
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
|
||||
byte[] controlData = new byte[blockSize * 3];
|
||||
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
for (byte b : controlData) {
|
||||
outputStream.write(b);
|
||||
}
|
||||
outputStream.flush();
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the output stream write(byte[], int, int) method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendThreeDataPackets3() throws Exception {
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
|
||||
// verify the data packets
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
|
||||
byte[] controlData = new byte[(blockSize * 3) - 2];
|
||||
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
int off = 0;
|
||||
for (int i = 1; i <= 7; i++) {
|
||||
outputStream.write(controlData, off, i);
|
||||
off += i;
|
||||
}
|
||||
outputStream.flush();
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the output stream flush() method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendThirtyDataPackets() throws Exception {
|
||||
byte[] controlData = new byte[blockSize * 3];
|
||||
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
|
||||
// verify the data packets
|
||||
for (int i = 0; i < controlData.length; i++) {
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
}
|
||||
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
for (byte b : controlData) {
|
||||
outputStream.write(b);
|
||||
outputStream.flush();
|
||||
}
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test successive calls to the output stream flush() method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendNothingOnSuccessiveCallsToFlush() throws Exception {
|
||||
byte[] controlData = new byte[blockSize * 3];
|
||||
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
|
||||
// verify the data packets
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
protocol.addResponse(null, incrementingSequence);
|
||||
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
outputStream.write(controlData);
|
||||
|
||||
outputStream.flush();
|
||||
outputStream.flush();
|
||||
outputStream.flush();
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If a data packet is received out of order the session should be closed. See XEP-0047 Section
|
||||
* 2.2.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendCloseRequestIfInvalidSequenceReceived() throws Exception {
|
||||
// confirm close request
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
protocol.addResponse(resultIQ, Verification.requestTypeSET,
|
||||
Verification.correspondingSenderReceiver);
|
||||
|
||||
// get IBB sessions data packet listener
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
// build invalid packet with out of order sequence
|
||||
String base64Data = StringUtils.encodeBase64("Data");
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, 123, base64Data);
|
||||
Message dataMessage = new Message();
|
||||
dataMessage.addExtension(dpe);
|
||||
|
||||
// add data packets
|
||||
listener.processPacket(dataMessage);
|
||||
|
||||
// read until exception is thrown
|
||||
try {
|
||||
inputStream.read();
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (IOException e) {
|
||||
assertTrue(e.getMessage().contains("Packets out of sequence"));
|
||||
}
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the input stream read(byte[], int, int) method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldReadAllReceivedData1() throws Exception {
|
||||
// create random data
|
||||
Random rand = new Random();
|
||||
byte[] controlData = new byte[3 * blockSize];
|
||||
rand.nextBytes(controlData);
|
||||
|
||||
// get IBB sessions data packet listener
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
// verify data packet and notify listener
|
||||
for (int i = 0; i < controlData.length / blockSize; i++) {
|
||||
String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize,
|
||||
false);
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data);
|
||||
Message dataMessage = new Message();
|
||||
dataMessage.addExtension(dpe);
|
||||
listener.processPacket(dataMessage);
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[3 * blockSize];
|
||||
int read = 0;
|
||||
read = inputStream.read(bytes, 0, blockSize);
|
||||
assertEquals(blockSize, read);
|
||||
read = inputStream.read(bytes, 10, blockSize);
|
||||
assertEquals(blockSize, read);
|
||||
read = inputStream.read(bytes, 20, blockSize);
|
||||
assertEquals(blockSize, read);
|
||||
|
||||
// verify data
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
assertEquals(controlData[i], bytes[i]);
|
||||
}
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the input stream read() method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldReadAllReceivedData2() throws Exception {
|
||||
// create random data
|
||||
Random rand = new Random();
|
||||
byte[] controlData = new byte[3 * blockSize];
|
||||
rand.nextBytes(controlData);
|
||||
|
||||
// get IBB sessions data packet listener
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
// verify data packet and notify listener
|
||||
for (int i = 0; i < controlData.length / blockSize; i++) {
|
||||
String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize,
|
||||
false);
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data);
|
||||
Message dataMessage = new Message();
|
||||
dataMessage.addExtension(dpe);
|
||||
listener.processPacket(dataMessage);
|
||||
}
|
||||
|
||||
// read data
|
||||
byte[] bytes = new byte[3 * blockSize];
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
bytes[i] = (byte) inputStream.read();
|
||||
}
|
||||
|
||||
// verify data
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
assertEquals(controlData[i], bytes[i]);
|
||||
}
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,698 @@
|
|||
package org.jivesoftware.smackx.ibb;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Random;
|
||||
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.PacketListener;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smackx.ibb.packet.Data;
|
||||
import org.jivesoftware.smackx.ibb.packet.DataPacketExtension;
|
||||
import org.jivesoftware.smackx.ibb.packet.Open;
|
||||
import org.jivesoftware.util.ConnectionUtils;
|
||||
import org.jivesoftware.util.Protocol;
|
||||
import org.jivesoftware.util.Verification;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
/**
|
||||
* Test for InBandBytestreamSession.
|
||||
* <p>
|
||||
* Tests the basic behavior of an In-Band Bytestream session along with sending data encapsulated in
|
||||
* IQ stanzas.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class InBandBytestreamSessionTest {
|
||||
|
||||
// settings
|
||||
String initiatorJID = "initiator@xmpp-server/Smack";
|
||||
String targetJID = "target@xmpp-server/Smack";
|
||||
String xmppServer = "xmpp-server";
|
||||
String sessionID = "session_id";
|
||||
|
||||
int blockSize = 10;
|
||||
|
||||
// protocol verifier
|
||||
Protocol protocol;
|
||||
|
||||
// mocked XMPP connection
|
||||
Connection connection;
|
||||
|
||||
InBandBytestreamManager byteStreamManager;
|
||||
|
||||
Open initBytestream;
|
||||
|
||||
Verification<Data, IQ> incrementingSequence;
|
||||
|
||||
/**
|
||||
* Initialize fields used in the tests.
|
||||
*/
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// build protocol verifier
|
||||
protocol = new Protocol();
|
||||
|
||||
// create mocked XMPP connection
|
||||
connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer);
|
||||
|
||||
// initialize InBandBytestreamManager to get the InitiationListener
|
||||
byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
|
||||
// create a In-Band Bytestream open packet
|
||||
initBytestream = new Open(sessionID, blockSize);
|
||||
initBytestream.setFrom(initiatorJID);
|
||||
initBytestream.setTo(targetJID);
|
||||
|
||||
incrementingSequence = new Verification<Data, IQ>() {
|
||||
|
||||
long lastSeq = 0;
|
||||
|
||||
public void verify(Data request, IQ response) {
|
||||
assertEquals(lastSeq++, request.getDataPacketExtension().getSeq());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the output stream write(byte[]) method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendThreeDataPackets1() throws Exception {
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
|
||||
// set acknowledgments for the data packets
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
|
||||
byte[] controlData = new byte[blockSize * 3];
|
||||
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
outputStream.write(controlData);
|
||||
outputStream.flush();
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the output stream write(byte) method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendThreeDataPackets2() throws Exception {
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
|
||||
// set acknowledgments for the data packets
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
|
||||
byte[] controlData = new byte[blockSize * 3];
|
||||
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
for (byte b : controlData) {
|
||||
outputStream.write(b);
|
||||
}
|
||||
outputStream.flush();
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the output stream write(byte[], int, int) method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendThreeDataPackets3() throws Exception {
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
|
||||
// set acknowledgments for the data packets
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
|
||||
byte[] controlData = new byte[(blockSize * 3) - 2];
|
||||
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
int off = 0;
|
||||
for (int i = 1; i <= 7; i++) {
|
||||
outputStream.write(controlData, off, i);
|
||||
off += i;
|
||||
}
|
||||
outputStream.flush();
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the output stream flush() method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendThirtyDataPackets() throws Exception {
|
||||
byte[] controlData = new byte[blockSize * 3];
|
||||
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
|
||||
// set acknowledgments for the data packets
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
for (int i = 0; i < controlData.length; i++) {
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
}
|
||||
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
for (byte b : controlData) {
|
||||
outputStream.write(b);
|
||||
outputStream.flush();
|
||||
}
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test successive calls to the output stream flush() method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendNothingOnSuccessiveCallsToFlush() throws Exception {
|
||||
byte[] controlData = new byte[blockSize * 3];
|
||||
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
|
||||
// set acknowledgments for the data packets
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
protocol.addResponse(resultIQ, incrementingSequence);
|
||||
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
outputStream.write(controlData);
|
||||
|
||||
outputStream.flush();
|
||||
outputStream.flush();
|
||||
outputStream.flush();
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the data is correctly chunked.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendDataCorrectly() throws Exception {
|
||||
// create random data
|
||||
Random rand = new Random();
|
||||
final byte[] controlData = new byte[256 * blockSize];
|
||||
rand.nextBytes(controlData);
|
||||
|
||||
// compares the data of each packet with the control data
|
||||
Verification<Data, IQ> dataVerification = new Verification<Data, IQ>() {
|
||||
|
||||
public void verify(Data request, IQ response) {
|
||||
byte[] decodedData = request.getDataPacketExtension().getDecodedData();
|
||||
int seq = (int) request.getDataPacketExtension().getSeq();
|
||||
for (int i = 0; i < decodedData.length; i++) {
|
||||
assertEquals(controlData[(seq * blockSize) + i], decodedData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// set acknowledgments for the data packets
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
for (int i = 0; i < controlData.length / blockSize; i++) {
|
||||
protocol.addResponse(resultIQ, incrementingSequence, dataVerification);
|
||||
}
|
||||
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
outputStream.write(controlData);
|
||||
outputStream.flush();
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the input stream is closed the output stream should not be closed as well.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotCloseBothStreamsIfOutputStreamIsClosed() throws Exception {
|
||||
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
OutputStream outputStream = session.getOutputStream();
|
||||
outputStream.close();
|
||||
|
||||
// verify data packet confirmation is of type RESULT
|
||||
protocol.addResponse(null, Verification.requestTypeRESULT);
|
||||
|
||||
// insert data to read
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
String base64Data = StringUtils.encodeBase64("Data");
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data);
|
||||
Data data = new Data(dpe);
|
||||
listener.processPacket(data);
|
||||
|
||||
// verify no packet send
|
||||
protocol.verifyAll();
|
||||
|
||||
try {
|
||||
outputStream.flush();
|
||||
fail("should throw an exception");
|
||||
}
|
||||
catch (IOException e) {
|
||||
assertTrue(e.getMessage().contains("closed"));
|
||||
}
|
||||
|
||||
assertTrue(inputStream.read() != 0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid data packets should be confirmed.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldConfirmReceivedDataPacket() throws Exception {
|
||||
// verify data packet confirmation is of type RESULT
|
||||
protocol.addResponse(null, Verification.requestTypeRESULT);
|
||||
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
String base64Data = StringUtils.encodeBase64("Data");
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data);
|
||||
Data data = new Data(dpe);
|
||||
|
||||
listener.processPacket(data);
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the data packet has a sequence that is already used an 'unexpected-request' error should
|
||||
* be returned. See XEP-0047 Section 2.2.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldReplyWithErrorIfAlreadyUsedSequenceIsReceived() throws Exception {
|
||||
// verify reply to first valid data packet is of type RESULT
|
||||
protocol.addResponse(null, Verification.requestTypeRESULT);
|
||||
|
||||
// verify reply to invalid data packet is an error
|
||||
protocol.addResponse(null, Verification.requestTypeERROR, new Verification<IQ, IQ>() {
|
||||
|
||||
public void verify(IQ request, IQ response) {
|
||||
assertEquals(XMPPError.Condition.unexpected_request.toString(),
|
||||
request.getError().getCondition());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// get IBB sessions data packet listener
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
// build data packets
|
||||
String base64Data = StringUtils.encodeBase64("Data");
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data);
|
||||
Data data1 = new Data(dpe);
|
||||
Data data2 = new Data(dpe);
|
||||
|
||||
// notify listener
|
||||
listener.processPacket(data1);
|
||||
listener.processPacket(data2);
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the data packet contains invalid Base64 encoding an 'bad-request' error should be
|
||||
* returned. See XEP-0047 Section 2.2.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldReplyWithErrorIfDataIsInvalid() throws Exception {
|
||||
// verify reply to invalid data packet is an error
|
||||
protocol.addResponse(null, Verification.requestTypeERROR, new Verification<IQ, IQ>() {
|
||||
|
||||
public void verify(IQ request, IQ response) {
|
||||
assertEquals(XMPPError.Condition.bad_request.toString(),
|
||||
request.getError().getCondition());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// get IBB sessions data packet listener
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
// build data packets
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, "AA=BB");
|
||||
Data data = new Data(dpe);
|
||||
|
||||
// notify listener
|
||||
listener.processPacket(data);
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If a data packet is received out of order the session should be closed. See XEP-0047 Section
|
||||
* 2.2.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSendCloseRequestIfInvalidSequenceReceived() throws Exception {
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
|
||||
// confirm data packet with invalid sequence
|
||||
protocol.addResponse(resultIQ);
|
||||
|
||||
// confirm close request
|
||||
protocol.addResponse(resultIQ, Verification.requestTypeSET,
|
||||
Verification.correspondingSenderReceiver);
|
||||
|
||||
// get IBB sessions data packet listener
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
// build invalid packet with out of order sequence
|
||||
String base64Data = StringUtils.encodeBase64("Data");
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, 123, base64Data);
|
||||
Data data = new Data(dpe);
|
||||
|
||||
// add data packets
|
||||
listener.processPacket(data);
|
||||
|
||||
// read until exception is thrown
|
||||
try {
|
||||
inputStream.read();
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (IOException e) {
|
||||
assertTrue(e.getMessage().contains("Packets out of sequence"));
|
||||
}
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the input stream read(byte[], int, int) method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldReadAllReceivedData1() throws Exception {
|
||||
// create random data
|
||||
Random rand = new Random();
|
||||
byte[] controlData = new byte[3 * blockSize];
|
||||
rand.nextBytes(controlData);
|
||||
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
|
||||
// get IBB sessions data packet listener
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
// set data packet acknowledgment and notify listener
|
||||
for (int i = 0; i < controlData.length / blockSize; i++) {
|
||||
protocol.addResponse(resultIQ);
|
||||
String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize,
|
||||
false);
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data);
|
||||
Data data = new Data(dpe);
|
||||
listener.processPacket(data);
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[3 * blockSize];
|
||||
int read = 0;
|
||||
read = inputStream.read(bytes, 0, blockSize);
|
||||
assertEquals(blockSize, read);
|
||||
read = inputStream.read(bytes, 10, blockSize);
|
||||
assertEquals(blockSize, read);
|
||||
read = inputStream.read(bytes, 20, blockSize);
|
||||
assertEquals(blockSize, read);
|
||||
|
||||
// verify data
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
assertEquals(controlData[i], bytes[i]);
|
||||
}
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the input stream read() method.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldReadAllReceivedData2() throws Exception {
|
||||
// create random data
|
||||
Random rand = new Random();
|
||||
byte[] controlData = new byte[3 * blockSize];
|
||||
rand.nextBytes(controlData);
|
||||
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
|
||||
// get IBB sessions data packet listener
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
// set data packet acknowledgment and notify listener
|
||||
for (int i = 0; i < controlData.length / blockSize; i++) {
|
||||
protocol.addResponse(resultIQ);
|
||||
String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize,
|
||||
false);
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data);
|
||||
Data data = new Data(dpe);
|
||||
listener.processPacket(data);
|
||||
}
|
||||
|
||||
// read data
|
||||
byte[] bytes = new byte[3 * blockSize];
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
bytes[i] = (byte) inputStream.read();
|
||||
}
|
||||
|
||||
// verify data
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
assertEquals(controlData[i], bytes[i]);
|
||||
}
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the output stream is closed the input stream should not be closed as well.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotCloseBothStreamsIfInputStreamIsClosed() throws Exception {
|
||||
// acknowledgment for data packet
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
protocol.addResponse(resultIQ);
|
||||
|
||||
// get IBB sessions data packet listener
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
// build data packet
|
||||
String base64Data = StringUtils.encodeBase64("Data");
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data);
|
||||
Data data = new Data(dpe);
|
||||
|
||||
// add data packets
|
||||
listener.processPacket(data);
|
||||
|
||||
inputStream.close();
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
try {
|
||||
while (inputStream.read() != -1) {
|
||||
}
|
||||
inputStream.read();
|
||||
fail("should throw an exception");
|
||||
}
|
||||
catch (IOException e) {
|
||||
assertTrue(e.getMessage().contains("closed"));
|
||||
}
|
||||
|
||||
session.getOutputStream().flush();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the session is closed the input stream and output stream should be closed as well.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldCloseBothStreamsIfSessionIsClosed() throws Exception {
|
||||
// acknowledgment for data packet
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
protocol.addResponse(resultIQ);
|
||||
|
||||
// acknowledgment for close request
|
||||
protocol.addResponse(resultIQ, Verification.correspondingSenderReceiver,
|
||||
Verification.requestTypeSET);
|
||||
|
||||
// get IBB sessions data packet listener
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
// build data packet
|
||||
String base64Data = StringUtils.encodeBase64("Data");
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data);
|
||||
Data data = new Data(dpe);
|
||||
|
||||
// add data packets
|
||||
listener.processPacket(data);
|
||||
|
||||
session.close();
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
try {
|
||||
while (inputStream.read() != -1) {
|
||||
}
|
||||
inputStream.read();
|
||||
fail("should throw an exception");
|
||||
}
|
||||
catch (IOException e) {
|
||||
assertTrue(e.getMessage().contains("closed"));
|
||||
}
|
||||
|
||||
try {
|
||||
session.getOutputStream().flush();
|
||||
fail("should throw an exception");
|
||||
}
|
||||
catch (IOException e) {
|
||||
assertTrue(e.getMessage().contains("closed"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the input stream is closed concurrently there should be no deadlock.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotDeadlockIfInputStreamIsClosed() throws Exception {
|
||||
// acknowledgment for data packet
|
||||
IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID);
|
||||
protocol.addResponse(resultIQ);
|
||||
|
||||
// get IBB sessions data packet listener
|
||||
InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream,
|
||||
initiatorJID);
|
||||
final InputStream inputStream = session.getInputStream();
|
||||
PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class);
|
||||
|
||||
// build data packet
|
||||
String base64Data = StringUtils.encodeBase64("Data");
|
||||
DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data);
|
||||
Data data = new Data(dpe);
|
||||
|
||||
// add data packets
|
||||
listener.processPacket(data);
|
||||
|
||||
Thread closer = new Thread(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
inputStream.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
closer.start();
|
||||
|
||||
try {
|
||||
byte[] bytes = new byte[20];
|
||||
while (inputStream.read(bytes) != -1) {
|
||||
}
|
||||
inputStream.read();
|
||||
fail("should throw an exception");
|
||||
}
|
||||
catch (IOException e) {
|
||||
assertTrue(e.getMessage().contains("closed"));
|
||||
}
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,327 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ibb;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smackx.bytestreams.BytestreamRequest;
|
||||
import org.jivesoftware.smackx.ibb.packet.Open;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
/**
|
||||
* Test for the InitiationListener class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class InitiationListenerTest {
|
||||
|
||||
String initiatorJID = "initiator@xmpp-server/Smack";
|
||||
String targetJID = "target@xmpp-server/Smack";
|
||||
String sessionID = "session_id";
|
||||
|
||||
Connection connection;
|
||||
InBandBytestreamManager byteStreamManager;
|
||||
InitiationListener initiationListener;
|
||||
Open initBytestream;
|
||||
|
||||
/**
|
||||
* Initialize fields used in the tests.
|
||||
*/
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// mock connection
|
||||
connection = mock(Connection.class);
|
||||
|
||||
// initialize InBandBytestreamManager to get the InitiationListener
|
||||
byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||
|
||||
// get the InitiationListener from InBandByteStreamManager
|
||||
initiationListener = Whitebox.getInternalState(byteStreamManager, InitiationListener.class);
|
||||
|
||||
// create a In-Band Bytestream open packet
|
||||
initBytestream = new Open(sessionID, 4096);
|
||||
initBytestream.setFrom(initiatorJID);
|
||||
initBytestream.setTo(targetJID);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If no listeners are registered for incoming In-Band Bytestream requests, all request should
|
||||
* be rejected with an error.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldRespondWithError() throws Exception {
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// capture reply to the In-Band Bytestream open request
|
||||
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
|
||||
verify(connection).sendPacket(argument.capture());
|
||||
|
||||
// assert that reply is the correct error packet
|
||||
assertEquals(initiatorJID, argument.getValue().getTo());
|
||||
assertEquals(IQ.Type.ERROR, argument.getValue().getType());
|
||||
assertEquals(XMPPError.Condition.no_acceptable.toString(),
|
||||
argument.getValue().getError().getCondition());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Open request with a block size that exceeds the maximum block size should be replied with an
|
||||
* resource-constraint error.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldRejectRequestWithTooBigBlockSize() throws Exception {
|
||||
byteStreamManager.setMaximumBlockSize(1024);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// capture reply to the In-Band Bytestream open request
|
||||
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
|
||||
verify(connection).sendPacket(argument.capture());
|
||||
|
||||
// assert that reply is the correct error packet
|
||||
assertEquals(initiatorJID, argument.getValue().getTo());
|
||||
assertEquals(IQ.Type.ERROR, argument.getValue().getType());
|
||||
assertEquals(XMPPError.Condition.resource_constraint.toString(),
|
||||
argument.getValue().getError().getCondition());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If a listener for all requests is registered it should be notified on incoming requests.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldInvokeListenerForAllRequests() throws Exception {
|
||||
|
||||
// add listener
|
||||
InBandBytestreamListener listener = mock(InBandBytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(listener);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert listener is called once
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(listener).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert that listener is called for the correct request
|
||||
assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If a listener for a specific user in registered it should be notified on incoming requests
|
||||
* for that user.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldInvokeListenerForUser() throws Exception {
|
||||
|
||||
// add listener
|
||||
InBandBytestreamListener listener = mock(InBandBytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(listener, initiatorJID);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert listener is called once
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(listener).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert that reply is the correct error packet
|
||||
assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If listener for a specific user is registered it should not be notified on incoming requests
|
||||
* from other users.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotInvokeListenerForUser() throws Exception {
|
||||
|
||||
// add listener for request of user "other_initiator"
|
||||
InBandBytestreamListener listener = mock(InBandBytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(listener, "other_" + initiatorJID);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert listener is not called
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(listener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// capture reply to the In-Band Bytestream open request
|
||||
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
|
||||
verify(connection).sendPacket(argument.capture());
|
||||
|
||||
// assert that reply is the correct error packet
|
||||
assertEquals(initiatorJID, argument.getValue().getTo());
|
||||
assertEquals(IQ.Type.ERROR, argument.getValue().getType());
|
||||
assertEquals(XMPPError.Condition.no_acceptable.toString(),
|
||||
argument.getValue().getError().getCondition());
|
||||
}
|
||||
|
||||
/**
|
||||
* If a user specific listener and an all requests listener is registered only the user specific
|
||||
* listener should be notified.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotInvokeAllRequestsListenerIfUserListenerExists() throws Exception {
|
||||
|
||||
// add listener for all request
|
||||
InBandBytestreamListener allRequestsListener = mock(InBandBytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(allRequestsListener);
|
||||
|
||||
// add listener for request of user "initiator"
|
||||
InBandBytestreamListener userRequestsListener = mock(InBandBytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert user request listener is called once
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert all requests listener is not called
|
||||
byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If a user specific listener and an all requests listener is registered only the all requests
|
||||
* listener should be notified on an incoming request for another user.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldInvokeAllRequestsListenerIfUserListenerExists() throws Exception {
|
||||
|
||||
// add listener for all request
|
||||
InBandBytestreamListener allRequestsListener = mock(InBandBytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(allRequestsListener);
|
||||
|
||||
// add listener for request of user "other_initiator"
|
||||
InBandBytestreamListener userRequestsListener = mock(InBandBytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(userRequestsListener, "other_"
|
||||
+ initiatorJID);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert user request listener is not called
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert all requests listener is called
|
||||
byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(allRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If a request with a specific session ID should be ignored no listeners should be notified.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldIgnoreInBandBytestreamRequestOnce() throws Exception {
|
||||
|
||||
// add listener for all request
|
||||
InBandBytestreamListener allRequestsListener = mock(InBandBytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(allRequestsListener);
|
||||
|
||||
// add listener for request of user "initiator"
|
||||
InBandBytestreamListener userRequestsListener = mock(InBandBytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID);
|
||||
|
||||
// ignore session ID
|
||||
byteStreamManager.ignoreBytestreamRequestOnce(sessionID);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert user request listener is not called
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert all requests listener is not called
|
||||
byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// run the listener with the initiation packet again
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert user request listener is called on the second request with the
|
||||
// same session ID
|
||||
verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert all requests listener is not called
|
||||
byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
}
|
||||
|
||||
}
|
80
test-unit/org/jivesoftware/smackx/ibb/packet/CloseTest.java
Normal file
80
test-unit/org/jivesoftware/smackx/ibb/packet/CloseTest.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ibb.packet;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.custommonkey.xmlunit.XMLAssert.*;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.jamesmurty.utils.XMLBuilder;
|
||||
|
||||
/**
|
||||
* Test for the Close class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class CloseTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotInstantiateWithInvalidArguments1() {
|
||||
new Close(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotInstantiateWithInvalidArguments2() {
|
||||
new Close("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeOfIQTypeSET() {
|
||||
Close close = new Close("sessionID");
|
||||
assertEquals(IQ.Type.SET, close.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetAllFieldsCorrectly() {
|
||||
Close close = new Close("sessionID");
|
||||
assertEquals("sessionID", close.getSessionID());
|
||||
}
|
||||
|
||||
private static Properties outputProperties = new Properties();
|
||||
{
|
||||
outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIQStanzaXML() throws Exception {
|
||||
String control = XMLBuilder.create("iq")
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "us71g45j")
|
||||
.a("type", "set")
|
||||
.e("close")
|
||||
.a("xmlns", "http://jabber.org/protocol/ibb")
|
||||
.a("sid", "i781hf64")
|
||||
.asString(outputProperties);
|
||||
|
||||
Close close = new Close("i781hf64");
|
||||
close.setFrom("romeo@montague.lit/orchard");
|
||||
close.setTo("juliet@capulet.lit/balcony");
|
||||
close.setPacketID("us71g45j");
|
||||
|
||||
assertXMLEqual(control, close.toXML());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ibb.packet;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.custommonkey.xmlunit.XMLAssert.*;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.jamesmurty.utils.XMLBuilder;
|
||||
|
||||
/**
|
||||
* Test for the DataPacketExtension class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class DataPacketExtensionTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotInstantiateWithInvalidArgument1() {
|
||||
new DataPacketExtension(null, 0, "data");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotInstantiateWithInvalidArgument2() {
|
||||
new DataPacketExtension("", 0, "data");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotInstantiateWithInvalidArgument3() {
|
||||
new DataPacketExtension("sessionID", -1, "data");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotInstantiateWithInvalidArgument4() {
|
||||
new DataPacketExtension("sessionID", 70000, "data");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotInstantiateWithInvalidArgument5() {
|
||||
new DataPacketExtension("sessionID", 0, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetAllFieldsCorrectly() {
|
||||
DataPacketExtension data = new DataPacketExtension("sessionID", 0, "data");
|
||||
assertEquals("sessionID", data.getSessionID());
|
||||
assertEquals(0, data.getSeq());
|
||||
assertEquals("data", data.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnNullIfDataIsInvalid() {
|
||||
// pad character is not at end of data
|
||||
DataPacketExtension data = new DataPacketExtension("sessionID", 0, "BBBB=CCC");
|
||||
assertNull(data.getDecodedData());
|
||||
|
||||
// invalid Base64 character
|
||||
data = new DataPacketExtension("sessionID", 0, new String(new byte[] { 123 }));
|
||||
assertNull(data.getDecodedData());
|
||||
}
|
||||
|
||||
private static Properties outputProperties = new Properties();
|
||||
{
|
||||
outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIQStanzaXML() throws Exception {
|
||||
String control = XMLBuilder.create("data")
|
||||
.a("xmlns", "http://jabber.org/protocol/ibb")
|
||||
.a("seq", "0")
|
||||
.a("sid", "i781hf64")
|
||||
.t("DATA")
|
||||
.asString(outputProperties);
|
||||
|
||||
DataPacketExtension data = new DataPacketExtension("i781hf64", 0, "DATA");
|
||||
assertXMLEqual(control, data.toXML());
|
||||
}
|
||||
|
||||
}
|
84
test-unit/org/jivesoftware/smackx/ibb/packet/DataTest.java
Normal file
84
test-unit/org/jivesoftware/smackx/ibb/packet/DataTest.java
Normal file
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ibb.packet;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.custommonkey.xmlunit.XMLAssert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.util.Base64;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.jamesmurty.utils.XMLBuilder;
|
||||
|
||||
/**
|
||||
* Test for the Data class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class DataTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotInstantiateWithInvalidArgument() {
|
||||
new Data(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeOfIQTypeSET() {
|
||||
DataPacketExtension dpe = mock(DataPacketExtension.class);
|
||||
Data data = new Data(dpe);
|
||||
assertEquals(IQ.Type.SET, data.getType());
|
||||
}
|
||||
|
||||
private static Properties outputProperties = new Properties();
|
||||
{
|
||||
outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIQStanzaXML() throws Exception {
|
||||
String encodedData = Base64.encodeBytes("Test".getBytes());
|
||||
|
||||
String control = XMLBuilder.create("iq")
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "kr91n475")
|
||||
.a("type", "set")
|
||||
.e("data")
|
||||
.a("xmlns", "http://jabber.org/protocol/ibb")
|
||||
.a("seq", "0")
|
||||
.a("sid", "i781hf64")
|
||||
.t(encodedData)
|
||||
.asString(outputProperties);
|
||||
|
||||
DataPacketExtension dpe = mock(DataPacketExtension.class);
|
||||
String dataTag = XMLBuilder.create("data")
|
||||
.a("xmlns", "http://jabber.org/protocol/ibb")
|
||||
.a("seq", "0")
|
||||
.a("sid", "i781hf64")
|
||||
.t(encodedData)
|
||||
.asString(outputProperties);
|
||||
when(dpe.toXML()).thenReturn(dataTag);
|
||||
Data data = new Data(dpe);
|
||||
data.setFrom("romeo@montague.lit/orchard");
|
||||
data.setTo("juliet@capulet.lit/balcony");
|
||||
data.setPacketID("kr91n475");
|
||||
|
||||
assertXMLEqual(control, data.toXML());
|
||||
}
|
||||
|
||||
}
|
103
test-unit/org/jivesoftware/smackx/ibb/packet/OpenTest.java
Normal file
103
test-unit/org/jivesoftware/smackx/ibb/packet/OpenTest.java
Normal file
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ibb.packet;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.custommonkey.xmlunit.XMLAssert.*;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smackx.ibb.InBandBytestreamManager.StanzaType;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.jamesmurty.utils.XMLBuilder;
|
||||
|
||||
/**
|
||||
* Test for the Open class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class OpenTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotInstantiateWithInvalidArguments1() {
|
||||
new Open(null, 1);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotInstantiateWithInvalidArguments2() {
|
||||
new Open("", 1);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotInstantiateWithInvalidArguments3() {
|
||||
new Open("sessionID", -1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetIQStanzaAsDefault() {
|
||||
Open open = new Open("sessionID", 4096);
|
||||
assertEquals(StanzaType.IQ, open.getStanza());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseMessageStanzaIfGiven() {
|
||||
Open open = new Open("sessionID", 4096, StanzaType.MESSAGE);
|
||||
assertEquals(StanzaType.MESSAGE, open.getStanza());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeOfIQTypeSET() {
|
||||
Open open = new Open("sessionID", 4096);
|
||||
assertEquals(IQ.Type.SET, open.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetAllFieldsCorrectly() {
|
||||
Open open = new Open("sessionID", 4096, StanzaType.MESSAGE);
|
||||
assertEquals("sessionID", open.getSessionID());
|
||||
assertEquals(4096, open.getBlockSize());
|
||||
assertEquals(StanzaType.MESSAGE, open.getStanza());
|
||||
}
|
||||
|
||||
private static Properties outputProperties = new Properties();
|
||||
{
|
||||
outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIQStanzaXML() throws Exception {
|
||||
String control = XMLBuilder.create("iq")
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "jn3h8g65")
|
||||
.a("type", "set")
|
||||
.e("open")
|
||||
.a("xmlns", "http://jabber.org/protocol/ibb")
|
||||
.a("block-size", "4096")
|
||||
.a("sid", "i781hf64")
|
||||
.a("stanza", "iq")
|
||||
.asString(outputProperties);
|
||||
|
||||
Open open = new Open("i781hf64", 4096, StanzaType.IQ);
|
||||
open.setFrom("romeo@montague.lit/orchard");
|
||||
open.setTo("juliet@capulet.lit/balcony");
|
||||
open.setPacketID("jn3h8g65");
|
||||
|
||||
assertXMLEqual(control, open.toXML());
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ibb.provider;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jivesoftware.smackx.ibb.InBandBytestreamManager.StanzaType;
|
||||
import org.jivesoftware.smackx.ibb.packet.Open;
|
||||
import org.junit.Test;
|
||||
import org.xmlpull.mxp1.MXParser;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import com.jamesmurty.utils.XMLBuilder;
|
||||
|
||||
/**
|
||||
* Test for the OpenIQProvider class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class OpenIQProviderTest {
|
||||
|
||||
private static Properties outputProperties = new Properties();
|
||||
{
|
||||
outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCorrectlyParseIQStanzaAttribute() throws Exception {
|
||||
String control = XMLBuilder.create("open")
|
||||
.a("xmlns", "http://jabber.org/protocol/ibb")
|
||||
.a("block-size", "4096")
|
||||
.a("sid", "i781hf64")
|
||||
.a("stanza", "iq")
|
||||
.asString(outputProperties);
|
||||
|
||||
OpenIQProvider oip = new OpenIQProvider();
|
||||
Open open = (Open) oip.parseIQ(getParser(control));
|
||||
|
||||
assertEquals(StanzaType.IQ, open.getStanza());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCorrectlyParseMessageStanzaAttribute() throws Exception {
|
||||
String control = XMLBuilder.create("open")
|
||||
.a("xmlns", "http://jabber.org/protocol/ibb")
|
||||
.a("block-size", "4096")
|
||||
.a("sid", "i781hf64")
|
||||
.a("stanza", "message")
|
||||
.asString(outputProperties);
|
||||
|
||||
OpenIQProvider oip = new OpenIQProvider();
|
||||
Open open = (Open) oip.parseIQ(getParser(control));
|
||||
|
||||
assertEquals(StanzaType.MESSAGE, open.getStanza());
|
||||
}
|
||||
|
||||
private XmlPullParser getParser(String control) throws XmlPullParserException,
|
||||
IOException {
|
||||
XmlPullParser parser = new MXParser();
|
||||
parser.setInput(new StringReader(control));
|
||||
while (true) {
|
||||
if (parser.next() == XmlPullParser.START_TAG
|
||||
&& parser.getName().equals("open")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return parser;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,305 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.socks5bytestream;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smackx.ServiceDiscoveryManager;
|
||||
import org.jivesoftware.smackx.bytestreams.BytestreamRequest;
|
||||
import org.jivesoftware.smackx.socks5bytestream.packet.Bytestream;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
/**
|
||||
* Test for the InitiationListener class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class InitiationListenerTest {
|
||||
|
||||
String initiatorJID = "initiator@xmpp-server/Smack";
|
||||
String targetJID = "target@xmpp-server/Smack";
|
||||
String xmppServer = "xmpp-server";
|
||||
String proxyJID = "proxy.xmpp-server";
|
||||
String proxyAddress = "127.0.0.1";
|
||||
String sessionID = "session_id";
|
||||
|
||||
Connection connection;
|
||||
Socks5BytestreamManager byteStreamManager;
|
||||
InitiationListener initiationListener;
|
||||
Bytestream initBytestream;
|
||||
|
||||
/**
|
||||
* Initialize fields used in the tests.
|
||||
*/
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// mock connection
|
||||
connection = mock(Connection.class);
|
||||
|
||||
// create service discovery manager for mocked connection
|
||||
new ServiceDiscoveryManager(connection);
|
||||
|
||||
// initialize Socks5ByteStreamManager to get the InitiationListener
|
||||
byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
|
||||
|
||||
// get the InitiationListener from Socks5ByteStreamManager
|
||||
initiationListener = Whitebox.getInternalState(byteStreamManager, InitiationListener.class);
|
||||
|
||||
// create a SOCKS5 Bytestream initiation packet
|
||||
initBytestream = Socks5PacketUtils.createBytestreamInitiation(initiatorJID, targetJID,
|
||||
sessionID);
|
||||
initBytestream.addStreamHost(proxyJID, proxyAddress, 7777);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If no listeners are registered for incoming SOCKS5 Bytestream requests, all request should be
|
||||
* rejected with an error.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldRespondWithError() throws Exception {
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// capture reply to the SOCKS5 Bytestream initiation
|
||||
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
|
||||
verify(connection).sendPacket(argument.capture());
|
||||
|
||||
// assert that reply is the correct error packet
|
||||
assertEquals(initiatorJID, argument.getValue().getTo());
|
||||
assertEquals(IQ.Type.ERROR, argument.getValue().getType());
|
||||
assertEquals(XMPPError.Condition.no_acceptable.toString(),
|
||||
argument.getValue().getError().getCondition());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If a listener for all requests is registered it should be notified on incoming requests.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldInvokeListenerForAllRequests() throws Exception {
|
||||
|
||||
// add listener
|
||||
Socks5BytestreamListener listener = mock(Socks5BytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(listener);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert listener is called once
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(listener).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert that listener is called for the correct request
|
||||
assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If a listener for a specific user in registered it should be notified on incoming requests
|
||||
* for that user.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldInvokeListenerForUser() throws Exception {
|
||||
|
||||
// add listener
|
||||
Socks5BytestreamListener listener = mock(Socks5BytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(listener, initiatorJID);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert listener is called once
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(listener).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert that reply is the correct error packet
|
||||
assertEquals(initiatorJID, byteStreamRequest.getValue().getFrom());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If listener for a specific user is registered it should not be notified on incoming requests
|
||||
* from other users.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotInvokeListenerForUser() throws Exception {
|
||||
|
||||
// add listener for request of user "other_initiator"
|
||||
Socks5BytestreamListener listener = mock(Socks5BytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(listener, "other_" + initiatorJID);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert listener is not called
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(listener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// capture reply to the SOCKS5 Bytestream initiation
|
||||
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
|
||||
verify(connection).sendPacket(argument.capture());
|
||||
|
||||
// assert that reply is the correct error packet
|
||||
assertEquals(initiatorJID, argument.getValue().getTo());
|
||||
assertEquals(IQ.Type.ERROR, argument.getValue().getType());
|
||||
assertEquals(XMPPError.Condition.no_acceptable.toString(),
|
||||
argument.getValue().getError().getCondition());
|
||||
}
|
||||
|
||||
/**
|
||||
* If a user specific listener and an all requests listener is registered only the user specific
|
||||
* listener should be notified.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotInvokeAllRequestsListenerIfUserListenerExists() throws Exception {
|
||||
|
||||
// add listener for all request
|
||||
Socks5BytestreamListener allRequestsListener = mock(Socks5BytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(allRequestsListener);
|
||||
|
||||
// add listener for request of user "initiator"
|
||||
Socks5BytestreamListener userRequestsListener = mock(Socks5BytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert user request listener is called once
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert all requests listener is not called
|
||||
byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If a user specific listener and an all requests listener is registered only the all requests
|
||||
* listener should be notified on an incoming request for another user.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldInvokeAllRequestsListenerIfUserListenerExists() throws Exception {
|
||||
|
||||
// add listener for all request
|
||||
Socks5BytestreamListener allRequestsListener = mock(Socks5BytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(allRequestsListener);
|
||||
|
||||
// add listener for request of user "other_initiator"
|
||||
Socks5BytestreamListener userRequestsListener = mock(Socks5BytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(userRequestsListener, "other_"
|
||||
+ initiatorJID);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert user request listener is not called
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert all requests listener is called
|
||||
byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(allRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If a request with a specific session ID should be ignored no listeners should be notified.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldIgnoreSocks5BytestreamRequestOnce() throws Exception {
|
||||
|
||||
// add listener for all request
|
||||
Socks5BytestreamListener allRequestsListener = mock(Socks5BytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(allRequestsListener);
|
||||
|
||||
// add listener for request of user "initiator"
|
||||
Socks5BytestreamListener userRequestsListener = mock(Socks5BytestreamListener.class);
|
||||
byteStreamManager.addIncomingBytestreamListener(userRequestsListener, initiatorJID);
|
||||
|
||||
// ignore session ID
|
||||
byteStreamManager.ignoreBytestreamRequestOnce(sessionID);
|
||||
|
||||
// run the listener with the initiation packet
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert user request listener is not called
|
||||
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert all requests listener is not called
|
||||
byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// run the listener with the initiation packet again
|
||||
initiationListener.processPacket(initBytestream);
|
||||
|
||||
// wait because packet is processed in an extra thread
|
||||
Thread.sleep(200);
|
||||
|
||||
// assert user request listener is called on the second request with the same session ID
|
||||
verify(userRequestsListener).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
// assert all requests listener is not called
|
||||
byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
|
||||
verify(allRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
|
||||
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,426 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.socks5bytestream;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.SmackConfiguration;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smackx.socks5bytestream.packet.Bytestream;
|
||||
import org.jivesoftware.util.ConnectionUtils;
|
||||
import org.jivesoftware.util.Protocol;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for the Socks5BytestreamRequest class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class Socks5ByteStreamRequestTest {
|
||||
|
||||
// settings
|
||||
String initiatorJID = "initiator@xmpp-server/Smack";
|
||||
String targetJID = "target@xmpp-server/Smack";
|
||||
String xmppServer = "xmpp-server";
|
||||
String proxyJID = "proxy.xmpp-server";
|
||||
String proxyAddress = "127.0.0.1";
|
||||
String sessionID = "session_id";
|
||||
|
||||
Protocol protocol;
|
||||
|
||||
Connection connection;
|
||||
|
||||
/**
|
||||
* Initialize fields used in the tests.
|
||||
*/
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// build protocol verifier
|
||||
protocol = new Protocol();
|
||||
|
||||
// create mocked XMPP connection
|
||||
connection = ConnectionUtils.createMockedConnection(protocol, targetJID, xmppServer);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepting a SOCKS5 Bytestream request should fail if the request doesn't contain any Socks5
|
||||
* proxies.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailIfRequestHasNoStreamHosts() throws Exception {
|
||||
|
||||
try {
|
||||
|
||||
// build SOCKS5 Bytestream initialization request with no SOCKS5 proxies
|
||||
Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation(
|
||||
initiatorJID, targetJID, sessionID);
|
||||
|
||||
// get SOCKS5 Bytestream manager for connection
|
||||
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
|
||||
|
||||
// build SOCKS5 Bytestream request with the bytestream initialization
|
||||
Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(
|
||||
byteStreamManager, bytestreamInitialization);
|
||||
|
||||
// accept the stream (this is the call that is tested here)
|
||||
byteStreamRequest.accept();
|
||||
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
assertTrue(e.getMessage().contains("Could not establish socket with any provided host"));
|
||||
}
|
||||
|
||||
// verify targets response
|
||||
assertEquals(1, protocol.getRequests().size());
|
||||
Packet targetResponse = protocol.getRequests().remove(0);
|
||||
assertTrue(IQ.class.isInstance(targetResponse));
|
||||
assertEquals(initiatorJID, targetResponse.getTo());
|
||||
assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType());
|
||||
assertEquals(XMPPError.Condition.item_not_found.toString(),
|
||||
((IQ) targetResponse).getError().getCondition());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepting a SOCKS5 Bytestream request should fail if target is not able to connect to any of
|
||||
* the provided SOCKS5 proxies.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailIfRequestHasInvalidStreamHosts() throws Exception {
|
||||
|
||||
try {
|
||||
|
||||
// build SOCKS5 Bytestream initialization request
|
||||
Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation(
|
||||
initiatorJID, targetJID, sessionID);
|
||||
// add proxy that is not running
|
||||
bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7778);
|
||||
|
||||
// get SOCKS5 Bytestream manager for connection
|
||||
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
|
||||
|
||||
// build SOCKS5 Bytestream request with the bytestream initialization
|
||||
Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(
|
||||
byteStreamManager, bytestreamInitialization);
|
||||
|
||||
// accept the stream (this is the call that is tested here)
|
||||
byteStreamRequest.accept();
|
||||
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
assertTrue(e.getMessage().contains("Could not establish socket with any provided host"));
|
||||
}
|
||||
|
||||
// verify targets response
|
||||
assertEquals(1, protocol.getRequests().size());
|
||||
Packet targetResponse = protocol.getRequests().remove(0);
|
||||
assertTrue(IQ.class.isInstance(targetResponse));
|
||||
assertEquals(initiatorJID, targetResponse.getTo());
|
||||
assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType());
|
||||
assertEquals(XMPPError.Condition.item_not_found.toString(),
|
||||
((IQ) targetResponse).getError().getCondition());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Target should not try to connect to SOCKS5 proxies that already failed twice.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldBlacklistInvalidProxyAfter2Failures() throws Exception {
|
||||
|
||||
// build SOCKS5 Bytestream initialization request
|
||||
Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation(
|
||||
initiatorJID, targetJID, sessionID);
|
||||
bytestreamInitialization.addStreamHost("invalid." + proxyJID, "127.0.0.2", 7778);
|
||||
|
||||
// get SOCKS5 Bytestream manager for connection
|
||||
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
|
||||
|
||||
// try to connect several times
|
||||
for (int i = 0; i < 2; i++) {
|
||||
try {
|
||||
// build SOCKS5 Bytestream request with the bytestream initialization
|
||||
Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(
|
||||
byteStreamManager, bytestreamInitialization);
|
||||
|
||||
// set timeouts
|
||||
byteStreamRequest.setTotalConnectTimeout(600);
|
||||
byteStreamRequest.setMinimumConnectTimeout(300);
|
||||
|
||||
// accept the stream (this is the call that is tested here)
|
||||
byteStreamRequest.accept();
|
||||
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
assertTrue(e.getMessage().contains(
|
||||
"Could not establish socket with any provided host"));
|
||||
}
|
||||
|
||||
// verify targets response
|
||||
assertEquals(1, protocol.getRequests().size());
|
||||
Packet targetResponse = protocol.getRequests().remove(0);
|
||||
assertTrue(IQ.class.isInstance(targetResponse));
|
||||
assertEquals(initiatorJID, targetResponse.getTo());
|
||||
assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType());
|
||||
assertEquals(XMPPError.Condition.item_not_found.toString(),
|
||||
((IQ) targetResponse).getError().getCondition());
|
||||
}
|
||||
|
||||
// create test data for stream
|
||||
byte[] data = new byte[] { 1, 2, 3 };
|
||||
Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7779);
|
||||
|
||||
assertTrue(socks5Proxy.isRunning());
|
||||
|
||||
// add a valid SOCKS5 proxy
|
||||
bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7779);
|
||||
|
||||
// build SOCKS5 Bytestream request with the bytestream initialization
|
||||
Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager,
|
||||
bytestreamInitialization);
|
||||
|
||||
// set timeouts
|
||||
byteStreamRequest.setTotalConnectTimeout(600);
|
||||
byteStreamRequest.setMinimumConnectTimeout(300);
|
||||
|
||||
// accept the stream (this is the call that is tested here)
|
||||
InputStream inputStream = byteStreamRequest.accept().getInputStream();
|
||||
|
||||
// create digest to get the socket opened by target
|
||||
String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID);
|
||||
|
||||
// test stream by sending some data
|
||||
OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream();
|
||||
outputStream.write(data);
|
||||
|
||||
// verify that data is transferred correctly
|
||||
byte[] result = new byte[3];
|
||||
inputStream.read(result);
|
||||
assertArrayEquals(data, result);
|
||||
|
||||
// verify targets response
|
||||
assertEquals(1, protocol.getRequests().size());
|
||||
Packet targetResponse = protocol.getRequests().remove(0);
|
||||
assertEquals(Bytestream.class, targetResponse.getClass());
|
||||
assertEquals(initiatorJID, targetResponse.getTo());
|
||||
assertEquals(IQ.Type.RESULT, ((Bytestream) targetResponse).getType());
|
||||
assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Target should not not blacklist any SOCKS5 proxies regardless of failing connections.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotBlacklistInvalidProxy() throws Exception {
|
||||
|
||||
// disable blacklisting
|
||||
Socks5BytestreamRequest.setConnectFailureThreshold(0);
|
||||
|
||||
// build SOCKS5 Bytestream initialization request
|
||||
Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation(
|
||||
initiatorJID, targetJID, sessionID);
|
||||
bytestreamInitialization.addStreamHost("invalid." + proxyJID, "127.0.0.2", 7778);
|
||||
|
||||
// get SOCKS5 Bytestream manager for connection
|
||||
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
|
||||
|
||||
// try to connect several times
|
||||
for (int i = 0; i < 10; i++) {
|
||||
try {
|
||||
// build SOCKS5 Bytestream request with the bytestream initialization
|
||||
Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(
|
||||
byteStreamManager, bytestreamInitialization);
|
||||
|
||||
// set timeouts
|
||||
byteStreamRequest.setTotalConnectTimeout(600);
|
||||
byteStreamRequest.setMinimumConnectTimeout(300);
|
||||
|
||||
// accept the stream (this is the call that is tested here)
|
||||
byteStreamRequest.accept();
|
||||
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
assertTrue(e.getMessage().contains(
|
||||
"Could not establish socket with any provided host"));
|
||||
}
|
||||
|
||||
// verify targets response
|
||||
assertEquals(1, protocol.getRequests().size());
|
||||
Packet targetResponse = protocol.getRequests().remove(0);
|
||||
assertTrue(IQ.class.isInstance(targetResponse));
|
||||
assertEquals(initiatorJID, targetResponse.getTo());
|
||||
assertEquals(IQ.Type.ERROR, ((IQ) targetResponse).getType());
|
||||
assertEquals(XMPPError.Condition.item_not_found.toString(),
|
||||
((IQ) targetResponse).getError().getCondition());
|
||||
}
|
||||
|
||||
// enable blacklisting
|
||||
Socks5BytestreamRequest.setConnectFailureThreshold(2);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the SOCKS5 Bytestream request contains multiple SOCKS5 proxies and the first one doesn't
|
||||
* respond, the connection attempt to this proxy should not consume the whole timeout for
|
||||
* connecting to the proxies.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotTimeoutIfFirstSocks5ProxyDoesNotRespond() throws Exception {
|
||||
|
||||
// start a local SOCKS5 proxy
|
||||
Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778);
|
||||
|
||||
// create a fake SOCKS5 proxy that doesn't respond to a request
|
||||
ServerSocket serverSocket = new ServerSocket(7779);
|
||||
|
||||
// build SOCKS5 Bytestream initialization request
|
||||
Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation(
|
||||
initiatorJID, targetJID, sessionID);
|
||||
bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7779);
|
||||
bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7778);
|
||||
|
||||
// create test data for stream
|
||||
byte[] data = new byte[] { 1, 2, 3 };
|
||||
|
||||
// get SOCKS5 Bytestream manager for connection
|
||||
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
|
||||
|
||||
// build SOCKS5 Bytestream request with the bytestream initialization
|
||||
Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager,
|
||||
bytestreamInitialization);
|
||||
|
||||
// set timeouts
|
||||
byteStreamRequest.setTotalConnectTimeout(2000);
|
||||
byteStreamRequest.setMinimumConnectTimeout(1000);
|
||||
|
||||
// accept the stream (this is the call that is tested here)
|
||||
InputStream inputStream = byteStreamRequest.accept().getInputStream();
|
||||
|
||||
// assert that client tries to connect to dumb SOCKS5 proxy
|
||||
Socket socket = serverSocket.accept();
|
||||
assertNotNull(socket);
|
||||
|
||||
// create digest to get the socket opened by target
|
||||
String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID);
|
||||
|
||||
// test stream by sending some data
|
||||
OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream();
|
||||
outputStream.write(data);
|
||||
|
||||
// verify that data is transferred correctly
|
||||
byte[] result = new byte[3];
|
||||
inputStream.read(result);
|
||||
assertArrayEquals(data, result);
|
||||
|
||||
// verify targets response
|
||||
assertEquals(1, protocol.getRequests().size());
|
||||
Packet targetResponse = protocol.getRequests().remove(0);
|
||||
assertEquals(Bytestream.class, targetResponse.getClass());
|
||||
assertEquals(initiatorJID, targetResponse.getTo());
|
||||
assertEquals(IQ.Type.RESULT, ((Bytestream) targetResponse).getType());
|
||||
assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID());
|
||||
|
||||
serverSocket.close();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepting the SOCKS5 Bytestream request should be successfully.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldAcceptSocks5BytestreamRequestAndReceiveData() throws Exception {
|
||||
|
||||
// start a local SOCKS5 proxy
|
||||
Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778);
|
||||
|
||||
// build SOCKS5 Bytestream initialization request
|
||||
Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation(
|
||||
initiatorJID, targetJID, sessionID);
|
||||
bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7778);
|
||||
|
||||
// create test data for stream
|
||||
byte[] data = new byte[] { 1, 2, 3 };
|
||||
|
||||
// get SOCKS5 Bytestream manager for connection
|
||||
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
|
||||
|
||||
// build SOCKS5 Bytestream request with the bytestream initialization
|
||||
Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager,
|
||||
bytestreamInitialization);
|
||||
|
||||
// accept the stream (this is the call that is tested here)
|
||||
InputStream inputStream = byteStreamRequest.accept().getInputStream();
|
||||
|
||||
// create digest to get the socket opened by target
|
||||
String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID);
|
||||
|
||||
// test stream by sending some data
|
||||
OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream();
|
||||
outputStream.write(data);
|
||||
|
||||
// verify that data is transferred correctly
|
||||
byte[] result = new byte[3];
|
||||
inputStream.read(result);
|
||||
assertArrayEquals(data, result);
|
||||
|
||||
// verify targets response
|
||||
assertEquals(1, protocol.getRequests().size());
|
||||
Packet targetResponse = protocol.getRequests().remove(0);
|
||||
assertEquals(Bytestream.class, targetResponse.getClass());
|
||||
assertEquals(initiatorJID, targetResponse.getTo());
|
||||
assertEquals(IQ.Type.RESULT, ((Bytestream) targetResponse).getType());
|
||||
assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop eventually started local SOCKS5 test proxy.
|
||||
*/
|
||||
@After
|
||||
public void cleanUp() {
|
||||
Socks5TestProxy.stopProxy();
|
||||
SmackConfiguration.setLocalSocks5ProxyEnabled(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.socks5bytestream;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.SmackConfiguration;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smack.packet.IQ.Type;
|
||||
import org.jivesoftware.smackx.socks5bytestream.packet.Bytestream;
|
||||
import org.jivesoftware.smackx.socks5bytestream.packet.Bytestream.StreamHost;
|
||||
import org.jivesoftware.util.ConnectionUtils;
|
||||
import org.jivesoftware.util.Protocol;
|
||||
import org.jivesoftware.util.Verification;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test for Socks5ClientForInitiator class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class Socks5ClientForInitiatorTest {
|
||||
|
||||
// settings
|
||||
String initiatorJID = "initiator@xmpp-server/Smack";
|
||||
String targetJID = "target@xmpp-server/Smack";
|
||||
String xmppServer = "xmpp-server";
|
||||
String proxyJID = "proxy.xmpp-server";
|
||||
String proxyAddress = "127.0.0.1";
|
||||
int proxyPort = 7890;
|
||||
String sessionID = "session_id";
|
||||
|
||||
// protocol verifier
|
||||
Protocol protocol;
|
||||
|
||||
// mocked XMPP connection
|
||||
Connection connection;
|
||||
|
||||
/**
|
||||
* Initialize fields used in the tests.
|
||||
*/
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
// build protocol verifier
|
||||
protocol = new Protocol();
|
||||
|
||||
// create mocked XMPP connection
|
||||
connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the target is not connected to the local SOCKS5 proxy an exception should be thrown.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailIfTargetIsNotConnectedToLocalSocks5Proxy() throws Exception {
|
||||
|
||||
// start a local SOCKS5 proxy
|
||||
SmackConfiguration.setLocalSocks5ProxyPort(proxyPort);
|
||||
Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
|
||||
socks5Proxy.start();
|
||||
|
||||
// build stream host information for local SOCKS5 proxy
|
||||
StreamHost streamHost = new StreamHost(connection.getUser(),
|
||||
socks5Proxy.getLocalAddresses().get(0));
|
||||
streamHost.setPort(socks5Proxy.getPort());
|
||||
|
||||
// create digest to get the socket opened by target
|
||||
String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID);
|
||||
|
||||
Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest,
|
||||
connection, sessionID, targetJID);
|
||||
|
||||
try {
|
||||
socks5Client.getSocket(10000);
|
||||
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
assertTrue(e.getMessage().contains("target is not connected to SOCKS5 proxy"));
|
||||
protocol.verifyAll(); // assert no XMPP messages were sent
|
||||
}
|
||||
|
||||
socks5Proxy.stop();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiator and target should successfully connect to the local SOCKS5 proxy.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSuccessfullyConnectThroughLocalSocks5Proxy() throws Exception {
|
||||
|
||||
// start a local SOCKS5 proxy
|
||||
SmackConfiguration.setLocalSocks5ProxyPort(proxyPort);
|
||||
Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
|
||||
socks5Proxy.start();
|
||||
|
||||
// test data
|
||||
final byte[] data = new byte[] { 1, 2, 3 };
|
||||
|
||||
// create digest
|
||||
final String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID);
|
||||
|
||||
// allow connection of target with this digest
|
||||
socks5Proxy.addTransfer(digest);
|
||||
|
||||
// build stream host information
|
||||
final StreamHost streamHost = new StreamHost(connection.getUser(),
|
||||
socks5Proxy.getLocalAddresses().get(0));
|
||||
streamHost.setPort(socks5Proxy.getPort());
|
||||
|
||||
// target connects to local SOCKS5 proxy
|
||||
Thread targetThread = new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socks5Client targetClient = new Socks5Client(streamHost, digest);
|
||||
Socket socket = targetClient.getSocket(10000);
|
||||
socket.getOutputStream().write(data);
|
||||
}
|
||||
catch (Exception e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
targetThread.start();
|
||||
|
||||
Thread.sleep(200);
|
||||
|
||||
// initiator connects
|
||||
Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest,
|
||||
connection, sessionID, targetJID);
|
||||
|
||||
Socket socket = socks5Client.getSocket(10000);
|
||||
|
||||
// verify test data
|
||||
InputStream in = socket.getInputStream();
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
assertEquals(data[i], in.read());
|
||||
}
|
||||
|
||||
targetThread.join();
|
||||
|
||||
protocol.verifyAll(); // assert no XMPP messages were sent
|
||||
|
||||
socks5Proxy.removeTransfer(digest);
|
||||
socks5Proxy.stop();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the initiator can connect to a SOCKS5 proxy but activating the stream fails an exception
|
||||
* should be thrown.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailIfActivateSocks5ProxyFails() throws Exception {
|
||||
|
||||
// build error response as reply to the stream activation
|
||||
XMPPError xmppError = new XMPPError(XMPPError.Condition.interna_server_error);
|
||||
IQ error = new IQ() {
|
||||
|
||||
public String getChildElementXML() {
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
error.setType(Type.ERROR);
|
||||
error.setFrom(proxyJID);
|
||||
error.setTo(initiatorJID);
|
||||
error.setError(xmppError);
|
||||
|
||||
protocol.addResponse(error, Verification.correspondingSenderReceiver,
|
||||
Verification.requestTypeSET);
|
||||
|
||||
// start a local SOCKS5 proxy
|
||||
Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort);
|
||||
socks5Proxy.start();
|
||||
|
||||
StreamHost streamHost = new StreamHost(proxyJID, socks5Proxy.getAddress());
|
||||
streamHost.setPort(socks5Proxy.getPort());
|
||||
|
||||
// create digest to get the socket opened by target
|
||||
String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID);
|
||||
|
||||
Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest,
|
||||
connection, sessionID, targetJID);
|
||||
|
||||
try {
|
||||
|
||||
socks5Client.getSocket(10000);
|
||||
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
assertTrue(e.getMessage().contains("activating SOCKS5 Bytestream failed"));
|
||||
protocol.verifyAll();
|
||||
}
|
||||
|
||||
socks5Proxy.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Target and initiator should successfully connect to a "remote" SOCKS5 proxy and the initiator
|
||||
* activates the bytestream.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSuccessfullyEstablishConnectionAndActivateSocks5Proxy() throws Exception {
|
||||
|
||||
// build activation confirmation response
|
||||
IQ activationResponse = new IQ() {
|
||||
|
||||
@Override
|
||||
public String getChildElementXML() {
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
activationResponse.setFrom(proxyJID);
|
||||
activationResponse.setTo(initiatorJID);
|
||||
activationResponse.setType(IQ.Type.RESULT);
|
||||
|
||||
protocol.addResponse(activationResponse, Verification.correspondingSenderReceiver,
|
||||
Verification.requestTypeSET, new Verification<Bytestream, IQ>() {
|
||||
|
||||
public void verify(Bytestream request, IQ response) {
|
||||
// verify that the correct stream should be activated
|
||||
assertNotNull(request.getToActivate());
|
||||
assertEquals(targetJID, request.getToActivate().getTarget());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// start a local SOCKS5 proxy
|
||||
Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort);
|
||||
socks5Proxy.start();
|
||||
|
||||
StreamHost streamHost = new StreamHost(proxyJID, socks5Proxy.getAddress());
|
||||
streamHost.setPort(socks5Proxy.getPort());
|
||||
|
||||
// create digest to get the socket opened by target
|
||||
String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID);
|
||||
|
||||
Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest,
|
||||
connection, sessionID, targetJID);
|
||||
|
||||
Socket initiatorSocket = socks5Client.getSocket(10000);
|
||||
InputStream in = initiatorSocket.getInputStream();
|
||||
|
||||
Socket targetSocket = socks5Proxy.getSocket(digest);
|
||||
OutputStream out = targetSocket.getOutputStream();
|
||||
|
||||
// verify test data
|
||||
for (int i = 0; i < 10; i++) {
|
||||
out.write(i);
|
||||
assertEquals(i, in.read());
|
||||
}
|
||||
|
||||
protocol.verifyAll();
|
||||
|
||||
initiatorSocket.close();
|
||||
targetSocket.close();
|
||||
socks5Proxy.stop();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset default port for local SOCKS5 proxy.
|
||||
*/
|
||||
@After
|
||||
public void cleanup() {
|
||||
SmackConfiguration.setLocalSocks5ProxyPort(7777);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,330 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.socks5bytestream;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smackx.socks5bytestream.packet.Bytestream.StreamHost;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test for Socks5Client class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class Socks5ClientTest {
|
||||
|
||||
// settings
|
||||
private String serverAddress = "127.0.0.1";
|
||||
private int serverPort = 7890;
|
||||
private String proxyJID = "proxy.xmpp-server";
|
||||
private String digest = "digest";
|
||||
private ServerSocket serverSocket;
|
||||
|
||||
/**
|
||||
* Initialize fields used in the tests.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
// create SOCKS5 proxy server socket
|
||||
serverSocket = new ServerSocket(serverPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* A SOCKS5 client MUST close connection if server doesn't accept any of the given
|
||||
* authentication methods. (See RFC1928 Section 3)
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldCloseSocketIfServerDoesNotAcceptAuthenticationMethod() throws Exception {
|
||||
|
||||
// start thread to connect to SOCKS5 proxy
|
||||
Thread serverThread = new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
StreamHost streamHost = new StreamHost(proxyJID, serverAddress);
|
||||
streamHost.setPort(serverPort);
|
||||
|
||||
Socks5Client socks5Client = new Socks5Client(streamHost, digest);
|
||||
|
||||
try {
|
||||
|
||||
socks5Client.getSocket(10000);
|
||||
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
assertTrue(e.getMessage().contains(
|
||||
"establishing connection to SOCKS5 proxy failed"));
|
||||
}
|
||||
catch (Exception e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
serverThread.start();
|
||||
|
||||
// accept connection form client
|
||||
Socket socket = serverSocket.accept();
|
||||
DataInputStream in = new DataInputStream(socket.getInputStream());
|
||||
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
|
||||
|
||||
// validate authentication request
|
||||
assertEquals((byte) 0x05, (byte) in.read()); // version
|
||||
assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods
|
||||
assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method
|
||||
|
||||
// respond that no authentication method is accepted
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0xFF });
|
||||
out.flush();
|
||||
|
||||
// wait for client to shutdown
|
||||
serverThread.join();
|
||||
|
||||
// assert socket is closed
|
||||
assertEquals(-1, in.read());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The SOCKS5 client should close connection if server replies in an unsupported way.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldCloseSocketIfServerRepliesInUnsupportedWay() throws Exception {
|
||||
|
||||
// start thread to connect to SOCKS5 proxy
|
||||
Thread serverThread = new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
StreamHost streamHost = new StreamHost(proxyJID, serverAddress);
|
||||
streamHost.setPort(serverPort);
|
||||
|
||||
Socks5Client socks5Client = new Socks5Client(streamHost, digest);
|
||||
try {
|
||||
socks5Client.getSocket(10000);
|
||||
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
assertTrue(e.getMessage().contains(
|
||||
"establishing connection to SOCKS5 proxy failed"));
|
||||
}
|
||||
catch (Exception e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
serverThread.start();
|
||||
|
||||
// accept connection from client
|
||||
Socket socket = serverSocket.accept();
|
||||
DataInputStream in = new DataInputStream(socket.getInputStream());
|
||||
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
|
||||
|
||||
// validate authentication request
|
||||
assertEquals((byte) 0x05, (byte) in.read()); // version
|
||||
assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods
|
||||
assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method
|
||||
|
||||
// respond that no no-authentication method is used
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0x00 });
|
||||
out.flush();
|
||||
|
||||
Socks5Utils.receiveSocks5Message(in);
|
||||
|
||||
// reply with unsupported address type
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00 });
|
||||
out.flush();
|
||||
|
||||
// wait for client to shutdown
|
||||
serverThread.join();
|
||||
|
||||
// assert socket is closed
|
||||
assertEquals(-1, in.read());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The SOCKS5 client should close connection if server replies with an error.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldCloseSocketIfServerRepliesWithError() throws Exception {
|
||||
|
||||
// start thread to connect to SOCKS5 proxy
|
||||
Thread serverThread = new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
StreamHost streamHost = new StreamHost(proxyJID, serverAddress);
|
||||
streamHost.setPort(serverPort);
|
||||
|
||||
Socks5Client socks5Client = new Socks5Client(streamHost, digest);
|
||||
try {
|
||||
socks5Client.getSocket(10000);
|
||||
|
||||
fail("exception should be thrown");
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
assertTrue(e.getMessage().contains(
|
||||
"establishing connection to SOCKS5 proxy failed"));
|
||||
}
|
||||
catch (Exception e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
serverThread.start();
|
||||
|
||||
Socket socket = serverSocket.accept();
|
||||
DataInputStream in = new DataInputStream(socket.getInputStream());
|
||||
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
|
||||
|
||||
// validate authentication request
|
||||
assertEquals((byte) 0x05, (byte) in.read()); // version
|
||||
assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods
|
||||
assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method
|
||||
|
||||
// respond that no no-authentication method is used
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0x00 });
|
||||
out.flush();
|
||||
|
||||
Socks5Utils.receiveSocks5Message(in);
|
||||
|
||||
// reply with full SOCKS5 message with an error code (01 = general SOCKS server
|
||||
// failure)
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00, (byte) 0x03 });
|
||||
byte[] address = digest.getBytes();
|
||||
out.write(address.length);
|
||||
out.write(address);
|
||||
out.write(new byte[] { (byte) 0x00, (byte) 0x00 });
|
||||
out.flush();
|
||||
|
||||
// wait for client to shutdown
|
||||
serverThread.join();
|
||||
|
||||
// assert socket is closed
|
||||
assertEquals(-1, in.read());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The SOCKS5 client should successfully connect to the SOCKS5 server
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSuccessfullyConnectToSocks5Server() throws Exception {
|
||||
|
||||
// start thread to connect to SOCKS5 proxy
|
||||
Thread serverThread = new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
StreamHost streamHost = new StreamHost(proxyJID, serverAddress);
|
||||
streamHost.setPort(serverPort);
|
||||
|
||||
Socks5Client socks5Client = new Socks5Client(streamHost, digest);
|
||||
|
||||
try {
|
||||
Socket socket = socks5Client.getSocket(10000);
|
||||
assertNotNull(socket);
|
||||
socket.getOutputStream().write(123);
|
||||
socket.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
serverThread.start();
|
||||
|
||||
Socket socket = serverSocket.accept();
|
||||
DataInputStream in = new DataInputStream(socket.getInputStream());
|
||||
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
|
||||
|
||||
// validate authentication request
|
||||
assertEquals((byte) 0x05, (byte) in.read()); // version
|
||||
assertEquals((byte) 0x01, (byte) in.read()); // number of supported auth methods
|
||||
assertEquals((byte) 0x00, (byte) in.read()); // no-authentication method
|
||||
|
||||
// respond that no no-authentication method is used
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0x00 });
|
||||
out.flush();
|
||||
|
||||
byte[] address = digest.getBytes();
|
||||
|
||||
assertEquals((byte) 0x05, (byte) in.read()); // version
|
||||
assertEquals((byte) 0x01, (byte) in.read()); // connect request
|
||||
assertEquals((byte) 0x00, (byte) in.read()); // reserved byte (always 0)
|
||||
assertEquals((byte) 0x03, (byte) in.read()); // address type (domain)
|
||||
assertEquals(address.length, (byte) in.read()); // address length
|
||||
for (int i = 0; i < address.length; i++) {
|
||||
assertEquals(address[i], (byte) in.read()); // address
|
||||
}
|
||||
assertEquals((byte) 0x00, (byte) in.read()); // port
|
||||
assertEquals((byte) 0x00, (byte) in.read());
|
||||
|
||||
// reply with success SOCKS5 message
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03 });
|
||||
out.write(address.length);
|
||||
out.write(address);
|
||||
out.write(new byte[] { (byte) 0x00, (byte) 0x00 });
|
||||
out.flush();
|
||||
|
||||
// wait for client to shutdown
|
||||
serverThread.join();
|
||||
|
||||
// verify data sent from client
|
||||
assertEquals(123, in.read());
|
||||
|
||||
// assert socket is closed
|
||||
assertEquals(-1, in.read());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close fake SOCKS5 proxy.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@After
|
||||
public void cleanup() throws Exception {
|
||||
serverSocket.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.socks5bytestream;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smackx.packet.DiscoverInfo;
|
||||
import org.jivesoftware.smackx.packet.DiscoverItems;
|
||||
import org.jivesoftware.smackx.socks5bytestream.packet.Bytestream;
|
||||
|
||||
/**
|
||||
* A collection of utility methods to create XMPP packets.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class Socks5PacketUtils {
|
||||
|
||||
/**
|
||||
* Returns a SOCKS5 Bytestream initialization request packet. The Request doesn't contain any
|
||||
* SOCKS5 proxies.
|
||||
*
|
||||
* @param from the initiator
|
||||
* @param to the target
|
||||
* @param sessionID the session ID
|
||||
* @return SOCKS5 Bytestream initialization request packet
|
||||
*/
|
||||
public static Bytestream createBytestreamInitiation(String from, String to, String sessionID) {
|
||||
Bytestream bytestream = new Bytestream();
|
||||
bytestream.getPacketID();
|
||||
bytestream.setFrom(from);
|
||||
bytestream.setTo(to);
|
||||
bytestream.setSessionID(sessionID);
|
||||
bytestream.setType(IQ.Type.SET);
|
||||
return bytestream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a response to a SOCKS5 Bytestream initialization request. The packet doesn't contain
|
||||
* the uses-host information.
|
||||
*
|
||||
* @param from the target
|
||||
* @param to the initiator
|
||||
* @return response to a SOCKS5 Bytestream initialization request
|
||||
*/
|
||||
public static Bytestream createBytestreamResponse(String from, String to) {
|
||||
Bytestream streamHostInfo = new Bytestream();
|
||||
streamHostInfo.getPacketID();
|
||||
streamHostInfo.setFrom(from);
|
||||
streamHostInfo.setTo(to);
|
||||
streamHostInfo.setType(IQ.Type.RESULT);
|
||||
return streamHostInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a response to an item discovery request. The packet doesn't contain any items.
|
||||
*
|
||||
* @param from the XMPP server
|
||||
* @param to the XMPP client
|
||||
* @return response to an item discovery request
|
||||
*/
|
||||
public static DiscoverItems createDiscoverItems(String from, String to) {
|
||||
DiscoverItems discoverItems = new DiscoverItems();
|
||||
discoverItems.getPacketID();
|
||||
discoverItems.setFrom(from);
|
||||
discoverItems.setTo(to);
|
||||
discoverItems.setType(IQ.Type.RESULT);
|
||||
return discoverItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a response to an info discovery request. The packet doesn't contain any infos.
|
||||
*
|
||||
* @param from the target
|
||||
* @param to the initiator
|
||||
* @return response to an info discovery request
|
||||
*/
|
||||
public static DiscoverInfo createDiscoverInfo(String from, String to) {
|
||||
DiscoverInfo discoverInfo = new DiscoverInfo();
|
||||
discoverInfo.getPacketID();
|
||||
discoverInfo.setFrom(from);
|
||||
discoverInfo.setTo(to);
|
||||
discoverInfo.setType(IQ.Type.RESULT);
|
||||
return discoverInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a response IQ for a activation request to the proxy.
|
||||
*
|
||||
* @param from JID of the proxy
|
||||
* @param to JID of the client who wants to activate the SOCKS5 Bytestream
|
||||
* @return response IQ for a activation request to the proxy
|
||||
*/
|
||||
public static IQ createActivationConfirmation(String from, String to) {
|
||||
IQ response = new IQ() {
|
||||
|
||||
@Override
|
||||
public String getChildElementXML() {
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
response.getPacketID();
|
||||
response.setFrom(from);
|
||||
response.setTo(to);
|
||||
response.setType(IQ.Type.RESULT);
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,359 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.socks5bytestream;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jivesoftware.smack.SmackConfiguration;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test for Socks5Proxy class.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class Socks5ProxyTest {
|
||||
|
||||
/**
|
||||
* The SOCKS5 proxy should be a singleton used by all XMPP connections
|
||||
*/
|
||||
@Test
|
||||
public void shouldBeASingleton() {
|
||||
SmackConfiguration.setLocalSocks5ProxyEnabled(false);
|
||||
|
||||
Socks5Proxy proxy1 = Socks5Proxy.getSocks5Proxy();
|
||||
Socks5Proxy proxy2 = Socks5Proxy.getSocks5Proxy();
|
||||
|
||||
assertNotNull(proxy1);
|
||||
assertNotNull(proxy2);
|
||||
assertSame(proxy1, proxy2);
|
||||
}
|
||||
|
||||
/**
|
||||
* The SOCKS5 proxy should not be started if disabled by configuration.
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotBeRunningIfDisabled() {
|
||||
SmackConfiguration.setLocalSocks5ProxyEnabled(false);
|
||||
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||
assertFalse(proxy.isRunning());
|
||||
}
|
||||
|
||||
/**
|
||||
* The SOCKS5 proxy should use a free port above the one configured.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseFreePortOnNegativeValues() throws Exception {
|
||||
SmackConfiguration.setLocalSocks5ProxyEnabled(false);
|
||||
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||
assertFalse(proxy.isRunning());
|
||||
|
||||
ServerSocket serverSocket = new ServerSocket(0);
|
||||
SmackConfiguration.setLocalSocks5ProxyPort(-serverSocket.getLocalPort());
|
||||
|
||||
proxy.start();
|
||||
|
||||
assertTrue(proxy.isRunning());
|
||||
|
||||
serverSocket.close();
|
||||
|
||||
assertTrue(proxy.getPort() > serverSocket.getLocalPort());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* When inserting new network addresses to the proxy the order should remain in the order they
|
||||
* were inserted.
|
||||
*/
|
||||
@Test
|
||||
public void shouldPreserveAddressOrderOnInsertions() {
|
||||
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||
List<String> addresses = new ArrayList<String>(proxy.getLocalAddresses());
|
||||
addresses.add("1");
|
||||
addresses.add("2");
|
||||
addresses.add("3");
|
||||
for (String address : addresses) {
|
||||
proxy.addLocalAddress(address);
|
||||
}
|
||||
|
||||
List<String> localAddresses = proxy.getLocalAddresses();
|
||||
for (int i = 0; i < addresses.size(); i++) {
|
||||
assertEquals(addresses.get(i), localAddresses.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When replacing network addresses of the proxy the order should remain in the order if the
|
||||
* given list.
|
||||
*/
|
||||
@Test
|
||||
public void shouldPreserveAddressOrderOnReplace() {
|
||||
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||
List<String> addresses = new ArrayList<String>(proxy.getLocalAddresses());
|
||||
addresses.add("1");
|
||||
addresses.add("2");
|
||||
addresses.add("3");
|
||||
|
||||
proxy.replaceLocalAddresses(addresses);
|
||||
|
||||
List<String> localAddresses = proxy.getLocalAddresses();
|
||||
for (int i = 0; i < addresses.size(); i++) {
|
||||
assertEquals(addresses.get(i), localAddresses.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserting the same address multiple times should not cause the proxy to return this address
|
||||
* multiple times.
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotReturnMultipleSameAddress() {
|
||||
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||
|
||||
proxy.addLocalAddress("same");
|
||||
proxy.addLocalAddress("same");
|
||||
proxy.addLocalAddress("same");
|
||||
|
||||
assertEquals(2, proxy.getLocalAddresses().size());
|
||||
}
|
||||
|
||||
/**
|
||||
* There should be only one thread executing the SOCKS5 proxy process.
|
||||
*/
|
||||
@Test
|
||||
public void shouldOnlyStartOneServerThread() {
|
||||
int threadCount = Thread.activeCount();
|
||||
|
||||
SmackConfiguration.setLocalSocks5ProxyPort(7890);
|
||||
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||
proxy.start();
|
||||
|
||||
assertTrue(proxy.isRunning());
|
||||
assertEquals(threadCount + 1, Thread.activeCount());
|
||||
|
||||
proxy.start();
|
||||
|
||||
assertTrue(proxy.isRunning());
|
||||
assertEquals(threadCount + 1, Thread.activeCount());
|
||||
|
||||
proxy.stop();
|
||||
|
||||
assertFalse(proxy.isRunning());
|
||||
assertEquals(threadCount, Thread.activeCount());
|
||||
|
||||
proxy.start();
|
||||
|
||||
assertTrue(proxy.isRunning());
|
||||
assertEquals(threadCount + 1, Thread.activeCount());
|
||||
|
||||
proxy.stop();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the SOCKS5 proxy accepts a connection that is not a SOCKS5 connection it should close the
|
||||
* corresponding socket.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldCloseSocketIfNoSocks5Request() throws Exception {
|
||||
SmackConfiguration.setLocalSocks5ProxyPort(7890);
|
||||
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||
proxy.start();
|
||||
|
||||
Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort());
|
||||
|
||||
OutputStream out = socket.getOutputStream();
|
||||
out.write(new byte[] { 1, 2, 3 });
|
||||
|
||||
assertEquals(-1, socket.getInputStream().read());
|
||||
|
||||
proxy.stop();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The SOCKS5 proxy should reply with an error message if no supported authentication methods
|
||||
* are given in the SOCKS5 request.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldRespondWithErrorIfNoSupportedAuthenticationMethod() throws Exception {
|
||||
SmackConfiguration.setLocalSocks5ProxyPort(7890);
|
||||
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||
proxy.start();
|
||||
|
||||
Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort());
|
||||
|
||||
OutputStream out = socket.getOutputStream();
|
||||
|
||||
// request username/password-authentication
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x02 });
|
||||
|
||||
InputStream in = socket.getInputStream();
|
||||
|
||||
assertEquals((byte) 0x05, (byte) in.read());
|
||||
assertEquals((byte) 0xFF, (byte) in.read());
|
||||
|
||||
assertEquals(-1, in.read());
|
||||
|
||||
proxy.stop();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The SOCKS5 proxy should respond with an error message if the client is not allowed to connect
|
||||
* with the proxy.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldRespondWithErrorIfConnectionIsNotAllowed() throws Exception {
|
||||
SmackConfiguration.setLocalSocks5ProxyPort(7890);
|
||||
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||
proxy.start();
|
||||
|
||||
Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort());
|
||||
|
||||
OutputStream out = socket.getOutputStream();
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00 });
|
||||
|
||||
InputStream in = socket.getInputStream();
|
||||
|
||||
assertEquals((byte) 0x05, (byte) in.read());
|
||||
assertEquals((byte) 0x00, (byte) in.read());
|
||||
|
||||
// send valid SOCKS5 message
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01,
|
||||
(byte) 0xAA, (byte) 0x00, (byte) 0x00 });
|
||||
|
||||
// verify error message
|
||||
assertEquals((byte) 0x05, (byte) in.read());
|
||||
assertFalse((byte) 0x00 == (byte) in.read()); // something other than 0 == success
|
||||
assertEquals((byte) 0x00, (byte) in.read());
|
||||
assertEquals((byte) 0x03, (byte) in.read());
|
||||
assertEquals((byte) 0x01, (byte) in.read());
|
||||
assertEquals((byte) 0xAA, (byte) in.read());
|
||||
assertEquals((byte) 0x00, (byte) in.read());
|
||||
assertEquals((byte) 0x00, (byte) in.read());
|
||||
|
||||
assertEquals(-1, in.read());
|
||||
|
||||
proxy.stop();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A Client should successfully establish a connection to the SOCKS5 proxy.
|
||||
*
|
||||
* @throws Exception should not happen
|
||||
*/
|
||||
@Test
|
||||
public void shouldSuccessfullyEstablishConnection() throws Exception {
|
||||
SmackConfiguration.setLocalSocks5ProxyPort(7890);
|
||||
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||
proxy.start();
|
||||
|
||||
assertTrue(proxy.isRunning());
|
||||
|
||||
String digest = new String(new byte[] { (byte) 0xAA });
|
||||
|
||||
// add digest to allow connection
|
||||
proxy.addTransfer(digest);
|
||||
|
||||
Socket socket = new Socket(proxy.getLocalAddresses().get(0), proxy.getPort());
|
||||
|
||||
OutputStream out = socket.getOutputStream();
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00 });
|
||||
|
||||
InputStream in = socket.getInputStream();
|
||||
|
||||
assertEquals((byte) 0x05, (byte) in.read());
|
||||
assertEquals((byte) 0x00, (byte) in.read());
|
||||
|
||||
// send valid SOCKS5 message
|
||||
out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01,
|
||||
(byte) 0xAA, (byte) 0x00, (byte) 0x00 });
|
||||
|
||||
// verify response
|
||||
assertEquals((byte) 0x05, (byte) in.read());
|
||||
assertEquals((byte) 0x00, (byte) in.read()); // success
|
||||
assertEquals((byte) 0x00, (byte) in.read());
|
||||
assertEquals((byte) 0x03, (byte) in.read());
|
||||
assertEquals((byte) 0x01, (byte) in.read());
|
||||
assertEquals((byte) 0xAA, (byte) in.read());
|
||||
assertEquals((byte) 0x00, (byte) in.read());
|
||||
assertEquals((byte) 0x00, (byte) in.read());
|
||||
|
||||
Thread.sleep(200);
|
||||
|
||||
Socket remoteSocket = proxy.getSocket(digest);
|
||||
|
||||
// remove digest
|
||||
proxy.removeTransfer(digest);
|
||||
|
||||
// test stream
|
||||
OutputStream remoteOut = remoteSocket.getOutputStream();
|
||||
byte[] data = new byte[] { 1, 2, 3, 4, 5 };
|
||||
remoteOut.write(data);
|
||||
remoteOut.flush();
|
||||
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
assertEquals(data[i], in.read());
|
||||
}
|
||||
|
||||
remoteSocket.close();
|
||||
|
||||
assertEquals(-1, in.read());
|
||||
|
||||
proxy.stop();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset SOCKS5 proxy settings.
|
||||
*/
|
||||
@After
|
||||
public void cleanup() {
|
||||
SmackConfiguration.setLocalSocks5ProxyEnabled(true);
|
||||
SmackConfiguration.setLocalSocks5ProxyPort(7777);
|
||||
Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
|
||||
try {
|
||||
String address = InetAddress.getLocalHost().getHostAddress();
|
||||
List<String> addresses = new ArrayList<String>();
|
||||
addresses.add(address);
|
||||
socks5Proxy.replaceLocalAddresses(addresses);
|
||||
}
|
||||
catch (UnknownHostException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
socks5Proxy.stop();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,285 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.socks5bytestream;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
|
||||
/**
|
||||
* Simple SOCKS5 proxy for testing purposes. It is almost the same as the Socks5Proxy class but the
|
||||
* port can be configured more easy and it all connections are allowed.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class Socks5TestProxy {
|
||||
|
||||
/* SOCKS5 proxy singleton */
|
||||
private static Socks5TestProxy socks5Server;
|
||||
|
||||
/* reusable implementation of a SOCKS5 proxy server process */
|
||||
private Socks5ServerProcess serverProcess;
|
||||
|
||||
/* thread running the SOCKS5 server process */
|
||||
private Thread serverThread;
|
||||
|
||||
/* server socket to accept SOCKS5 connections */
|
||||
private ServerSocket serverSocket;
|
||||
|
||||
/* assigns a connection to a digest */
|
||||
private final Map<String, Socket> connectionMap = new ConcurrentHashMap<String, Socket>();
|
||||
|
||||
/* port of the test proxy */
|
||||
private int port = 7777;
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*/
|
||||
private Socks5TestProxy(int port) {
|
||||
this.serverProcess = new Socks5ServerProcess();
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local SOCKS5 proxy server
|
||||
*
|
||||
* @param port of the test proxy
|
||||
* @return the local SOCKS5 proxy server
|
||||
*/
|
||||
public static synchronized Socks5TestProxy getProxy(int port) {
|
||||
if (socks5Server == null) {
|
||||
socks5Server = new Socks5TestProxy(port);
|
||||
socks5Server.start();
|
||||
}
|
||||
return socks5Server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the test proxy
|
||||
*/
|
||||
public static synchronized void stopProxy() {
|
||||
if (socks5Server != null) {
|
||||
socks5Server.stop();
|
||||
socks5Server = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the local SOCKS5 proxy server. If it is already running, this method does nothing.
|
||||
*/
|
||||
public synchronized void start() {
|
||||
if (isRunning()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.serverSocket = new ServerSocket(this.port);
|
||||
this.serverThread = new Thread(this.serverProcess);
|
||||
this.serverThread.start();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the local SOCKS5 proxy server. If it is not running this method does nothing.
|
||||
*/
|
||||
public synchronized void stop() {
|
||||
if (!isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.serverSocket.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// do nothing
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (this.serverThread != null && this.serverThread.isAlive()) {
|
||||
try {
|
||||
this.serverThread.interrupt();
|
||||
this.serverThread.join();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// do nothing
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
this.serverThread = null;
|
||||
this.serverSocket = null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host address of the local SOCKS5 proxy server.
|
||||
*
|
||||
* @return the host address of the local SOCKS5 proxy server
|
||||
*/
|
||||
public String getAddress() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostAddress();
|
||||
}
|
||||
catch (UnknownHostException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port of the local SOCKS5 proxy server. If it is not running -1 will be returned.
|
||||
*
|
||||
* @return the port of the local SOCKS5 proxy server or -1 if proxy is not running
|
||||
*/
|
||||
public int getPort() {
|
||||
if (!isRunning()) {
|
||||
return -1;
|
||||
}
|
||||
return this.serverSocket.getLocalPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the socket for the given digest.
|
||||
*
|
||||
* @param digest identifying the connection
|
||||
* @return socket or null if there is no socket for the given digest
|
||||
*/
|
||||
public Socket getSocket(String digest) {
|
||||
return this.connectionMap.get(digest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the local SOCKS5 proxy server is running, otherwise false.
|
||||
*
|
||||
* @return true if the local SOCKS5 proxy server is running, otherwise false
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return this.serverSocket != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of a simplified SOCKS5 proxy server.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
class Socks5ServerProcess implements Runnable {
|
||||
|
||||
public void run() {
|
||||
while (true) {
|
||||
Socket socket = null;
|
||||
|
||||
try {
|
||||
|
||||
if (Socks5TestProxy.this.serverSocket.isClosed()
|
||||
|| Thread.currentThread().isInterrupted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// accept connection
|
||||
socket = Socks5TestProxy.this.serverSocket.accept();
|
||||
|
||||
// initialize connection
|
||||
establishConnection(socket);
|
||||
|
||||
}
|
||||
catch (SocketException e) {
|
||||
/* do nothing */
|
||||
}
|
||||
catch (Exception e) {
|
||||
try {
|
||||
e.printStackTrace();
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException e1) {
|
||||
/* Do Nothing */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Negotiates a SOCKS5 connection and stores it on success.
|
||||
*
|
||||
* @param socket connection to the client
|
||||
* @throws XMPPException if client requests a connection in an unsupported way
|
||||
* @throws IOException if a network error occurred
|
||||
*/
|
||||
private void establishConnection(Socket socket) throws XMPPException, IOException {
|
||||
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
|
||||
DataInputStream in = new DataInputStream(socket.getInputStream());
|
||||
|
||||
// first byte is version should be 5
|
||||
int b = in.read();
|
||||
if (b != 5) {
|
||||
throw new XMPPException("Only SOCKS5 supported");
|
||||
}
|
||||
|
||||
// second byte number of authentication methods supported
|
||||
b = in.read();
|
||||
|
||||
// read list of supported authentication methods
|
||||
byte[] auth = new byte[b];
|
||||
in.readFully(auth);
|
||||
|
||||
byte[] authMethodSelectionResponse = new byte[2];
|
||||
authMethodSelectionResponse[0] = (byte) 0x05; // protocol version
|
||||
|
||||
// only authentication method 0, no authentication, supported
|
||||
boolean noAuthMethodFound = false;
|
||||
for (int i = 0; i < auth.length; i++) {
|
||||
if (auth[i] == (byte) 0x00) {
|
||||
noAuthMethodFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!noAuthMethodFound) {
|
||||
authMethodSelectionResponse[1] = (byte) 0xFF; // no acceptable methods
|
||||
out.write(authMethodSelectionResponse);
|
||||
out.flush();
|
||||
throw new XMPPException("Authentication method not supported");
|
||||
}
|
||||
|
||||
authMethodSelectionResponse[1] = (byte) 0x00; // no-authentication method
|
||||
out.write(authMethodSelectionResponse);
|
||||
out.flush();
|
||||
|
||||
// receive connection request
|
||||
byte[] connectionRequest = Socks5Utils.receiveSocks5Message(in);
|
||||
|
||||
// extract digest
|
||||
String responseDigest = new String(connectionRequest, 5, connectionRequest[4]);
|
||||
|
||||
connectionRequest[1] = (byte) 0x00; // set return status to 0 (success)
|
||||
out.write(connectionRequest);
|
||||
out.flush();
|
||||
|
||||
// store connection
|
||||
Socks5TestProxy.this.connectionMap.put(responseDigest, socket);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
94
test-unit/org/jivesoftware/util/ConnectionUtils.java
Normal file
94
test-unit/org/jivesoftware/util/ConnectionUtils.java
Normal file
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.util;
|
||||
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.jivesoftware.smack.PacketCollector;
|
||||
import org.jivesoftware.smack.Connection;
|
||||
import org.jivesoftware.smack.filter.PacketFilter;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smackx.ServiceDiscoveryManager;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
/**
|
||||
* A collection of utility methods to create mocked XMPP connections.
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class ConnectionUtils {
|
||||
|
||||
/**
|
||||
* Creates a mocked XMPP connection that stores every packet that is send over this
|
||||
* connection in the given protocol instance and returns the predefined answer packets
|
||||
* form the protocol instance.
|
||||
* <p>
|
||||
* This mocked connection can used to collect packets that require a reply using a
|
||||
* PacketCollector.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* PacketCollector collector = connection.createPacketCollector(new PacketFilter());
|
||||
* connection.sendPacket(packet);
|
||||
* Packet reply = collector.nextResult();
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @param protocol protocol helper containing answer packets
|
||||
* @param initiatorJID the user associated to the XMPP connection
|
||||
* @param xmppServer the XMPP server associated to the XMPP connection
|
||||
* @return a mocked XMPP connection
|
||||
*/
|
||||
public static Connection createMockedConnection(final Protocol protocol,
|
||||
String initiatorJID, String xmppServer) {
|
||||
|
||||
// mock XMPP connection
|
||||
Connection connection = mock(Connection.class);
|
||||
when(connection.getUser()).thenReturn(initiatorJID);
|
||||
when(connection.getServiceName()).thenReturn(xmppServer);
|
||||
|
||||
// mock packet collector
|
||||
PacketCollector collector = mock(PacketCollector.class);
|
||||
when(connection.createPacketCollector(isA(PacketFilter.class))).thenReturn(
|
||||
collector);
|
||||
Answer<Object> addIncoming = new Answer<Object>() {
|
||||
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
protocol.getRequests().add((Packet) invocation.getArguments()[0]);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// mock send method
|
||||
doAnswer(addIncoming).when(connection).sendPacket(isA(Packet.class));
|
||||
Answer<Packet> answer = new Answer<Packet>() {
|
||||
|
||||
public Packet answer(InvocationOnMock invocation) throws Throwable {
|
||||
return protocol.getResponses().poll();
|
||||
}
|
||||
};
|
||||
|
||||
// mock nextResult method
|
||||
when(collector.nextResult(anyInt())).thenAnswer(answer);
|
||||
when(collector.nextResult()).thenAnswer(answer);
|
||||
|
||||
// initialize service discovery manager for this connection
|
||||
new ServiceDiscoveryManager(connection);
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
}
|
195
test-unit/org/jivesoftware/util/Protocol.java
Normal file
195
test-unit/org/jivesoftware/util/Protocol.java
Normal file
|
@ -0,0 +1,195 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* This class can be used in conjunction with a mocked XMPP connection (
|
||||
* {@link ConnectionUtils#createMockedConnection(Protocol, String, String)}) to
|
||||
* verify a XMPP protocol. This can be accomplished in the following was:
|
||||
* <ul>
|
||||
* <li>add responses to packets sent over the mocked XMPP connection by the
|
||||
* method to test in the order the tested method awaits them</li>
|
||||
* <li>call the method to test</li>
|
||||
* <li>call {@link #verifyAll()} to run assertions on the request/response pairs
|
||||
* </li>
|
||||
* </ul>
|
||||
* Example:
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* public void methodToTest() {
|
||||
* Packet packet = new Packet(); // create an XMPP packet
|
||||
* PacketCollector collector = connection.createPacketCollector(new PacketIDFilter());
|
||||
* connection.sendPacket(packet);
|
||||
* Packet reply = collector.nextResult();
|
||||
* }
|
||||
*
|
||||
* public void testMethod() {
|
||||
* // create protocol
|
||||
* Protocol protocol = new Protocol();
|
||||
* // create mocked connection
|
||||
* Connection connection = ConnectionUtils.createMockedConnection(protocol, "user@xmpp-server", "xmpp-server");
|
||||
*
|
||||
* // add reply packet to protocol
|
||||
* Packet reply = new Packet();
|
||||
* protocol.add(reply);
|
||||
*
|
||||
* // call method to test
|
||||
* methodToTest();
|
||||
*
|
||||
* // verify protocol
|
||||
* protocol.verifyAll();
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* Additionally to adding the response to the protocol instance you can pass
|
||||
* verifications that will be executed when {@link #verifyAll()} is invoked.
|
||||
* (See {@link Verification} for more details.)
|
||||
* <p>
|
||||
* If the {@link #printProtocol} flag is set to true {@link #verifyAll()} will
|
||||
* also print out the XML messages in the order they are sent to the console.
|
||||
* This may be useful to inspect the whole protocol "by hand".
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public class Protocol {
|
||||
|
||||
/**
|
||||
* Set to <code>true</code> to print XML messages to the console while
|
||||
* verifying the protocol.
|
||||
*/
|
||||
public boolean printProtocol = false;
|
||||
|
||||
// responses to requests are taken form this queue
|
||||
Queue<Packet> responses = new LinkedList<Packet>();
|
||||
|
||||
// list of verifications
|
||||
List<Verification<?, ?>[]> verificationList = new ArrayList<Verification<?, ?>[]>();
|
||||
|
||||
// list of requests
|
||||
List<Packet> requests = new ArrayList<Packet>();
|
||||
|
||||
// list of all responses
|
||||
List<Packet> responsesList = new ArrayList<Packet>();
|
||||
|
||||
/**
|
||||
* Adds a responses and all verifications for the request/response pair to
|
||||
* the protocol.
|
||||
*
|
||||
* @param response the response for a request
|
||||
* @param verifications verifications for request/response pair
|
||||
*/
|
||||
public void addResponse(Packet response, Verification<?, ?>... verifications) {
|
||||
responses.offer(response);
|
||||
verificationList.add(verifications);
|
||||
responsesList.add(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the request/response pairs by checking if their numbers match
|
||||
* and executes the verification for each pair.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void verifyAll() {
|
||||
assertEquals(requests.size(), responsesList.size());
|
||||
|
||||
if (printProtocol)
|
||||
System.out.println("=================== Start ===============\n");
|
||||
|
||||
for (int i = 0; i < requests.size(); i++) {
|
||||
Packet request = requests.get(i);
|
||||
Packet response = responsesList.get(i);
|
||||
|
||||
if (printProtocol) {
|
||||
System.out.println("------------------- Request -------------\n");
|
||||
System.out.println(prettyFormat(request.toXML()));
|
||||
System.out.println("------------------- Response ------------\n");
|
||||
if (response != null) {
|
||||
System.out.println(prettyFormat(response.toXML()));
|
||||
}
|
||||
else {
|
||||
System.out.println("No response");
|
||||
}
|
||||
}
|
||||
|
||||
Verification<?, ?>[] verifications = verificationList.get(i);
|
||||
if (verifications != null) {
|
||||
for (Verification verification : verifications) {
|
||||
verification.verify(request, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (printProtocol)
|
||||
System.out.println("=================== End =================\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the responses queue.
|
||||
*
|
||||
* @return the responses queue
|
||||
*/
|
||||
protected Queue<Packet> getResponses() {
|
||||
return responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all collected requests.
|
||||
*
|
||||
* @return list of requests
|
||||
*/
|
||||
public List<Packet> getRequests() {
|
||||
return requests;
|
||||
}
|
||||
|
||||
private String prettyFormat(String input, int indent) {
|
||||
try {
|
||||
Source xmlInput = new StreamSource(new StringReader(input));
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
StreamResult xmlOutput = new StreamResult(stringWriter);
|
||||
Transformer transformer = TransformerFactory.newInstance().newTransformer();
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount",
|
||||
String.valueOf(indent));
|
||||
transformer.transform(xmlInput, xmlOutput);
|
||||
return xmlOutput.getWriter().toString();
|
||||
}
|
||||
catch (Exception e) {
|
||||
return "error while formatting the XML: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private String prettyFormat(String input) {
|
||||
return prettyFormat(input, 2);
|
||||
}
|
||||
|
||||
}
|
97
test-unit/org/jivesoftware/util/Verification.java
Normal file
97
test-unit/org/jivesoftware/util/Verification.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Implement this interface to verify a request/response pair.
|
||||
* <p>
|
||||
* For convenience there are some useful predefined implementations.
|
||||
*
|
||||
* @param <T> class of the request
|
||||
* @param <S> class of the response
|
||||
*
|
||||
* @author Henning Staib
|
||||
*/
|
||||
public interface Verification<T extends Packet, S extends Packet> {
|
||||
|
||||
/**
|
||||
* Verifies that the "To" field of the request corresponds with the "From" field of
|
||||
* the response.
|
||||
*/
|
||||
public static Verification<Packet, Packet> correspondingSenderReceiver = new Verification<Packet, Packet>() {
|
||||
|
||||
public void verify(Packet request, Packet response) {
|
||||
assertEquals(response.getFrom(), request.getTo());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies that the type of the request is a GET.
|
||||
*/
|
||||
public static Verification<IQ, Packet> requestTypeGET = new Verification<IQ, Packet>() {
|
||||
|
||||
public void verify(IQ request, Packet response) {
|
||||
assertEquals(IQ.Type.GET, request.getType());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies that the type of the request is a SET.
|
||||
*/
|
||||
public static Verification<IQ, Packet> requestTypeSET = new Verification<IQ, Packet>() {
|
||||
|
||||
public void verify(IQ request, Packet response) {
|
||||
assertEquals(IQ.Type.SET, request.getType());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies that the type of the request is a RESULT.
|
||||
*/
|
||||
public static Verification<IQ, Packet> requestTypeRESULT = new Verification<IQ, Packet>() {
|
||||
|
||||
public void verify(IQ request, Packet response) {
|
||||
assertEquals(IQ.Type.RESULT, request.getType());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies that the type of the request is an ERROR.
|
||||
*/
|
||||
public static Verification<IQ, Packet> requestTypeERROR = new Verification<IQ, Packet>() {
|
||||
|
||||
public void verify(IQ request, Packet response) {
|
||||
assertEquals(IQ.Type.ERROR, request.getType());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Implement this method to make assertions of the request/response pairs.
|
||||
*
|
||||
* @param request the request collected by the mocked XMPP connection
|
||||
* @param response the response added to the protocol instance
|
||||
*/
|
||||
public void verify(T request, S response);
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue