Skip to content

Commit 6b33587

Browse files
authored
Merge pull request #2658 from eclipse/jetty-9.4.x-issue-2655-wsclient-session-removal
Issue #2655 - Removing closed WebSocket Session's from WebSocketClient
2 parents 93a8afc + 2e5f106 commit 6b33587

File tree

8 files changed

+166
-107
lines changed

8 files changed

+166
-107
lines changed

jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/DelayedStartClientOnServerTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,15 +247,15 @@ public void testHttpClientThreads_AfterClientConnectTo() throws Exception
247247
assertThat("Response", response, startsWith("Connected to ws://"));
248248
List<String> threadNames = getThreadNames(server);
249249
assertNoHttpClientPoolThreads(threadNames);
250-
assertThat("Threads", threadNames, hasItem(containsString("WebSocketContainer@")));
250+
assertThat("Threads", threadNames, hasItem(containsString("WebSocketClient@")));
251251
}
252252
finally
253253
{
254254
server.stop();
255255
}
256256
}
257257

258-
@Test
258+
@Test(timeout = 5000)
259259
public void testHttpClientThreads_AfterServerConnectTo() throws Exception
260260
{
261261
Server server = new Server(0);

jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPolicy.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,12 @@ public void assertValidTextMessageSize(int requestedSize)
144144

145145
public WebSocketPolicy clonePolicy()
146146
{
147-
WebSocketPolicy clone = new WebSocketPolicy(this.behavior);
147+
return clonePolicy(this.behavior);
148+
}
149+
150+
public WebSocketPolicy clonePolicy(WebSocketBehavior behavior)
151+
{
152+
WebSocketPolicy clone = new WebSocketPolicy(behavior);
148153
clone.idleTimeout = this.idleTimeout;
149154
clone.maxTextMessageSize = this.maxTextMessageSize;
150155
clone.maxTextMessageBufferSize = this.maxTextMessageBufferSize;

jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/DefaultHttpClientProvider.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.util.concurrent.Executor;
2222

2323
import org.eclipse.jetty.client.HttpClient;
24+
import org.eclipse.jetty.io.ByteBufferPool;
25+
import org.eclipse.jetty.io.MappedByteBufferPool;
2426
import org.eclipse.jetty.util.ssl.SslContextFactory;
2527
import org.eclipse.jetty.util.thread.QueuedThreadPool;
2628
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
@@ -31,11 +33,13 @@ public static HttpClient newHttpClient(WebSocketContainerScope scope)
3133
{
3234
SslContextFactory sslContextFactory = null;
3335
Executor executor = null;
36+
ByteBufferPool bufferPool = null;
3437

3538
if (scope != null)
3639
{
3740
sslContextFactory = scope.getSslContextFactory();
3841
executor = scope.getExecutor();
42+
bufferPool = scope.getBufferPool();
3943
}
4044

4145
if (sslContextFactory == null)
@@ -53,6 +57,13 @@ public static HttpClient newHttpClient(WebSocketContainerScope scope)
5357
executor = threadPool;
5458
}
5559
client.setExecutor(executor);
60+
61+
if (bufferPool == null)
62+
{
63+
bufferPool = new MappedByteBufferPool();
64+
}
65+
client.setByteBufferPool(bufferPool);
66+
5667
return client;
5768
}
5869
}

jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java

Lines changed: 59 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@
2929
import java.util.Set;
3030
import java.util.concurrent.Executor;
3131
import java.util.concurrent.Future;
32-
import java.util.concurrent.ThreadLocalRandom;
32+
import java.util.function.Supplier;
3333

3434
import org.eclipse.jetty.client.HttpClient;
3535
import org.eclipse.jetty.io.ByteBufferPool;
36-
import org.eclipse.jetty.io.MappedByteBufferPool;
3736
import org.eclipse.jetty.util.DecoratedObjectFactory;
3837
import org.eclipse.jetty.util.StringUtil;
3938
import org.eclipse.jetty.util.component.ContainerLifeCycle;
@@ -56,7 +55,6 @@
5655
import org.eclipse.jetty.websocket.common.WebSocketSessionFactory;
5756
import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
5857
import org.eclipse.jetty.websocket.common.extensions.WebSocketExtensionFactory;
59-
import org.eclipse.jetty.websocket.common.scopes.DelegatedContainerScope;
6058
import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
6159
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
6260

@@ -70,14 +68,15 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketCont
7068
// From HttpClient
7169
private final HttpClient httpClient;
7270

73-
//
74-
private final WebSocketContainerScope containerScope;
71+
// CDI layer
72+
private final Supplier<DecoratedObjectFactory> objectFactorySupplier;
73+
74+
// WebSocket Specifics
75+
private final WebSocketPolicy policy;
7576
private final WebSocketExtensionFactory extensionRegistry;
7677
private final EventDriverFactory eventDriverFactory;
7778
private final SessionFactory sessionFactory;
7879

79-
private final int id = ThreadLocalRandom.current().nextInt();
80-
8180
// defaults to true for backwards compatibility
8281
private boolean stopAtShutdown = true;
8382

