Rework WebSocket code

Related to SMACK-835.
ramabit.avatar
Florian Schmaus 2 years ago
parent 0c013e4f29
commit c5a546554b
  1. 45
      smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
  2. 15
      smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java
  3. 25
      smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java
  4. 6
      smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnectionConfiguration.java
  5. 5
      smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnectionModuleDescriptor.java
  6. 8
      smack-core/src/main/java/org/jivesoftware/smack/c2s/StreamOpenAndCloseFactory.java
  7. 4
      smack-core/src/main/java/org/jivesoftware/smack/c2s/XmppClientToServerTransport.java
  8. 19
      smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java
  9. 23
      smack-core/src/main/java/org/jivesoftware/smack/fsm/State.java
  10. 10
      smack-core/src/main/java/org/jivesoftware/smack/fsm/StateTransitionResult.java
  11. 3
      smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractStreamOpen.java
  12. 8
      smack-core/src/main/java/org/jivesoftware/smack/packet/StreamClose.java
  13. 4
      smack-core/src/main/java/org/jivesoftware/smack/packet/StreamOpen.java
  14. 3
      smack-java8-full/build.gradle
  15. 110
      smack-java8-full/src/main/java/org/jivesoftware/smack/full/WebSocketConnectionTest.java
  16. 36
      smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/Nio.java
  17. 45
      smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/WebSocketConnection.java
  18. 46
      smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/XmppTools.java
  19. 8
      smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
  20. 32
      smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java
  21. 32
      smack-websocket-okhttp/src/main/java/org/jivesoftware/smack/websocket/okhttp/LoggingInterceptor.java
  22. 163
      smack-websocket-okhttp/src/main/java/org/jivesoftware/smack/websocket/okhttp/OkHttpWebSocket.java
  23. 7
      smack-websocket-okhttp/src/main/java/org/jivesoftware/smack/websocket/okhttp/OkHttpWebSocketFactory.java
  24. 6
      smack-websocket-okhttp/src/test/java/org/jivesoftware/smack/websocket/okhttp/OkHttpWebSocketFactoryServiceTest.java
  25. 125
      smack-websocket/src/main/java/org/jivesoftware/smack/websocket/WebSocketConnectionAttemptState.java
  26. 16
      smack-websocket/src/main/java/org/jivesoftware/smack/websocket/WebSocketException.java
  27. 154
      smack-websocket/src/main/java/org/jivesoftware/smack/websocket/XmppWebSocketTransportModule.java
  28. 57
      smack-websocket/src/main/java/org/jivesoftware/smack/websocket/XmppWebSocketTransportModuleDescriptor.java
  29. 70
      smack-websocket/src/main/java/org/jivesoftware/smack/websocket/impl/AbstractWebSocket.java
  30. 6
      smack-websocket/src/main/java/org/jivesoftware/smack/websocket/impl/WebSocketFactory.java
  31. 8
      smack-websocket/src/main/java/org/jivesoftware/smack/websocket/impl/WebSocketFactoryService.java
  32. 39
      smack-websocket/src/main/java/org/jivesoftware/smack/websocket/rce/InsecureWebSocketRemoteConnectionEndpoint.java
  33. 39
      smack-websocket/src/main/java/org/jivesoftware/smack/websocket/rce/SecureWebSocketRemoteConnectionEndpoint.java
  34. 88
      smack-websocket/src/main/java/org/jivesoftware/smack/websocket/rce/WebSocketRemoteConnectionEndpoint.java
  35. 104
      smack-websocket/src/main/java/org/jivesoftware/smack/websocket/rce/WebSocketRemoteConnectionEndpointLookup.java
  36. 45
      smack-websocket/src/test/java/org/jivesoftware/smack/websocket/XmppWebSocketTransportModuleTest.java
  37. 10
      smack-websocket/src/test/java/org/jivesoftware/smack/websocket/rce/WebSocketRemoteConnectionEndpointTest.java
  38. 11
      smack-websocket/src/testFixtures/java/org/jivesoftware/smack/websocket/test/WebSocketFactoryServiceTestUtil.java

