diff options
14 files changed, 1044 insertions, 32 deletions
diff --git a/src/com/isode/stroke/elements/RawXMLPayload.java b/src/com/isode/stroke/elements/RawXMLPayload.java index 30c9f74..a1652c5 100644 --- a/src/com/isode/stroke/elements/RawXMLPayload.java +++ b/src/com/isode/stroke/elements/RawXMLPayload.java @@ -15,6 +15,14 @@ package com.isode.stroke.elements; public class RawXMLPayload extends Payload { private String rawXML_ = ""; + public RawXMLPayload() { + this(""); + } + + public RawXMLPayload(String data) { + this.rawXML_ = data; + } + public void setRawXML(String data) { rawXML_ = data; } diff --git a/src/com/isode/stroke/elements/SoftwareVersion.java b/src/com/isode/stroke/elements/SoftwareVersion.java index 9d2bc6e..4b5b9a4 100644 --- a/src/com/isode/stroke/elements/SoftwareVersion.java +++ b/src/com/isode/stroke/elements/SoftwareVersion.java @@ -22,7 +22,15 @@ public class SoftwareVersion extends Payload { } public SoftwareVersion() { - + this("","",""); + } + + public SoftwareVersion(final String name) { + this(name, "", ""); + } + + public SoftwareVersion(final String name, final String version) { + this(name, version, ""); } public String getName() { diff --git a/src/com/isode/stroke/queries/IQRouter.java b/src/com/isode/stroke/queries/IQRouter.java index 0dd55f2..7b6e5d7 100644 --- a/src/com/isode/stroke/queries/IQRouter.java +++ b/src/com/isode/stroke/queries/IQRouter.java @@ -10,6 +10,7 @@ import com.isode.stroke.elements.ErrorPayload; import com.isode.stroke.elements.IQ; import com.isode.stroke.signals.Slot1; import com.isode.stroke.jid.JID; +import java.util.Collections; /** * This class is responsible for routing all IQ stanzas to the handlers. It's @@ -21,12 +22,16 @@ import com.isode.stroke.jid.JID; */ public class IQRouter { - private final Vector<IQHandler> handlers_ = new Vector<IQHandler>(); - private final IQChannel channel_; + private Vector<IQHandler> handlers_ = new Vector<IQHandler>(); + private IQChannel channel_; private JID jid_ = new JID(); + private JID from_ = new JID(); + private Vector<IQHandler> queuedRemoves_ = new Vector<IQHandler>(); + private boolean queueRemoves_; public IQRouter(IQChannel channel) { channel_ = channel; + queueRemoves_ = false; channel_.onIQReceived.connect(new Slot1<IQ>() { public void call(IQ p1) { @@ -42,12 +47,20 @@ public class IQRouter { } public void removeHandler(IQHandler handler) { - synchronized (handlers_) { - handlers_.remove(handler); + if (queueRemoves_) { + queuedRemoves_.add(handler); + } + else { + synchronized (handlers_) { + handlers_.remove(handler); + } } } public void sendIQ(IQ iq) { + if (from_.isValid() && !iq.getFrom().isValid()) { + iq.setFrom(from_); + } channel_.sendIQ(iq); } @@ -70,9 +83,13 @@ public class IQRouter { } private void handleIQ(IQ iq) { + queueRemoves_ = true; boolean handled = false; + Vector<IQHandler> i = new Vector<IQHandler>(); + i.addAll(handlers_); + Collections.reverse(i); synchronized (handlers_) { - for (IQHandler handler : handlers_) { + for (IQHandler handler : i) { handled |= handler.handleIQ(iq); if (handled) { break; @@ -82,6 +99,18 @@ public class IQRouter { if (!handled && (iq.getType().equals(IQ.Type.Get) || iq.getType().equals(IQ.Type.Set))) { sendIQ(IQ.createError(iq.getFrom(), iq.getID(), ErrorPayload.Condition.FeatureNotImplemented, ErrorPayload.Type.Cancel)); } + + processPendingRemoves(); + queueRemoves_ = false; + } + + public void processPendingRemoves() { + synchronized(handlers_) { + for(IQHandler handler : queuedRemoves_) { + handlers_.remove(handler); + } + } + queuedRemoves_.clear(); } /** @@ -98,5 +127,15 @@ public class IQRouter { public JID getJID() { return jid_; } - + + /** + * Sets the 'from' JID for all outgoing IQ stanzas. + * + * By default, IQRouter does not add a from to IQ stanzas, since + * this is automatically added by the server. This overrides this + * default behavior, which is necessary for e.g. components. + */ + public void setFrom(final JID from) { + from_ = from; + } } diff --git a/src/com/isode/stroke/queries/RawRequest.java b/src/com/isode/stroke/queries/RawRequest.java new file mode 100644 index 0000000..eaa31bc --- /dev/null +++ b/src/com/isode/stroke/queries/RawRequest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.queries; + +import com.isode.stroke.queries.Request; +import com.isode.stroke.elements.RawXMLPayload; +import com.isode.stroke.elements.ErrorPayload; +import com.isode.stroke.elements.Payload; +import com.isode.stroke.elements.IQ; +import com.isode.stroke.jid.JID; +import com.isode.stroke.serializer.PayloadSerializer; +import com.isode.stroke.serializer.payloadserializers.ErrorSerializer; +import com.isode.stroke.serializer.payloadserializers.FullPayloadSerializerCollection; +import com.isode.stroke.signals.Signal1; +import com.isode.stroke.signals.SignalConnection; + +public class RawRequest extends Request { + + private FullPayloadSerializerCollection serializers = new FullPayloadSerializerCollection(); + + public RawRequest(IQ.Type type, final JID receiver, final String data, IQRouter router) { + super(type, receiver, new RawXMLPayload(data), router); + } + + public static RawRequest create(IQ.Type type, final JID recipient, final String data, IQRouter router) { + return new RawRequest(type, recipient, data, router); + } + + public final Signal1<String> onResponse = new Signal1<String>(); + + protected void handleResponse(Payload payload, ErrorPayload error) { + if (error != null) { + onResponse.emit(new ErrorSerializer(serializers).serializePayload(error)); + } + else { + assert(payload != null); + PayloadSerializer serializer = serializers.getPayloadSerializer(payload); + assert(serializer != null); + onResponse.emit(serializer.serialize(payload)); + } + } +} + diff --git a/src/com/isode/stroke/queries/Request.java b/src/com/isode/stroke/queries/Request.java index 52e3854..c5c9999 100644 --- a/src/com/isode/stroke/queries/Request.java +++ b/src/com/isode/stroke/queries/Request.java @@ -13,6 +13,7 @@ import com.isode.stroke.elements.ErrorPayload; import com.isode.stroke.elements.IQ; import com.isode.stroke.elements.IQ.Type; import com.isode.stroke.elements.Payload; +import com.isode.stroke.elements.RawXMLPayload; import com.isode.stroke.jid.JID; import java.util.logging.Logger; @@ -20,13 +21,13 @@ import java.util.logging.Logger; * Base class for IQ requests. */ public abstract class Request implements IQHandler { - protected final Type type_; - protected final IQRouter router_; - protected final JID receiver_; - protected final JID sender_; + protected Type type_; + protected IQRouter router_; + protected JID receiver_ = new JID(); + protected JID sender_ = new JID(); private boolean sent_; private Payload payload_; - private String id_; + private String id_ = ""; private Logger logger_ = Logger.getLogger(this.getClass().getName()); /** @@ -99,11 +100,15 @@ public abstract class Request implements IQHandler { if (isCorrectSender(iq.getFrom())) { if (iq.getType().equals(IQ.Type.Result)) { - handleResponse(iq.getPayload(payload_), null); + Payload payload = iq.getPayload(payload_); + if (payload == null && (payload_ instanceof RawXMLPayload) && !iq.getPayloads().isEmpty()) { + payload = iq.getPayloads().firstElement(); + } + handleResponse(payload, null); } else { ErrorPayload errorPayload = iq.getPayload(new ErrorPayload()); if (errorPayload != null) { - handleResponse(null, errorPayload); + handleResponse(null, errorPayload); } else { handleResponse(null, new ErrorPayload(ErrorPayload.Condition.UndefinedCondition)); } @@ -117,20 +122,17 @@ public abstract class Request implements IQHandler { } private boolean isCorrectSender(final JID jid) { - if (isAccountJID(receiver_)) { - return isAccountJID(jid); + if (router_.isAccountJID(receiver_)) { + if (jid.isValid() && jid.compare(router_.getJID(), JID.CompareType.WithResource) == 0) { + // This unspecified behavior seems to happen in ejabberd versions (e.g. 2.0.5) + logger_.warning("Server responded to an account request with a full JID, which is not allowed. Handling it anyway."); + return true; + } + return router_.isAccountJID(jid); } - return (jid.compare(receiver_, JID.CompareType.WithResource) == 0); - } - - private boolean isAccountJID(final JID jid) { - // If the router's JID is not set, we don't check anything - if (!router_.getJID().isValid()) { - return true; + else { + return jid.compare(receiver_, JID.CompareType.WithResource) == 0; } - - return jid.isValid() ? - router_.getJID().compare(jid, JID.CompareType.WithoutResource) == 0 : true; } public JID getReceiver() { diff --git a/src/com/isode/stroke/queries/Responder.java b/src/com/isode/stroke/queries/Responder.java index 203dd7e..831bcd3 100644 --- a/src/com/isode/stroke/queries/Responder.java +++ b/src/com/isode/stroke/queries/Responder.java @@ -30,6 +30,7 @@ public abstract class Responder<PAYLOAD_TYPE extends Payload> implements IQHandl public Responder(final PAYLOAD_TYPE payloadType, IQRouter router) { payloadType_ = payloadType; router_ = router; + isFinalResonder_ = true; } /** @@ -114,6 +115,10 @@ public abstract class Responder<PAYLOAD_TYPE extends Payload> implements IQHandl return router_; } + protected void setFinal(boolean isFinal) { + isFinalResonder_ = isFinal; + } + @Override public boolean handleIQ(IQ iq) { if (IQ.Type.Set.equals(iq.getType()) || IQ.Type.Get.equals(iq.getType())) { @@ -127,7 +132,11 @@ public abstract class Responder<PAYLOAD_TYPE extends Payload> implements IQHandl result = handleGetRequest(iq.getFrom(), iq.getTo(), iq.getID(), payload); } if (!result) { - router_.sendIQ(IQ.createError(iq.getFrom(), iq.getID(), ErrorPayload.Condition.NotAllowed, ErrorPayload.Type.Cancel)); + if(isFinalResonder_) { + router_.sendIQ(IQ.createError(iq.getFrom(), iq.getID(), ErrorPayload.Condition.NotAllowed, ErrorPayload.Type.Cancel)); + } else { + return false; + } } return true; } @@ -136,4 +145,5 @@ public abstract class Responder<PAYLOAD_TYPE extends Payload> implements IQHandl } private IQRouter router_; private PAYLOAD_TYPE payloadType_; + private boolean isFinalResonder_; }; diff --git a/src/com/isode/stroke/queries/requests/GetInBandRegistrationFormRequest.java b/src/com/isode/stroke/queries/requests/GetInBandRegistrationFormRequest.java new file mode 100644 index 0000000..57182e6 --- /dev/null +++ b/src/com/isode/stroke/queries/requests/GetInBandRegistrationFormRequest.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.queries.requests; + +import com.isode.stroke.queries.GenericRequest; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.elements.InBandRegistrationPayload; +import com.isode.stroke.elements.IQ; +import com.isode.stroke.jid.JID; + +public class GetInBandRegistrationFormRequest extends GenericRequest<InBandRegistrationPayload> { + + public GetInBandRegistrationFormRequest(final JID to, IQRouter router) { + super(IQ.Type.Get, to, new InBandRegistrationPayload(), router); + } + + public static GetInBandRegistrationFormRequest create(final JID to, IQRouter router) { + return new GetInBandRegistrationFormRequest(to, router); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/queries/requests/GetSoftwareVersionRequest.java b/src/com/isode/stroke/queries/requests/GetSoftwareVersionRequest.java new file mode 100644 index 0000000..aeec635 --- /dev/null +++ b/src/com/isode/stroke/queries/requests/GetSoftwareVersionRequest.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.queries.requests; + +import com.isode.stroke.queries.GenericRequest; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.elements.SoftwareVersion; +import com.isode.stroke.elements.IQ; +import com.isode.stroke.jid.JID; + +public class GetSoftwareVersionRequest extends GenericRequest<SoftwareVersion> { + + public GetSoftwareVersionRequest(final JID recipient, IQRouter router) { + super(IQ.Type.Get, recipient, new SoftwareVersion(), router); + } + + public static GetSoftwareVersionRequest create(final JID recipient, IQRouter router) { + return new GetSoftwareVersionRequest(recipient, router); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/queries/requests/SetInBandRegistrationRequest.java b/src/com/isode/stroke/queries/requests/SetInBandRegistrationRequest.java new file mode 100644 index 0000000..e282847 --- /dev/null +++ b/src/com/isode/stroke/queries/requests/SetInBandRegistrationRequest.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.queries.requests; + +import com.isode.stroke.queries.Request; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.elements.InBandRegistrationPayload; +import com.isode.stroke.elements.Payload; +import com.isode.stroke.elements.IQ; +import com.isode.stroke.elements.ErrorPayload; +import com.isode.stroke.signals.Signal2; +import com.isode.stroke.jid.JID; + +public class SetInBandRegistrationRequest extends Request { + + public final Signal2<Payload, ErrorPayload> onResponse = new Signal2<Payload, ErrorPayload>(); + + public SetInBandRegistrationRequest(final JID to, InBandRegistrationPayload payload, IQRouter router) { + super(IQ.Type.Set, to, (InBandRegistrationPayload)payload, router); + } + + public static SetInBandRegistrationRequest create(final JID to, InBandRegistrationPayload payload, IQRouter router) { + return new SetInBandRegistrationRequest(to, payload, router); + } + + protected void handleResponse(Payload payload, ErrorPayload error) { + onResponse.emit(payload, error); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/queries/responders/SoftwareVersionResponder.java b/src/com/isode/stroke/queries/responders/SoftwareVersionResponder.java index 1fd5b36..941e03b 100644 --- a/src/com/isode/stroke/queries/responders/SoftwareVersionResponder.java +++ b/src/com/isode/stroke/queries/responders/SoftwareVersionResponder.java @@ -20,6 +20,10 @@ public class SoftwareVersionResponder extends GetResponder<SoftwareVersion> { super(new SoftwareVersion(), router); } + public void setVersion(final String client, final String version) { + setVersion(client, version, ""); + } + public void setVersion(final String client, final String version, final String os) { this.client = client; this.version = version; @@ -31,7 +35,7 @@ public class SoftwareVersionResponder extends GetResponder<SoftwareVersion> { sendResponse(from, id, new SoftwareVersion(client, version, os)); return true; } - private String client; - private String version; - private String os; + private String client = ""; + private String version = ""; + private String os = ""; }
\ No newline at end of file diff --git a/src/com/isode/stroke/serializer/payloadserializers/ErrorSerializer.java b/src/com/isode/stroke/serializer/payloadserializers/ErrorSerializer.java index 47373f8..5540d2b 100644 --- a/src/com/isode/stroke/serializer/payloadserializers/ErrorSerializer.java +++ b/src/com/isode/stroke/serializer/payloadserializers/ErrorSerializer.java @@ -15,7 +15,7 @@ import com.isode.stroke.serializer.xml.XMLTextNode; import com.isode.stroke.serializer.PayloadSerializerCollection; import com.isode.stroke.serializer.PayloadSerializer; -class ErrorSerializer extends GenericPayloadSerializer<ErrorPayload> { +public class ErrorSerializer extends GenericPayloadSerializer<ErrorPayload> { private PayloadSerializerCollection serializers; @@ -25,7 +25,7 @@ class ErrorSerializer extends GenericPayloadSerializer<ErrorPayload> { } @Override - protected String serializePayload(ErrorPayload error) { + public String serializePayload(ErrorPayload error) { String result = "<error type=\""; switch (error.getType()) { case Continue: result += "continue"; break; diff --git a/test/com/isode/stroke/queries/IQRouterTest.java b/test/com/isode/stroke/queries/IQRouterTest.java new file mode 100644 index 0000000..6e25891 --- /dev/null +++ b/test/com/isode/stroke/queries/IQRouterTest.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.queries; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.Before; +import com.isode.stroke.queries.IQHandler; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.queries.DummyIQChannel; +import com.isode.stroke.jid.JID; +import com.isode.stroke.elements.IQ; +import com.isode.stroke.elements.ErrorPayload; + +public class IQRouterTest { + + private DummyIQChannel channel_; + + private class DummyIQHandler implements IQHandler { + + public DummyIQHandler(boolean handle, IQRouter router) { + this.handle = handle; + this.router = router; + this.called = 0; + router.addHandler(this); + } + + public void delete() { + router.removeHandler(this); + } + + @Override + public boolean handleIQ(IQ iq) { + called++; + return handle; + } + + public boolean handle; + public IQRouter router; + public int called; + } + + private class RemovingIQHandler implements IQHandler { + + public RemovingIQHandler(IQRouter router) { + this.router = router; + this.called = 0; + router.addHandler(this); + } + + @Override + public boolean handleIQ(IQ iq) { + called++; + router.removeHandler(this); + return false; + } + + public IQRouter router; + public int called; + } + + @Before + public void setUp() { + channel_ = new DummyIQChannel(); + } + + @Test + public void testRemoveHandler() { + IQRouter testling = new IQRouter(channel_); + DummyIQHandler handler1 = new DummyIQHandler(true, testling); + DummyIQHandler handler2 = new DummyIQHandler(true, testling); + testling.removeHandler(handler1); + + channel_.onIQReceived.emit(new IQ()); + + assertEquals(0, handler1.called); + assertEquals(1, handler2.called); + } + + @Test + public void testRemoveHandler_AfterHandleIQ() { + IQRouter testling = new IQRouter(channel_); + DummyIQHandler handler2 = new DummyIQHandler(true, testling); + DummyIQHandler handler1 = new DummyIQHandler(true, testling); + + channel_.onIQReceived.emit(new IQ()); + testling.removeHandler(handler1); + channel_.onIQReceived.emit(new IQ()); + + assertEquals(1, handler1.called); + assertEquals(1, handler2.called); + } + + @Test + public void testHandleIQ_SuccesfulHandlerFirst() { + IQRouter testling = new IQRouter(channel_); + DummyIQHandler handler2 = new DummyIQHandler(false, testling); + DummyIQHandler handler1 = new DummyIQHandler(true, testling); + + channel_.onIQReceived.emit(new IQ()); + + assertEquals(1, handler1.called); + assertEquals(0, handler2.called); + assertEquals(0, channel_.iqs_.size()); + } + + @Test + public void testHandleIQ_SuccesfulHandlerLast() { + IQRouter testling = new IQRouter(channel_); + DummyIQHandler handler2 = new DummyIQHandler(true, testling); + DummyIQHandler handler1 = new DummyIQHandler(false, testling); + + channel_.onIQReceived.emit(new IQ()); + + assertEquals(1, handler1.called); + assertEquals(1, handler2.called); + assertEquals(0, channel_.iqs_.size()); + } + + @Test + public void testHandleIQ_NoSuccesfulHandler() { + IQRouter testling = new IQRouter(channel_); + DummyIQHandler handler = new DummyIQHandler(false, testling); + + channel_.onIQReceived.emit(new IQ()); + + assertEquals(1, channel_.iqs_.size()); + assertNotNull(channel_.iqs_.get(0).getPayload(new ErrorPayload())); + } + + @Test + public void testHandleIQ_HandlerRemovedDuringHandle() { + IQRouter testling = new IQRouter(channel_); + DummyIQHandler handler2 = new DummyIQHandler(true, testling); + RemovingIQHandler handler1 = new RemovingIQHandler(testling); + + channel_.onIQReceived.emit(new IQ()); + channel_.onIQReceived.emit(new IQ()); + + assertEquals(1, handler1.called); + assertEquals(2, handler2.called); + } + + @Test + public void testSendIQ_WithFrom() { + IQRouter testling = new IQRouter(channel_); + testling.setFrom(new JID("foo@bar.com/baz")); + + testling.sendIQ(new IQ()); + + assertEquals(new JID("foo@bar.com/baz"), channel_.iqs_.get(0).getFrom()); + } + + @Test + public void testSendIQ_WithoutFrom() { + IQRouter testling = new IQRouter(channel_); + + testling.sendIQ(new IQ()); + + assertEquals(new JID(), channel_.iqs_.get(0).getFrom()); + } + + @Test + public void testHandleIQ_WithFrom() { + IQRouter testling = new IQRouter(channel_); + testling.setFrom(new JID("foo@bar.com/baz")); + DummyIQHandler handler = new DummyIQHandler(false, testling); + + channel_.onIQReceived.emit(new IQ()); + + assertEquals(new JID("foo@bar.com/baz"), channel_.iqs_.get(0).getFrom()); + } +}
\ No newline at end of file diff --git a/test/com/isode/stroke/queries/RequestTest.java b/test/com/isode/stroke/queries/RequestTest.java new file mode 100644 index 0000000..9c89414 --- /dev/null +++ b/test/com/isode/stroke/queries/RequestTest.java @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.queries; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.Before; +import com.isode.stroke.queries.GenericRequest; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.queries.DummyIQChannel; +import com.isode.stroke.jid.JID; +import com.isode.stroke.elements.IQ; +import com.isode.stroke.elements.ErrorPayload; +import com.isode.stroke.elements.Payload; +import com.isode.stroke.elements.RawXMLPayload; +import com.isode.stroke.signals.Signal2; +import com.isode.stroke.signals.Slot2; +import java.util.Vector; + +public class RequestTest { + + private IQRouter router_; + private DummyIQChannel channel_; + private Payload payload_; + private Payload responsePayload_; + private int responsesReceived_; + private Vector<ErrorPayload> receivedErrors = new Vector<ErrorPayload>(); + + public class MyPayload extends Payload { + + public MyPayload() { + this(""); + } + + public MyPayload(final String s) { + this.text_ = s; + } + + public String text_ = ""; + } + + public class MyOtherPayload extends Payload { + + } + + public class MyRequest extends Request { + + public MyRequest(IQ.Type type, final JID receiver, Payload payload, IQRouter router) { + super(type, receiver, payload, router); + } + + public void handleResponse(Payload payload, ErrorPayload error) { + onResponse.emit(payload, error); + } + + public final Signal2<Payload, ErrorPayload> onResponse = new Signal2<Payload, ErrorPayload>(); + } + + private void handleResponse(Payload p, ErrorPayload e) { + if (e != null) { + receivedErrors.add(e); + } + else { + MyPayload payload = (MyPayload)p; + assertNotNull(payload); + assertEquals("bar", payload.text_); + ++responsesReceived_; + } + } + + private void handleDifferentResponse(Payload p, ErrorPayload e) { + assertNull(e); + assertNull(p); + ++responsesReceived_; + } + + private void handleRawXMLResponse(Payload p, ErrorPayload e) { + assertNull(e); + assertNotNull(p); + assertNotNull((MyOtherPayload)p); + ++responsesReceived_; + } + + private IQ createResponse(final JID from, final String id) { + IQ iq = new IQ(IQ.Type.Result); + iq.setFrom(from); + iq.addPayload(responsePayload_); + iq.setID(id); + return iq; + } + + private IQ createError(final JID from, final String id) { + IQ iq = new IQ(IQ.Type.Error); + iq.setFrom(from); + iq.setID(id); + return iq; + } + + @Before + public void setUp() { + channel_ = new DummyIQChannel(); + router_ = new IQRouter(channel_); + payload_ = (Payload)(new MyPayload("foo")); + responsePayload_ = (Payload)(new MyPayload("bar")); + responsesReceived_ = 0; + } + + @Test + public void testSendSet() { + MyRequest testling = new MyRequest(IQ.Type.Set, new JID("foo@bar.com/baz"), payload_, router_); + testling.send(); + + assertEquals(1, (channel_.iqs_.size())); + assertEquals(new JID("foo@bar.com/baz"), channel_.iqs_.get(0).getTo()); + assertEquals(IQ.Type.Set, channel_.iqs_.get(0).getType()); + assertEquals(("test-id"), channel_.iqs_.get(0).getID()); + } + + @Test + public void testSendGet() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID("foo@bar.com/baz"), payload_, router_); + testling.send(); + + assertEquals(1, (channel_.iqs_.size())); + assertEquals(IQ.Type.Get, channel_.iqs_.get(0).getType()); + } + + @Test + public void testHandleIQ() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + channel_.onIQReceived.emit(createResponse(new JID("foo@bar.com/baz"),"test-id")); + + assertEquals(1, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + } + + // FIXME: Doesn't test that it didn't handle the payload + @Test + public void testHandleIQ_InvalidID() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + channel_.onIQReceived.emit(createResponse(new JID("foo@bar.com/baz"),"different-id")); + + assertEquals(0, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + } + + @Test + public void testHandleIQ_Error() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + IQ error = createError(new JID("foo@bar.com/baz"),"test-id"); + Payload errorPayload = new ErrorPayload(ErrorPayload.Condition.InternalServerError); + error.addPayload(errorPayload); + channel_.onIQReceived.emit(error); + + assertEquals(0, responsesReceived_); + assertEquals(1, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + assertEquals(ErrorPayload.Condition.InternalServerError, receivedErrors.get(0).getCondition()); + } + + @Test + public void testHandleIQ_ErrorWithoutPayload() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + channel_.onIQReceived.emit(createError(new JID("foo@bar.com/baz"),"test-id")); + + assertEquals(0, responsesReceived_); + assertEquals(1, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + assertEquals(ErrorPayload.Condition.UndefinedCondition, receivedErrors.get(0).getCondition()); + } + + @Test + public void testHandleIQ_BeforeSend() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + channel_.onIQReceived.emit(createResponse(new JID("foo@bar.com/baz"),"test-id")); + + assertEquals(0, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(0, (channel_.iqs_.size())); + } + + @Test + public void testHandleIQ_DifferentPayload() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleDifferentResponse(p, e); + } + }); + testling.send(); + + responsePayload_ = new MyOtherPayload(); + channel_.onIQReceived.emit(createResponse(new JID("foo@bar.com/baz"),"test-id")); + + assertEquals(1, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + } + + @Test + public void testHandleIQ_RawXMLPayload() { + payload_ = new RawXMLPayload("<bla/>"); + MyRequest testling = new MyRequest(IQ.Type.Get, new JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleRawXMLResponse(p, e); + } + }); + testling.send(); + + responsePayload_ = new MyOtherPayload(); + channel_.onIQReceived.emit(createResponse(new JID("foo@bar.com/baz"),"test-id")); + + assertEquals(1, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + } + + @Test + public void testHandleIQ_GetWithSameID() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + IQ response = createResponse(new JID("foo@bar.com/baz"),"test-id"); + response.setType(IQ.Type.Get); + channel_.onIQReceived.emit(response); + + assertEquals(0, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(2, (channel_.iqs_.size())); + } + + @Test + public void testHandleIQ_SetWithSameID() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + IQ response = createResponse(new JID("foo@bar.com/baz"), "test-id"); + response.setType(IQ.Type.Set); + channel_.onIQReceived.emit(response); + + assertEquals(0, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(2, (channel_.iqs_.size())); + } + + @Test + public void testHandleIQ_IncorrectSender() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID("foo@bar.com/baz"), payload_, router_); + router_.setJID(new JID("alice@wonderland.lit/TeaParty")); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + channel_.onIQReceived.emit(createResponse(new JID("anotherfoo@bar.com/baz"), "test-id")); + + assertEquals(0, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + } + + @Test + public void testHandleIQ_IncorrectSenderForServerQuery() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID(), payload_, router_); + router_.setJID(new JID("alice@wonderland.lit/TeaParty")); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + channel_.onIQReceived.emit(createResponse(new JID("foo@bar.com/baz"), "test-id")); + + assertEquals(0, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + } + + @Test + public void testHandleIQ_IncorrectOtherResourceSenderForServerQuery() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID(), payload_, router_); + router_.setJID(new JID("alice@wonderland.lit/TeaParty")); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + channel_.onIQReceived.emit(createResponse(new JID("alice@wonderland.lit/RabbitHole"), "test-id")); + + assertEquals(0, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + } + + @Test + public void testHandleIQ_ServerRespondsWithDomain() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID(), payload_, router_); + router_.setJID(new JID("alice@wonderland.lit/TeaParty")); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + channel_.onIQReceived.emit(createResponse(new JID("wonderland.lit"),"test-id")); + + assertEquals(0, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + } + + @Test + public void testHandleIQ_ServerRespondsWithBareJID() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID(), payload_, router_); + router_.setJID(new JID("alice@wonderland.lit/TeaParty")); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + channel_.onIQReceived.emit(createResponse(new JID("alice@wonderland.lit"),"test-id")); + + assertEquals(1, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + } + + // This tests a bug in ejabberd servers (2.0.5) + @Test + public void testHandleIQ_ServerRespondsWithFullJID() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID(), payload_, router_); + router_.setJID(new JID("alice@wonderland.lit/TeaParty")); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + channel_.onIQReceived.emit(createResponse(new JID("alice@wonderland.lit/TeaParty"),"test-id")); + + assertEquals(1, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + } + + @Test + public void testHandleIQ_ServerRespondsWithoutFrom() { + MyRequest testling = new MyRequest(IQ.Type.Get, new JID(), payload_, router_); + router_.setJID(new JID("alice@wonderland.lit/TeaParty")); + testling.onResponse.connect(new Slot2<Payload, ErrorPayload>() { + @Override + public void call(Payload p, ErrorPayload e) { + handleResponse(p, e); + } + }); + testling.send(); + + channel_.onIQReceived.emit(createResponse(new JID(),"test-id")); + + assertEquals(1, responsesReceived_); + assertEquals(0, (receivedErrors.size())); + assertEquals(1, (channel_.iqs_.size())); + } +} diff --git a/test/com/isode/stroke/queries/ResponderTest.java b/test/com/isode/stroke/queries/ResponderTest.java new file mode 100644 index 0000000..54c8621 --- /dev/null +++ b/test/com/isode/stroke/queries/ResponderTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.queries; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.Before; +import com.isode.stroke.queries.Responder; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.queries.DummyIQChannel; +import com.isode.stroke.jid.JID; +import com.isode.stroke.elements.IQ; +import com.isode.stroke.elements.ErrorPayload; +import com.isode.stroke.elements.SoftwareVersion; +import java.util.Vector; + +public class ResponderTest { + + private IQRouter router_; + private DummyIQChannel channel_; + private SoftwareVersion payload_; + + private class MyResponder extends Responder<SoftwareVersion> { + + public MyResponder(IQRouter router) { + super(new SoftwareVersion(), router); + getRequestResponse_ = true; + setRequestResponse_ = true; + } + + public boolean handleGetRequest(final JID from, final JID to, final String id, SoftwareVersion payload) { + assertEquals(new JID("foo@bar.com/baz"), from); + assertEquals("myid", id); + getPayloads_.add(payload); + return getRequestResponse_; + } + + public boolean handleSetRequest(final JID from, final JID to, final String id, SoftwareVersion payload) { + assertEquals(new JID("foo@bar.com/baz"), from); + assertEquals("myid", id); + setPayloads_.add(payload); + return setRequestResponse_; + } + + public boolean getRequestResponse_; + public boolean setRequestResponse_; + public Vector<SoftwareVersion> getPayloads_ = new Vector<SoftwareVersion>(); + public Vector<SoftwareVersion> setPayloads_ = new Vector<SoftwareVersion>(); + } + + private IQ createRequest(IQ.Type type) { + IQ iq = new IQ(type); + iq.addPayload(payload_); + iq.setID("myid"); + iq.setFrom(new JID("foo@bar.com/baz")); + return iq; + } + + @Before + public void setUp() { + channel_ = new DummyIQChannel(); + router_ = new IQRouter(channel_); + payload_ = new SoftwareVersion("foo"); + } + + @Test + public void testConstructor() { + MyResponder testling = new MyResponder(router_); + + channel_.onIQReceived.emit(createRequest(IQ.Type.Set)); + + assertEquals(0, testling.setPayloads_.size()); + } + + @Test + public void testStart() { + MyResponder testling = new MyResponder(router_); + + testling.start(); + channel_.onIQReceived.emit(createRequest(IQ.Type.Set)); + + assertEquals(1, testling.setPayloads_.size()); + } + + @Test + public void testStop() { + MyResponder testling = new MyResponder(router_); + + testling.start(); + testling.stop(); + channel_.onIQReceived.emit(createRequest(IQ.Type.Set)); + + assertEquals(0, testling.setPayloads_.size()); + } + + @Test + public void testHandleIQ_Set() { + MyResponder testling = new MyResponder(router_); + + assertTrue(((IQHandler)testling).handleIQ(createRequest(IQ.Type.Set))); + + assertEquals(1, testling.setPayloads_.size()); + assertEquals(payload_, testling.setPayloads_.get(0)); + assertEquals(0, testling.getPayloads_.size()); + } + + @Test + public void testHandleIQ_Get() { + MyResponder testling = new MyResponder(router_); + + assertTrue(((IQHandler)testling).handleIQ(createRequest(IQ.Type.Get))); + + assertEquals(1, testling.getPayloads_.size()); + assertEquals(0, testling.setPayloads_.size()); + assertEquals(payload_, testling.getPayloads_.get(0)); + } + + @Test + public void testHandleIQ_Error() { + MyResponder testling = new MyResponder(router_); + + assertFalse(((IQHandler)testling).handleIQ(createRequest(IQ.Type.Error))); + + assertEquals(0, testling.getPayloads_.size()); + assertEquals(0, testling.setPayloads_.size()); + } + + @Test + public void testHandleIQ_Result() { + MyResponder testling = new MyResponder(router_); + + assertFalse(((IQHandler)testling).handleIQ(createRequest(IQ.Type.Result))); + + assertEquals(0, testling.getPayloads_.size()); + assertEquals(0, testling.setPayloads_.size()); + } + + @Test + public void testHandleIQ_NoPayload() { + MyResponder testling = new MyResponder(router_); + + assertFalse(((IQHandler)testling).handleIQ(new IQ(IQ.Type.Get))); + + assertEquals(0, testling.getPayloads_.size()); + assertEquals(0, testling.setPayloads_.size()); + } +} |