mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-23 20:42:06 +01:00
Implement transport fallback
This commit is contained in:
parent
23eb34932f
commit
a07a1cdc61
5 changed files with 122 additions and 43 deletions
|
@ -18,6 +18,7 @@ package org.jivesoftware.smackx.jingle;
|
|||
|
||||
import org.jivesoftware.smackx.jingle.components.JingleContent;
|
||||
import org.jivesoftware.smackx.jingle.components.JingleTransport;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||
|
||||
/**
|
||||
* Manager for JingleTransport components.
|
||||
|
@ -30,6 +31,8 @@ public interface JingleTransportManager extends Comparable<JingleTransportManage
|
|||
|
||||
JingleTransport<?> createTransport(JingleContent content, JingleTransport<?> peersTransport);
|
||||
|
||||
JingleTransport<?> createTransport(JingleContent content, JingleContentTransportElement peersTransportElement);
|
||||
|
||||
/**
|
||||
* Return a (usually) positive integer, which is used to define a strict order over the set of available transport
|
||||
* managers.
|
||||
|
|
|
@ -62,7 +62,7 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
|
|||
private JingleTransport<?> transport;
|
||||
private JingleSecurity<?> security;
|
||||
|
||||
private JingleTransport<?> replaceTransport = null;
|
||||
private JingleTransport<?> pendingReplacingTransport = null;
|
||||
|
||||
private final List<Callback> callbacks = Collections.synchronizedList(new ArrayList<Callback>());
|
||||
private final Set<String> transportBlacklist = Collections.synchronizedSet(new HashSet<String>());
|
||||
|
@ -165,6 +165,7 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
|
|||
for (JingleContentElement c : request.getContents()) {
|
||||
if (c.getName().equals(getName())) {
|
||||
contentElement = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,11 +199,12 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
|
|||
}
|
||||
|
||||
public IQ handleTransportAccept(JingleElement request, XMPPConnection connection) {
|
||||
if (replaceTransport == null) {
|
||||
if (pendingReplacingTransport == null) {
|
||||
LOGGER.log(Level.WARNING, "Received transport-accept, but apparently we did not try to replace the transport.");
|
||||
return JingleElement.createJingleErrorOutOfOrder(request);
|
||||
}
|
||||
transport = replaceTransport;
|
||||
transport = pendingReplacingTransport;
|
||||
pendingReplacingTransport = null;
|
||||
|
||||
onAccept(connection);
|
||||
|
||||
|
@ -221,6 +223,54 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
|
|||
}
|
||||
|
||||
public IQ handleTransportReplace(JingleElement request, XMPPConnection connection) {
|
||||
JingleContentElement contentElement = null;
|
||||
for (JingleContentElement c : request.getContents()) {
|
||||
if (c.getName().equals(getName())) {
|
||||
contentElement = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (contentElement == null) {
|
||||
throw new AssertionError("Unknown content");
|
||||
}
|
||||
|
||||
final JingleSession session = getParent();
|
||||
final JingleContentTransportElement transportElement = contentElement.getTransport();
|
||||
|
||||
JingleTransportManager tm = session.getJingleManager().getTransportManager(transportElement.getNamespace());
|
||||
|
||||
// Unsupported/Blacklisted transport -> reject.
|
||||
if (tm == null || getTransportBlacklist().contains(transportElement.getNamespace())) {
|
||||
Async.go(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
getParent().getJingleManager().getConnection().createStanzaCollectorAndSend(JingleElement.createTransportReject(session.getOurJid(), session.getPeer(), session.getSessionId(), getCreator(), getName(), transportElement));
|
||||
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not send transport-reject: " + e, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
//Blacklist current transport
|
||||
this.getTransportBlacklist().add(this.transport.getNamespace());
|
||||
|
||||
this.transport = tm.createTransport(this, transportElement);
|
||||
Async.go(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
getParent().getJingleManager().getConnection().createStanzaCollectorAndSend(JingleElement.createTransportAccept(session.getOurJid(), session.getPeer(), session.getSessionId(), getCreator(), getName(), transport.getElement()));
|
||||
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not send transport-accept: " + e, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
onAccept(connection);
|
||||
}
|
||||
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
|
@ -403,10 +453,10 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
|
|||
return;
|
||||
}
|
||||
|
||||
JingleTransport<?> rTransport = rManager.createTransport(this);
|
||||
pendingReplacingTransport = rManager.createTransport(this);
|
||||
|
||||
JingleElement transportReplace = JingleElement.createTransportReplace(session.getInitiator(), session.getPeer(),
|
||||
session.getSessionId(), getCreator(), getName(), rTransport.getElement());
|
||||
session.getSessionId(), getCreator(), getName(), pendingReplacingTransport.getElement());
|
||||
|
||||
connection.createStanzaCollectorAndSend(transportReplace).nextResultOrThrow();
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.jivesoftware.smackx.jingle.JingleManager;
|
|||
import org.jivesoftware.smackx.jingle.JingleTransportManager;
|
||||
import org.jivesoftware.smackx.jingle.components.JingleContent;
|
||||
import org.jivesoftware.smackx.jingle.components.JingleTransport;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.provider.JingleIBBTransportProvider;
|
||||
|
||||
/**
|
||||
|
@ -75,6 +76,12 @@ public final class JingleIBBTransportManager extends Manager implements JingleTr
|
|||
return new JingleIBBTransport(other.getSid(), (short) Math.min(other.getBlockSize(), MAX_BLOCKSIZE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JingleTransport<?> createTransport(JingleContent content, JingleContentTransportElement peersTransportElement) {
|
||||
JingleIBBTransport other = new JingleIBBTransportAdapter().transportFromElement(peersTransportElement);
|
||||
return createTransport(content, other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return -1;
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.jivesoftware.smack.XMPPConnection;
|
|||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.util.Async;
|
||||
import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;
|
||||
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
|
||||
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils;
|
||||
|
@ -59,7 +60,7 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
public static final String NAMESPACE_V1 = "urn:xmpp:jingle:transports:s5b:1";
|
||||
public static final String NAMESPACE = NAMESPACE_V1;
|
||||
|
||||
public static final int MAX_TIMEOUT = 10 * 1000;
|
||||
private static final int MAX_TIMEOUT = 10 * 1000;
|
||||
|
||||
private final String sid;
|
||||
|
||||
|
@ -75,26 +76,15 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
|
||||
private JingleTransportCallback callback;
|
||||
|
||||
@Override
|
||||
public void handleSessionAccept(JingleContentTransportElement transportElement, XMPPConnection connection) {
|
||||
JingleS5BTransportElement transport = (JingleS5BTransportElement) transportElement;
|
||||
theirDstAddr = transport.getDestinationAddress();
|
||||
theirMode = transport.getMode();
|
||||
for (JingleContentTransportCandidateElement c : transport.getCandidates()) {
|
||||
JingleS5BTransportCandidateElement candidate = (JingleS5BTransportCandidateElement) c;
|
||||
addTheirCandidate(new JingleS5BTransportCandidate(candidate));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create transport as initiator.
|
||||
* @param initiator
|
||||
* @param responder
|
||||
* @param sid
|
||||
* @param mode
|
||||
* @param ourCandidates
|
||||
* @param initiator initiator of the Jingle session.
|
||||
* @param responder responder.
|
||||
* @param sid sessionId of the Jingle session.
|
||||
* @param mode TCP/UDP.
|
||||
* @param ourCandidates our proxy candidates.
|
||||
*/
|
||||
public JingleS5BTransport(FullJid initiator, FullJid responder, String sid, Bytestream.Mode mode, List<JingleTransportCandidate<?>> ourCandidates) {
|
||||
JingleS5BTransport(FullJid initiator, FullJid responder, String sid, Bytestream.Mode mode, List<JingleTransportCandidate<?>> ourCandidates) {
|
||||
this.sid = sid;
|
||||
this.ourMode = mode;
|
||||
this.ourDstAddr = Socks5Utils.createDigest(sid, initiator, responder);
|
||||
|
@ -107,12 +97,12 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
|
||||
/**
|
||||
* Create simple transport as responder.
|
||||
* @param initiator
|
||||
* @param responder
|
||||
* @param ourCandidates
|
||||
* @param other
|
||||
* @param initiator initiator of the Jingle session.
|
||||
* @param responder responder.
|
||||
* @param ourCandidates our proxy candidates.
|
||||
* @param other transport of the other party.
|
||||
*/
|
||||
public JingleS5BTransport(FullJid initiator, FullJid responder, List<JingleTransportCandidate<?>> ourCandidates, JingleS5BTransport other) {
|
||||
JingleS5BTransport(FullJid initiator, FullJid responder, List<JingleTransportCandidate<?>> ourCandidates, JingleS5BTransport other) {
|
||||
this.sid = other.sid;
|
||||
this.ourMode = other.theirMode;
|
||||
this.ourDstAddr = Socks5Utils.createDigest(other.sid, initiator, responder);
|
||||
|
@ -131,15 +121,15 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
|
||||
/**
|
||||
* Create custom transport as responder.
|
||||
* @param sid
|
||||
* @param ourMode
|
||||
* @param theirMode
|
||||
* @param ourDstAddr
|
||||
* @param theirDstAddr
|
||||
* @param ourCandidates
|
||||
* @param theirCandidates
|
||||
* @param sid sessionId of the Jingle session.
|
||||
* @param ourMode UPD/TCP.
|
||||
* @param theirMode UPD/TCP.
|
||||
* @param ourDstAddr SOCKS5 destination address (digest)
|
||||
* @param theirDstAddr SOCKS5 destination address (digest)
|
||||
* @param ourCandidates our proxy candidates.
|
||||
* @param theirCandidates their proxy candidates.
|
||||
*/
|
||||
public JingleS5BTransport(String sid, Bytestream.Mode ourMode, Bytestream.Mode theirMode, String ourDstAddr, String theirDstAddr, List<JingleTransportCandidate<?>> ourCandidates, List<JingleTransportCandidate<?>> theirCandidates) {
|
||||
JingleS5BTransport(String sid, Bytestream.Mode ourMode, Bytestream.Mode theirMode, String ourDstAddr, String theirDstAddr, List<JingleTransportCandidate<?>> ourCandidates, List<JingleTransportCandidate<?>> theirCandidates) {
|
||||
this.sid = sid;
|
||||
this.ourMode = ourMode;
|
||||
this.theirMode = theirMode;
|
||||
|
@ -160,7 +150,7 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
|
||||
/**
|
||||
* Copy constructor.
|
||||
* @param original
|
||||
* @param original which will be copied.
|
||||
*/
|
||||
public JingleS5BTransport(JingleS5BTransport original) {
|
||||
this.sid = original.sid;
|
||||
|
@ -287,8 +277,8 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
}
|
||||
|
||||
void connectIfReady() {
|
||||
JingleS5BTransportManager jingleS5BTransportManager = JingleS5BTransportManager.getInstanceFor(getParent().getParent().getJingleManager().getConnection());
|
||||
JingleSession session = getParent().getParent();
|
||||
final JingleS5BTransportManager jingleS5BTransportManager = JingleS5BTransportManager.getInstanceFor(getParent().getParent().getJingleManager().getConnection());
|
||||
final JingleSession session = getParent().getParent();
|
||||
|
||||
if (ourSelectedCandidate == null || theirSelectedCandidate == null) {
|
||||
// Not yet ready if we or peer did not yet decide on a candidate.
|
||||
|
@ -322,6 +312,8 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
nominated = theirSelectedCandidate;
|
||||
}
|
||||
|
||||
boolean isProxy = nominated.getType() == JingleS5BTransportCandidateElement.Type.proxy;
|
||||
|
||||
if (nominated == theirSelectedCandidate) {
|
||||
|
||||
LOGGER.log(Level.INFO, "Their choice, so our proposed candidate is used.");
|
||||
|
@ -330,12 +322,22 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
nominated = nominated.connect(MAX_TIMEOUT, false);
|
||||
} catch (InterruptedException | IOException | XMPPException | SmackException | TimeoutException e) {
|
||||
LOGGER.log(Level.INFO, "Could not connect to our candidate.", e);
|
||||
|
||||
Async.go(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
session.getJingleManager().getConnection().createStanzaCollectorAndSend(jingleS5BTransportManager.createProxyError(JingleS5BTransport.this));
|
||||
} catch (SmackException.NotConnectedException | InterruptedException e1) {
|
||||
LOGGER.log(Level.SEVERE, "Could not send proxy error: " + e, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
callback.onTransportFailed(new S5BTransportException.CandidateError(e));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isProxy = nominated.getType() == JingleS5BTransportCandidateElement.Type.proxy;
|
||||
|
||||
if (isProxy) {
|
||||
LOGGER.log(Level.INFO, "Send candidate-activate.");
|
||||
JingleElement candidateActivate = jingleS5BTransportManager.createCandidateActivated((JingleS5BTransport) nominated.getParent(), nominated);
|
||||
|
@ -358,7 +360,6 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
//Our choice
|
||||
else {
|
||||
LOGGER.log(Level.INFO, "Our choice, so their candidate was used.");
|
||||
boolean isProxy = nominated.getType() == JingleS5BTransportCandidateElement.Type.proxy;
|
||||
if (!isProxy) {
|
||||
LOGGER.log(Level.INFO, "Start transmission on " + nominated.getCandidateId());
|
||||
this.bytestreamSession = new Socks5BytestreamSession(nominated.getSocket(), true);
|
||||
|
@ -370,7 +371,7 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
}
|
||||
|
||||
private void activateProxy(JingleS5BTransportCandidate candidate) throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||
LOGGER.log(Level.INFO, "Activate proxy: " + candidate.getCandidateId() + " " + candidate.getStreamHost().getAddress() + ":" + candidate.getStreamHost().getPort() + " " + candidate.getStreamHost().getJID());
|
||||
LOGGER.log(Level.INFO, "Activate proxy: " + candidate.getCandidateId() + " " + candidate.getStreamHost().getAddress() + ":" + candidate.getStreamHost().getPort() + " " + candidate.getStreamHost().getJID() + " for " + getParent().getParent().getPeer());
|
||||
Bytestream activate = new Bytestream(getSid());
|
||||
activate.setMode(null);
|
||||
activate.setType(IQ.Type.set);
|
||||
|
@ -380,6 +381,17 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
|
|||
getParent().getParent().getJingleManager().getConnection().createStanzaCollectorAndSend(activate).nextResultOrThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSessionAccept(JingleContentTransportElement transportElement, XMPPConnection connection) {
|
||||
JingleS5BTransportElement transport = (JingleS5BTransportElement) transportElement;
|
||||
theirDstAddr = transport.getDestinationAddress();
|
||||
theirMode = transport.getMode();
|
||||
for (JingleContentTransportCandidateElement c : transport.getCandidates()) {
|
||||
JingleS5BTransportCandidateElement candidate = (JingleS5BTransportCandidateElement) c;
|
||||
addTheirCandidate(new JingleS5BTransportCandidate(candidate));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IQ handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping) {
|
||||
switch (info.getElementName()) {
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.jivesoftware.smackx.jingle.components.JingleTransport;
|
|||
import org.jivesoftware.smackx.jingle.components.JingleTransportCandidate;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement;
|
||||
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportElement;
|
||||
|
@ -108,6 +109,12 @@ public final class JingleS5BTransportManager extends Manager implements JingleTr
|
|||
return new JingleS5BTransport(session.getInitiator(), session.getResponder(), collectCandidates(), (JingleS5BTransport) peersTransport);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JingleTransport<?> createTransport(JingleContent content, JingleContentTransportElement peersTransportElement) {
|
||||
JingleS5BTransport other = new JingleS5BTransportAdapter().transportFromElement(peersTransportElement);
|
||||
return createTransport(content, other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 10000;
|
||||
|
|
Loading…
Reference in a new issue