@@ -86,9 +85,7 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketCont
8685
*/
8786
public WebSocketClient()
8887
{
89-
// Create synthetic HttpClient
90-
this(HttpClientProvider.get(null));
91-
addBean(this.httpClient);
88+
this((HttpClient)null);
9289
}
9390

9491
/**
@@ -99,7 +96,7 @@ public WebSocketClient()
9996
*/
10097
public WebSocketClient(HttpClient httpClient)
10198
{
102-
this(httpClient,new DecoratedObjectFactory());
99+
this(httpClient, null);
103100
}
104101

105102
/**
@@ -112,46 +109,45 @@ public WebSocketClient(HttpClient httpClient)
112109
*/
113110
public WebSocketClient(HttpClient httpClient, DecoratedObjectFactory objectFactory)
114111
{
115-
this.containerScope = new SimpleContainerScope(WebSocketPolicy.newClientPolicy(),new MappedByteBufferPool(),objectFactory);
116-
this.httpClient = httpClient;
117-
this.extensionRegistry = new WebSocketExtensionFactory(containerScope);
118-
this.eventDriverFactory = new EventDriverFactory(containerScope);
119-
this.sessionFactory = new WebSocketSessionFactory(containerScope);
112+
this(new SimpleContainerScope(new WebSocketPolicy(WebSocketBehavior.CLIENT), null, null, null, objectFactory), null, null, httpClient);
120113
}
121114

122115
/**
123116
* Create a new WebSocketClient
124117
*
125-
* @param executor
126-
* the executor to use
118+
* @param sslContextFactory
119+
* ssl context factory to use
127120
* @deprecated use {@link #WebSocketClient(HttpClient)} instead
128121
*/
129122
@Deprecated
130-
public WebSocketClient(Executor executor)
123+
public WebSocketClient(SslContextFactory sslContextFactory)
131124
{
132-
this(null,executor);
125+
this(sslContextFactory,null, null);
133126
}
134127

135128
/**
136129
* Create a new WebSocketClient
137130
*
138-
* @param bufferPool
139-
* byte buffer pool to use
131+
* @param executor
132+
* the executor to use
133+
* @deprecated use {@link #WebSocketClient(HttpClient)} instead
140134
*/
141-
public WebSocketClient(ByteBufferPool bufferPool)
135+
public WebSocketClient(Executor executor)
142136
{
143-
this(null,null,bufferPool);
137+
this(null, executor, null);
144138
}
145139

146140
/**
147141
* Create a new WebSocketClient
148142
*
149-
* @param sslContextFactory
150-
* ssl context factory to use
143+
* @param bufferPool
144+
* byte buffer pool to use
145+
* @deprecated use {@link #WebSocketClient(HttpClient)} instead
151146
*/
152-
public WebSocketClient(SslContextFactory sslContextFactory)
147+
@Deprecated
148+
public WebSocketClient(ByteBufferPool bufferPool)
153149
{
154-
this(sslContextFactory,null);
150+
this(null, null, bufferPool);
155151
}
156152

157153
/**
@@ -166,7 +162,7 @@ public WebSocketClient(SslContextFactory sslContextFactory)
166162
@Deprecated
167163
public WebSocketClient(SslContextFactory sslContextFactory, Executor executor)
168164
{
169-
this(sslContextFactory,executor,new MappedByteBufferPool());
165+
this(sslContextFactory, executor, null);
170166
}
171167

172168
/**
@@ -178,7 +174,7 @@ public WebSocketClient(SslContextFactory sslContextFactory, Executor executor)
178174
*/
179175
public WebSocketClient(WebSocketContainerScope scope)
180176
{
181-
this(scope.getSslContextFactory(),scope.getExecutor(),scope.getBufferPool(),scope.getObjectFactory());
177+
this(scope, null, null, null);
182178
}
183179

184180
/**
@@ -193,7 +189,7 @@ public WebSocketClient(WebSocketContainerScope scope)
193189
*/
194190
public WebSocketClient(WebSocketContainerScope scope, SslContextFactory sslContextFactory)
195191
{
196-
this(sslContextFactory,scope.getExecutor(),scope.getBufferPool(),scope.getObjectFactory());
192+
this(sslContextFactory, scope.getExecutor(), scope.getBufferPool(), scope.getObjectFactory());
197193
}
198194

199195
/**
@@ -209,7 +205,7 @@ public WebSocketClient(WebSocketContainerScope scope, SslContextFactory sslConte
209205
*/
210206
public WebSocketClient(SslContextFactory sslContextFactory, Executor executor, ByteBufferPool bufferPool)
211207
{
212-
this(sslContextFactory,executor,bufferPool,new DecoratedObjectFactory());
208+
this(sslContextFactory, executor, bufferPool, null);
213209
}
214210

215211
/**
@@ -227,17 +223,8 @@ public WebSocketClient(SslContextFactory sslContextFactory, Executor executor, B
227223
*/
228224
private WebSocketClient(SslContextFactory sslContextFactory, Executor executor, ByteBufferPool bufferPool, DecoratedObjectFactory objectFactory)
229225
{
230-
this.httpClient = new HttpClient(sslContextFactory);
231-
this.httpClient.setExecutor(executor);
232-
this.httpClient.setByteBufferPool(bufferPool);
226+
this(new SimpleContainerScope(new WebSocketPolicy(WebSocketBehavior.CLIENT), bufferPool, executor, sslContextFactory, objectFactory));
233227
addBean(this.httpClient);
234-
235-
this.containerScope = new SimpleContainerScope(WebSocketPolicy.newClientPolicy(), bufferPool, objectFactory);
236-
237-
this.extensionRegistry = new WebSocketExtensionFactory(containerScope);
238-
239-
this.eventDriverFactory = new EventDriverFactory(containerScope);
240-
this.sessionFactory = new WebSocketSessionFactory(containerScope);
241228
}
242229

243230
/**
@@ -271,20 +258,7 @@ public WebSocketClient(final WebSocketContainerScope scope, EventDriverFactory e
271258
*/
272259
public WebSocketClient(final WebSocketContainerScope scope, EventDriverFactory eventDriverFactory, SessionFactory sessionFactory, HttpClient httpClient)
273260
{
274-
WebSocketContainerScope clientScope;
275-
if (scope.getPolicy().getBehavior() == WebSocketBehavior.CLIENT)
276-
{
277-
clientScope = scope;
278-
}
279-
else
280-
{
281-
// We need to wrap the scope
282-
clientScope = new DelegatedContainerScope(WebSocketPolicy.newClientPolicy(), scope);
283-
}
284-
285-
this.containerScope = clientScope;
286-
287-
if(httpClient == null)
261+
if (httpClient == null)
288262
{
289263
this.httpClient = HttpClientProvider.get(scope);
290264
addBean(this.httpClient);
@@ -293,11 +267,15 @@ public WebSocketClient(final WebSocketContainerScope scope, EventDriverFactory e
293267
{
294268
this.httpClient = httpClient;
295269
}
296-
297-
this.extensionRegistry = new WebSocketExtensionFactory(containerScope);
298-
299-
this.eventDriverFactory = eventDriverFactory;
300-
this.sessionFactory = sessionFactory;
270+
271+
// Ensure we get a Client version of the policy.
272+
this.policy = scope.getPolicy().clonePolicy(WebSocketBehavior.CLIENT);
273+
// Support Late Binding of Object Factory (for CDI)
274+
this.objectFactorySupplier = () -> scope.getObjectFactory();
275+
this.extensionRegistry = new WebSocketExtensionFactory(this);
276+
277+
this.eventDriverFactory = eventDriverFactory == null ? new EventDriverFactory(this) : eventDriverFactory;
278+
this.sessionFactory = sessionFactory == null ? new WebSocketSessionFactory(this) : sessionFactory;
301279
}
302280

303281
public Future<Session> connect(Object websocket, URI toUri) throws IOException
@@ -439,7 +417,7 @@ public boolean isDispatchIO()
439417
*/
440418
public long getAsyncWriteTimeout()
441419
{
442-
return this.containerScope.getPolicy().getAsyncWriteTimeout();
420+
return getPolicy().getAsyncWriteTimeout();
443421
}
444422

445423
public SocketAddress getBindAddress()
@@ -548,7 +526,7 @@ public long getMaxTextMessageSize()
548526
@Override
549527
public DecoratedObjectFactory getObjectFactory()
550528
{
551-
return this.containerScope.getObjectFactory();
529+
return this.objectFactorySupplier.get();
552530
}
553531

554532
public Set<WebSocketSession> getOpenSessions()
@@ -559,7 +537,7 @@ public Set<WebSocketSession> getOpenSessions()
559537
@Override
560538
public WebSocketPolicy getPolicy()
561539
{
562-
return this.containerScope.getPolicy();
540+
return this.policy;
563541
}
564542

565543
public Scheduler getScheduler()
@@ -753,11 +731,27 @@ public boolean isStopAtShutdown()
753731
return stopAtShutdown;
754732
}
755733

734+
@Override
735+
public boolean equals(Object o)
736+
{
737+
if (this == o) return true;
738+
if (!(o instanceof WebSocketClient)) return false;
739+
WebSocketClient that = (WebSocketClient) o;
740+
return Objects.equals(this.httpClient, that.httpClient) &&
741+
Objects.equals(this.policy, that.policy);
742+
}
743+
744+
@Override
745+
public int hashCode()
746+
{
747+
return Objects.hash(httpClient, policy);
748+
}
749+
756750
@Override
757751
public String toString()
758752
{
759753
final StringBuilder sb = new StringBuilder("WebSocketClient@");
760-
sb.append(Integer.toHexString(id));
754+
sb.append(Integer.toHexString(hashCode()));
761755
sb.append("[httpClient=").append(httpClient);
762756
sb.append(",openSessions.size=");
763757
sb.append(getOpenSessions().size());

0 commit comments

Comments
 (0)