@ -1,6 +1,6 @@
/**
*
* Copyright 2009 Jive Software, 2018-2020 Florian Schmaus.
* Copyright 2009 Jive Software, 2018-2021 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -2201,18 +2201,29 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
return SMACK_REACTOR.schedule(runnable, delay, unit, ScheduledAction.Kind.NonBlocking);
}
protected void onStreamOpen(XmlPullParser parser) {
// We found an opening stream.
if ("jabber:client".equals(parser.getNamespace(null))) {
streamId = parser.getAttributeValue("", "id");
incomingStreamXmlEnvironment = XmlEnvironment.from(parser);
/**
* Must be called when a XMPP stream open tag is encountered. Sets values like the stream ID and the incoming stream
* XML environment.
* <p>
* This method also returns a matching stream close tag. For example if the stream open is {@code <stream >}, then
* {@code </stream>} is returned. But if it is {@code <stream:stream>}, then {@code </stream:stream>} is returned.
* Or if it is {@code <foo:stream>}, then {@code </foo:stream>} is returned.
* </p>
*
* @param parser an XML parser that is positioned at the start of the stream open.
* @return a String representing the corresponding stream end tag.
*/
protected String onStreamOpen(XmlPullParser parser) {
assert StreamOpen.ETHERX_JABBER_STREAMS_NAMESPACE.equals(parser.getNamespace());
assert StreamOpen.UNPREFIXED_ELEMENT.equals(parser.getName());
String reportedServerDomainString = parser.getAttributeValue("", "from");
if (reportedServerDomainString == null) {
// RFC 6120 § 4.7.1. makes no explicit statement whether or not 'from' in the stream open from the server
// in c2s connections is required or not.
return;
}
streamId = parser.getAttributeValue("id");
incomingStreamXmlEnvironment = XmlEnvironment.from(parser);
String reportedServerDomainString = parser.getAttributeValue("from");
// RFC 6120 § 4.7.1. makes no explicit statement whether or not 'from' in the stream open from the server
// in c2s connections is required or not.
if (reportedServerDomainString != null) {
DomainBareJid reportedServerDomain;
try {
reportedServerDomain = JidCreate.domainBareFrom(reportedServerDomainString);
@ -2226,6 +2237,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
+ "' as reported by server could not be transformed to a valid JID", e);
}
}
String prefix = parser.getPrefix();
if (StringUtils.isNotEmpty(prefix)) {
return "</" + prefix + ":stream>";
}
return "</stream>";
}
protected final void sendStreamOpen() throws NotConnectedException, InterruptedException {
@ -2233,7 +2250,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// possible. The 'to' attribute is *always* available. The 'from' attribute if set by the user and no external
// mechanism is used to determine the local entity (user). And the 'id' attribute is available after the first
// response from the server (see e.g. RFC 6120 § 9.1.1 Step 2.)
CharSequence to = getXMPPServiceDomain();
DomainBareJid to = getXMPPServiceDomain();
CharSequence from = null;
CharSequence localpart = config.getUsername();
if (localpart != null) {
@ -2247,7 +2264,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
updateOutgoingStreamXmlEnvironmentOnStreamOpen(streamOpen);
}
protected AbstractStreamOpen getStreamOpen(CharSequence to, CharSequence from, String id, String lang) {
protected AbstractStreamOpen getStreamOpen(DomainBareJid to, CharSequence from, String id, String lang) {
return new StreamOpen(to, from, id, lang);
}

@ -1,6 +1,6 @@
/**
*
* Copyright 2017-2020 Florian Schmaus
* Copyright 2017-2021 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -75,6 +75,10 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
@Override
public final synchronized boolean isDone() {
return result != null || exception != null || cancelled;
}
public final synchronized boolean wasSuccessful() {
return result != null;
}
@ -162,6 +166,10 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
return result;
}
public E getExceptionIfAvailable() {
return exception;
}
protected final synchronized void maybeInvokeCallbacks() {
if (cancelled) {
return;
@ -326,6 +334,11 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
return future;
}
public static boolean await(Collection<? extends SmackFuture<?, ?>> futures, long timeout)
throws InterruptedException {
return await(futures, timeout, TimeUnit.MILLISECONDS);
}
public static boolean await(Collection<? extends SmackFuture<?, ?>> futures, long timeout, TimeUnit unit) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(futures.size());
for (SmackFuture<?, ?> future : futures) {

@ -1,6 +1,6 @@
/**
*
* Copyright 2018-2020 Florian Schmaus
* Copyright 2018-2021 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -139,13 +139,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne
}
@Override
public void setCurrentConnectionExceptionAndNotify(Exception exception) {
ModularXmppClientToServerConnection.this.setCurrentConnectionExceptionAndNotify(exception);
}
@Override
public void onStreamOpen(XmlPullParser parser) {
ModularXmppClientToServerConnection.this.onStreamOpen(parser);
public String onStreamOpen(XmlPullParser parser) {
return ModularXmppClientToServerConnection.this.onStreamOpen(parser);
}
@Override
@ -571,7 +566,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne
}
@Override
protected AbstractStreamOpen getStreamOpen(CharSequence to, CharSequence from, String id, String lang) {
protected AbstractStreamOpen getStreamOpen(DomainBareJid to, CharSequence from, String id, String lang) {
StreamOpenAndCloseFactory streamOpenAndCloseFactory = activeTransport.getStreamOpenAndCloseFactory();
return streamOpenAndCloseFactory.createStreamOpen(to, from, id, lang);
}
@ -720,6 +715,11 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne
throw SmackException.NoEndpointsDiscoveredException.from(lookupFailures);
}
if (!lookupFailures.isEmpty()) {
// TODO: Put those non-fatal lookup failures into a sink of the connection so that the user is able to
// be aware of them.
}
// Even though the outgoing elements queue is unrelated to the lookup remote connection endpoints state, we
// do start the queue at this point. The transports will need it available, and we use the state's reset()
// function to close the queue again on failure.
@ -1110,7 +1110,12 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne
XmppClientToServerTransport.Stats stats = entry.getValue();
StringUtils.appendHeading(appendable, transportClass.getName());
appendable.append(stats.toString()).append('\n');
if (stats != null) {
appendable.append(stats.toString());
} else {
appendable.append("No stats available.");
}
appendable.append('\n');
}
for (Map.Entry<String, Object> entry : filtersStats.entrySet()) {

@ -1,6 +1,6 @@
/**
*
* Copyright 2019-2020 Florian Schmaus
* Copyright 2019-2021 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -62,6 +62,10 @@ public final class ModularXmppClientToServerConnectionConfiguration extends Conn
// configuration, e.g. there is no edge from disconnected to connected.
throw new IllegalStateException(e);
}
for (ModularXmppClientToServerConnectionModuleDescriptor moduleDescriptor : moduleDescriptors) {
moduleDescriptor.validateConfiguration(this);
}
}
public void printStateGraphInDotFormat(PrintWriter pw, boolean breakStateName) {

@ -1,6 +1,6 @@
/**
*
* Copyright 2019-2020 Florian Schmaus
* Copyright 2019-2021 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,6 +28,9 @@ public abstract class ModularXmppClientToServerConnectionModuleDescriptor {
protected abstract ModularXmppClientToServerConnectionModule<? extends ModularXmppClientToServerConnectionModuleDescriptor> constructXmppConnectionModule(
ModularXmppClientToServerConnectionInternal connectionInternal);
protected void validateConfiguration(ModularXmppClientToServerConnectionConfiguration configuration) {
}
public abstract static class Builder {
private final ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder;

@ -1,6 +1,6 @@
/**
*
* Copyright 2020 Aditya Borikar.
* Copyright 2020 Aditya Borikar, 2021 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,8 +19,12 @@ package org.jivesoftware.smack.c2s;
import org.jivesoftware.smack.packet.AbstractStreamClose;
import org.jivesoftware.smack.packet.AbstractStreamOpen;
import org.jxmpp.jid.DomainBareJid;
public interface StreamOpenAndCloseFactory {
AbstractStreamOpen createStreamOpen(CharSequence to, CharSequence from, String id, String lang);
AbstractStreamOpen createStreamOpen(DomainBareJid to, CharSequence from, String id, String lang);
AbstractStreamClose createStreamClose();
}

@ -1,6 +1,6 @@
/**
*
* Copyright 2019-2020 Florian Schmaus
* Copyright 2019-2021 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -37,6 +37,8 @@ public abstract class XmppClientToServerTransport {
protected abstract void loadConnectionEndpoints(LookupConnectionEndpointsSuccess lookupConnectionEndpointsSuccess);
public abstract boolean hasUseableConnectionEndpoints();
/**
* Notify the transport that new outgoing data is available. Usually this method does not need to be called
* explicitly, only if the filters are modified so that they potentially produced new data.

@ -1,6 +1,6 @@
/**
*
* Copyright 2020 Florian Schmaus
* Copyright 2020-2021 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,6 +16,7 @@
*/
package org.jivesoftware.smack.c2s.internal;
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
@ -39,8 +40,10 @@ import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.Consumer;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.util.Supplier;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
public abstract class ModularXmppClientToServerConnectionInternal {
@ -85,9 +88,19 @@ public abstract class ModularXmppClientToServerConnectionInternal {
public abstract void notifyConnectionError(Exception e);
public abstract void setCurrentConnectionExceptionAndNotify(Exception exception);
public final String onStreamOpen(String streamOpen) {
XmlPullParser streamOpenParser;
try {
streamOpenParser = PacketParserUtils.getParserFor(streamOpen);
} catch (XmlPullParserException | IOException e) {
// Should never happen.
throw new AssertionError(e);
}
String streamClose = onStreamOpen(streamOpenParser);
return streamClose;
}
public abstract void onStreamOpen(XmlPullParser parser);
public abstract String onStreamOpen(XmlPullParser parser);
public abstract void onStreamClosed();

@ -1,6 +1,6 @@
/**
*
* Copyright 2018-2020 Florian Schmaus
* Copyright 2018-2021 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,6 +20,7 @@ import java.io.IOException;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.c2s.XmppClientToServerTransport;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.c2s.internal.WalkStateGraphContext;
@ -75,4 +76,24 @@ public abstract class State {
}
}
public abstract static class AbstractTransport extends State {
private final XmppClientToServerTransport transport;
protected AbstractTransport(XmppClientToServerTransport transport, StateDescriptor stateDescriptor,
ModularXmppClientToServerConnectionInternal connectionInternal) {
super(stateDescriptor, connectionInternal);
this.transport = transport;
}
@Override
public StateTransitionResult.TransitionImpossible isTransitionToPossible(WalkStateGraphContext walkStateGraphContext)
throws SmackException {
if (!transport.hasUseableConnectionEndpoints()) {
return new StateTransitionResult.TransitionImpossibleBecauseNoEndpointsDiscovered(transport);
}
return super.isTransitionToPossible(walkStateGraphContext);
}
}
}

@ -1,6 +1,6 @@
/**
*
* Copyright 2018-2020 Florian Schmaus
* Copyright 2018-2021 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,6 +16,8 @@
*/
package org.jivesoftware.smack.fsm;
import org.jivesoftware.smack.c2s.XmppClientToServerTransport;
public abstract class StateTransitionResult {
private final String message;
@ -92,4 +94,10 @@ public abstract class StateTransitionResult {
super(stateDescriptor.getFullStateName(false) + " is not implemented (yet)");
}
}
public static class TransitionImpossibleBecauseNoEndpointsDiscovered extends TransitionImpossibleReason {
public TransitionImpossibleBecauseNoEndpointsDiscovered(XmppClientToServerTransport transport) {
super("The transport " + transport + " did not discover any endpoints");
}
}
}

@ -1,6 +1,6 @@
/**
*
* Copyright 2020 Florian Schmaus, Aditya Borikar
* Copyright 2020-2021 Florian Schmaus, 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.
@ -28,6 +28,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
* be achieved through {@link XMPPConnection#sendNonza(Nonza)}.
*/
public abstract class AbstractStreamOpen implements Nonza {
public static final String ETHERX_JABBER_STREAMS_NAMESPACE = "http://etherx.jabber.org/streams";
public static final String CLIENT_NAMESPACE = "jabber:client";
public static final String SERVER_NAMESPACE = "jabber:server";

@ -1,6 +1,6 @@
/**
*
* Copyright 2018 Florian Schmaus
* Copyright 2018-2021 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,6 +20,8 @@ public final class StreamClose extends AbstractStreamClose {
public static final StreamClose INSTANCE = new StreamClose();
public static final String STRING = "</" + StreamOpen.ELEMENT + ">";
private StreamClose() {
}
@ -39,4 +41,8 @@ public final class StreamClose extends AbstractStreamClose {
return StreamOpen.ELEMENT;
}
@Override
public String toString() {
return STRING;
}
}

@ -23,7 +23,9 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
* The stream open <b>tag</b>.
*/
public final class StreamOpen extends AbstractStreamOpen {
public static final String ELEMENT = "stream:stream";
public static final String UNPREFIXED_ELEMENT = "stream";
public static final String ELEMENT = "stream:" + UNPREFIXED_ELEMENT;
public StreamOpen(CharSequence to) {
this(to, null, null, null, StreamContentNamespace.client);

@ -12,7 +12,8 @@ dependencies {
api project(':smack-openpgp')
api project(':smack-resolver-minidns')
api project(':smack-resolver-minidns-dox')
api project(':smack-websocket')
// TODO: Change this to smack-websocket-java11 once it arrives.
api project(':smack-websocket-okhttp')
api project(':smack-tcp')
testImplementation(testFixtures(project(":smack-core")))

@ -0,0 +1,110 @@
/**
*
* Copyright 2021 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.full;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.logging.Logger;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
import org.jivesoftware.smack.debugger.ConsoleDebugger;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.websocket.XmppWebSocketTransportModuleDescriptor;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jxmpp.util.XmppDateTime;
public class WebSocketConnectionTest {
static {
SmackConfiguration.DEBUG = true;
}
public static void main(String[] args)
throws URISyntaxException, SmackException, IOException, XMPPException, InterruptedException {
String jid, password, websocketEndpoint, messageTo = null;
if (args.length < 3 || args.length > 4) {
throw new IllegalArgumentException();
}
jid = args[0];
password = args[1];
websocketEndpoint = args[2];
if (args.length >= 4) {
messageTo = args[3];
}
testWebSocketConnection(jid, password, websocketEndpoint, messageTo);
}
public static void testWebSocketConnection(String jid, String password, String websocketEndpoint)
throws URISyntaxException, SmackException, IOException, XMPPException, InterruptedException {
testWebSocketConnection(jid, password, websocketEndpoint, null);
}
public static void testWebSocketConnection(String jid, String password, String websocketEndpoint, String messageTo)
throws URISyntaxException, SmackException, IOException, XMPPException, InterruptedException {
ModularXmppClientToServerConnectionConfiguration.Builder builder = ModularXmppClientToServerConnectionConfiguration.builder();
builder.removeAllModules()
.setXmppAddressAndPassword(jid, password)
.setDebuggerFactory(ConsoleDebugger.Factory.INSTANCE)
;
XmppWebSocketTransportModuleDescriptor.Builder websocketBuilder = XmppWebSocketTransportModuleDescriptor.getBuilder(builder);
websocketBuilder.explicitlySetWebSocketEndpointAndDiscovery(websocketEndpoint, false);
builder.addModule(websocketBuilder.build());
ModularXmppClientToServerConnectionConfiguration config = builder.build();
ModularXmppClientToServerConnection connection = new ModularXmppClientToServerConnection(config);
connection.setReplyTimeout(5 * 60 * 1000);
connection.addConnectionStateMachineListener((event, c) -> {
Logger.getAnonymousLogger().info("Connection event: " + event);
});
connection.connect();
connection.login();
if (messageTo != null) {
Message message = connection.getStanzaFactory().buildMessageStanza()
.to(messageTo)
.setBody("It is alive! " + XmppDateTime.formatXEP0082Date(new Date()))
.build()
;
connection.sendStanza(message);
}
Thread.sleep(1000);
connection.disconnect();
ModularXmppClientToServerConnection.Stats connectionStats = connection.getStats();
ServiceDiscoveryManager.Stats serviceDiscoveryManagerStats = ServiceDiscoveryManager.getInstanceFor(connection).getStats();
// CHECKSTYLE:OFF
System.out.println("WebSocket successfully finished, yeah!\n" + connectionStats + '\n' + serviceDiscoveryManagerStats);
// CHECKSTYLE:ON
}
}

@ -1,6 +1,6 @@
/**
*
* Copyright 2018-2020 Florian Schmaus
* Copyright 2018-2021 Florian Schmaus
*
* This file is part of smack-repl.
*
@ -25,8 +25,6 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.logging.Logger;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.SmackException;
@ -38,18 +36,11 @@ import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.compression.XMPPInputOutputStream.FlushMethod;
import org.jivesoftware.smack.debugger.ConsoleDebugger;
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.sm.StreamManagementModuleDescriptor;
import org.jivesoftware.smack.tcp.XmppTcpTransportModuleDescriptor;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jxmpp.util.XmppDateTime;
public class Nio {
private static final Logger LOGGER = Logger.getLogger(Nio.class.getName());
public static void main(String[] args) throws SmackException, IOException, XMPPException, InterruptedException {
doNio(args[0], args[1], args[2]);
}
@ -111,30 +102,7 @@ public class Nio {
connection.setReplyTimeout(5 * 60 * 1000);
connection.addConnectionStateMachineListener((event, c) -> {
LOGGER.info("Connection event: " + event);
});
connection.connect();
connection.login();
Message message = connection.getStanzaFactory().buildMessageStanza()
.to("flo@geekplace.eu")
.setBody("It is alive! " + XmppDateTime.formatXEP0082Date(new Date()))
.build();
connection.sendStanza(message);
Thread.sleep(1000);
connection.disconnect();
ModularXmppClientToServerConnection.Stats connectionStats = connection.getStats();
ServiceDiscoveryManager.Stats serviceDiscoveryManagerStats = ServiceDiscoveryManager.getInstanceFor(connection).getStats();
// CHECKSTYLE:OFF
System.out.println("NIO successfully finished, yeah!\n" + connectionStats + '\n' + serviceDiscoveryManagerStats);
// CHECKSTYLE:ON
XmppTools.modularConnectionTest(connection, "flo@geekplace.eu");
}
}

@ -1,6 +1,6 @@
/**
*
* Copyright 2020 Aditya Borikar
* Copyright 2021 Florian Schmaus
*
* This file is part of smack-repl.
*
@ -21,7 +21,6 @@
package org.igniterealtime.smack.smackrepl;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.jivesoftware.smack.SmackException;
@ -29,25 +28,51 @@ import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
import org.jivesoftware.smack.util.TLSUtils;
import org.jivesoftware.smack.websocket.XmppWebSocketTransportModuleDescriptor;
public class WebSocketConnection {
public static void main(String[] args) throws SmackException, IOException, XMPPException, InterruptedException, URISyntaxException {
public static void main(String[] args)
throws URISyntaxException, SmackException, IOException, XMPPException, InterruptedException {
String jid, password, websocketEndpoint, messageTo = null;
if (args.length < 3 || args.length > 4) {
throw new IllegalArgumentException();
}
jid = args[0];
password = args[1];
websocketEndpoint = args[2];
if (args.length >= 4) {
messageTo = args[3];
}
TLSUtils.setDefaultTrustStoreTypeToJksIfRequired();
testWebSocketConnection(jid, password, websocketEndpoint, messageTo);
}
public static void testWebSocketConnection(String jid, String password, String websocketEndpoint)
throws URISyntaxException, SmackException, IOException, XMPPException, InterruptedException {
testWebSocketConnection(jid, password, websocketEndpoint, null);
}
public static void testWebSocketConnection(String jid, String password, String websocketEndpoint, String messageTo)
throws URISyntaxException, SmackException, IOException, XMPPException, InterruptedException {
ModularXmppClientToServerConnectionConfiguration.Builder builder = ModularXmppClientToServerConnectionConfiguration.builder();
builder.removeAllModules();
builder.setXmppAddressAndPassword(args[0], args[1]);
builder.removeAllModules()
.setXmppAddressAndPassword(jid, password)
;
// Set a fallback uri into websocket transport descriptor and add this descriptor into connection builder.
XmppWebSocketTransportModuleDescriptor.Builder websocketBuilder = XmppWebSocketTransportModuleDescriptor.getBuilder(builder);
websocketBuilder.explicitlySetWebSocketEndpointAndDiscovery(new URI(args[2]), false);
websocketBuilder.explicitlySetWebSocketEndpointAndDiscovery(websocketEndpoint, false);
builder.addModule(websocketBuilder.build());
ModularXmppClientToServerConnectionConfiguration config = builder.build();
ModularXmppClientToServerConnection connection = new ModularXmppClientToServerConnection(config);
connection.connect();
connection.login();
connection.disconnect();
connection.setReplyTimeout(5 * 60 * 1000);
XmppTools.modularConnectionTest(connection, messageTo);
}
}

@ -1,6 +1,6 @@
/**
*
* Copyright 2016 Florian Schmaus
* Copyright 2016-2021 Florian Schmaus
*
* This file is part of smack-repl.
*
@ -23,6 +23,8 @@ package org.igniterealtime.smack.smackrepl;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NoResponseException;
@ -30,15 +32,19 @@ import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smack.util.TLSUtils;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.iqregister.AccountManager;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Localpart;
import org.jxmpp.stringprep.XmppStringprepException;
import org.jxmpp.util.XmppDateTime;
public class XmppTools {
@ -106,4 +112,40 @@ public class XmppTools {
connection.disconnect();
}
}
public static void modularConnectionTest(ModularXmppClientToServerConnection connection, String messageTo) throws XMPPException, SmackException, IOException, InterruptedException {
connection.addConnectionStateMachineListener((event, c) -> {
Logger.getAnonymousLogger().info("Connection event: " + event);
});
connection.connect();
connection.login();
XmppTools.sendItsAlive(messageTo, connection);
Thread.sleep(1000);
connection.disconnect();
ModularXmppClientToServerConnection.Stats connectionStats = connection.getStats();
ServiceDiscoveryManager.Stats serviceDiscoveryManagerStats = ServiceDiscoveryManager.getInstanceFor(connection).getStats();
// CHECKSTYLE:OFF
System.out.println("NIO successfully finished, yeah!\n" + connectionStats + '\n' + serviceDiscoveryManagerStats);
// CHECKSTYLE:ON
}
public static void sendItsAlive(String to, XMPPConnection connection)
throws XmppStringprepException, NotConnectedException, InterruptedException {
if (to == null) {
return;
}
Message message = connection.getStanzaFactory().buildMessageStanza()
.to(to)
.setBody("It is alive! " + XmppDateTime.formatXEP0082Date(new Date()))
.build();
connection.sendStanza(message);
}
}

@ -83,6 +83,7 @@ import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StartTls;
import org.jivesoftware.smack.packet.StreamError;
import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smack.sasl.packet.SaslNonza;
import org.jivesoftware.smack.sm.SMUtils;
@ -961,6 +962,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
switch (eventType) {
case START_ELEMENT:
final String name = parser.getName();
final String namespace = parser.getNamespace();
switch (name) {
case Message.ELEMENT:
case IQ.IQ_ELEMENT:
@ -972,7 +975,9 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
}
break;
case "stream":
onStreamOpen(parser);
if (StreamOpen.ETHERX_JABBER_STREAMS_NAMESPACE.equals(namespace)) {
onStreamOpen(parser);
}
break;
case "error":
StreamError streamError = PacketParserUtils.parseStreamError(parser);
@ -989,7 +994,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
openStreamAndResetParser();
break;
case "failure":
String namespace = parser.getNamespace(null);
switch (namespace) {
case "urn:ietf:params:xml:ns:xmpp-tls":
// TLS negotiation has failed. The server will close the connection

@ -1,6 +1,6 @@
/**
*
* Copyright 2019-2020 Florian Schmaus
* Copyright 2019-2021 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -80,14 +80,12 @@ import org.jivesoftware.smack.tcp.rce.RemoteXmppTcpConnectionEndpoints;
import org.jivesoftware.smack.tcp.rce.RemoteXmppTcpConnectionEndpoints.Result;
import org.jivesoftware.smack.tcp.rce.Rfc6120TcpRemoteConnectionEndpoint;
import org.jivesoftware.smack.util.CollectionUtil;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.UTF8;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.util.JidUtil;
import org.jxmpp.xml.splitter.Utf8ByteXmppXmlSplitter;
@ -213,6 +211,8 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
}
final String prefixXmlns = "xmlns:" + prefix;
// TODO: Use the return value of onStreamOpen(), which now returns the
// corresponding stream close tag, instead of creating it here.
final StringBuilder streamClose = new StringBuilder(32);
final StringBuilder streamOpen = new StringBuilder(256);
@ -253,14 +253,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
this.streamOpen = streamOpen.toString();
this.streamClose = streamClose.toString();
XmlPullParser streamOpenParser;
try {
streamOpenParser = PacketParserUtils.getParserFor(this.streamOpen);
} catch (XmlPullParserException | IOException e) {
// Should never happen.
throw new AssertionError(e);
}
connectionInternal.onStreamOpen(streamOpenParser);
connectionInternal.onStreamOpen(this.streamOpen);
}
@Override
@ -586,7 +579,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
public StreamOpenAndCloseFactory getStreamOpenAndCloseFactory() {
return new StreamOpenAndCloseFactory() {
@Override
public StreamOpen createStreamOpen(CharSequence to, CharSequence from, String id, String lang) {
public StreamOpen createStreamOpen(DomainBareJid to, CharSequence from, String id, String lang) {
String xmlLang = connectionInternal.connection.getConfiguration().getXmlLang();
StreamOpen streamOpen = new StreamOpen(to, from, id, xmlLang, StreamOpen.StreamContentNamespace.client);
return streamOpen;
@ -603,6 +596,11 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
discoveredTcpEndpoints = null;
}
@Override
public boolean hasUseableConnectionEndpoints() {
return discoveredTcpEndpoints != null;
}
@Override
protected List<SmackFuture<LookupConnectionEndpointsResult, Exception>> lookupConnectionEndpoints() {
// Assert that there are no stale discovered endpoints prior performing the lookup.
@ -750,10 +748,10 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
return new EstablishingTcpConnectionState(stateDescriptor, connectionInternal);
}
final class EstablishingTcpConnectionState extends State {
final class EstablishingTcpConnectionState extends State.AbstractTransport {
private EstablishingTcpConnectionState(EstablishingTcpConnectionStateDescriptor stateDescriptor,
ModularXmppClientToServerConnectionInternal connectionInternal) {
super(stateDescriptor, connectionInternal);
super(tcpNioTransport, stateDescriptor, connectionInternal);
}
@Override
@ -777,6 +775,10 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
connectionInternal.setTransport(tcpNioTransport);
// TODO: It appears this should be done in a generic way. I'd assume we always
// have to wait for stream features after the connection was established. If this is true then consider
// moving this into State.AbstractTransport. But I am not yet 100% positive that this is the case for every
// transport. Hence keep it here for now.
connectionInternal.newStreamOpenWaitForFeaturesSequence("stream features after initial connection");
return new TcpSocketConnectedResult(remoteAddress);

@ -1,6 +1,6 @@
/**
*
* Copyright 2020 Aditya Borikar
* Copyright 2020 Aditya Borikar, 2021 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,7 +17,6 @@
package org.jivesoftware.smack.websocket.okhttp;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -27,39 +26,34 @@ import org.jivesoftware.smack.debugger.SmackDebugger;
import okhttp3.Headers;
import okhttp3.Response;
import org.jxmpp.xml.splitter.Utf8ByteXmppXmlSplitter;
import org.jxmpp.xml.splitter.XmlPrettyPrinter;
import org.jxmpp.xml.splitter.XmppXmlSplitter;
public final class LoggingInterceptor {
private static final Logger LOGGER = Logger.getAnonymousLogger();
private static final int MAX_ELEMENT_SIZE = 64 * 1024;
private static final Logger LOGGER = Logger.getLogger(LoggingInterceptor.class.getName());
private final SmackDebugger debugger;
private final Utf8ByteXmppXmlSplitter incomingTextSplitter;
private final Utf8ByteXmppXmlSplitter outgoingTextSplitter;
private final XmppXmlSplitter incomingXmlSplitter;
private final XmppXmlSplitter outgoingXmlSplitter;
public LoggingInterceptor(SmackDebugger smackDebugger) {
LoggingInterceptor(SmackDebugger smackDebugger) {
this.debugger = smackDebugger;
XmlPrettyPrinter incomingTextPrinter = XmlPrettyPrinter.builder()
.setPrettyWriter(sb -> debugger.incomingStreamSink(sb))
.setTabWidth(4)
.build();
XmppXmlSplitter incomingXmlSplitter = new XmppXmlSplitter(MAX_ELEMENT_SIZE, null,
incomingTextPrinter);
incomingTextSplitter = new Utf8ByteXmppXmlSplitter(incomingXmlSplitter);
incomingXmlSplitter = new XmppXmlSplitter(incomingTextPrinter);
XmlPrettyPrinter outgoingTextPrinter = XmlPrettyPrinter.builder()
.setPrettyWriter(sb -> debugger.outgoingStreamSink(sb))
.setTabWidth(4)
.build();
XmppXmlSplitter outgoingXmlSplitter = new XmppXmlSplitter(MAX_ELEMENT_SIZE, null,
outgoingTextPrinter);
outgoingTextSplitter = new Utf8ByteXmppXmlSplitter(outgoingXmlSplitter);
outgoingXmlSplitter = new XmppXmlSplitter(outgoingTextPrinter);
}
// Open response received here isn't in the form of an Xml an so, there isn't much to format.
public void interceptOpenResponse(Response response) {
void interceptOpenResponse(Response response) {
Headers headers = response.headers();
Iterator<?> iterator = headers.iterator();
StringBuilder sb = new StringBuilder();
@ -70,18 +64,18 @@ public final class LoggingInterceptor {
debugger.incomingStreamSink(sb);
}
public void interceptReceivedText(String text) {
void interceptReceivedText(String text) {
try {
incomingTextSplitter.write(text.getBytes(Charset.defaultCharset()));
incomingXmlSplitter.write(text);
} catch (IOException e) {
// Connections shouldn't be terminated due to exceptions encountered during debugging. hence only log them.
LOGGER.log(Level.WARNING, "IOException encountered while parsing received text: " + text, e);
}
}
public void interceptSentText(String text) {
void interceptSentText(String text) {
try {
outgoingTextSplitter.write(text.getBytes(Charset.defaultCharset()));
outgoingXmlSplitter.write(text);
} catch (IOException e) {
// Connections shouldn't be terminated due to exceptions encountered during debugging, hence only log them.
LOGGER.log(Level.WARNING, "IOException encountered while parsing outgoing text: " + text, e);

@ -1,6 +1,6 @@
/**
*
* Copyright 2020 Aditya Borikar
* Copyright 2020 Aditya Borikar, 2020-2021 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,22 +16,17 @@
*/
package org.jivesoftware.smack.websocket.okhttp;
import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLSession;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.SmackFuture;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.websocket.WebSocketException;
import org.jivesoftware.smack.websocket.elements.WebSocketOpenElement;
import org.jivesoftware.smack.websocket.impl.AbstractWebSocket;
import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.xml.XmlPullParserException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
@ -43,135 +38,119 @@ public final class OkHttpWebSocket extends AbstractWebSocket {
private static final Logger LOGGER = Logger.getLogger(OkHttpWebSocket.class.getName());
private static OkHttpClient okHttpClient = null;
private static final OkHttpClient okHttpClient = new OkHttpClient();
// This is a potential candidate to be placed into AbstractWebSocket, but I keep it here until smack-websocket-java11
// arrives.
private final SmackFuture.InternalSmackFuture<AbstractWebSocket, Exception> future = new SmackFuture.InternalSmackFuture<>();
private final ModularXmppClientToServerConnectionInternal connectionInternal;
private final LoggingInterceptor interceptor;
private String openStreamHeader;
private WebSocket currentWebSocket;
private WebSocketConnectionPhase phase;
private WebSocketRemoteConnectionEndpoint connectedEndpoint;
private final WebSocket okHttpWebSocket;
public OkHttpWebSocket(ModularXmppClientToServerConnectionInternal connectionInternal) {
this.connectionInternal = connectionInternal;
public OkHttpWebSocket(WebSocketRemoteConnectionEndpoint endpoint,
ModularXmppClientToServerConnectionInternal connectionInternal) {
super(endpoint, connectionInternal);
if (okHttpClient == null) {
// Creates an instance of okHttp client.
OkHttpClient.Builder builder = new OkHttpClient.Builder();
okHttpClient = builder.build();
}
// Add some mechanism to enable and disable this interceptor.
if (connectionInternal.smackDebugger != null) {
interceptor = new LoggingInterceptor(connectionInternal.smackDebugger);
} else {
interceptor = null;
}
}
@Override
public void connect(WebSocketRemoteConnectionEndpoint endpoint) throws InterruptedException, SmackException, XMPPException {
final String currentUri = endpoint.getWebSocketEndpoint().toString();
final URI uri = endpoint.getUri();
final String url = uri.toString();
Request request = new Request.Builder()
.url(currentUri)
.url(url)
.header("Sec-WebSocket-Protocol", "xmpp")
.build();
WebSocketListener listener = new WebSocketListener() {
okHttpWebSocket = okHttpClient.newWebSocket(request, listener);
}
private final WebSocketListener listener = new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
LOGGER.log(Level.FINER, "WebSocket is open");
phase = WebSocketConnectionPhase.openFrameSent;
if (interceptor != null) {
interceptor.interceptOpenResponse(response);
}
send(new WebSocketOpenElement(connectionInternal.connection.getXMPPServiceDomain()));
@Override
public void onOpen(WebSocket webSocket, Response response) {
LOGGER.log(Level.FINER, "OkHttp invoked onOpen() for {0}. Response: {1}",
new Object[] { webSocket, response });
if (interceptor != null) {
interceptor.interceptOpenResponse(response);
}
@Override
public void onMessage(WebSocket webSocket, String text) {
if (interceptor != null) {
interceptor.interceptReceivedText(text);
}
if (isCloseElement(text)) {
connectionInternal.onStreamClosed();
return;
}
String closingStream = "</stream>";
switch (phase) {
case openFrameSent:
if (isOpenElement(text)) {
// Converts the <open> element received into <stream> element.
openStreamHeader = getStreamFromOpenElement(text);
phase = WebSocketConnectionPhase.exchangingTopLevelStreamElements;
try {
connectionInternal.onStreamOpen(PacketParserUtils.getParserFor(openStreamHeader));
} catch (XmlPullParserException | IOException e) {
LOGGER.log(Level.WARNING, "Exception caught:", e);
}
} else {
LOGGER.log(Level.WARNING, "Unexpected Frame received", text);
}
break;
case exchangingTopLevelStreamElements:
connectionInternal.parseAndProcessElement(openStreamHeader + text + closingStream);
break;
default:
LOGGER.log(Level.INFO, "Default text: " + text);
}
future.setResult(OkHttpWebSocket.this);
}
@Override
public void onMessage(WebSocket webSocket, String text) {
if (interceptor != null) {
interceptor.interceptReceivedText(text);
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
LOGGER.log(Level.INFO, "Exception caught", t);
WebSocketException websocketException = new WebSocketException(t);
if (connectionInternal.connection.isConnected()) {
connectionInternal.notifyConnectionError(websocketException);
} else {
connectionInternal.setCurrentConnectionExceptionAndNotify(websocketException);
}
onIncomingWebSocketElement(text);
}
@Override
public void onFailure(WebSocket webSocket, Throwable throwable, Response response) {
LOGGER.log(Level.FINER, "OkHttp invoked onFailure() for " + webSocket + ". Response: " + response, throwable);
WebSocketException websocketException = new WebSocketException(throwable);
// If we are already connected, then we need to notify the connection that it got tear down. Otherwise we
// need to notify the thread calling connect() that the connection failed.
if (future.wasSuccessful()) {
connectionInternal.notifyConnectionError(websocketException);
} else {
future.setException(websocketException);
}
};
}
// Creates an instance of websocket through okHttpClient.
currentWebSocket = okHttpClient.newWebSocket(request, listener);
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
LOGGER.log(Level.FINER, "OkHttp invoked onClosing() for " + webSocket + ". Code: " + code + ". Reason: " + reason);
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
LOGGER.log(Level.FINER, "OkHttp invoked onClosed() for " + webSocket + ". Code: " + code + ". Reason: " + reason);
}
// Open a new stream and wait until features are received.
connectionInte