package com.aimmac23.hub.proxy;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.openqa.grid.common.RegistrationRequest;
import org.openqa.grid.internal.Registry;
import org.openqa.grid.internal.TestSession;
import org.openqa.grid.selenium.proxy.DefaultRemoteProxy;
import org.openqa.grid.web.servlet.handler.RequestType;
import org.openqa.grid.web.servlet.handler.SeleniumBasedRequest;
import org.openqa.selenium.remote.internal.HttpClientFactory;
import com.aimmac23.hub.HubVideoRegistry;
public class VideoProxy extends DefaultRemoteProxy {
private static final Logger log = Logger.getLogger(VideoProxy.class.getName());
private String serviceUrl;
boolean isCurrentlyRecording = false;
private HttpClient client;
private HttpHost remoteHost;
public VideoProxy(RegistrationRequest request, Registry registry) {
super(RegistrationRequestCorrector.correctRegistrationRequest(request), registry);
serviceUrl = getRemoteHost() + "/extra/VideoRecordingControlServlet";
remoteHost = new HttpHost(getRemoteHost().getHost(),
getRemoteHost().getPort());
HttpClientFactory httpClientFactory = new HttpClientFactory();
client = httpClientFactory.getHttpClient();
// its possible that the Hub may have crashed/been terminated/lost network connection, leaving the
// node still recording. Tell it to get back to a known-state (https://github.com/aimmac23/selenium-video-node/issues/5)
HttpPost r = new HttpPost(serviceUrl + "?command=reset");
try {
HttpResponse response = client.execute(r);
if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
String body = EntityUtils.toString(response.getEntity());
log.log(Level.SEVERE, "Could not reset node " + this.getId() +
" to known recording status - incorrect HTTP response. Got: HTTP " +
response.getStatusLine().getStatusCode() + " code with body: '" + body + "'");
// XXX: Does this mean that the node is unusable? We would probably take this path if the node was on an older
// version of this library
}
} catch (Exception e) {
log.log(Level.SEVERE, "Could not reset node " + this.getId() + " to known recording status - caught exception", e);
// XXX: Does this mean that the node is unusable?
}
finally {
r.releaseConnection();
}
}
@Override
public void beforeSession(TestSession arg0) {
super.beforeSession(arg0);
HttpPost r = new HttpPost(serviceUrl + "?command=start");
try {
HttpResponse response = client.execute(remoteHost, r);
if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
log.warning("Could not start video reporting: " + EntityUtils.toString(response.getEntity()));
return;
}
isCurrentlyRecording = true;
log.info("Started recording for new session on node: " + getId());
} catch (Exception e) {
log.warning("Could not start video reporting due to exception: " + e.getMessage());
e.printStackTrace();
return;
}
finally {
r.releaseConnection();
}
}
@Override
public void beforeCommand(TestSession session, HttpServletRequest request,
HttpServletResponse response) {
super.beforeCommand(session, request, response);
SeleniumBasedRequest seleniumRequest = SeleniumBasedRequest.createFromRequest(request, getRegistry());
// https://github.com/aimmac23/selenium-video-node/issues/10 - we need to mark the session as stopping, before we
// pass back the client any information to prevent race conditions when downloading the video
if(RequestType.STOP_SESSION.equals(seleniumRequest.getRequestType())) {
if(isCurrentlyRecording) {
HubVideoRegistry.declareSessionStopping(session);
}
else {
log.severe("Recording not started for " + session.getExternalKey() + " on node " + getId() + " - cannot register session as stopping.");
}
}
}
@Override
public void afterCommand(TestSession session, HttpServletRequest request,
HttpServletResponse response) {
super.afterCommand(session, request, response);
// its a shame we have to extract this again
SeleniumBasedRequest seleniumRequest = SeleniumBasedRequest.createFromRequest(request, getRegistry());
if(RequestType.STOP_SESSION.equals(seleniumRequest.getRequestType())) {
if(isCurrentlyRecording) {
log.info("Selenium session closed for " + session.getExternalKey() + " on node " + getId() + " - stopping recording.");
stopRecording(session);
}
else {
log.severe("Recording not started for " + session.getExternalKey() + " on node " + getId() +
" and session being deleted - this could be a bug in the code.");
}
}
}
@Override
public void afterSession(TestSession session) {
super.afterSession(session);
if(isCurrentlyRecording) {
log.warning("Session session terminated ungracefully for " + session.getExternalKey() + " on node " + getId() + " - stopping recording");
stopRecording(session);
}
}
void stopRecording(TestSession session) {
HttpPost r = new HttpPost(serviceUrl + "?command=stop");
try {
HttpResponse response = client.execute(remoteHost, r);
if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
log.warning("Could not stop video reporting: " + EntityUtils.toString(response.getEntity()));
}
isCurrentlyRecording = false;
// check that the session didn't explode on startup
if(session.getExternalKey() != null) {
JSONObject json = new JSONObject(EntityUtils.toString(response.getEntity()));
String fileKey = json.getString("filekey");
HubVideoRegistry.copyVideoToHub(session, fileKey, getRemoteHost());
}
} catch (Exception e) {
log.warning("Could not stop video reporting due to exception: " + e.getMessage());
e.printStackTrace();
}
finally {
r.releaseConnection();
}
}
}