1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-09-09 10:19:41 +02:00

Introduce Smack's Modular Connection Architecture

This is a complete redesign of what was previously
XmppNioTcpConnection. The new architecture allows to extend an XMPP
client to server (c2s) connection with new transport bindings and
other extensions.
This commit is contained in:
Florian Schmaus 2020-04-04 13:03:31 +02:00
parent cec312fe64
commit cc636fff21
142 changed files with 6819 additions and 4068 deletions

View file

@ -1,21 +0,0 @@
.PHONY := all clean
GRADLE_QUITE_ARGS := --quiet --console plain
XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG := src/javadoc/org/jivesoftware/smack/tcp/doc-files/XmppNioTcpConnectionStateGraph.png
XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT := $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG:.png=.dot)
GENERATED_FILES := $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG) $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT)
all: $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG)
clean:
rm -f $(GENERATED_FILES)
%.png: %.dot
dot -Tpng -o $@ $^
$(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT): src/main/java/org/jivesoftware/smack/tcp/XmppNioTcpConnection.java ../smack-core/src/main/java/org/jivesoftware/smack/fsm/AbstractXmppStateMachineConnection.java
# TODO: This also creates the dot file even if the command
# fails. It would be better if this was not the case.
gradle $(GRADLE_QUITE_ARGS) :smack-repl:printXmppNioTcpConnectionStateGraph > $@

View file

@ -1,2 +0,0 @@
/XmppNioTcpConnectionStateGraph.png
/XmppNioTcpConnectionStateGraph.dot

View file

