2121import static org .openqa .selenium .remote .RemoteTags .SESSION_ID ;
2222import static org .openqa .selenium .remote .RemoteTags .SESSION_ID_EVENT ;
2323import static org .openqa .selenium .remote .http .Contents .asJson ;
24+ import static org .openqa .selenium .remote .http .Contents .reader ;
25+ import static org .openqa .selenium .remote .http .HttpMethod .GET ;
2426import static org .openqa .selenium .remote .tracing .Tags .EXCEPTION ;
2527import static org .openqa .selenium .remote .tracing .Tags .HTTP_REQUEST ;
2628import static org .openqa .selenium .remote .tracing .Tags .HTTP_REQUEST_EVENT ;
2729import static org .openqa .selenium .remote .tracing .Tags .HTTP_RESPONSE ;
2830
2931import java .io .Closeable ;
32+ import java .io .Reader ;
3033import java .net .URI ;
34+ import java .time .Duration ;
3135import java .time .Instant ;
3236import java .time .temporal .ChronoUnit ;
3337import java .util .Iterator ;
4347import org .openqa .selenium .NoSuchSessionException ;
4448import org .openqa .selenium .concurrent .ExecutorServices ;
4549import org .openqa .selenium .concurrent .GuardedRunnable ;
50+ import org .openqa .selenium .grid .data .NodeStatus ;
4651import org .openqa .selenium .grid .sessionmap .SessionMap ;
4752import org .openqa .selenium .grid .web .ReverseProxyHandler ;
4853import org .openqa .selenium .internal .Require ;
54+ import org .openqa .selenium .json .Json ;
55+ import org .openqa .selenium .json .JsonInput ;
4956import org .openqa .selenium .remote .ErrorCodec ;
5057import org .openqa .selenium .remote .SessionId ;
5158import org .openqa .selenium .remote .http .ClientConfig ;
@@ -229,8 +236,7 @@ private Callable<UsageCountingReverseProxyHandler> loadSessionId(
229236 return entry ;
230237 }
231238
232- ClientConfig config =
233- ClientConfig .defaultConfig ().baseUri (sessionUri ).withRetries ();
239+ ClientConfig config = fetchNodeSessionTimeout (sessionUri );
234240 HttpClient httpClient = httpClientFactory .createClient (config );
235241
236242 return new CacheEntry (httpClient , 1 );
@@ -246,6 +252,49 @@ private Callable<UsageCountingReverseProxyHandler> loadSessionId(
246252 });
247253 }
248254
255+ private ClientConfig fetchNodeSessionTimeout (URI uri ) {
256+ ClientConfig config = ClientConfig .defaultConfig ().baseUri (uri ).withRetries ();
257+ Duration sessionTimeout = config .readTimeout ();
258+ try (HttpClient httpClient = httpClientFactory .createClient (config )) {
259+ HttpRequest statusRequest = new HttpRequest (GET , "/status" );
260+ HttpResponse res = httpClient .execute (statusRequest );
261+ Reader reader = reader (res );
262+ Json JSON = new Json ();
263+ JsonInput in = JSON .newInput (reader );
264+ in .beginObject ();
265+ // Skip everything until we find "value"
266+ while (in .hasNext ()) {
267+ if ("value" .equals (in .nextName ())) {
268+ in .beginObject ();
269+ while (in .hasNext ()) {
270+ if ("node" .equals (in .nextName ())) {
271+ NodeStatus nodeStatus = in .read (NodeStatus .class );
272+ sessionTimeout = nodeStatus .getSessionTimeout ();
273+ LOG .fine (
274+ "Fetched session timeout from node status (read timeout: "
275+ + sessionTimeout .toSeconds ()
276+ + " seconds) for "
277+ + uri );
278+ } else {
279+ in .skipValue ();
280+ }
281+ }
282+ in .endObject ();
283+ } else {
284+ in .skipValue ();
285+ }
286+ }
287+ } catch (Exception e ) {
288+ LOG .fine (
289+ "Use default from ClientConfig (read timeout: "
290+ + config .readTimeout ().toSeconds ()
291+ + " seconds) for "
292+ + uri );
293+ }
294+ config = config .readTimeout (sessionTimeout );
295+ return config ;
296+ }
297+
249298 @ Override
250299 public void close () {
251300 ExecutorServices .shutdownGracefully (
0 commit comments