Skip to content

Commit e853c62

Browse files
authored
Merge pull request #430 from Red5/bug/ws
Bug/ws
2 parents a941add + da1068b commit e853c62

13 files changed

Lines changed: 75 additions & 32 deletions

File tree

client/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<parent>
44
<groupId>org.red5</groupId>
55
<artifactId>red5-parent</artifactId>
6-
<version>2.0.26</version>
6+
<version>2.0.27</version>
77
</parent>
88
<modelVersion>4.0.0</modelVersion>
99
<artifactId>red5-client</artifactId>

client/src/main/java/org/red5/client/Red5Client.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public final class Red5Client {
1818
/**
1919
* Current server version with revision
2020
*/
21-
public static final String VERSION = "Red5 Client 2.0.26";
21+
public static final String VERSION = "Red5 Client 2.0.27";
2222

2323
/**
2424
* Create a new Red5Client object using the connection local to the current thread A bit of magic that lets you access the red5 scope

common/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<parent>
44
<groupId>org.red5</groupId>
55
<artifactId>red5-parent</artifactId>
6-
<version>2.0.26</version>
6+
<version>2.0.27</version>
77
</parent>
88
<modelVersion>4.0.0</modelVersion>
99
<artifactId>red5-server-common</artifactId>

common/src/main/java/org/red5/server/api/Red5.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ public final class Red5 {
5757
/**
5858
* Server version with revision
5959
*/
60-
public static final String VERSION = "Red5 Server 2.0.26";
60+
public static final String VERSION = "Red5 Server 2.0.27";
6161

6262
/**
6363
* Server version for fmsVer requests
6464
*/
65-
public static final String FMS_VERSION = "RED5/2,0,26,0";
65+
public static final String FMS_VERSION = "RED5/2,0,27,0";
6666

6767
/**
6868
* Server capabilities

io/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<parent>
44
<groupId>org.red5</groupId>
55
<artifactId>red5-parent</artifactId>
6-
<version>2.0.26</version>
6+
<version>2.0.27</version>
77
</parent>
88
<modelVersion>4.0.0</modelVersion>
99
<artifactId>red5-io</artifactId>

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<name>Red5</name>
2525
<description>The Red5 server</description>
2626
<groupId>org.red5</groupId>
27-
<version>2.0.26</version>
27+
<version>2.0.27</version>
2828
<url>https://github.com/Red5/red5-server</url>
2929
<inceptionYear>2005</inceptionYear>
3030
<organization>

server/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<parent>
44
<groupId>org.red5</groupId>
55
<artifactId>red5-parent</artifactId>
6-
<version>2.0.26</version>
6+
<version>2.0.27</version>
77
</parent>
88
<modelVersion>4.0.0</modelVersion>
99
<artifactId>red5-server</artifactId>

server/src/main/java/org/red5/net/websocket/WebSocketPlugin.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -312,10 +312,13 @@ public void setApplication(MultiThreadedApplicationAdapter application) {
312312
log.info("WebSocketPlugin application: {}", application);
313313
// get the app scope
314314
final IScope appScope = application.getScope();
315-
// put if not already there
316-
managerMap.putIfAbsent(appScope, new WebSocketScopeManager());
317-
// add the app scope to the manager
318-
managerMap.get(appScope).setApplication(appScope);
315+
// atomically create and initialize manager to prevent race condition
316+
// where getManager() could return a manager before setApplication() is called
317+
managerMap.computeIfAbsent(appScope, scope -> {
318+
WebSocketScopeManager manager = new WebSocketScopeManager();
319+
manager.setApplication(scope);
320+
return manager;
321+
});
319322
super.setApplication(application);
320323
}
321324

server/src/main/java/org/red5/net/websocket/WebSocketScopeManager.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,10 @@ public void makeScope(String path) {
333333
*/
334334
public void makeScope(IScope scope) {
335335
log.debug("makeScope: {}", scope);
336+
if (scope == null) {
337+
log.warn("Cannot create WebSocket scope for null IScope");
338+
return;
339+
}
336340
String path = scope.getContextPath();
337341
if (!scopes.containsKey(path)) {
338342
// add the name to the collection (no '/' prefix)
@@ -358,6 +362,10 @@ public void makeScope(IScope scope) {
358362
*/
359363
public WebSocketScope getScope(String path) {
360364
log.debug("getScope: {}", path);
365+
// normalize path by removing trailing slashes (e.g., "/live/" -> "/live")
366+
while (path != null && path.length() > 1 && path.endsWith("/")) {
367+
path = path.substring(0, path.length() - 1);
368+
}
361369
WebSocketScope scope = scopes.get(path);
362370
// if we dont find a scope, go for default
363371
if (scope == null) {
@@ -453,6 +461,15 @@ public boolean setApplication(IScope appScope) {
453461
return activeRooms.add(appScope.getName());
454462
}
455463

464+
/**
465+
* Returns the application scope for this manager.
466+
*
467+
* @return the application scope or null if not set
468+
*/
469+
public IScope getApplication() {
470+
return appScope;
471+
}
472+
456473
/**
457474
* <p>Setter for the field <code>copyListeners</code>.</p>
458475
*

server/src/main/java/org/red5/net/websocket/server/DefaultServerEndpointConfigurator.java

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request,
144144
if (idx != -1) {
145145
path = path.substring(0, idx);
146146
}
147+
// normalize path by removing trailing slashes (e.g., "/live/" -> "/live")
148+
while (path.length() > 1 && path.endsWith("/")) {
149+
path = path.substring(0, path.length() - 1);
150+
}
151+
log.debug("Normalized path: {}", path);
147152
// get the manager
148153
WebSocketPlugin plugin = (WebSocketPlugin) PluginRegistry.getPlugin(WebSocketPlugin.NAME);
149154
WebSocketScopeManager manager = plugin.getManager(path);
@@ -156,8 +161,12 @@ public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request,
156161
if (scope == null) {
157162
// split up the path into usable scope names
158163
String[] paths = path.split("\\/");
159-
// parent scope
160-
IScope appScope = Optional.ofNullable(applicationScope).orElse(plugin.getApplicationScope(path));
164+
// parent scope - prefer manager's app scope over separate lookup
165+
IScope appScope = Optional.ofNullable(applicationScope).orElse(manager.getApplication());
166+
if (appScope == null) {
167+
// fallback to plugin lookup
168+
appScope = plugin.getApplicationScope(path);
169+
}
161170
IScope parentScope = appScope;
162171
// room scope
163172
IScope roomScope = null;
@@ -177,23 +186,37 @@ public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request,
177186
parentScope = roomScope;
178187
}
179188
}
180-
// create and add the websocket scope for the new room scope
181-
manager.makeScope(roomScope);
182-
// get the new ws scope
183-
scope = manager.getScope(path);
184-
// copy the listeners from the app websocket scope
185-
Set<IWebSocketDataListener> listeners = ((WebSocketScope) appScope.getAttribute(WSConstants.WS_SCOPE)).getListeners();
186-
for (IWebSocketDataListener listener : listeners) {
187-
log.debug("Adding listener: {}", listener);
188-
scope.addListener(listener);
189+
// create and add the websocket scope for the room or app scope
190+
IScope scopeToUse = roomScope != null ? roomScope : appScope;
191+
if (scopeToUse != null) {
192+
manager.makeScope(scopeToUse);
193+
// get the new ws scope
194+
scope = manager.getScope(path);
195+
// copy the listeners from the app websocket scope if available
196+
if (appScope != null && appScope.hasAttribute(WSConstants.WS_SCOPE)) {
197+
WebSocketScope appWsScope = (WebSocketScope) appScope.getAttribute(WSConstants.WS_SCOPE);
198+
if (appWsScope != null && scope != null) {
199+
Set<IWebSocketDataListener> listeners = appWsScope.getListeners();
200+
for (IWebSocketDataListener listener : listeners) {
201+
log.debug("Adding listener: {}", listener);
202+
scope.addListener(listener);
203+
}
204+
}
205+
}
206+
} else {
207+
log.warn("Cannot create websocket scope - no valid scope available for path: {}", path);
189208
}
190209
}
191-
// add the websocket scope to the user props
192-
sec.getUserProperties().put(WSConstants.WS_SCOPE, scope);
193-
// run through any modifiers
194-
handshakeModifiers.forEach(modifier -> {
195-
modifier.modifyHandshake(request, response);
196-
});
210+
// add the websocket scope to the user props if available
211+
if (scope != null) {
212+
sec.getUserProperties().put(WSConstants.WS_SCOPE, scope);
213+
// run through any modifiers
214+
handshakeModifiers.forEach(modifier -> {
215+
modifier.modifyHandshake(request, response);
216+
});
217+
} else {
218+
log.warn("WebSocket scope is null for path: {} - handshake may fail", path);
219+
}
197220
} else {
198221
log.warn("No websocket manager found for path: {} requested uri: {}", path, request.getRequestURI().toString());
199222
}

0 commit comments

Comments
 (0)