mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-26 05:52:06 +01:00
Jingle Session Establishment Refactoring
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@7457 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
4a86390b17
commit
41fbc3906a
7 changed files with 289 additions and 72 deletions
|
@ -496,7 +496,7 @@ public class JingleManager implements JingleSessionListener {
|
|||
jingleSession.removeListener(this);
|
||||
jingleSessions.remove(jingleSession);
|
||||
jingleSession.close();
|
||||
System.err.println("Declined");
|
||||
System.err.println("Declined:"+reason);
|
||||
}
|
||||
|
||||
public void sessionRedirected(String redirection, JingleSession jingleSession) {
|
||||
|
|
|
@ -98,8 +98,8 @@ public class ICECandidate extends TransportCandidate implements Comparable {
|
|||
* @param type type as defined in ICE-12
|
||||
*/
|
||||
public ICECandidate(String ip, int generation, int network,
|
||||
String password, int port, String username,
|
||||
int preference, String type) {
|
||||
String password, int port, String username,
|
||||
int preference, String type) {
|
||||
super(ip, port, generation);
|
||||
|
||||
proto = Protocol.UDP;
|
||||
|
@ -252,50 +252,61 @@ public class ICECandidate extends TransportCandidate implements Comparable {
|
|||
|
||||
Thread checkThread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
boolean isUsable = false;
|
||||
|
||||
final TestResult result = new TestResult();
|
||||
|
||||
// Media Proxy don´t have Echo features.
|
||||
// If its a relayed candidate we assumpt that is Checked.
|
||||
// If its a relayed candidate we assumpt that is NOT Valid while other candidates still being checked.
|
||||
// The negotiator MUST add then in the correct situations
|
||||
if (getType().equals("relay")) {
|
||||
try {
|
||||
Thread.sleep(TransportNegotiator.CANDIDATES_ACCEPT_PERIOD*2);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// Do Nothing
|
||||
}
|
||||
triggerCandidateChecked(true);
|
||||
triggerCandidateChecked(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ResultListener resultListener = new ResultListener() {
|
||||
public void testFinished(TestResult testResult) {
|
||||
if (testResult.isReachable()) {
|
||||
result.setResult(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (TransportCandidate candidate : localCandidates) {
|
||||
CandidateEcho echo = candidate.getCandidateEcho();
|
||||
if (echo != null) {
|
||||
try {
|
||||
InetAddress address = InetAddress.getByName(getIp());
|
||||
if (echo.test(address, getPort(),2000)) isUsable = true;
|
||||
if (isUsable) break;
|
||||
}
|
||||
catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
if (candidate instanceof ICECandidate) {
|
||||
ICECandidate iceCandidate = (ICECandidate) candidate;
|
||||
if (!iceCandidate.getType().equals("relay")) {
|
||||
try {
|
||||
echo.addResultListener(resultListener);
|
||||
InetAddress address = InetAddress.getByName(getIp());
|
||||
echo.testASync(address, getPort());
|
||||
}
|
||||
catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isUsable) {
|
||||
System.out.println("Checked using UDP Echo:" + getLocalIp());
|
||||
triggerCandidateChecked(isUsable);
|
||||
return;
|
||||
for (int i = 0; i < 10 && !result.isReachable(); i++)
|
||||
try {
|
||||
System.err.println(i);
|
||||
Thread.sleep(300);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for (TransportCandidate candidate : localCandidates) {
|
||||
CandidateEcho echo = candidate.getCandidateEcho();
|
||||
if (echo != null) {
|
||||
echo.removeResultListener(resultListener);
|
||||
}
|
||||
}
|
||||
|
||||
InetAddress candAddress;
|
||||
try {
|
||||
candAddress = InetAddress.getByName(getIp());
|
||||
isUsable = candAddress.isReachable(TransportResolver.CHECK_TIMEOUT);
|
||||
}
|
||||
catch (Exception e) {
|
||||
isUsable = false;
|
||||
}
|
||||
triggerCandidateChecked(isUsable);
|
||||
triggerCandidateChecked(result.isReachable());
|
||||
|
||||
//TODO candidate is being checked trigger
|
||||
//candidatesChecking.remove(cand);
|
||||
|
@ -327,14 +338,16 @@ public class ICECandidate extends TransportCandidate implements Comparable {
|
|||
if (other.getChannel() != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!getChannel().equals(other.getChannel())) {
|
||||
}
|
||||
else if (!getChannel().equals(other.getChannel())) {
|
||||
return false;
|
||||
}
|
||||
if (getId() == null) {
|
||||
if (other.getId() != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!getId().equals(other.getId())) {
|
||||
}
|
||||
else if (!getId().equals(other.getId())) {
|
||||
return false;
|
||||
}
|
||||
if (getNetwork() != other.getNetwork()) {
|
||||
|
@ -344,7 +357,8 @@ public class ICECandidate extends TransportCandidate implements Comparable {
|
|||
if (other.getPassword() != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!getPassword().equals(other.password)) {
|
||||
}
|
||||
else if (!getPassword().equals(other.password)) {
|
||||
return false;
|
||||
}
|
||||
if (getPreference() != other.getPreference()) {
|
||||
|
@ -354,14 +368,16 @@ public class ICECandidate extends TransportCandidate implements Comparable {
|
|||
if (other.getProto() != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!getProto().equals(other.getProto())) {
|
||||
}
|
||||
else if (!getProto().equals(other.getProto())) {
|
||||
return false;
|
||||
}
|
||||
if (getUsername() == null) {
|
||||
if (other.getUsername() != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!getUsername().equals(other.getUsername())) {
|
||||
}
|
||||
else if (!getUsername().equals(other.getUsername())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -370,9 +386,11 @@ public class ICECandidate extends TransportCandidate implements Comparable {
|
|||
public boolean isNull() {
|
||||
if (super.isNull()) {
|
||||
return true;
|
||||
} else if (getProto().isNull()) {
|
||||
}
|
||||
else if (getProto().isNull()) {
|
||||
return true;
|
||||
} else if (getChannel().isNull()) {
|
||||
}
|
||||
else if (getChannel().isNull()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -391,7 +409,8 @@ public class ICECandidate extends TransportCandidate implements Comparable {
|
|||
ICECandidate tc = (ICECandidate) arg;
|
||||
if (getPreference() < tc.getPreference()) {
|
||||
return -1;
|
||||
} else if (getPreference() > tc.getPreference()) {
|
||||
}
|
||||
else if (getPreference() > tc.getPreference()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2005 Jive Software.
|
||||
*
|
||||
* 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.jingle.nat;
|
||||
|
||||
/**
|
||||
* Listener for ECHO Test Results
|
||||
*
|
||||
* @author Thiago Camargo
|
||||
*/
|
||||
public interface ResultListener {
|
||||
|
||||
public void testFinished(TestResult result);
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jive Software. All rights reserved.
|
||||
* ====================================================================
|
||||
* The Jive Software License (based on Apache Software License, Version 1.1)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. The end-user documentation included with the redistribution,
|
||||
* if any, must include the following acknowledgment:
|
||||
* "This product includes software developed by
|
||||
* Jive Software (http://www.jivesoftware.com)."
|
||||
* Alternately, this acknowledgment may appear in the software itself,
|
||||
* if and wherever such third-party acknowledgments normally appear.
|
||||
*
|
||||
* 4. The names "Smack" and "Jive Software" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please
|
||||
* contact webmaster@jivesoftware.com.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Smack",
|
||||
* nor may "Smack" appear in their name, without prior written
|
||||
* permission of Jive Software.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.jingle.nat;
|
||||
|
||||
/**
|
||||
* Result of an ECHO Test
|
||||
*
|
||||
* @author Thiago Camargo
|
||||
*/
|
||||
public class TestResult {
|
||||
|
||||
private boolean result = false;
|
||||
|
||||
public boolean isReachable() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(boolean result) {
|
||||
this.result = result;
|
||||
}
|
||||
}
|
||||
|
|
@ -649,12 +649,13 @@ public abstract class TransportCandidate {
|
|||
DatagramSocket socket = null;
|
||||
byte password[] = null;
|
||||
List<DatagramListener> listeners = new ArrayList<DatagramListener>();
|
||||
List<ResultListener> resultListeners = new ArrayList<ResultListener>();
|
||||
boolean enabled = true;
|
||||
boolean ended = false;
|
||||
long tries = 10;
|
||||
|
||||
public CandidateEcho(TransportCandidate candidate) throws UnknownHostException, SocketException {
|
||||
this.socket = new DatagramSocket(candidate.getPort(), InetAddress.getByName("0.0.0.0"));
|
||||
this.socket = new DatagramSocket(candidate.getPort(), InetAddress.getByName(candidate.getLocalIp()));
|
||||
Random r = new Random();
|
||||
password = longToByteArray((Math.abs(r.nextLong())));
|
||||
}
|
||||
|
@ -712,27 +713,30 @@ public abstract class TransportCandidate {
|
|||
socket.close();
|
||||
}
|
||||
|
||||
private void fireTestResult(TestResult testResult) {
|
||||
for (ResultListener resultListener : resultListeners)
|
||||
resultListener.testFinished(testResult);
|
||||
}
|
||||
|
||||
public boolean test(final InetAddress address, final int port) {
|
||||
return test(address, port, 2000);
|
||||
}
|
||||
|
||||
public boolean test(final InetAddress address, final int port, int timeout) {
|
||||
|
||||
ended=false;
|
||||
ended = false;
|
||||
|
||||
final TestResults testResults = new TestResults();
|
||||
final TestResult testResult = new TestResult();
|
||||
|
||||
DatagramListener listener = new DatagramListener() {
|
||||
public boolean datagramReceived(DatagramPacket datagramPacket) {
|
||||
if (datagramPacket.getAddress().equals(address) && datagramPacket.getPort() == port) {
|
||||
if (Arrays.equals(datagramPacket.getData(), password)) {
|
||||
testResults.setResult(true);
|
||||
testResult.setResult(true);
|
||||
ended = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
testResults.setResult(false);
|
||||
ended = true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@ -765,7 +769,59 @@ public abstract class TransportCandidate {
|
|||
|
||||
this.removeListener(listener);
|
||||
|
||||
return testResults.isReachable();
|
||||
return testResult.isReachable();
|
||||
}
|
||||
|
||||
public void testASync(final InetAddress address, final int port) {
|
||||
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
|
||||
DatagramListener listener = new DatagramListener() {
|
||||
public boolean datagramReceived(DatagramPacket datagramPacket) {
|
||||
if (datagramPacket.getAddress().equals(address) && datagramPacket.getPort() == port) {
|
||||
if (Arrays.equals(datagramPacket.getData(), password)) {
|
||||
TestResult testResult = new TestResult();
|
||||
testResult.setResult(true);
|
||||
fireTestResult(testResult);
|
||||
ended = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
addListener(listener);
|
||||
|
||||
DatagramPacket packet = new DatagramPacket(password, password.length);
|
||||
|
||||
packet.setAddress(address);
|
||||
packet.setPort(port);
|
||||
|
||||
long delay = 200;
|
||||
|
||||
try {
|
||||
for (int i = 0; i < tries; i++) {
|
||||
socket.send(packet);
|
||||
if (ended) break;
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// Do Nothing
|
||||
}
|
||||
|
||||
removeListener(listener);
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public void addListener(DatagramListener listener) {
|
||||
|
@ -776,17 +832,12 @@ public abstract class TransportCandidate {
|
|||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public class TestResults {
|
||||
public void addResultListener(ResultListener resultListener) {
|
||||
resultListeners.add(resultListener);
|
||||
}
|
||||
|
||||
private boolean result=false;
|
||||
|
||||
public boolean isReachable() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(boolean result) {
|
||||
this.result = result;
|
||||
}
|
||||
public void removeResultListener(ResultListener resultListener) {
|
||||
resultListeners.remove(resultListener);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public abstract class TransportNegotiator extends JingleNegotiator {
|
|||
|
||||
// The time we give to the candidates check before we accept or decline the
|
||||
// transport (in milliseconds)
|
||||
public final static int CANDIDATES_ACCEPT_PERIOD = 3000;
|
||||
public final static int CANDIDATES_ACCEPT_PERIOD = 4000;
|
||||
|
||||
// The session this nenotiator belongs to
|
||||
private final JingleSession session;
|
||||
|
@ -69,6 +69,9 @@ public abstract class TransportNegotiator extends JingleNegotiator {
|
|||
// Valid remote candidates
|
||||
private final List<TransportCandidate> validRemoteCandidates = new ArrayList<TransportCandidate>();
|
||||
|
||||
// Accepted Remote Candidates
|
||||
private final List<TransportCandidate> acceptedRemoteCandidates = new ArrayList<TransportCandidate>();
|
||||
|
||||
// The best local candidate we have offered (and accepted by the other part)
|
||||
private TransportCandidate acceptedLocalCandidate;
|
||||
|
||||
|
@ -165,10 +168,6 @@ public abstract class TransportNegotiator extends JingleNegotiator {
|
|||
public void close() {
|
||||
super.close();
|
||||
|
||||
for (TransportCandidate candidate : offeredCandidates)
|
||||
if (candidate.getCandidateEcho() != null)
|
||||
candidate.removeCandidateEcho();
|
||||
|
||||
}
|
||||
|
||||
public List<TransportCandidate> getOfferedCandidates() {
|
||||
|
@ -283,10 +282,10 @@ public abstract class TransportNegotiator extends JingleNegotiator {
|
|||
|
||||
// Sleep for some time, waiting for the candidates checks
|
||||
|
||||
int totalTime = (CANDIDATES_ACCEPT_PERIOD + (TransportResolver.CHECK_TIMEOUT * (resolver.getCandidatesList().size() + 1)));
|
||||
int totalTime = (CANDIDATES_ACCEPT_PERIOD + TransportResolver.CHECK_TIMEOUT);
|
||||
int tries = (int) Math.ceil(totalTime / 1000);
|
||||
|
||||
for (int i = 0; i < tries; i++) {
|
||||
for (int i = 0; i < tries - 2; i++) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
@ -301,18 +300,64 @@ public abstract class TransportNegotiator extends JingleNegotiator {
|
|||
|
||||
if (bestRemote != null && (state == pending || state == active)) {
|
||||
// Accepting the remote candidate
|
||||
Jingle jout = new Jingle(Jingle.Action.TRANSPORTACCEPT);
|
||||
jout.addTransport(getJingleTransport(bestRemote));
|
||||
|
||||
// Send the packet
|
||||
js.sendFormattedJingle(jin, jout);
|
||||
if (!acceptedRemoteCandidates.contains(bestRemote)) {
|
||||
Jingle jout = new Jingle(Jingle.Action.TRANSPORTACCEPT);
|
||||
jout.addTransport(getJingleTransport(bestRemote));
|
||||
|
||||
// Send the packet
|
||||
js.sendFormattedJingle(jin, jout);
|
||||
acceptedRemoteCandidates.add(bestRemote);
|
||||
}
|
||||
if (isEstablished()) {
|
||||
setState(active);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Once we are in pending state, look for any valid remote
|
||||
// candidate, and send an "accept" if we have one...
|
||||
TransportCandidate bestRemote = getBestRemoteCandidate();
|
||||
|
||||
if (bestRemote == null) {
|
||||
for (TransportCandidate candidate : remoteCandidates) {
|
||||
if (candidate instanceof ICECandidate) {
|
||||
ICECandidate iceCandidate = (ICECandidate) candidate;
|
||||
if (iceCandidate.getType().equals("relay")) {
|
||||
//TODO Check if the relay is reacheable
|
||||
addValidRemoteCandidate(iceCandidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
bestRemote = getBestRemoteCandidate();
|
||||
State state = getState();
|
||||
if (bestRemote != null && (state == pending || state == active)) {
|
||||
if (!acceptedRemoteCandidates.contains(bestRemote)) {
|
||||
Jingle jout = new Jingle(Jingle.Action.TRANSPORTACCEPT);
|
||||
jout.addTransport(getJingleTransport(bestRemote));
|
||||
|
||||
// Send the packet
|
||||
js.sendFormattedJingle(jin, jout);
|
||||
acceptedRemoteCandidates.add(bestRemote);
|
||||
}
|
||||
if (isEstablished()) {
|
||||
setState(active);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getState() == null || !getState().equals(active)) {
|
||||
try {
|
||||
session.terminate();
|
||||
|
@ -698,9 +743,7 @@ public abstract class TransportNegotiator extends JingleNegotiator {
|
|||
setAcceptedLocalCandidate(cand);
|
||||
|
||||
if (isEstablished()) {
|
||||
|
||||
System.out.println("SET ACTIVE");
|
||||
|
||||
setState(active);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ public abstract class TransportResolver {
|
|||
public Type type = Type.rawupd;
|
||||
|
||||
// the time, in milliseconds, before a check aborts
|
||||
public static final int CHECK_TIMEOUT = 4000;
|
||||
public static final int CHECK_TIMEOUT = 5000;
|
||||
|
||||
// Listeners for events
|
||||
private final ArrayList<TransportResolverListener> listeners = new ArrayList<TransportResolverListener>();
|
||||
|
|
Loading…
Reference in a new issue