@ -0,0 +1,141 @@
/**
*
* Copyright 2019-2020 Florian Schmaus
*
* 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.smack.sm;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.AuthenticatedAndResourceBoundStateDescriptor;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.AuthenticatedButUnboundStateDescriptor;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.ResourceBindingStateDescriptor;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModule;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.c2s.internal.WalkStateGraphContext;
import org.jivesoftware.smack.compression.CompressionModule.CompressionStateDescriptor;
import org.jivesoftware.smack.fsm.State;
import org.jivesoftware.smack.fsm.StateDescriptor;
import org.jivesoftware.smack.fsm.StateTransitionResult;
public class StreamManagementModule extends ModularXmppClientToServerConnectionModule<StreamManagementModuleDescriptor> {
protected StreamManagementModule(StreamManagementModuleDescriptor moduleDescriptor,
ModularXmppClientToServerConnectionInternal connectionInternal) {
super(moduleDescriptor, connectionInternal);
}
private boolean useSm = true;
private boolean useSmResumption = true;
public static final class EnableStreamManagementStateDescriptor extends StateDescriptor {
private EnableStreamManagementStateDescriptor() {
super(StreamManagementModule.EnableStreamManagementState.class, 198, StateDescriptor.Property.notImplemented);
addPredeccessor(ResourceBindingStateDescriptor.class);
addSuccessor(AuthenticatedAndResourceBoundStateDescriptor.class);
declarePrecedenceOver(AuthenticatedAndResourceBoundStateDescriptor.class);
}
@Override
protected StreamManagementModule.EnableStreamManagementState constructState(ModularXmppClientToServerConnectionInternal connectionInternal) {
// This is the trick: the module is constructed prior the states, so we get the actual state out of the module by fetching the module from the connection.
StreamManagementModule smModule = connectionInternal.connection.getConnectionModuleFor(StreamManagementModuleDescriptor.class);
return smModule.constructEnableStreamMangementState(this, connectionInternal);
}
}
private EnableStreamManagementState constructEnableStreamMangementState(
EnableStreamManagementStateDescriptor enableStreamManagementStateDescriptor,
ModularXmppClientToServerConnectionInternal connectionInternal) {
return new EnableStreamManagementState(enableStreamManagementStateDescriptor, connectionInternal);
}
private final class EnableStreamManagementState extends State {
private EnableStreamManagementState(EnableStreamManagementStateDescriptor stateDescriptor,
ModularXmppClientToServerConnectionInternal connectionInternal) {
super(stateDescriptor, connectionInternal);
}
@Override
public StateTransitionResult.TransitionImpossible isTransitionToPossible(WalkStateGraphContext walkStateGraphContext) {
if (!useSm) {
return new StateTransitionResult.TransitionImpossibleReason("Stream management not enabled");
}
return new StateTransitionResult.TransitionImpossibleBecauseNotImplemented(stateDescriptor);
}
@Override
public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) {
throw new IllegalStateException("SM not implemented");
}
}
public static final class ResumeStreamStateDescriptor extends StateDescriptor {
private ResumeStreamStateDescriptor() {
super(StreamManagementModule.ResumeStreamState.class, 198, StateDescriptor.Property.notImplemented);
addPredeccessor(AuthenticatedButUnboundStateDescriptor.class);
addSuccessor(AuthenticatedAndResourceBoundStateDescriptor.class);
declarePrecedenceOver(ResourceBindingStateDescriptor.class);
declareInferiortyTo(CompressionStateDescriptor.class);
}
@Override
protected StreamManagementModule.ResumeStreamState constructState(ModularXmppClientToServerConnectionInternal connectionInternal) {
StreamManagementModule smModule = connectionInternal.connection.getConnectionModuleFor(StreamManagementModuleDescriptor.class);
return smModule.constructResumeStreamState(this, connectionInternal);
}
}
private ResumeStreamState constructResumeStreamState(
ResumeStreamStateDescriptor resumeStreamStateDescriptor,
ModularXmppClientToServerConnectionInternal connectionInternal) {
return new ResumeStreamState(resumeStreamStateDescriptor, connectionInternal);
}
private final class ResumeStreamState extends State {
private ResumeStreamState(ResumeStreamStateDescriptor stateDescriptor,
ModularXmppClientToServerConnectionInternal connectionInternal) {
super(stateDescriptor, connectionInternal);
}
@Override
public StateTransitionResult.TransitionImpossible isTransitionToPossible(WalkStateGraphContext walkStateGraphContext) {
if (!useSmResumption) {
return new StateTransitionResult.TransitionImpossibleReason("Stream resumption not enabled");
}
return new StateTransitionResult.TransitionImpossibleBecauseNotImplemented(stateDescriptor);
}
@Override
public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) {
throw new IllegalStateException("Stream resumption not implemented");
}
}
public void setStreamManagementEnabled(boolean useSm) {
this.useSm = useSm;
}
public void setStreamResumptionEnabled(boolean useSmResumption) {
this.useSmResumption = useSmResumption;
}
}

View file

@ -0,0 +1,58 @@
/**
*
* Copyright 2019-2020 Florian Schmaus
*
* 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.smack.sm;
import java.util.HashSet;
import java.util.Set;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModuleDescriptor;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.fsm.StateDescriptor;
import org.jivesoftware.smack.sm.StreamManagementModule.EnableStreamManagementStateDescriptor;
import org.jivesoftware.smack.sm.StreamManagementModule.ResumeStreamStateDescriptor;
public class StreamManagementModuleDescriptor extends ModularXmppClientToServerConnectionModuleDescriptor {
private static final StreamManagementModuleDescriptor INSTANCE = new StreamManagementModuleDescriptor();
@Override
protected Set<Class<? extends StateDescriptor>> getStateDescriptors() {
Set<Class<? extends StateDescriptor>> res = new HashSet<>();
res.add(EnableStreamManagementStateDescriptor.class);
res.add(ResumeStreamStateDescriptor.class);
return res;
}
@Override
protected StreamManagementModule constructXmppConnectionModule(
ModularXmppClientToServerConnectionInternal connectionInternal) {
return new StreamManagementModule(this, connectionInternal);
}
public static class Builder extends ModularXmppClientToServerConnectionModuleDescriptor.Builder {
protected Builder(ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder) {
super(connectionConfigurationBuilder);
}
@Override
protected StreamManagementModuleDescriptor build() {
return INSTANCE;
}
}
}

View file

@ -0,0 +1,175 @@
/**
*
* Copyright 2019-2020 Florian Schmaus
*
* 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.smack.tcp;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.jivesoftware.smack.SmackException.ConnectionException;
import org.jivesoftware.smack.SmackException.EndpointConnectionException;
import org.jivesoftware.smack.SynchronizationPoint;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.tcp.XmppTcpTransportModule.EstablishingTcpConnectionState;
import org.jivesoftware.smack.tcp.rce.Rfc6120TcpRemoteConnectionEndpoint;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpoint;
import org.jivesoftware.smack.util.rce.RemoteConnectionException;
public final class ConnectionAttemptState {
private final ModularXmppClientToServerConnectionInternal connectionInternal;
private final XmppTcpTransportModule.XmppTcpNioTransport.DiscoveredTcpEndpoints discoveredEndpoints;
private final EstablishingTcpConnectionState establishingTcpConnectionState;
// TODO: Check if we can re-use the socket channel in case some InetSocketAddress fail to connect to.
final SocketChannel socketChannel;
final List<RemoteConnectionException<?>> connectionExceptions;
final SynchronizationPoint<ConnectionException> tcpConnectionEstablishedSyncPoint;
final Iterator<Rfc6120TcpRemoteConnectionEndpoint> connectionEndpointIterator;
/** The current connection endpoint we are trying */
Rfc6120TcpRemoteConnectionEndpoint connectionEndpoint;
Iterator<? extends InetAddress> inetAddressIterator;
ConnectionAttemptState(ModularXmppClientToServerConnectionInternal connectionInternal,
XmppTcpTransportModule.XmppTcpNioTransport.DiscoveredTcpEndpoints discoveredEndpoints,
EstablishingTcpConnectionState establishingTcpConnectionState) throws IOException {
this.connectionInternal = connectionInternal;
this.discoveredEndpoints = discoveredEndpoints;
this.establishingTcpConnectionState = establishingTcpConnectionState;
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
connectionEndpointIterator = discoveredEndpoints.result.discoveredRemoteConnectionEndpoints.iterator();
connectionEndpoint = connectionEndpointIterator.next();
connectionExceptions = new ArrayList<>(discoveredEndpoints.result.discoveredRemoteConnectionEndpoints.size());
tcpConnectionEstablishedSyncPoint = new SynchronizationPoint<>(connectionInternal.connection,
"TCP connection establishment");
}
void establishTcpConnection() {
RemoteConnectionEndpoint.InetSocketAddressCoupling<Rfc6120TcpRemoteConnectionEndpoint> address = nextAddress();
establishTcpConnection(address);
}
private void establishTcpConnection(
RemoteConnectionEndpoint.InetSocketAddressCoupling<Rfc6120TcpRemoteConnectionEndpoint> address) {
TcpHostEvent.ConnectingToHostEvent connectingToHostEvent = new TcpHostEvent.ConnectingToHostEvent(
establishingTcpConnectionState, address);
connectionInternal.invokeConnectionStateMachineListener(connectingToHostEvent);
final boolean connected;
final InetSocketAddress inetSocketAddress = address.getInetSocketAddress();
try {
connected = socketChannel.connect(inetSocketAddress);
} catch (IOException e) {
onIOExceptionWhenEstablishingTcpConnection(e, address);
return;
}
if (connected) {
TcpHostEvent.ConnectedToHostEvent connectedToHostEvent = new TcpHostEvent.ConnectedToHostEvent(
establishingTcpConnectionState, address, true);
connectionInternal.invokeConnectionStateMachineListener(connectedToHostEvent);
tcpConnectionEstablishedSyncPoint.reportSuccess();
return;
}
try {
connectionInternal.registerWithSelector(socketChannel, SelectionKey.OP_CONNECT,
(selectedChannel, selectedSelectionKey) -> {
SocketChannel selectedSocketChannel = (SocketChannel) selectedChannel;
boolean finishConnected;
try {
finishConnected = selectedSocketChannel.finishConnect();
} catch (IOException e) {
Async.go(() -> onIOExceptionWhenEstablishingTcpConnection(e, address));
return;
}
if (!finishConnected) {
Async.go(() -> onIOExceptionWhenEstablishingTcpConnection(new IOException("finishConnect() failed"), address));
return;
}
TcpHostEvent.ConnectedToHostEvent connectedToHostEvent = new TcpHostEvent.ConnectedToHostEvent(
establishingTcpConnectionState, address, false);
connectionInternal.invokeConnectionStateMachineListener(connectedToHostEvent);
// Do not set 'state' here, since this is processed by a reactor thread, which doesn't hold
// the objects lock.
tcpConnectionEstablishedSyncPoint.reportSuccess();
});
} catch (ClosedChannelException e) {
onIOExceptionWhenEstablishingTcpConnection(e, address);
}
}
private void onIOExceptionWhenEstablishingTcpConnection(IOException exception,
RemoteConnectionEndpoint.InetSocketAddressCoupling<Rfc6120TcpRemoteConnectionEndpoint> failedAddress) {
RemoteConnectionEndpoint.InetSocketAddressCoupling<Rfc6120TcpRemoteConnectionEndpoint> nextInetSocketAddress = nextAddress();
if (nextInetSocketAddress == null) {
EndpointConnectionException connectionException = EndpointConnectionException.from(
discoveredEndpoints.result.lookupFailures, connectionExceptions);
tcpConnectionEstablishedSyncPoint.reportFailure(connectionException);
return;
}
tcpConnectionEstablishedSyncPoint.resetTimeout();
RemoteConnectionException<Rfc6120TcpRemoteConnectionEndpoint> rce = new RemoteConnectionException<>(
failedAddress, exception);
connectionExceptions.add(rce);
TcpHostEvent.ConnectionToHostFailedEvent connectionToHostFailedEvent = new TcpHostEvent.ConnectionToHostFailedEvent(
establishingTcpConnectionState, nextInetSocketAddress, exception);
connectionInternal.invokeConnectionStateMachineListener(connectionToHostFailedEvent);
establishTcpConnection(nextInetSocketAddress);
}
private RemoteConnectionEndpoint.InetSocketAddressCoupling<Rfc6120TcpRemoteConnectionEndpoint> nextAddress() {
if (inetAddressIterator == null || !inetAddressIterator.hasNext()) {
if (!connectionEndpointIterator.hasNext()) {
return null;
}
connectionEndpoint = connectionEndpointIterator.next();
inetAddressIterator = connectionEndpoint.getInetAddresses().iterator();
// Every valid connection addresspoint must have a non-empty collection of inet addresses.
assert inetAddressIterator.hasNext();
}
InetAddress inetAddress = inetAddressIterator.next();
return new RemoteConnectionEndpoint.InetSocketAddressCoupling<>(connectionEndpoint, inetAddress);
}
}

