Compare commits

...

11 Commits

Author SHA1 Message Date
Florian Schmaus b7fe56fb9b [xdata] Add message to IllegalArgumentException 2020-07-06 11:51:12 +02:00
Florian Schmaus 3e8666cd91 [xdata] Fix case in FillableForm 2020-07-06 11:51:12 +02:00
Florian Schmaus 89e39fadd2
Merge pull request #403 from adiaholic/bugFix
Remove unrequired assignment of value to connectionEndpoint variable
2020-07-06 11:31:26 +02:00
Florian Schmaus b1c0f3fa26 Merge branch 'master' of github.com:igniterealtime/Smack 2020-07-06 11:22:27 +02:00
Florian Schmaus c80afbbce7
Merge pull request #404 from Flowdalic/ibb-manager-fix
[ibb] Ensure InBandBytestreamManager is a singleton
2020-07-06 11:21:00 +02:00
Florian Schmaus ed02bcf0d4 [ibb] Ensure InBandBytestreamManager is a singleton
InBandBytestreamManager followed an unusual pattern: Within the
connectionTermianted() callback, it would remove itself from the
'managers' map. This allowed for multiple instances of an
InBandBytestreamManager to exist for the same connection, causing all
kinds of issues.

This fixes the issue by changing InBandBytestreamManager to use the
Smack-idiomatic pattern used by managers.

We also do no longer reset the listeners if the connection is
termianted, as listeners (and handlers) typically persist until they
are explicitly removed by the user.

As positive side-effect, the number of indeterministic unit-tests,
caused by using Thread.sleep(), is reduced. The executor service in
InitiationListener was also removed, because the IQ handler is already
called asynchronously to the connections main loop.

Thanks to Anno van Vliet for reporting this.
2020-07-03 22:01:15 +02:00
Florian Schmaus 049d687778 [gradle] Add fix for javadoc search 2020-07-03 22:01:06 +02:00
Aditya Borikar 45f75d5ce0 Remove unrequired assignment of value to connectionEndpoint variable
The current code would work just fine for a connection having
multiple endpoints. However, when there is only one endpoint
ConnectionAttemptState.nextAddress() would return null, since
connectionEndpointIterator has already iterated over the only
possible value in the contructor leading to a NullPointerException.
This means that during establishment of a connection having multiple
endpoints, the first value inside connectionEndpointIterator would
always be overlooked.
2020-07-02 01:59:58 +05:30
Florian Schmaus 0eeb89409a [gradle] Add fix for javadoc search 2020-06-28 17:42:01 +02:00
Florian Schmaus bc503c7475
Merge pull request #401 from adiaholic/xep-0156
Add support for HTTP lookup method through XEP-0156
2020-06-25 18:22:02 +02:00
Aditya Borikar dcb66eef59 Add support for HTTP lookup method through xep-0156 2020-06-25 21:10:43 +05:30
10 changed files with 306 additions and 141 deletions

View File

