• 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

0.0
/smack-websocket-java11/src/main/java/org/jivesoftware/smack/websocket/java11/Java11WebSocket.java
1
/**
2
 *
3
 * Copyright 2021-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.java11;
18

19
import java.net.URI;
20
import java.net.http.HttpClient;
21
import java.net.http.WebSocket;
22
import java.nio.ByteBuffer;
23
import java.util.concurrent.CompletableFuture;
24
import java.util.concurrent.CompletionStage;
25
import java.util.concurrent.ExecutionException;
26
import java.util.logging.Level;
27

28
import javax.net.ssl.SSLSession;
29

30
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
31
import org.jivesoftware.smack.util.LazyStringBuilder;
32
import org.jivesoftware.smack.websocket.impl.AbstractWebSocket;
33
import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpoint;
34

35
public final class Java11WebSocket extends AbstractWebSocket {
36

37
    private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder().build();
×
38

39
    private WebSocket webSocket;
40

41
    enum PingPong {
×
42
        ping,
×
43
        pong,
×
44
    };
45

46
    Java11WebSocket(WebSocketRemoteConnectionEndpoint endpoint,
47
                    ModularXmppClientToServerConnectionInternal connectionInternal) {
48
        super(endpoint, connectionInternal);
×
49

50
        final WebSocket.Listener listener = new WebSocket.Listener() {
×
51
            @Override
52
            public void onOpen(WebSocket webSocket) {
53
                LOGGER.finer(webSocket + " opened");
×
54
                webSocket.request(1);
×
55
            }
×
56

57
            LazyStringBuilder received = new LazyStringBuilder();
×
58

59
            @Override
60
            public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
61
                received.append(data);
×
62
                webSocket.request(1);
×
63

64
                if (last) {
×
65
                    String wholeMessage = received.toString();
×
66
                    received = new LazyStringBuilder();
×
67
                    onIncomingWebSocketElement(wholeMessage);
×
68
                }
69

70
                return null;
×
71
            }
72

73
            @Override
74
            public void onError(WebSocket webSocket, Throwable error) {
75
                onWebSocketFailure(error);
×
76
            }
×
77

78
            @Override
79
            public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) {
80
                LOGGER.finer(webSocket + " closed with status code " + statusCode + ". Provided reason: " + reason);
×
81
                // TODO: What should we do here? What if some server implementation closes the WebSocket out of the
82
                // blue? Ideally, we should react on this. Some situation in the okhttp implementation.
83
                return null;
×
84
            }
85

86
            @Override
87
            public CompletionStage<?> onPing(WebSocket webSocket, ByteBuffer message) {
88
                logPingPong(PingPong.ping, webSocket, message);
×
89

90
                webSocket.request(1);
×
91
                return null;
×
92
            }
93

94
            @Override
95
            public CompletionStage<?> onPong(WebSocket webSocket, ByteBuffer message) {
96
                logPingPong(PingPong.pong, webSocket, message);
×
97

98
                webSocket.request(1);
×
99
                return null;
×
100
            }
101

102
            private void logPingPong(PingPong pingPong, WebSocket webSocket, ByteBuffer message) {
103
                final Level pingPongLogLevel = Level.FINER;
×
104
                if (!LOGGER.isLoggable(pingPongLogLevel)) {
×
105
                    return;
×
106
                }
107

108
                LOGGER.log(pingPongLogLevel, "Received " + pingPong + " over " + webSocket + ". Message: " + message);
×
109
            }
×
110
        };
111

112
        final URI uri = endpoint.getUri();
×
113
        CompletionStage<WebSocket> webSocketFuture = HTTP_CLIENT.newWebSocketBuilder()
×
114
                        .subprotocols(SEC_WEBSOCKET_PROTOCOL_HEADER_FILED_VALUE_XMPP)
×
115
                        .buildAsync(uri, listener);
×
116

117
        webSocketFuture.whenComplete((webSocket, throwable) -> {
×
118
            if (throwable == null) {
×
119
                this.webSocket = webSocket;
×
120
                future.setResult(this);
×
121
            } else {
122
                onWebSocketFailure(throwable);
×
123
            }
124
        });
×
125
    }
×
126

127
    @Override
128
    protected void send(String element) {
129
        CompletableFuture<WebSocket> completableFuture = webSocket.sendText(element, true);
×
130
        try {
131
            completableFuture.get();
×
132
        } catch (ExecutionException e) {
×
133
            onWebSocketFailure(e);
×
134
        } catch (InterruptedException e) {
×
135
            // This thread should never be interrupted, as it is a Smack internal thread.
136
            throw new AssertionError(e);
×
137
        }
×
138
    }
×
139

140
    @Override
141
    public void disconnect(int code, String message) {
142
        try {
143
            if (!webSocket.isOutputClosed()) {
×
144
                CompletableFuture<WebSocket> completableFuture = webSocket.sendClose(code, message);
×
145
                completableFuture.get();
×
146
            }
147
        } catch (ExecutionException e) {
×
148
            LOGGER.log(Level.WARNING, "Failed to send final close when disconnecting " + this, e);
×
149
        } catch (InterruptedException e) {
×
150
            // This thread should never be interrupted, as it is a Smack internal thread.
151
            throw new AssertionError(e);
×
152
        } finally {
153
            webSocket.abort();
×
154
        }
155
    }
×
156

157
    @Override
158
    public SSLSession getSSLSession() {
159
        return null;
×
160
    }
161

162
    private void onWebSocketFailure(ExecutionException executionException) {
163
        Throwable cause = executionException.getCause();
×
164
        onWebSocketFailure(cause);
×
165
    }
×
166
}
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