View file

@ -1,6 +1,6 @@
/**
*
* Copyright © 2014 Florian Schmaus
* Copyright © 2014-2020 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,10 +16,17 @@
*/
package org.jivesoftware.smack.tcp;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.initializer.UrlInitializer;
import org.jivesoftware.smack.sm.StreamManagementModuleDescriptor;
public class TCPInitializer extends UrlInitializer {
static {
SmackConfiguration.addModule(StreamManagementModuleDescriptor.class);
SmackConfiguration.addModule(XmppTcpTransportModuleDescriptor.class);
}
@Override
protected String getProvidersUri() {
return "classpath:org.jivesoftware.smack.tcp/smacktcp.providers";

View file

@ -0,0 +1,79 @@
/**
*
* Copyright 2019-2020 Florian Schmaus
*
* 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.smack.tcp;
import java.io.IOException;
import org.jivesoftware.smack.fsm.ConnectionStateEvent.DetailedTransitionIntoInformation;
import org.jivesoftware.smack.fsm.State;
import org.jivesoftware.smack.tcp.rce.Rfc6120TcpRemoteConnectionEndpoint;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpoint;
public abstract class TcpHostEvent extends DetailedTransitionIntoInformation {
protected final RemoteConnectionEndpoint.InetSocketAddressCoupling<Rfc6120TcpRemoteConnectionEndpoint> address;
protected TcpHostEvent(State state, RemoteConnectionEndpoint.InetSocketAddressCoupling<Rfc6120TcpRemoteConnectionEndpoint> address) {
super(state);
this.address = address;
}
public RemoteConnectionEndpoint.InetSocketAddressCoupling<Rfc6120TcpRemoteConnectionEndpoint> getAddress() {
return address;
}
@Override
public String toString() {
return super.toString() + ": " + address;
}
public static final class ConnectingToHostEvent extends TcpHostEvent {
ConnectingToHostEvent(State state,
RemoteConnectionEndpoint.InetSocketAddressCoupling<Rfc6120TcpRemoteConnectionEndpoint> address) {
super(state, address);
}
}
public static final class ConnectedToHostEvent extends TcpHostEvent {
private final boolean connectionEstablishedImmediately;
ConnectedToHostEvent(State state, RemoteConnectionEndpoint.InetSocketAddressCoupling<Rfc6120TcpRemoteConnectionEndpoint> address, boolean immediately) {
super(state, address);
this.connectionEstablishedImmediately = immediately;
}
@Override
public String toString() {
return super.toString() + (connectionEstablishedImmediately ? "" : " not") + " connected immediately";
}
}
public static final class ConnectionToHostFailedEvent extends TcpHostEvent {
private final IOException ioException;
ConnectionToHostFailedEvent(State state,
RemoteConnectionEndpoint.InetSocketAddressCoupling<Rfc6120TcpRemoteConnectionEndpoint> address,
IOException ioException) {
super(state, address);
this.ioException = ioException;
}
@Override
public String toString() {
return super.toString() + ioException;
}
}
}

View file

@ -64,6 +64,7 @@ import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.AlreadyConnectedException;
import org.jivesoftware.smack.SmackException.AlreadyLoggedInException;
import org.jivesoftware.smack.SmackException.ConnectionException;
import org.jivesoftware.smack.SmackException.EndpointConnectionException;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.SmackException.NotLoggedInException;
@ -78,6 +79,7 @@ import org.jivesoftware.smack.XMPPException.StreamErrorException;
import org.jivesoftware.smack.compress.packet.Compress;
import org.jivesoftware.smack.compress.packet.Compressed;
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.datatypes.UInt16;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.packet.Element;
import org.jivesoftware.smack.packet.IQ;
@ -105,6 +107,8 @@ import org.jivesoftware.smack.sm.packet.StreamManagement.Resumed;
import org.jivesoftware.smack.sm.packet.StreamManagement.StreamManagementFeature;
import org.jivesoftware.smack.sm.predicates.Predicate;
import org.jivesoftware.smack.sm.provider.ParseStreamManagement;
import org.jivesoftware.smack.tcp.rce.RemoteXmppTcpConnectionEndpoints;
import org.jivesoftware.smack.tcp.rce.Rfc6120TcpRemoteConnectionEndpoint;
import org.jivesoftware.smack.util.ArrayBlockingQueueWithShutdown;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smack.util.CloseableUtil;
@ -112,7 +116,7 @@ import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.TLSUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smack.util.dns.HostAddress;
import org.jivesoftware.smack.util.rce.RemoteConnectionException;
import org.jivesoftware.smack.xml.SmackXmlParser;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
@ -556,19 +560,23 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
}
private void connectUsingConfiguration() throws ConnectionException, IOException, InterruptedException {
List<HostAddress> failedAddresses = populateHostAddresses();
RemoteXmppTcpConnectionEndpoints.Result<Rfc6120TcpRemoteConnectionEndpoint> result = RemoteXmppTcpConnectionEndpoints.lookup(config);
List<RemoteConnectionException<Rfc6120TcpRemoteConnectionEndpoint>> connectionExceptions = new ArrayList<>();
SocketFactory socketFactory = config.getSocketFactory();
ProxyInfo proxyInfo = config.getProxyInfo();
int timeout = config.getConnectTimeout();
if (socketFactory == null) {
socketFactory = SocketFactory.getDefault();
}
for (HostAddress hostAddress : hostAddresses) {
Iterator<InetAddress> inetAddresses;
String host = hostAddress.getHost();
int port = hostAddress.getPort();
for (Rfc6120TcpRemoteConnectionEndpoint endpoint : result.discoveredRemoteConnectionEndpoints) {
Iterator<? extends InetAddress> inetAddresses;
String host = endpoint.getHost().toString();
UInt16 portUint16 = endpoint.getPort();
int port = portUint16.intValue();
if (proxyInfo == null) {
inetAddresses = hostAddress.getInetAddresses().iterator();
inetAddresses = endpoint.getInetAddresses().iterator();
assert inetAddresses.hasNext();
innerloop: while (inetAddresses.hasNext()) {
@ -584,7 +592,9 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
try {
socket = socketFuture.getOrThrow();
} catch (IOException e) {
hostAddress.setException(inetAddress, e);
RemoteConnectionException<Rfc6120TcpRemoteConnectionEndpoint> rce = new RemoteConnectionException<>(
endpoint, inetAddress, e);
connectionExceptions.add(rce);
if (inetAddresses.hasNext()) {
continue innerloop;
} else {
@ -594,34 +604,36 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
LOGGER.finer("Established TCP connection to " + inetSocketAddress);
// We found a host to connect to, return here
this.host = host;
this.port = port;
this.port = portUint16;
return;
}
failedAddresses.add(hostAddress);
} else {
// TODO: Move this into the inner-loop above. There appears no reason why we should not try a proxy
// connection to every inet address of each connection endpoint.
socket = socketFactory.createSocket();
StringUtils.requireNotNullNorEmpty(host, "Host of HostAddress " + hostAddress + " must not be null when using a Proxy");
StringUtils.requireNotNullNorEmpty(host, "Host of endpoint " + endpoint + " must not be null when using a Proxy");
final String hostAndPort = host + " at port " + port;
LOGGER.finer("Trying to establish TCP connection via Proxy to " + hostAndPort);
try {
proxyInfo.getProxySocketConnection().connect(socket, host, port, timeout);
} catch (IOException e) {
CloseableUtil.maybeClose(socket, LOGGER);
hostAddress.setException(e);
failedAddresses.add(hostAddress);
RemoteConnectionException<Rfc6120TcpRemoteConnectionEndpoint> rce = new RemoteConnectionException<>(endpoint, null, e);
connectionExceptions.add(rce);
continue;
}
LOGGER.finer("Established TCP connection to " + hostAndPort);
// We found a host to connect to, return here
this.host = host;
this.port = port;
this.port = portUint16;
return;
}
}
// There are no more host addresses to try
// throw an exception and report all tried
// HostAddresses in the exception
throw ConnectionException.from(failedAddresses);
throw EndpointConnectionException.from(result.lookupFailures, connectionExceptions);
}
/**
@ -815,7 +827,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
*/
@Override
protected void connectInternal() throws SmackException, IOException, XMPPException, InterruptedException {
closingStreamReceived.init();
// Establishes the TCP connection to the server and does setup the reader and writer. Throws an exception if
// there is an error establishing the connection
connectUsingConfiguration();
@ -1125,6 +1136,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
}
}
catch (Exception e) {
// TODO: Move the call closingStreamReceived.reportFailure(e) into notifyConnectionError?
closingStreamReceived.reportFailure(e);
// The exception can be ignored if the the connection is 'done'
// or if the it was caused because the socket got closed. It can not be ignored if it

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,91 @@
/**
*
* Copyright 2019-2020 Florian Schmaus
*
* 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.smack.tcp;
import java.util.HashSet;
import java.util.Set;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModuleDescriptor;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.fsm.StateDescriptor;
import org.jivesoftware.smack.tcp.XmppTcpTransportModule.EstablishTlsStateDescriptor;
import org.jivesoftware.smack.tcp.XmppTcpTransportModule.EstablishingTcpConnectionStateDescriptor;
public class XmppTcpTransportModuleDescriptor extends ModularXmppClientToServerConnectionModuleDescriptor {
private final boolean startTls;
private final boolean directTls;
public XmppTcpTransportModuleDescriptor(Builder builder) {
startTls = builder.startTls;
directTls = builder.directTls;
}
@Override
protected Set<Class<? extends StateDescriptor>> getStateDescriptors() {
Set<Class<? extends StateDescriptor>> res = new HashSet<>();
res.add(EstablishingTcpConnectionStateDescriptor.class);
if (startTls) {
res.add(EstablishTlsStateDescriptor.class);
}
if (directTls) {
// TODO: Add direct TLS.
throw new IllegalArgumentException("DirectTLS is not implemented yet");
}
return res;
}
@Override
protected XmppTcpTransportModule constructXmppConnectionModule(ModularXmppClientToServerConnectionInternal connectionInternal) {
return new XmppTcpTransportModule(this, connectionInternal);
}
public boolean isStartTlsEnabled() {
return startTls;
}
public boolean isDirectTlsEnabled() {
return directTls;
}
public static final class Builder extends ModularXmppClientToServerConnectionModuleDescriptor.Builder {
private Builder(ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder) {
super(connectionConfigurationBuilder);
}
private boolean startTls = true;
private boolean directTls = false;
public Builder disableDirectTls() {
directTls = false;
return this;
}
public Builder disableStartTls() {
startTls = false;
return this;
}
@Override
protected XmppTcpTransportModuleDescriptor build() {
return new XmppTcpTransportModuleDescriptor(this);
}
}
}

View file

@ -0,0 +1,78 @@
/**
*
* Copyright 2020 Florian Schmaus.
*
* 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.smack.tcp.rce;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import org.jivesoftware.smack.datatypes.UInt16;
import org.jivesoftware.smack.util.rce.SingleAddressRemoteConnectionEndpoint;
import org.minidns.record.A;
import org.minidns.record.AAAA;
import org.minidns.record.InternetAddressRR;
public final class IpTcpRemoteConnectionEndpoint<IARR extends InternetAddressRR>
implements Rfc6120TcpRemoteConnectionEndpoint, SingleAddressRemoteConnectionEndpoint {
private final CharSequence host;
private final UInt16 port;
private final IARR internetAddressResourceRecord;
public IpTcpRemoteConnectionEndpoint(CharSequence host, UInt16 port, IARR internetAddressResourceRecord) {
this.host = host;
this.port = port;
this.internetAddressResourceRecord = internetAddressResourceRecord;
}
public static IpTcpRemoteConnectionEndpoint<InternetAddressRR> from(CharSequence host, int port,
InetAddress inetAddress) {
InternetAddressRR internetAddressResourceRecord;
// TODO: Use InternetAddressRR.from(InetAddress) once MiniDNS is updated.
if (inetAddress instanceof Inet4Address) {
internetAddressResourceRecord = new A((Inet4Address) inetAddress);
} else {
internetAddressResourceRecord = new AAAA((Inet6Address) inetAddress);
}
return new IpTcpRemoteConnectionEndpoint<InternetAddressRR>(host, UInt16.from(port),
internetAddressResourceRecord);
}
@Override
public CharSequence getHost() {
return host;
}
@Override
public UInt16 getPort() {
return port;
}
@Override
public InetAddress getInetAddress() {
return internetAddressResourceRecord.getInetAddress();
}
@Override
public String getDescription() {
return "RFC 6120 A/AAAA Endpoint + [" + host + ":" + port + "]";
}
}

View file

@ -0,0 +1,232 @@
/**
*
* Copyright 2015-2020 Florian Schmaus.
*
* 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.smack.tcp.rce;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode;
import org.jivesoftware.smack.util.DNSUtil;
import org.jivesoftware.smack.util.dns.DNSResolver;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpoint;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure;
import org.minidns.dnsname.DnsName;
import org.minidns.record.InternetAddressRR;
import org.minidns.record.SRV;
import org.minidns.util.SrvUtil;
public class RemoteXmppTcpConnectionEndpoints {
private static final Logger LOGGER = Logger.getLogger(RemoteXmppTcpConnectionEndpoints.class.getName());
public static final String XMPP_CLIENT_DNS_SRV_PREFIX = "_xmpp-client._tcp";
public static final String XMPP_SERVER_DNS_SRV_PREFIX = "_xmpp-server._tcp";
/**
* Lookups remote connection endpoints on the server for XMPP connections over TCP taking A, AAAA and SRV resource
* records into account. If no host address was configured and all lookups failed, for example with NX_DOMAIN, then
* result will be populated with the empty list.
*
* @param config the connection configuration to lookup the endpoints for.
* @return a lookup result.
*/
public static Result<Rfc6120TcpRemoteConnectionEndpoint> lookup(ConnectionConfiguration config) {
List<Rfc6120TcpRemoteConnectionEndpoint> discoveredRemoteConnectionEndpoints;
List<RemoteConnectionEndpointLookupFailure> lookupFailures;
final InetAddress hostAddress = config.getHostAddress();
final DnsName host = config.getHost();
if (hostAddress != null) {
lookupFailures = Collections.emptyList();
IpTcpRemoteConnectionEndpoint<InternetAddressRR> connectionEndpoint = IpTcpRemoteConnectionEndpoint.from(
hostAddress.toString(), config.getPort(), hostAddress);
discoveredRemoteConnectionEndpoints = Collections.singletonList(connectionEndpoint);
} else if (host != null) {
lookupFailures = new ArrayList<>(1);
List<InetAddress> hostAddresses = DNSUtil.getDNSResolver().lookupHostAddress(host,
lookupFailures, config.getDnssecMode());
if (hostAddresses != null) {
discoveredRemoteConnectionEndpoints = new ArrayList<>(hostAddresses.size());
int port = config.getPort();
for (InetAddress inetAddress : hostAddresses) {
IpTcpRemoteConnectionEndpoint<InternetAddressRR> connectionEndpoint = IpTcpRemoteConnectionEndpoint.from(
host, port, inetAddress);
discoveredRemoteConnectionEndpoints.add(connectionEndpoint);
}
} else {
discoveredRemoteConnectionEndpoints = Collections.emptyList();
}
} else {
lookupFailures = new ArrayList<>();
// N.B.: Important to use config.serviceName and not AbstractXMPPConnection.serviceName
DnsName dnsName = config.getXmppServiceDomainAsDnsNameIfPossible();
if (dnsName == null) {
// TODO: ConnectionConfiguration should check on construction time that either the given XMPP service
// name is also a valid DNS name, or that a host is explicitly configured.
throw new IllegalStateException();
}
discoveredRemoteConnectionEndpoints = resolveXmppServiceDomain(dnsName, lookupFailures, config.getDnssecMode());
}
// Either the populated host addresses are not empty *or* there must be at least one failed address.
assert !discoveredRemoteConnectionEndpoints.isEmpty() || !lookupFailures.isEmpty();
return new Result<>(discoveredRemoteConnectionEndpoints, lookupFailures);
}
public static final class Result<RCE extends RemoteConnectionEndpoint> {
public final List<RCE> discoveredRemoteConnectionEndpoints;
public final List<RemoteConnectionEndpointLookupFailure> lookupFailures;
private Result(List<RCE> discoveredRemoteConnectionEndpoints, List<RemoteConnectionEndpointLookupFailure> lookupFailures) {
this.discoveredRemoteConnectionEndpoints = discoveredRemoteConnectionEndpoints;
this.lookupFailures = lookupFailures;
}
}
@SuppressWarnings("ImmutableEnumChecker")
enum DomainType {
server(XMPP_SERVER_DNS_SRV_PREFIX),
client(XMPP_CLIENT_DNS_SRV_PREFIX),
;
public final DnsName srvPrefix;
DomainType(String srvPrefixString) {
srvPrefix = DnsName.from(srvPrefixString);
}
}
/**
* Returns a list of HostAddresses under which the specified XMPP server can be reached at for client-to-server
* communication. A DNS lookup for a SRV record in the form "_xmpp-client._tcp.example.com" is attempted, according
* to section 3.2.1 of RFC 6120. If that lookup fails, it's assumed that the XMPP server lives at the host resolved
* by a DNS lookup at the specified domain on the default port of 5222.
* <p>
* As an example, a lookup for "example.com" may return "im.example.com:5269".
* </p>
*
* @param domain the domain.
* @param lookupFailures on optional list that will be populated with host addresses that failed to resolve.
* @param dnssecMode DNSSec mode.
* @return List of HostAddress, which encompasses the hostname and port that the
* XMPP server can be reached at for the specified domain.
*/
public static List<Rfc6120TcpRemoteConnectionEndpoint> resolveXmppServiceDomain(DnsName domain,
List<RemoteConnectionEndpointLookupFailure> lookupFailures, DnssecMode dnssecMode) {
DNSResolver dnsResolver = getDnsResolverOrThrow();
return resolveDomain(domain, DomainType.client, lookupFailures, dnssecMode, dnsResolver);
}
/**
* Returns a list of HostAddresses under which the specified XMPP server can be reached at for server-to-server
* communication. A DNS lookup for a SRV record in the form "_xmpp-server._tcp.example.com" is attempted, according
* to section 3.2.1 of RFC 6120. If that lookup fails , it's assumed that the XMPP server lives at the host resolved
* by a DNS lookup at the specified domain on the default port of 5269.
* <p>
* As an example, a lookup for "example.com" may return "im.example.com:5269".
* </p>
*
* @param domain the domain.
* @param lookupFailures a list that will be populated with host addresses that failed to resolve.
* @param dnssecMode DNSSec mode.
* @return List of HostAddress, which encompasses the hostname and port that the
* XMPP server can be reached at for the specified domain.
*/
public static List<Rfc6120TcpRemoteConnectionEndpoint> resolveXmppServerDomain(DnsName domain,
List<RemoteConnectionEndpointLookupFailure> lookupFailures, DnssecMode dnssecMode) {
DNSResolver dnsResolver = getDnsResolverOrThrow();
return resolveDomain(domain, DomainType.server, lookupFailures, dnssecMode, dnsResolver);
}
/**
*
* @param domain the domain.
* @param domainType the XMPP domain type, server or client.
* @param failedAddresses a list that will be populated with host addresses that failed to resolve.
* @return a list of resolver host addresses for this domain.
*/
private static List<Rfc6120TcpRemoteConnectionEndpoint> resolveDomain(DnsName domain, DomainType domainType,
List<RemoteConnectionEndpointLookupFailure> lookupFailures, DnssecMode dnssecMode, DNSResolver dnsResolver) {
List<Rfc6120TcpRemoteConnectionEndpoint> endpoints = new ArrayList<>();
// Step one: Do SRV lookups
DnsName srvDomain = DnsName.from(domainType.srvPrefix, domain);
Collection<SRV> srvRecords = dnsResolver.lookupSrvRecords(srvDomain, lookupFailures, dnssecMode);
if (srvRecords != null && !srvRecords.isEmpty()) {
if (LOGGER.isLoggable(Level.FINE)) {
String logMessage = "Resolved SRV RR for " + srvDomain + ":";
for (SRV r : srvRecords)
logMessage += " " + r;
LOGGER.fine(logMessage);
}
List<SRV> sortedSrvRecords = SrvUtil.sortSrvRecords(srvRecords);
for (SRV srv : sortedSrvRecords) {
List<InetAddress> targetInetAddresses = dnsResolver.lookupHostAddress(srv.target, lookupFailures, dnssecMode);
SrvXmppRemoteConnectionEndpoint endpoint = new SrvXmppRemoteConnectionEndpoint(srv, targetInetAddresses);
endpoints.add(endpoint);
}
} else {
LOGGER.info("Could not resolve DNS SRV resource records for " + srvDomain + ". Consider adding those.");
}
int defaultPort;
switch (domainType) {
case client:
defaultPort = 5222;
break;
case server:
defaultPort = 5269;
break;
default:
throw new AssertionError();
}
// Step two: Add the hostname to the end of the list
List<InetAddress> hostAddresses = dnsResolver.lookupHostAddress(domain, lookupFailures, dnssecMode);
if (hostAddresses != null) {
for (InetAddress inetAddress : hostAddresses) {
IpTcpRemoteConnectionEndpoint<InternetAddressRR> endpoint = IpTcpRemoteConnectionEndpoint.from(domain, defaultPort, inetAddress);
endpoints.add(endpoint);
}
}
return endpoints;
}
private static DNSResolver getDnsResolverOrThrow() {
final DNSResolver dnsResolver = DNSUtil.getDNSResolver();
if (dnsResolver == null) {
throw new IllegalStateException("No DNS resolver configured in Smack");
}
return dnsResolver;
}
}

View file

@ -0,0 +1,23 @@
/**
*
* Copyright 2020 Florian Schmaus.
*
* 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.smack.tcp.rce;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpoint;
public interface Rfc6120TcpRemoteConnectionEndpoint extends RemoteConnectionEndpoint {
}

View file

@ -0,0 +1,57 @@
/**
*
* Copyright 2020 Florian Schmaus.
*
* 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.smack.tcp.rce;
import java.net.InetAddress;
import java.util.Collection;
import java.util.List;
import org.jivesoftware.smack.datatypes.UInt16;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpoint;
import org.minidns.record.SRV;
public abstract class SrvRemoteConnectionEndpoint implements RemoteConnectionEndpoint {
protected final SRV srv;
protected final UInt16 port;
private final List<? extends InetAddress> inetAddresses;
protected SrvRemoteConnectionEndpoint(SRV srv, List<? extends InetAddress> inetAddresses) {
this.srv = srv;
this.port = UInt16.from(srv.port);
this.inetAddresses = inetAddresses;
}
@Override
public final CharSequence getHost() {
return srv.target;
}
@Override
public final UInt16 getPort() {
return port;
}
@Override
public final Collection<? extends InetAddress> getInetAddresses() {
return inetAddresses;
}
}

View file

@ -0,0 +1,35 @@
/**
*
* Copyright 2020 Florian Schmaus.
*
* 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.smack.tcp.rce;
import java.net.InetAddress;
import java.util.List;
import org.minidns.record.SRV;
public final class SrvXmppRemoteConnectionEndpoint extends SrvRemoteConnectionEndpoint
implements Rfc6120TcpRemoteConnectionEndpoint {
protected SrvXmppRemoteConnectionEndpoint(SRV srv, List<? extends InetAddress> inetAddresses) {
super(srv, inetAddresses);
}
@Override
public String getDescription() {
return "RFC 6120 SRV Endpoint + ['xmpp', " + srv + "]";
}
}

View file

@ -0,0 +1,34 @@
/**
*
* Copyright 2020 Florian Schmaus.
*
* 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.smack.tcp.rce;
import java.net.InetAddress;
import java.util.List;
import org.minidns.record.SRV;
public class SrvXmppsRemoteConnectionEndpoint extends SrvRemoteConnectionEndpoint {
protected SrvXmppsRemoteConnectionEndpoint(SRV srv, List<? extends InetAddress> inetAddresses) {
super(srv, inetAddresses);
}
@Override
public String getDescription() {
return "XEP-0368 SRV Endpoint + ['xmpps', " + srv + "]";
}
}

View file

@ -0,0 +1,21 @@
/**
*
* Copyright 2020 Florian Schmaus
*
* 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.
*/
/**
* Smack's internal API for XMPP connections over TCP.
*/
package org.jivesoftware.smack.tcp.rce;

View file

@ -1,33 +0,0 @@
/**
*
* Copyright 2018 Florian Schmaus
*
* 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.smack.tcp;
import org.jivesoftware.smack.fsm.StateDescriptor;
import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex;
import org.jivesoftware.smack.tcp.XmppNioTcpConnection.InstantShutdownStateDescriptor;
public class XmppNioTcpConnectionTest {
public void graphComplete() {
assertContains(XmppNioTcpConnection.INITIAL_STATE_DESCRIPTOR_VERTEX, InstantShutdownStateDescriptor.class);
}
private static void assertContains(GraphVertex<StateDescriptor> graph, Class<? extends StateDescriptor> state) {
// TODO: Implement this.
throw new Error("Implement me: " + graph + " " + state);
}
}

View file

@ -0,0 +1,87 @@
/**
*
* Copyright 2018-2020 Florian Schmaus.
*
* 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.smack.tcp.rce;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.datatypes.UInt16;
import org.jivesoftware.smack.tcp.rce.RemoteXmppTcpConnectionEndpoints.DomainType;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpoint;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure;
import org.jivesoftware.smack.util.rce.RemoteConnectionException;
import org.junit.jupiter.api.Test;
import org.minidns.record.A;
public class RemoteXmppTcpConnectionEndpointsTest {
@Test
public void simpleDomainTypeTest() {
DomainType client = DomainType.client;
assertEquals(RemoteXmppTcpConnectionEndpoints.XMPP_CLIENT_DNS_SRV_PREFIX, client.srvPrefix.ace);
DomainType server = DomainType.server;
assertEquals(RemoteXmppTcpConnectionEndpoints.XMPP_SERVER_DNS_SRV_PREFIX, server.srvPrefix.ace);
}
@Test
public void testConnectionException() {
List<RemoteConnectionException<? extends RemoteConnectionEndpoint>> connectionExceptions = new ArrayList<>();
{
A aRr = new A("1.2.3.4");
UInt16 port = UInt16.from(1234);
String host = "example.org";
IpTcpRemoteConnectionEndpoint<A> remoteConnectionEndpoint = new IpTcpRemoteConnectionEndpoint<>(host, port,
aRr);
Exception exception = new Exception("Failed for some reason");
RemoteConnectionException<IpTcpRemoteConnectionEndpoint<A>> remoteConnectionException = RemoteConnectionException.from(
remoteConnectionEndpoint, exception);
connectionExceptions.add(remoteConnectionException);
}
{
A aRr = new A("1.3.3.7");
UInt16 port = UInt16.from(5678);
String host = "other.example.org";
IpTcpRemoteConnectionEndpoint<A> remoteConnectionEndpoint = new IpTcpRemoteConnectionEndpoint<>(host, port,
aRr);
Exception exception = new Exception("Failed for some other reason");
RemoteConnectionException<IpTcpRemoteConnectionEndpoint<A>> remoteConnectionException = RemoteConnectionException.from(
remoteConnectionEndpoint, exception);
connectionExceptions.add(remoteConnectionException);
}
List<RemoteConnectionEndpointLookupFailure> lookupFailures = Collections.emptyList();
SmackException.EndpointConnectionException endpointConnectionException = SmackException.EndpointConnectionException.from(
lookupFailures, connectionExceptions);
String message = endpointConnectionException.getMessage();
assertEquals("The following addresses failed: "
+ "'RFC 6120 A/AAAA Endpoint + [example.org:1234] (/1.2.3.4:1234)' failed because: java.lang.Exception: Failed for some reason, "
+ "'RFC 6120 A/AAAA Endpoint + [other.example.org:5678] (/1.3.3.7:5678)' failed because: java.lang.Exception: Failed for some other reason",
message);
}
}