33
33
import org .openqa .selenium .Capabilities ;
34
34
import org .openqa .selenium .devtools .CdpEndpointFinder ;
35
35
import org .openqa .selenium .grid .data .Session ;
36
+ import org .openqa .selenium .internal .Require ;
36
37
import org .openqa .selenium .remote .SessionId ;
37
38
import org .openqa .selenium .remote .http .BinaryMessage ;
38
39
import org .openqa .selenium .remote .http .ClientConfig ;
@@ -75,57 +76,68 @@ public Optional<Consumer<Message>> apply(String uri, Consumer<Message> downstrea
75
76
return Optional .empty ();
76
77
}
77
78
78
- String sessionId =
79
- Stream .of (fwdMatch , cdpMatch , bidiMatch , vncMatch )
80
- .filter (Objects ::nonNull )
81
- .findFirst ()
82
- .get ()
83
- .getParameters ()
84
- .get ("sessionId" );
79
+ Optional <UrlTemplate .Match > firstMatch =
80
+ Stream .of (fwdMatch , cdpMatch , bidiMatch , vncMatch ).filter (Objects ::nonNull ).findFirst ();
81
+
82
+ if (firstMatch .isEmpty ()) {
83
+ LOG .warning ("No session id found in uri " + uri );
84
+ return Optional .empty ();
85
+ }
86
+
87
+ String sessionId = firstMatch .get ().getParameters ().get ("sessionId" );
85
88
86
89
LOG .fine ("Matching websockets for session id: " + sessionId );
87
90
SessionId id = new SessionId (sessionId );
88
91
89
92
if (!node .isSessionOwner (id )) {
90
- LOG .info ("Not owner of " + id );
93
+ LOG .warning ("Not owner of " + id );
91
94
return Optional .empty ();
92
95
}
93
96
94
97
Session session = node .getSession (id );
95
98
Capabilities caps = session .getCapabilities ();
96
99
LOG .fine ("Scanning for endpoint: " + caps );
97
100
101
+ // Used by the ForwardingListener to notify the node that the session is still active
102
+ Consumer <SessionId > sessionConsumer = node ::isSessionOwner ;
103
+
98
104
if (bidiMatch != null ) {
99
- return findBiDiEndpoint (downstream , caps );
105
+ return findBiDiEndpoint (downstream , caps , sessionConsumer , id );
100
106
}
101
107
102
108
if (vncMatch != null ) {
103
- return findVncEndpoint (downstream , caps );
109
+ // Passing a fake consumer to the ForwardingListener to avoid sending a session notification
110
+ // when VNC is used.
111
+ sessionConsumer = fakeConsumer -> {};
112
+ return findVncEndpoint (downstream , caps , sessionConsumer , id );
104
113
}
105
114
106
115
// This match happens when a user wants to do CDP over Dynamic Grid
107
116
if (fwdMatch != null ) {
108
117
LOG .info ("Matched endpoint where CDP connection is being forwarded" );
109
- return findCdpEndpoint (downstream , caps );
118
+ return findCdpEndpoint (downstream , caps , sessionConsumer , id );
110
119
}
111
120
if (caps .getCapabilityNames ().contains ("se:forwardCdp" )) {
112
121
LOG .info ("Found endpoint where CDP connection needs to be forwarded" );
113
- return findForwardCdpEndpoint (downstream , caps );
122
+ return findForwardCdpEndpoint (downstream , caps , sessionConsumer , id );
114
123
}
115
- return findCdpEndpoint (downstream , caps );
124
+ return findCdpEndpoint (downstream , caps , sessionConsumer , id );
116
125
}
117
126
118
127
private Optional <Consumer <Message >> findCdpEndpoint (
119
- Consumer <Message > downstream , Capabilities caps ) {
120
- // Using strings here to avoid Node depending upon specific drivers.
128
+ Consumer <Message > downstream ,
129
+ Capabilities caps ,
130
+ Consumer <SessionId > sessionConsumer ,
131
+ SessionId sessionId ) {
132
+
121
133
for (String cdpEndpointCap : CDP_ENDPOINT_CAPS ) {
122
134
Optional <URI > reportedUri = CdpEndpointFinder .getReportedUri (cdpEndpointCap , caps );
123
135
Optional <HttpClient > client =
124
136
reportedUri .map (uri -> CdpEndpointFinder .getHttpClient (clientFactory , uri ));
125
137
Optional <URI > cdpUri ;
126
138
127
139
try {
128
- cdpUri = client .flatMap (httpClient -> CdpEndpointFinder . getCdpEndPoint ( httpClient ) );
140
+ cdpUri = client .flatMap (CdpEndpointFinder :: getCdpEndPoint );
129
141
} catch (Exception e ) {
130
142
try {
131
143
client .ifPresent (HttpClient ::close );
@@ -137,7 +149,7 @@ private Optional<Consumer<Message>> findCdpEndpoint(
137
149
138
150
if (cdpUri .isPresent ()) {
139
151
LOG .log (getDebugLogLevel (), String .format ("Endpoint found in %s" , cdpEndpointCap ));
140
- return cdpUri .map (cdp -> createWsEndPoint (cdp , downstream ));
152
+ return cdpUri .map (cdp -> createWsEndPoint (cdp , downstream , sessionConsumer , sessionId ));
141
153
} else {
142
154
try {
143
155
client .ifPresent (HttpClient ::close );
@@ -154,30 +166,41 @@ private Optional<Consumer<Message>> findCdpEndpoint(
154
166
}
155
167
156
168
private Optional <Consumer <Message >> findBiDiEndpoint (
157
- Consumer <Message > downstream , Capabilities caps ) {
169
+ Consumer <Message > downstream ,
170
+ Capabilities caps ,
171
+ Consumer <SessionId > sessionConsumer ,
172
+ SessionId sessionId ) {
158
173
try {
159
174
URI uri = new URI (String .valueOf (caps .getCapability ("webSocketUrl" )));
160
- return Optional .of (uri ).map (bidi -> createWsEndPoint (bidi , downstream ));
175
+ return Optional .of (uri )
176
+ .map (bidi -> createWsEndPoint (bidi , downstream , sessionConsumer , sessionId ));
161
177
} catch (URISyntaxException e ) {
162
178
LOG .warning ("Unable to create URI from: " + caps .getCapability ("webSocketUrl" ));
163
179
return Optional .empty ();
164
180
}
165
181
}
166
182
167
183
private Optional <Consumer <Message >> findForwardCdpEndpoint (
168
- Consumer <Message > downstream , Capabilities caps ) {
184
+ Consumer <Message > downstream ,
185
+ Capabilities caps ,
186
+ Consumer <SessionId > sessionConsumer ,
187
+ SessionId sessionId ) {
169
188
// When using Dynamic Grid, we need to connect to a container before using the debuggerAddress
170
189
try {
171
190
URI uri = new URI (String .valueOf (caps .getCapability ("se:forwardCdp" )));
172
- return Optional .of (uri ).map (cdp -> createWsEndPoint (cdp , downstream ));
191
+ return Optional .of (uri )
192
+ .map (cdp -> createWsEndPoint (cdp , downstream , sessionConsumer , sessionId ));
173
193
} catch (URISyntaxException e ) {
174
194
LOG .warning ("Unable to create URI from: " + caps .getCapability ("se:forwardCdp" ));
175
195
return Optional .empty ();
176
196
}
177
197
}
178
198
179
199
private Optional <Consumer <Message >> findVncEndpoint (
180
- Consumer <Message > downstream , Capabilities caps ) {
200
+ Consumer <Message > downstream ,
201
+ Capabilities caps ,
202
+ Consumer <SessionId > sessionConsumer ,
203
+ SessionId sessionId ) {
181
204
String vncLocalAddress = (String ) caps .getCapability ("se:vncLocalAddress" );
182
205
Optional <URI > vncUri ;
183
206
try {
@@ -187,40 +210,57 @@ private Optional<Consumer<Message>> findVncEndpoint(
187
210
return Optional .empty ();
188
211
}
189
212
LOG .log (getDebugLogLevel (), String .format ("Endpoint found in %s" , "se:vncLocalAddress" ));
190
- return vncUri .map (vnc -> createWsEndPoint (vnc , downstream ));
213
+ return vncUri .map (vnc -> createWsEndPoint (vnc , downstream , sessionConsumer , sessionId ));
191
214
}
192
215
193
- private Consumer <Message > createWsEndPoint (URI uri , Consumer <Message > downstream ) {
194
- Objects .requireNonNull (uri );
216
+ private Consumer <Message > createWsEndPoint (
217
+ URI uri ,
218
+ Consumer <Message > downstream ,
219
+ Consumer <SessionId > sessionConsumer ,
220
+ SessionId sessionId ) {
221
+ Require .nonNull ("downstream" , downstream );
222
+ Require .nonNull ("uri" , uri );
223
+ Require .nonNull ("sessionConsumer" , sessionConsumer );
224
+ Require .nonNull ("sessionId" , sessionId );
195
225
196
226
LOG .info ("Establishing connection to " + uri );
197
227
198
228
HttpClient client = clientFactory .createClient (ClientConfig .defaultConfig ().baseUri (uri ));
199
229
WebSocket upstream =
200
- client .openSocket (new HttpRequest (GET , uri .toString ()), new ForwardingListener (downstream ));
230
+ client .openSocket (
231
+ new HttpRequest (GET , uri .toString ()),
232
+ new ForwardingListener (downstream , sessionConsumer , sessionId ));
201
233
return upstream ::send ;
202
234
}
203
235
204
236
private static class ForwardingListener implements WebSocket .Listener {
205
237
private final Consumer <Message > downstream ;
238
+ private final Consumer <SessionId > sessionConsumer ;
239
+ private final SessionId sessionId ;
206
240
207
- public ForwardingListener (Consumer <Message > downstream ) {
241
+ public ForwardingListener (
242
+ Consumer <Message > downstream , Consumer <SessionId > sessionConsumer , SessionId sessionId ) {
208
243
this .downstream = Objects .requireNonNull (downstream );
244
+ this .sessionConsumer = Objects .requireNonNull (sessionConsumer );
245
+ this .sessionId = Objects .requireNonNull (sessionId );
209
246
}
210
247
211
248
@ Override
212
249
public void onBinary (byte [] data ) {
213
250
downstream .accept (new BinaryMessage (data ));
251
+ sessionConsumer .accept (sessionId );
214
252
}
215
253
216
254
@ Override
217
255
public void onClose (int code , String reason ) {
218
256
downstream .accept (new CloseMessage (code , reason ));
257
+ sessionConsumer .accept (sessionId );
219
258
}
220
259
221
260
@ Override
222
261
public void onText (CharSequence data ) {
223
262
downstream .accept (new TextMessage (data ));
263
+ sessionConsumer .accept (sessionId );
224
264
}
225
265
226
266
@ Override
0 commit comments