• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

igniterealtime / Smack / #2853

pending completion
#2853

push

github-actions

web-flow
Merge pull request #550 from Flowdalic/break-loop-in-webscoket-disconnect

[websocket-java11] Prevent infinite recursion in disconnect()

5 of 5 new or added lines in 2 files covered. (100.0%)

16236 of 41753 relevant lines covered (38.89%)

0.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

39.58
/smack-websocket/src/main/java/org/jivesoftware/smack/websocket/impl/AbstractWebSocket.java
1
/**
2
 *
3
 * Copyright 2020 Aditya Borikar, 2020-2023 Florian Schmaus
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
package org.jivesoftware.smack.websocket.impl;
18

19
import java.util.logging.Logger;
20

21
import javax.net.ssl.SSLSession;
22

23
import org.jivesoftware.smack.SmackFuture;
24
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
25
import org.jivesoftware.smack.debugger.SmackDebugger;
26
import org.jivesoftware.smack.packet.TopLevelStreamElement;
27
import org.jivesoftware.smack.packet.XmlEnvironment;
28
import org.jivesoftware.smack.websocket.WebSocketException;
29
import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpoint;
30

31
public abstract class AbstractWebSocket {
32

33
    protected static final Logger LOGGER = Logger.getLogger(AbstractWebSocket.class.getName());
1✔
34

35
    protected static final String SEC_WEBSOCKET_PROTOCOL_HEADER_FILED_NAME = "Sec-WebSocket-Protocol";
36
    protected static final String SEC_WEBSOCKET_PROTOCOL_HEADER_FILED_VALUE_XMPP = "xmpp";
37

38
    protected final SmackFuture.InternalSmackFuture<AbstractWebSocket, Exception> future = new SmackFuture.InternalSmackFuture<>();
1✔
39

40
    protected final ModularXmppClientToServerConnectionInternal connectionInternal;
41

42
    protected final WebSocketRemoteConnectionEndpoint endpoint;
43

44
    private final SmackWebSocketDebugger debugger;
45

46
    protected AbstractWebSocket(WebSocketRemoteConnectionEndpoint endpoint,
47
                    ModularXmppClientToServerConnectionInternal connectionInternal) {
1✔
48
        this.endpoint = endpoint;
1✔
49
        this.connectionInternal = connectionInternal;
1✔
50

51
        final SmackDebugger smackDebugger = connectionInternal.smackDebugger;
1✔
52
        if (smackDebugger != null) {
1✔
53
            debugger = new SmackWebSocketDebugger(smackDebugger);
×
54
        } else {
55
            debugger = null;
1✔
56
        }
57
    }
1✔
58

59
    public final WebSocketRemoteConnectionEndpoint getEndpoint() {
60
        return endpoint;
×
61
    }
62

63
    private String streamOpen;
64
    private String streamClose;
65

66
    protected final void onIncomingWebSocketElement(String element) {
67
        if (debugger != null) {
×
68
            debugger.incoming(element);
×
69
        }
70

71
        // TODO: Once smack-websocket-java15 is there, we have to re-evaluate if the async operation here is still
72
        // required, or if it should only be performed if OkHTTP is used.
73
        if (isOpenElement(element)) {
×
74
            // Transform the XMPP WebSocket <open/> element to a RFC 6120 <stream> open tag.
75
            streamOpen = getStreamFromOpenElement(element);
×
76
            streamClose = connectionInternal.onStreamOpen(streamOpen);
×
77
            return;
×
78
        }
79

80
        if (isCloseElement(element)) {
×
81
            connectionInternal.onStreamClosed();
×
82
            return;
×
83
        }
84

85
        connectionInternal.withSmackDebugger(debugger -> debugger.onIncomingElementCompleted());
×
86

87
        // TODO: Do we need to wrap the element again in the stream open to get the
88
        // correct XML scoping (just like the modular TCP connection does)? It appears
89
        // that this not really required, as onStreamOpen() will set the incomingStreamEnvironment, which is used for
90
        // parsing.
91
        String wrappedCompleteElement = streamOpen + element + streamClose;
×
92
        connectionInternal.parseAndProcessElement(wrappedCompleteElement);
×
93
    }
×
94

95
    static String getStreamFromOpenElement(String openElement) {
96
        String streamElement = openElement.replaceFirst("\\A<open ", "<stream:stream ")
1✔
97
                                          .replace("urn:ietf:params:xml:ns:xmpp-framing", "jabber:client")
1✔
98
                                          .replaceFirst("/>\\s*\\z", " xmlns:stream='http://etherx.jabber.org/streams'>");
1✔
99
        return streamElement;
1✔
100
    }
101

102
    // TODO: Make this method less fragile, e.g. by parsing a little bit into the element to ensure that this is an
103
    // <open/> element qualified by the correct namespace.
104
    static boolean isOpenElement(String text) {
105
        if (text.startsWith("<open ")) {
1✔
106
            return true;
1✔
107
        }
108
        return false;
1✔
109
    }
110

111
    // TODO: Make this method less fragile, e.g. by parsing a little bit into the element to ensure that this is an
112
    // <close/> element qualified by the correct namespace. The fragility comes due the fact that the element could,
113
    // inter alia, be specified as
114
    // <close:close xmlns:close="urn:ietf:params:xml:ns:xmpp-framing"/>
115
    static boolean isCloseElement(String text) {
116
        if (text.startsWith("<close xmlns='urn:ietf:params:xml:ns:xmpp-framing'/>")) {
1✔
117
            return true;
1✔
118
        }
119
        return false;
1✔
120
    }
121

122
    protected void onWebSocketFailure(Throwable throwable) {
123
        WebSocketException websocketException = new WebSocketException(throwable);
×
124

125
        // If we are already connected, then we need to notify the connection that it got tear down. Otherwise we
126
        // need to notify the thread calling connect() that the connection failed.
127
        if (future.wasSuccessful()) {
×
128
            connectionInternal.notifyConnectionError(websocketException);
×
129
        } else {
130
            future.setException(websocketException);
×
131
        }
132
    }
×
133

134
    public final SmackFuture<AbstractWebSocket, Exception> getFuture() {
135
        return future;
×
136
    }
137

138
    public final void send(TopLevelStreamElement element) {
139
        XmlEnvironment outgoingStreamXmlEnvironment = connectionInternal.getOutgoingStreamXmlEnvironment();
×
140
        String elementString = element.toXML(outgoingStreamXmlEnvironment).toString();
×
141

142
        // TODO: We could make use of Java 11's WebSocket (is)last feature when sending
143
        if (debugger != null) {
×
144
            debugger.outgoing(elementString);
×
145
        }
146

147
        send(elementString);
×
148
    }
×
149

150
    protected abstract void send(String element);
151

152
    public abstract void disconnect(int code, String message);
153

154
    public boolean isConnectionSecure() {
155
        return endpoint.isSecureEndpoint();
×
156
    }
157

158
    public abstract SSLSession getSSLSession();
159

160
    @Override
161
    public final String toString() {
162
        return getClass().getSimpleName() + "[" + connectionInternal.connection + "]";
×
163
    }
164
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc