Skip to content

Commit

Permalink
KristianRosenvold: Made nodes obey timeout from hub
Browse files Browse the repository at this point in the history
r16444
  • Loading branch information
krosenvold committed Apr 4, 2012
1 parent 5053ea5 commit fecf74a
Show file tree
Hide file tree
Showing 28 changed files with 492 additions and 109 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,5 @@ rb/.yardoc
.tm_properties
maven/*/src
maven/*/target

java/client/build/
java/server/build/
1 change: 1 addition & 0 deletions java/client/src/org/openqa/selenium/remote/ErrorCodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public class ErrorCodes {
public static final int INVALID_XPATH_SELECTOR = 51;
public static final int INVALID_XPATH_SELECTOR_RETURN_TYPER = 52;
// The following error codes are derived straight from HTTP return codes.
public static final int GONE = 404;
public static final int METHOD_NOT_ALLOWED = 405;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ protected Response execute(String driverCommand, Map<String, ?> parameters) {
response.setValue(value);
log(sessionId, command.getName(), command, When.AFTER);
} catch (SessionTerminatedException e){
throw new UnreachableBrowserException("Session was terminated by hanging browser detection", e);
throw e;
} catch (Exception e) {
log(sessionId, command.getName(), command, When.EXCEPTION);
String errorMessage = "Error communicating with the remote browser. " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@
*/
package org.openqa.selenium.remote;

import org.openqa.selenium.WebDriverException;

import java.io.IOException;

/**
* Indicates that the session was terminated by the server
*/
public class SessionTerminatedException extends IOException {
public class SessionTerminatedException extends WebDriverException {

public SessionTerminatedException() {
}

}
29 changes: 17 additions & 12 deletions java/server/src/org/openqa/grid/common/RegistrationRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,7 @@

package org.openqa.grid.common;

import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import com.google.common.collect.Maps;

import org.json.JSONArray;
import org.json.JSONException;
Expand All @@ -40,7 +30,17 @@
import org.openqa.selenium.server.RemoteControlConfiguration;
import org.openqa.selenium.server.cli.RemoteControlLauncher;

import com.google.common.collect.Maps;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
* helper to register to the grid. Using JSON to exchange the object between the node and grid.
Expand Down Expand Up @@ -74,7 +74,9 @@ public class RegistrationRequest {
public static final String REGISTER_CYCLE = "registerCycle";
public static final String PROXY_CLASS = CapabilityType.PROXY;
public static final String CLEAN_UP_CYCLE = "cleanUpCycle";
// Client timeout
public static final String TIME_OUT = "timeout";
public static final String BROWSER_TIME_OUT = "browserTimeout";

// TODO delete to keep only HUB_HOSt and HUB_PORT
public static final String REMOTE_HOST = "remoteHost";
Expand Down Expand Up @@ -457,6 +459,9 @@ private void loadFromCommandLine(String[] args) {
if (helper.isParamPresent("-timeout")) {
configuration.put(TIME_OUT, Integer.parseInt(helper.getParamValue("-timeout")));
}
if (helper.isParamPresent("-browserTimeout")) {
configuration.put(BROWSER_TIME_OUT, Integer.parseInt(helper.getParamValue("-browserTimeout")));
}
if (helper.isParamPresent("-maxSession")) {
configuration.put(MAX_SESSION, Integer.parseInt(helper.getParamValue("-maxSession")));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@

"cleanUpCycle": 5000,
"timeout": 300000,
"browserTimeout": 0,
"maxSession": 5
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ hubConfig = (hub) a JSON file following grid2 format.
# config that will be inherited by the proxy and used for the node management.
cleanupCycle = (node) <XXXX> in ms. How often a proxy will check for timed out thread.
nodeTimeout = (node) <XXXX> the timeout in seconds before the hub automatically ends a test that hasn't had aby activity than XX sec.The browser will be released for another test to use.This typically takes care of the client crashes.
browserTimeout= (hub/node) The timeout in seconds a browser can hang
hub = (node) <http://localhost:4444/grid/register> : the url that will be used to post the registration request.
proxy = (node) the class that will be used to represent the node. By default org.openqa.grid.selenium.proxy.SeleniumRemoteProxy ( selenium1 / RC ) org.openqa.grid.selenium.proxy.WebDriverRemoteProxy ( selenium2 / webdriver )
maxSession = (node) max number of tests that can run at the same time on the node, independently of the browser used.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

public enum SessionTerminationReason {
TIMEOUT,
BROWSER_TIMEOUT,
ORPHAN,
CLIENT_STOPPED_SESSION,
CLIENT_GONE,
Expand Down
72 changes: 44 additions & 28 deletions java/server/src/org/openqa/grid/internal/TestSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ public String getInternalKey() {
/**
* Creates a test session on the specified testSlot.
*/
public TestSession(TestSlot slot, Map<String, Object> requestedCapabilities, TimeSource timeSource) {
public TestSession(TestSlot slot, Map<String, Object> requestedCapabilities,
TimeSource timeSource) {
internalKey = UUID.randomUUID().toString();
this.slot = slot;
this.requestedCapabilities = requestedCapabilities;
Expand Down Expand Up @@ -143,8 +144,8 @@ public boolean isOrphaned() {
// seen any new
// commands during that time frame.
return slot.getProtocol().isSelenium()
&& elapsedSinceCreation > MAX_IDLE_TIME_BEFORE_CONSIDERED_ORPHANED
&& sessionCreatedAt == lastActivity;
&& elapsedSinceCreation > MAX_IDLE_TIME_BEFORE_CONSIDERED_ORPHANED
&& sessionCreatedAt == lastActivity;
}

/**
Expand Down Expand Up @@ -180,7 +181,7 @@ public boolean equals(Object obj) {
@Override
public String toString() {
return externalKey != null ? "ext. key " + externalKey : internalKey
+ " (int. key, remote not contacted yet.)";
+ " (int. key, remote not contacted yet.)";
}


Expand All @@ -191,14 +192,15 @@ private HttpClient getClient() {
/**
* forwards the request to the node.
*/
public String forward(SeleniumBasedRequest request, HttpServletResponse response, boolean newSessionRequest)
public String forward(SeleniumBasedRequest request, HttpServletResponse response,
boolean newSessionRequest)
throws IOException {
String res = null;

String currentThreadName = Thread.currentThread().getName();
setThreadDisplayName();
forwardingRequest = true;

try {
if (slot.getProxy() instanceof CommandListener) {
((CommandListener) slot.getProxy()).beforeCommand(this, request, response);
Expand All @@ -212,25 +214,32 @@ public String forward(SeleniumBasedRequest request, HttpServletResponse response

lastActivity = timeSource.currentTimeInMillis();

response.setStatus(proxyResponse.getStatusLine().getStatusCode());
final int statusCode = proxyResponse.getStatusLine().getStatusCode();
response.setStatus(statusCode);
processResponseHeaders(request, response, slot.getRemoteURL(), proxyResponse);

if (proxyResponse.getStatusLine().getStatusCode() != 500) {
if (statusCode != HttpServletResponse.SC_INTERNAL_SERVER_ERROR &&
statusCode != HttpServletResponse.SC_NOT_FOUND) {
System.out.println("regular");
updateHubIfNewWebDriverSession(request, proxyResponse);
} else if (newSessionRequest) {
}
if (newSessionRequest && statusCode == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
removeIncompleteNewSessionRequest();
}
if (statusCode == HttpServletResponse.SC_NOT_FOUND) {
removeSessionBrowserTimeout();
}

HttpEntity responseBody = proxyResponse.getEntity();
HttpEntity responseBody = proxyResponse.getEntity();
byte[] contentBeingForwarded = null;
if (responseBody != null) {
try {
InputStream in = responseBody.getContent();

if (request.getRequestType() == RequestType.START_SESSION
&& request instanceof LegacySeleniumRequest) {
res = getResponseUtf8Content(in);

updateHubNewSeleniumSession(res);

in = new ByteArrayInputStream(res.getBytes("UTF-8"));
Expand All @@ -246,7 +255,7 @@ public String forward(SeleniumBasedRequest request, HttpServletResponse response
}

if (slot.getProxy() instanceof CommandListener) {
SeleniumBasedResponse wrappedResponse = new SeleniumBasedResponse(response);
SeleniumBasedResponse wrappedResponse = new SeleniumBasedResponse(response);
wrappedResponse.setForwardedContent(contentBeingForwarded);
((CommandListener) slot.getProxy()).afterCommand(this, request, wrappedResponse);
}
Expand All @@ -259,26 +268,30 @@ public String forward(SeleniumBasedRequest request, HttpServletResponse response
}
}

private void setThreadDisplayName() {
DateFormat dfmt=DateFormat.getTimeInstance();
String name = "Forwarding " + this + " to " + slot.getRemoteURL() + " at " +
dfmt.format(Calendar.getInstance().getTime());
Thread.currentThread().setName(name);
}
private void setThreadDisplayName() {
DateFormat dfmt = DateFormat.getTimeInstance();
String name = "Forwarding " + this + " to " + slot.getRemoteURL() + " at " +
dfmt.format(Calendar.getInstance().getTime());
Thread.currentThread().setName(name);
}

private void removeIncompleteNewSessionRequest() {
private void removeIncompleteNewSessionRequest() {
RemoteProxy proxy = slot.getProxy();
proxy.getRegistry().terminate(this, SessionTerminationReason.CREATIONFAILED);
}

private void removeSessionBrowserTimeout() {
RemoteProxy proxy = slot.getProxy();
proxy.getRegistry().terminate(this, SessionTerminationReason.BROWSER_TIMEOUT);
}

private void updateHubNewSeleniumSession(String content) {
ExternalSessionKey key = ExternalSessionKey.fromResponseBody(content);
setExternalKey(key);
}

private void updateHubIfNewWebDriverSession(SeleniumBasedRequest request,
HttpResponse proxyResponse) {
HttpResponse proxyResponse) {
if (request.getRequestType() == RequestType.START_SESSION
&& request instanceof WebDriverRequest) {
Header h = proxyResponse.getFirstHeader("Location");
Expand All @@ -292,23 +305,24 @@ private void updateHubIfNewWebDriverSession(SeleniumBasedRequest request,
}

private HttpResponse sendRequestToNode(HttpRequest proxyRequest) throws ClientProtocolException,
IOException {
IOException {
HttpClient client = getClient();
URL remoteURL = slot.getRemoteURL();
HttpHost host = new HttpHost(remoteURL.getHost(), remoteURL.getPort());

return client.execute(host, proxyRequest);
}

private HttpRequest prepareProxyRequest(HttpServletRequest request/*, ForwardConfiguration config*/)
private HttpRequest prepareProxyRequest(HttpServletRequest request
/*, ForwardConfiguration config*/)
throws IOException {
URL remoteURL = slot.getRemoteURL();

String pathSpec = request.getServletPath() + request.getContextPath();
String path = request.getRequestURI();
if (!path.startsWith(pathSpec)) {
throw new IllegalStateException("Expected path " + path + " to start with pathSpec "
+ pathSpec);
+ pathSpec);
}
String end = path.substring(pathSpec.length());
String ok = remoteURL + end;
Expand All @@ -320,7 +334,7 @@ private HttpRequest prepareProxyRequest(HttpServletRequest request/*, ForwardCon
}

HttpRequest proxyRequest;

if (body != null) {
BasicHttpEntityEnclosingRequest r =
new BasicHttpEntityEnclosingRequest(request.getMethod(), uri);
Expand All @@ -330,7 +344,7 @@ private HttpRequest prepareProxyRequest(HttpServletRequest request/*, ForwardCon
proxyRequest = new BasicHttpRequest(request.getMethod(), uri);
}

for (Enumeration<?> e = request.getHeaderNames(); e.hasMoreElements();) {
for (Enumeration<?> e = request.getHeaderNames(); e.hasMoreElements(); ) {
String headerName = (String) e.nextElement();

if ("Content-Length".equalsIgnoreCase(headerName)) {
Expand Down Expand Up @@ -399,7 +413,8 @@ private String getResponseUtf8Content(InputStream in) {
}

private void processResponseHeaders(HttpServletRequest request, HttpServletResponse response,
URL remoteURL, HttpResponse proxyResponse) throws MalformedURLException {
URL remoteURL, HttpResponse proxyResponse)
throws MalformedURLException {
String pathSpec = request.getServletPath() + request.getContextPath();
for (Header header : proxyResponse.getAllHeaders()) {
String name = header.getName();
Expand Down Expand Up @@ -466,7 +481,8 @@ public boolean sendDeleteSessionRequest() {
case Selenium:
request =
new BasicHttpRequest("POST", remoteURL.toExternalForm()
+ "/?cmd=testComplete&sessionId=" + getExternalKey().getKey());
+ "/?cmd=testComplete&sessionId=" + getExternalKey()
.getKey());
break;
case WebDriver:
String uri = remoteURL.toString() + "/session/" + externalKey;
Expand Down
Loading

0 comments on commit fecf74a

Please sign in to comment.