Skip to content

Commit cee7f6b

Browse files
bhecquettitusfortnerdiemol
authored
Prevent browser to be created and unused in case a short session-request-timeout is set (#12848)
* Add logs * Prevent a session to be created while its timeout has expired * restore missing "remove" * Close timedout session on creation to prevent them to be staled * Correct stopping of expired sessions * Remove logs for final commit * removed a test that is now useless as request session timeout is now strict * Simplify session stopping if timeout expires * Add some tests * remove logs * Return value directly * remove unused method * Removed hard coded value * correct according to comments * Correct according to comments * Remove spaces * remove spaces * Remove unused import * Add comments to tell why session is valid * fix linter issues * Add a test when session time out * test improved * Running format script --------- Co-authored-by: titusfortner <titus.fortner@gmail.com> Co-authored-by: Diego Molina <diemol@users.noreply.github.com> Co-authored-by: Diego Molina <diemol@gmail.com>
1 parent cfe7e1f commit cee7f6b

File tree

8 files changed

+385
-51
lines changed

8 files changed

+385
-51
lines changed

java/src/org/openqa/selenium/grid/distributor/local/LocalDistributor.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import java.util.HashMap;
4343
import java.util.List;
4444
import java.util.Map;
45+
import java.util.Optional;
4546
import java.util.Set;
4647
import java.util.concurrent.ConcurrentHashMap;
4748
import java.util.concurrent.Executor;
@@ -838,8 +839,42 @@ private void handleNewSessionRequest(SessionRequest sessionRequest) {
838839
}
839840
}
840841

841-
sessionQueue.complete(reqId, response);
842+
// 'complete' will return 'true' if the session has not timed out during the creation
843+
// process: it's still a valid session as it can be used by the client
844+
boolean isSessionValid = sessionQueue.complete(reqId, response);
845+
// If the session request has timed out, tell the Node to remove the session, so that does
846+
// not stall
847+
if (!isSessionValid && response.isRight()) {
848+
LOG.log(
849+
Debug.getDebugLogLevel(),
850+
"Session for request {0} has been created but it has timed out, stopping it to avoid"
851+
+ " stalled browser",
852+
reqId.toString());
853+
URI nodeURI = response.right().getSession().getUri();
854+
Node node = getNodeFromURI(nodeURI);
855+
if (node != null) {
856+
node.stop(response.right().getSession().getId());
857+
}
858+
}
842859
}
843860
}
844861
}
862+
863+
protected Node getNodeFromURI(URI uri) {
864+
Lock readLock = this.lock.readLock();
865+
readLock.lock();
866+
try {
867+
Optional<NodeStatus> nodeStatus =
868+
model.getSnapshot().stream()
869+
.filter(node -> node.getExternalUri().equals(uri))
870+
.findFirst();
871+
if (nodeStatus.isPresent()) {
872+
return nodes.get(nodeStatus.get().getNodeId());
873+
} else {
874+
return null;
875+
}
876+
} finally {
877+
readLock.unlock();
878+
}
879+
}
845880
}