@ -259,6 +259,13 @@ allprojects {
if (JavaVersion.current().isJava9Compatible()) {
tasks.withType(Javadoc) {
options.addStringOption('-release', javaMajor)
// Fix for javadoc search. If not set, the search result would direct to
// javadoc/undefined/org/jivesoftware/smack/altconnections/HttpLookupMethod.html
// instead of
// javadoc/org/jivesoftware/smack/altconnections/HttpLookupMethod.html
// https://stackoverflow.com/a/53732633/194894
options.addBooleanOption("-no-module-directories", true)
}
tasks.withType(JavaCompile) {
options.compilerArgs.addAll([

View File

@ -14,6 +14,7 @@ Currently supported XEPs of Smack (all sub-projects)
| Name | XEP | Version | Description |
|---------------------------------------------|--------------------------------------------------------|-----------|----------------------------------------------------------------------------------------------------------|
| Discovering Alternative XMPP Connection Methods | [XEP-0156](https://xmpp.org/extensions/xep-0156.html) | 1.3.0 | Defines ways to discover alternative connection methods. |
| Nonzas | [XEP-0360](https://xmpp.org/extensions/xep-0360.html) | n/a | Defines the term "Nonza", describing every top level stream element that is not a Stanza. |
Currently supported XEPs of smack-tcp

View File

@ -0,0 +1,176 @@
/**
*
* Copyright 2020 Aditya Borikar.
*
* 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.altconnections;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jxmpp.jid.DomainBareJid;
/**
* The HTTP lookup method uses web host metadata to list the URIs of alternative connection methods.
*
* <p>In order to obtain host-meta XRD element from the host in the form of an <i>InputStream</i>,
* use {@link #getXrdStream(DomainBareJid)} method. To obtain endpoints for Bosh or Websocket
* connection endpoints from host, use {@link LinkRelation#BOSH} and {@link LinkRelation#WEBSOCKET}
* respectively with the {@link #lookup(DomainBareJid, LinkRelation)} method. In case one is looking
* for endpoints described by other than BOSH or Websocket LinkRelation, use the more flexible
* {@link #lookup(DomainBareJid, String)} method.</p>
* Example:<br>
* <pre>
* {@code
* DomainBareJid xmppServerAddress = JidCreate.domainBareFrom("example.org");
* List<URI> endpoints = HttpLookupMethod.lookup(xmppServiceAddress, LinkRelation.WEBSOCKET);
* }
* </pre>
* @see <a href="https://xmpp.org/extensions/xep-0156.html#http">
* HTTP Lookup Method from XEP-0156.
* </a>
*/
public final class HttpLookupMethod {
private static final String XRD_NAMESPACE = "http://docs.oasis-open.org/ns/xri/xrd-1.0";
/**
* Specifies a link relation for the selected type of connection.
*/
public enum LinkRelation {
/**
* Selects link relation attribute as "urn:xmpp:alt-connections:xbosh".
*/
BOSH("urn:xmpp:alt-connections:xbosh"),
/**
* Selects link relation attribute as "urn:xmpp:alt-connections:websocket".
*/
WEBSOCKET("urn:xmpp:alt-connections:websocket");
private final String attribute;
LinkRelation(String relAttribute) {
this.attribute = relAttribute;
}
}
/**
* Get remote endpoints for the given LinkRelation from host.
*
* @param xmppServiceAddress address of host
* @param relation LinkRelation as a string specifying type of connection
* @return list of endpoints
* @throws IOException exception due to input/output operations
* @throws XmlPullParserException exception encountered during XML parsing
* @throws URISyntaxException exception to indicate that a string could not be parsed as a URI reference
*/
public static List<URI> lookup(DomainBareJid xmppServiceAddress, String relation) throws IOException, XmlPullParserException, URISyntaxException {
try (InputStream inputStream = getXrdStream(xmppServiceAddress)) {
XmlPullParser xmlPullParser = PacketParserUtils.getParserFor(inputStream);
List<URI> endpoints = parseXrdLinkReferencesFor(xmlPullParser, relation);
return endpoints;
}
}
/**
* Get remote endpoints for the given LinkRelation from host.
*
* @param xmppServiceAddress address of host
* @param relation {@link LinkRelation} specifying type of connection
* @return list of endpoints
* @throws IOException exception due to input/output operations
* @throws XmlPullParserException exception encountered during XML parsing
* @throws URISyntaxException exception to indicate that a string could not be parsed as a URI reference
*/
public static List<URI> lookup(DomainBareJid xmppServiceAddress, LinkRelation relation) throws IOException, XmlPullParserException, URISyntaxException {
return lookup(xmppServiceAddress, relation.attribute);
}
/**
* Constructs a HTTP connection with the host specified by the DomainBareJid
* and retrieves XRD element in the form of an InputStream. The method will
* throw a {@link FileNotFoundException} if host-meta isn't published.
*
* @param xmppServiceAddress address of host
* @return InputStream containing XRD element
* @throws IOException exception due to input/output operations
*/
public static InputStream getXrdStream(DomainBareJid xmppServiceAddress) throws IOException {
final String metadataUrl = "https://" + xmppServiceAddress + "/.well-known/host-meta";
final URL putUrl = new URL(metadataUrl);
final URLConnection urlConnection = putUrl.openConnection();
return urlConnection.getInputStream();
}
/**
* Get remote endpoints for the provided LinkRelation from provided XmlPullParser.
*
* @param parser XmlPullParser that contains LinkRelations
* @param relation type of endpoints specified by the given LinkRelation
* @return list of endpoints
* @throws IOException exception due to input/output operations
* @throws XmlPullParserException exception encountered during XML parsing
* @throws URISyntaxException exception to indicate that a string could not be parsed as a URI reference
*/
public static List<URI> parseXrdLinkReferencesFor(XmlPullParser parser, String relation) throws IOException, XmlPullParserException, URISyntaxException {
List<URI> uriList = new ArrayList<>();
int initialDepth = parser.getDepth();
loop: while (true) {
XmlPullParser.TagEvent tag = parser.nextTag();
switch (tag) {
case START_ELEMENT:
String name = parser.getName();
String namespace = parser.getNamespace();
String rel = parser.getAttributeValue("rel");
if (!namespace.equals(XRD_NAMESPACE) || !name.equals("Link") || !rel.equals(relation)) {
continue loop;
}
String endpointUri = parser.getAttributeValue("href");
URI uri = new URI(endpointUri);
uriList.add(uri);
break;
case END_ELEMENT:
if (parser.getDepth() == initialDepth) {
break loop;
}
break;
}
}
return uriList;
}
/**
* Get remote endpoints for the provided LinkRelation from provided XmlPullParser.
*
* @param parser XmlPullParser that contains LinkRelations
* @param relation type of endpoints specified by the given LinkRelation
* @return list of endpoints
* @throws IOException exception due to input/output operations
* @throws XmlPullParserException exception encountered during XML parsing
* @throws URISyntaxException exception to indicate that a string could not be parsed as a URI reference
*/
public static List<URI> parseXrdLinkReferencesFor(XmlPullParser parser, LinkRelation relation) throws IOException, XmlPullParserException, URISyntaxException {
return parseXrdLinkReferencesFor(parser, relation.attribute);
}
}

View File

@ -0,0 +1,22 @@
/**
*
* Copyright 2020 Aditya Borikar.
*
* 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 API for <a href="https://xmpp.org/extensions/xep-0156.html"> XEP-0156: Discovering Alternative XMPP Connection Methods</a>.
* <br>
* XEP is partially supported as HTTP lookup is supported but DNS lookup isn't.
*/
package org.jivesoftware.smack.altconnections;

View File

@ -0,0 +1,57 @@
/**
*
* Copyright 2020 Aditya Borikar.
*
* 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.altconnections;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.altconnections.HttpLookupMethod.LinkRelation;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.junit.jupiter.api.Test;
import org.jxmpp.stringprep.XmppStringprepException;
public class HttpLookupMethodTest {
private static final String XRD_XML = "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" +
"<Link rel='urn:xmpp:alt-connections:xbosh' href='https://igniterealtime.org:443/http-bind/'/>" +
"<Link rel='urn:xmpp:alt-connections:websocket' href='wss://xmpp.igniterealtime.org:7483/ws/'/>" +
"<Link rel='urn:xmpp:alt-connections:websocket' href='ws://xmpp.igniterealtime.org:7070/ws/'/>" +
"</XRD>";
@Test
public void parseXrdLinkReferencesForWebsockets() throws XmppStringprepException, IOException, XmlPullParserException, URISyntaxException {
List<URI> endpoints = new ArrayList<>();
endpoints.add(new URI("wss://xmpp.igniterealtime.org:7483/ws/"));
endpoints.add(new URI("ws://xmpp.igniterealtime.org:7070/ws/"));
List<URI> expectedEndpoints = HttpLookupMethod.parseXrdLinkReferencesFor(PacketParserUtils.getParserFor(XRD_XML), LinkRelation.WEBSOCKET);
assertEquals(expectedEndpoints, endpoints);
}
@Test
public void parseXrdLinkReferencesForBosh() throws URISyntaxException, IOException, XmlPullParserException {
List<URI> endpoints = new ArrayList<>();
endpoints.add(new URI("https://igniterealtime.org:443/http-bind/"));
List<URI> expectedEndpoints = HttpLookupMethod.parseXrdLinkReferencesFor(PacketParserUtils.getParserFor(XRD_XML), LinkRelation.BOSH);
assertEquals(expectedEndpoints, endpoints);
}
}

View File

@ -110,22 +110,6 @@ public final class InBandBytestreamManager extends Manager implements Bytestream
public void connectionCreated(final XMPPConnection connection) {
// create the manager for this connection
InBandBytestreamManager.getByteStreamManager(connection);
// register shutdown listener
connection.addConnectionListener(new AbstractConnectionClosedListener() {
@Override
public void connectionTerminated() {
InBandBytestreamManager.getByteStreamManager(connection).disableService();
}
@Override
public void connected(XMPPConnection connection) {
InBandBytestreamManager.getByteStreamManager(connection);
}
});
}
});
}
@ -206,6 +190,15 @@ public final class InBandBytestreamManager extends Manager implements Bytestream
private InBandBytestreamManager(XMPPConnection connection) {
super(connection);
connection.addConnectionListener(new AbstractConnectionClosedListener() {
@Override
public void connectionTerminated() {
// reset internal status
InBandBytestreamManager.this.sessions.clear();
InBandBytestreamManager.this.ignoredBytestreamRequests.clear();
}
});
// register bytestream open packet listener
this.initiationListener = new InitiationListener(this);
connection.registerIQRequestHandler(initiationListener);
@ -453,19 +446,6 @@ public final class InBandBytestreamManager extends Manager implements Bytestream
connection().sendStanza(error);
}
/**
* Responses to the given IQ packet's sender with an XMPP error that an In-Band Bytestream open
* request is rejected because its block size is greater than the maximum allowed block size.
*
* @param request IQ stanza that should be answered with a resource-constraint error
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted.
*/
protected void replyResourceConstraintPacket(IQ request) throws NotConnectedException, InterruptedException {
IQ error = IQ.createErrorResponse(request, StanzaError.Condition.resource_constraint);
connection().sendStanza(error);
}
/**
* Responses to the given IQ packet's sender with an XMPP error that an In-Band Bytestream
* session could not be found.
@ -539,30 +519,4 @@ public final class InBandBytestreamManager extends Manager implements Bytestream
return ignoredBytestreamRequests;
}
/**
* Disables the InBandBytestreamManager by removing its stanza listeners and resetting its
* internal status, which includes removing this instance from the managers map.
*/
private void disableService() {
final XMPPConnection connection = connection();
// remove manager from static managers map
managers.remove(connection);
// remove all listeners registered by this manager
connection.unregisterIQRequestHandler(initiationListener);
connection.unregisterIQRequestHandler(dataListener);
connection.unregisterIQRequestHandler(closeListener);
// shutdown threads
this.initiationListener.shutdown();
// reset internal status
this.userListeners.clear();
this.allRequestListeners.clear();
this.sessions.clear();
this.ignoredBytestreamRequests.clear();
}
}

View File

@ -16,15 +16,9 @@
*/
package org.jivesoftware.smackx.bytestreams.ibb;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smackx.bytestreams.BytestreamListener;
import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;
@ -44,14 +38,10 @@ import org.jivesoftware.smackx.filetransfer.StreamNegotiator;
* @author Henning Staib
*/
class InitiationListener extends AbstractIqRequestHandler {
private static final Logger LOGGER = Logger.getLogger(InitiationListener.class.getName());
/* manager containing the listeners and the XMPP connection */
private final InBandBytestreamManager manager;
/* executor service to process incoming requests concurrently */
private final ExecutorService initiationListenerExecutor;
/**
* Constructor.
*
@ -60,40 +50,29 @@ class InitiationListener extends AbstractIqRequestHandler {
protected InitiationListener(InBandBytestreamManager manager) {
super(Open.ELEMENT, Open.NAMESPACE, IQ.Type.set, Mode.async);
this.manager = manager;
initiationListenerExecutor = Executors.newCachedThreadPool();
}
@Override
public IQ handleIQRequest(final IQ packet) {
initiationListenerExecutor.execute(new Runnable() {
@Override
public void run() {
try {
processRequest(packet);
}
catch (InterruptedException | NotConnectedException e) {
LOGGER.log(Level.WARNING, "proccessRequest", e);
}
}
});
return null;
}
private void processRequest(Stanza packet) throws NotConnectedException, InterruptedException {
Open ibbRequest = (Open) packet;
public IQ handleIQRequest(final IQ iqRequest) {
Open ibbRequest = (Open) iqRequest;
int blockSize = ibbRequest.getBlockSize();
int maximumBlockSize = manager.getMaximumBlockSize();
// validate that block size is within allowed range
if (ibbRequest.getBlockSize() > this.manager.getMaximumBlockSize()) {
this.manager.replyResourceConstraintPacket(ibbRequest);
return;
if (blockSize > maximumBlockSize) {
StanzaError error = StanzaError.getBuilder().setCondition(StanzaError.Condition.resource_constraint)
.setDescriptiveEnText("Requests block size of " + blockSize + " exceeds maximum block size of "
+ maximumBlockSize)
.build();
return IQ.createErrorResponse(iqRequest, error);
}
StreamNegotiator.signal(ibbRequest.getFrom().toString() + '\t' + ibbRequest.getSessionID(), ibbRequest);
// ignore request if in ignore list
if (this.manager.getIgnoredBytestreamRequests().remove(ibbRequest.getSessionID()))
return;
if (this.manager.getIgnoredBytestreamRequests().remove(ibbRequest.getSessionID())) {
return null;
}
// build bytestream request from packet
InBandBytestreamRequest request = new InBandBytestreamRequest(this.manager, ibbRequest);
@ -102,7 +81,6 @@ class InitiationListener extends AbstractIqRequestHandler {
BytestreamListener userListener = this.manager.getUserListener(ibbRequest.getFrom());
if (userListener != null) {
userListener.incomingBytestreamRequest(request);
}
else if (!this.manager.getAllRequestListeners().isEmpty()) {
/*
@ -111,21 +89,16 @@ class InitiationListener extends AbstractIqRequestHandler {
for (BytestreamListener listener : this.manager.getAllRequestListeners()) {
listener.incomingBytestreamRequest(request);
}
}
else {
/*
* if there is no listener for this initiation request, reply with reject message
*/
this.manager.replyRejectPacket(ibbRequest);
StanzaError error = StanzaError.getBuilder()
.setCondition(StanzaError.Condition.not_acceptable)
.setDescriptiveEnText("No file-transfer listeners registered")
.build();
return IQ.createErrorResponse(iqRequest, error);
}
}
/**
* Shuts down the listeners executor service.
*/
protected void shutdown() {
this.initiationListenerExecutor.shutdownNow();
return null;
}
}

View File

@ -193,10 +193,10 @@ public class FillableForm extends FilledForm {
return FormField.textSingleBuilder(fieldName);
case hidden:
return FormField.hiddenBuilder(fieldName);
case list_multi:
case list_single:
return FormField.listSingleBuilder(fieldName);
default:
throw new IllegalArgumentException();
throw new IllegalArgumentException("Unsupported type: " + type);
}
}

View File

@ -83,23 +83,14 @@ public class InitiationListenerTest extends SmackTestSuite {
*/
@Test
public void shouldRespondWithError() throws Exception {
// run the listener with the initiation packet
initiationListener.handleIQRequest(initBytestream);
// wait because packet is processed in an extra thread
Thread.sleep(200);
// capture reply to the In-Band Bytestream open request
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
verify(connection).sendStanza(argument.capture());
IQ response = initiationListener.handleIQRequest(initBytestream);
// assert that reply is the correct error packet
assertEquals(initiatorJID, argument.getValue().getTo());
assertEquals(IQ.Type.error, argument.getValue().getType());
assertEquals(initiatorJID, response.getTo());
assertEquals(IQ.Type.error, response.getType());
assertEquals(StanzaError.Condition.not_acceptable,
argument.getValue().getError().getCondition());
response.getError().getCondition());
}
/**
@ -113,21 +104,13 @@ public class InitiationListenerTest extends SmackTestSuite {
byteStreamManager.setMaximumBlockSize(1024);
// run the listener with the initiation packet
initiationListener.handleIQRequest(initBytestream);
// wait because packet is processed in an extra thread
Thread.sleep(200);
// capture reply to the In-Band Bytestream open request
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
verify(connection).sendStanza(argument.capture());
IQ response = initiationListener.handleIQRequest(initBytestream);
// assert that reply is the correct error packet
assertEquals(initiatorJID, argument.getValue().getTo());
assertEquals(IQ.Type.error, argument.getValue().getType());
assertEquals(initiatorJID, response.getTo());
assertEquals(IQ.Type.error, response.getType());
assertEquals(StanzaError.Condition.resource_constraint,
argument.getValue().getError().getCondition());
response.getError().getCondition());
}
/**
@ -199,24 +182,17 @@ public class InitiationListenerTest extends SmackTestSuite {
byteStreamManager.addIncomingBytestreamListener(listener, JidCreate.from("other_" + initiatorJID));
// run the listener with the initiation packet
initiationListener.handleIQRequest(initBytestream);
// wait because packet is processed in an extra thread
Thread.sleep(200);
IQ response = initiationListener.handleIQRequest(initBytestream);
// assert listener is not called
ArgumentCaptor<BytestreamRequest> byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class);
verify(listener, never()).incomingBytestreamRequest(byteStreamRequest.capture());
// capture reply to the In-Band Bytestream open request
ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class);
verify(connection).sendStanza(argument.capture());
// assert that reply is the correct error packet
assertEquals(initiatorJID, argument.getValue().getTo());
assertEquals(IQ.Type.error, argument.getValue().getType());
assertEquals(initiatorJID, response.getTo());
assertEquals(IQ.Type.error, response.getType());
assertEquals(StanzaError.Condition.not_acceptable,
argument.getValue().getError().getCondition());
response.getError().getCondition());
}
/**

View File

@ -69,7 +69,6 @@ public final class ConnectionAttemptState {
List<Rfc6120TcpRemoteConnectionEndpoint> endpoints = discoveredEndpoints.result.discoveredRemoteConnectionEndpoints;
connectionEndpointIterator = endpoints.iterator();
connectionEndpoint = connectionEndpointIterator.next();
connectionExceptions = new ArrayList<>(endpoints.size());
}