java/src/org/openqa/selenium/grid/sessionqueue/NewSessionQueue.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private RequestId requestIdFrom(Map<String, String> params) {
111111

112112
public abstract List<SessionRequest> getNextAvailable(Map<Capabilities, Long> stereotypes);
113113

114-
public abstract void complete(
114+
public abstract boolean complete(
115115
RequestId reqId, Either<SessionNotCreatedException, CreateSessionResponse> result);
116116

117117
public abstract int clearQueue();

java/src/org/openqa/selenium/grid/sessionqueue/SessionCreated.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package org.openqa.selenium.grid.sessionqueue;
1919

20+
import static java.util.Collections.singletonMap;
21+
import static org.openqa.selenium.remote.http.Contents.asJson;
2022
import static org.openqa.selenium.remote.tracing.HttpTracing.newSpanAsChildOf;
2123
import static org.openqa.selenium.remote.tracing.Tags.HTTP_REQUEST;
2224
import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE;
@@ -50,9 +52,14 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException {
5052
HTTP_REQUEST.accept(span, req);
5153

5254
CreateSessionResponse response = Contents.fromJson(req, CreateSessionResponse.class);
53-
queue.complete(requestId, Either.right(response));
5455

55-
HttpResponse res = new HttpResponse();
56+
// 'complete' will return 'true' if the session has not timed out during the creation process:
57+
// it's still a valid session as it can be used by the client
58+
boolean isSessionValid = queue.complete(requestId, Either.right(response));
59+
60+
HttpResponse res =
61+
new HttpResponse().setContent(asJson(singletonMap("value", isSessionValid)));
62+
5663
HTTP_RESPONSE.accept(span, res);
5764
return res;
5865
}

java/src/org/openqa/selenium/grid/sessionqueue/SessionNotCreated.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package org.openqa.selenium.grid.sessionqueue;
1919

20+
import static java.util.Collections.singletonMap;
21+
import static org.openqa.selenium.remote.http.Contents.asJson;
2022
import static org.openqa.selenium.remote.tracing.HttpTracing.newSpanAsChildOf;
2123
import static org.openqa.selenium.remote.tracing.Tags.HTTP_REQUEST;
2224
import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE;
@@ -52,9 +54,13 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException {
5254

5355
String message = Contents.fromJson(req, String.class);
5456
SessionNotCreatedException exception = new SessionNotCreatedException(message);
55-
queue.complete(requestId, Either.left(exception));
5657

57-
HttpResponse res = new HttpResponse();
58+
// 'complete' will return 'true' if the session has not timed out during the creation process:
59+
// it's still a valid session as it can be used by the client
60+
boolean isSessionValid = queue.complete(requestId, Either.left(exception));
61+
62+
HttpResponse res =
63+
new HttpResponse().setContent(asJson(singletonMap("value", isSessionValid)));
5864
HTTP_RESPONSE.accept(span, res);
5965
return res;
6066
}

java/src/org/openqa/selenium/grid/sessionqueue/local/LocalNewSessionQueue.java

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,6 @@ public HttpResponse addToQueue(SessionRequest request) {
214214
try {
215215

216216
boolean sessionCreated = data.latch.await(requestTimeout.toMillis(), MILLISECONDS);
217-
if (!(sessionCreated || isRequestInQueue(request.getRequestId()))) {
218-
sessionCreated = data.latch.await(5000, MILLISECONDS);
219-
}
220217

221218
if (sessionCreated) {
222219
result = data.result;
@@ -293,6 +290,12 @@ public boolean retryAddToQueue(SessionRequest request) {
293290
if (!requests.containsKey(request.getRequestId())) {
294291
return false;
295292
}
293+
if (isTimedOut(Instant.now(), requests.get(request.getRequestId()))) {
294+
// as we try to re-add a session request that has already expired, force session timeout
295+
failDueToTimeout(request.getRequestId());
296+
// return true to avoid handleNewSessionRequest to call 'complete' an other time
297+
return true;
298+
}
296299

297300
if (queue.contains(request)) {
298301
// No need to re-add this
@@ -330,19 +333,6 @@ public Optional<SessionRequest> remove(RequestId reqId) {
330333
}
331334
}
332335

333-
private boolean isRequestInQueue(RequestId requestId) {
334-
Lock readLock = lock.readLock();
335-
readLock.lock();
336-
337-
try {
338-
Optional<SessionRequest> result =
339-
queue.stream().filter(req -> req.getRequestId().equals(requestId)).findAny();
340-
return result.isPresent();
341-
} finally {
342-
readLock.unlock();
343-
}
344-
}
345-
346336
@Override
347337
public List<SessionRequest> getNextAvailable(Map<Capabilities, Long> stereotypes) {
348338
Require.nonNull("Stereotypes", stereotypes);
@@ -378,8 +368,9 @@ public List<SessionRequest> getNextAvailable(Map<Capabilities, Long> stereotypes
378368
}
379369
}
380370

371+
/** Returns true if the session is still valid (not timed out) */
381372
@Override
382-
public void complete(
373+
public boolean complete(
383374
RequestId reqId, Either<SessionNotCreatedException, CreateSessionResponse> result) {
384375
Require.nonNull("New session request", reqId);
385376
Require.nonNull("Result", result);
@@ -388,14 +379,17 @@ public void complete(
388379
Lock readLock = lock.readLock();
389380
readLock.lock();
390381
Data data;
382+
boolean isSessionTimedOut = false;
391383
try {
392384
data = requests.get(reqId);
393385
} finally {
394386
readLock.unlock();
395387
}
396388

397389
if (data == null) {
398-
return;
390+
return false;
391+
} else {
392+
isSessionTimedOut = isTimedOut(Instant.now(), data);
399393
}
400394

401395
Lock writeLock = lock.writeLock();
@@ -409,6 +403,8 @@ public void complete(
409403
}
410404

411405
data.setResult(result);
406+
407+
return !isSessionTimedOut;
412408
}
413409
}
414410

java/src/org/openqa/selenium/grid/sessionqueue/remote/RemoteNewSessionQueue.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ public List<SessionRequest> getNextAvailable(Map<Capabilities, Long> stereotypes
154154
}
155155

156156
@Override
157-
public void complete(
157+
public boolean complete(
158158
RequestId reqId, Either<SessionNotCreatedException, CreateSessionResponse> result) {
159159
Require.nonNull("Request ID", reqId);
160160
Require.nonNull("Result", result);
@@ -171,7 +171,8 @@ public void complete(
171171
}
172172

173173
HttpTracing.inject(tracer, tracer.getCurrentContext(), upstream);
174-
client.with(addSecret).execute(upstream);
174+
HttpResponse response = client.with(addSecret).execute(upstream);
175+
return Values.get(response, Boolean.class);
175176
}
176177

177178
@Override

0 commit comments

Comments
 (0)