=== modified file 'src/com/goldencode/p2j/main/ClientCore.java' --- src/com/goldencode/p2j/main/ClientCore.java 2017-04-01 23:33:34 +0000 +++ src/com/goldencode/p2j/main/ClientCore.java 2017-06-21 18:39:15 +0000 @@ -252,7 +252,13 @@ // some client code must read configuration from the directory DirectoryManager.initRemote(session); + + WebClientRegistrator registrator = + (WebClientRegistrator) RemoteObject.obtainNetworkInstance( + WebClientRegistrator.class, session); + registrator.registerWebClientSession(uuid, session.getNodeAddress()); } + } catch (Exception exc) { @@ -349,6 +355,11 @@ // dependent threads need to be shutdown, even if the client restarts on CTRL-C ThinClient.terminate(); + if (!running) + { + driver.shutdown(); + } + // shut down session if (single) { @@ -359,10 +370,6 @@ session.terminate(); } - if (!running) - { - driver.shutdown(); - } // clear after me SecurityManager.clearInstance(); === modified file 'src/com/goldencode/p2j/main/StandardServer.java' --- src/com/goldencode/p2j/main/StandardServer.java 2017-04-01 23:33:34 +0000 +++ src/com/goldencode/p2j/main/StandardServer.java 2017-06-21 18:39:15 +0000 @@ -948,7 +948,7 @@ ready.countDown(); // main listening loop; service incoming socket connections - sessMgr.listen(null); + sessMgr.listen(WebClientsManager.getInstance()); } finally === added file 'src/com/goldencode/p2j/main/WebClientAllocator.java' --- src/com/goldencode/p2j/main/WebClientAllocator.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/main/WebClientAllocator.java 2017-06-20 06:10:28 +0000 @@ -0,0 +1,8 @@ +package com.goldencode.p2j.main; + + + +public interface WebClientAllocator +{ + WebClientConfig allocateClient(String forwardedHost, String forwardedProto); +} === added file 'src/com/goldencode/p2j/main/WebClientConfig.java' --- src/com/goldencode/p2j/main/WebClientConfig.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/main/WebClientConfig.java 2017-06-21 18:39:15 +0000 @@ -0,0 +1,88 @@ +package com.goldencode.p2j.main; + +import java.io.Serializable; + +public class WebClientConfig implements Serializable +{ + /** web client id */ + private String uuid; + /** + * A direct host assigned to launch the spawned web client + */ + private String host; + + /** + * A direct port assigned for the spawned web client + */ + private int port; + + /** + * A web root prefix for all resources of the web client + */ + private String webRoot; + + private String forwardedHost; + + private String forwardedProto; + + public String getUuid() + { + return uuid; + } + + public void setUuid(String uuid) + { + this.uuid = uuid; + } + + public String getHost() + { + return host; + } + + public int getPort() + { + return port; + } + + public String getWebRoot() + { + return webRoot; + } + + public String getForwardedHost() + { + return forwardedHost; + } + + public String getForwardedProto() + { + return forwardedProto; + } + + public void setHost(String host) + { + this.host = host; + } + + public void setPort(int port) + { + this.port = port; + } + + public void setWebRoot(String webRoot) + { + this.webRoot = webRoot; + } + + public void setForwardedHost(String forwardedHost) + { + this.forwardedHost = forwardedHost; + } + + public void setForwardedProto(String forwardedProto) + { + this.forwardedProto = forwardedProto; + } +} + === added file 'src/com/goldencode/p2j/main/WebClientRegistrator.java' --- src/com/goldencode/p2j/main/WebClientRegistrator.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/main/WebClientRegistrator.java 2017-06-21 18:39:15 +0000 @@ -0,0 +1,7 @@ +package com.goldencode.p2j.main; + + +public interface WebClientRegistrator +{ + void registerWebClientSession(String webClientId, int peerNode); +} === modified file 'src/com/goldencode/p2j/main/WebClientSpawner.java' --- src/com/goldencode/p2j/main/WebClientSpawner.java 2017-04-01 23:33:34 +0000 +++ src/com/goldencode/p2j/main/WebClientSpawner.java 2017-06-21 18:39:15 +0000 @@ -75,8 +75,15 @@ package com.goldencode.p2j.main; +import java.net.URI; +import java.net.URISyntaxException; + import com.goldencode.p2j.cfg.*; import com.goldencode.p2j.directory.*; +import com.goldencode.p2j.main.WebClientConfig; +import com.goldencode.p2j.net.Session; +import com.goldencode.p2j.net.SessionListener; +import com.goldencode.p2j.net.SessionManager; import com.goldencode.p2j.ui.client.driver.web.*; import com.goldencode.p2j.ui.client.chui.driver.web.*; import com.goldencode.p2j.ui.client.gui.driver.web.*; @@ -117,6 +124,8 @@ /** Flag that denotes client type (true for GUI, false for ChUI). */ private boolean gui; + private final WebClientConfig webClientConfig; + /** * Create a new web client spawner. * @@ -131,10 +140,13 @@ * @param options * The command-line configuration options. May be null. */ - public WebClientSpawner(boolean gui, boolean trusted, String referrer, String[] options) + public WebClientSpawner(WebClientConfig webClient, + boolean gui, boolean trusted, String referrer, String[] options) { super(new WebClientBuilderParameters(gui, trusted, referrer, options)); this.gui = gui; + this.webClientConfig = webClient; + WebClientsManager.getInstance().registerWebClientUuid(cfg.getUuid(), webClient); } /** @@ -148,7 +160,6 @@ public void clientIsReady(Object data) { remoteUri = (String) data; - super.clientIsReady(data); } @@ -180,13 +191,19 @@ * Get the remote server URI to which the browser will be redirected. * * @return The remote server URI or null. + * @throws URISyntaxException */ - public String getRemoteUri() + public String getRemoteUri() throws URISyntaxException { String uri = remoteUri; if (uri != null) { + if (webClientConfig.getForwardedHost() != null && !webClientConfig.getForwardedHost().isEmpty()) + { + uri = rewriteUri(webClientConfig); + } + // set authorization token for the redirect Object store = getServerData(); @@ -203,6 +220,17 @@ return uri; } + + private String rewriteUri(WebClientConfig webClient) + throws URISyntaxException + { + String forwardedProto = webClient.getForwardedProto(); + String forwardedHost = webClient.getForwardedHost(); + String forwardedPath = webClient.getWebRoot(); + URI uri = URI.create(forwardedProto + "://" + forwardedHost + forwardedPath + "/"); + //rewrite uri + return uri.toString(); + } /** * Get a {@link TemporaryClient} worker which will do the work after authenticating on the P2J @@ -213,7 +241,7 @@ @Override public TemporaryClient getTemporaryClient() { - return new TemporaryClientTask(gui); + return new TemporaryClientTask(gui, webClientConfig); } /** @@ -229,6 +257,7 @@ /** Flag that denotes client type. */ private boolean gui; + private WebClientConfig webClient; /** * Create a new web client spawner. * @@ -236,9 +265,10 @@ * Flag that denotes client type (true for GUI, false * for ChUI). */ - public TemporaryClientTask(boolean gui) + public TemporaryClientTask(boolean gui, WebClientConfig webClient) { this.gui = gui; + this.webClient = webClient; } /** @@ -285,6 +315,30 @@ int port = config.getInt("client", "web", "port", 0); String host = config.getString("client", "web", "host", null); + if (webClient != null) + { + //override host and port if new values are provided + if (webClient.getPort() > 0) + { + port = webClient.getPort(); + config.setConfigItem("client", "web", "port", String.valueOf(port)); + } + if (webClient.getHost() != null && + !webClient.getHost().isEmpty() && + !"localhost".equals(webClient.getHost())) + { + host = webClient.getHost(); + config.setConfigItem("client", "web", "host", host); + } + // set web root + if (!"/".equals(webClient.getWebRoot())) + { + config.setConfigItem("client", "web", "webRoot", webClient.getWebRoot()); + } +// config.setConfigItem("client", "web", "forwardedHost", webClient.getForwardedHost()); +// config.setConfigItem("client", "web", "forwardedProto", webClient.getForwardedProtocol()); + } + // create web screen driver ScreenDriver driver = gui ? new GuiWebDriver(config) : new ChuiWebDriver(config); driver.init(); === added file 'src/com/goldencode/p2j/main/WebClientsManager.java' --- src/com/goldencode/p2j/main/WebClientsManager.java 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/main/WebClientsManager.java 2017-06-21 18:39:15 +0000 @@ -0,0 +1,146 @@ +package com.goldencode.p2j.main; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import com.goldencode.p2j.net.RemoteObject; +import com.goldencode.p2j.net.Session; +import com.goldencode.p2j.net.SessionListener; + +public class WebClientsManager implements WebClientAllocator, WebClientRegistrator, SessionListener +{ + /** + * one instance per standard server + */ + private static WebClientsManager instance; + + private int port; + + private final Map webClientSessions; + + private final Map webClientConfigs; + + private final Set usedLocalPorts; + + private WebClientsManager() + { + // range of ports + // range of hosts + // port varies from 7449 up + port = 7449; + + webClientSessions = new LinkedHashMap(); + + webClientConfigs = new LinkedHashMap(); + + usedLocalPorts = new LinkedHashSet(); + + RemoteObject.registerNetworkServer(WebClientRegistrator.class, this); + } + + public static synchronized WebClientsManager getInstance() + { + if (instance == null) + { + instance = new WebClientsManager(); + } + + return instance; + } + + /** + * Allocates free open port in a range + * + * @return The allocated resources for the web client will be spawned. + */ + public synchronized WebClientConfig allocateClient(String forwardedHost, String forwardedProto) + { + StringBuilder webRootBuilder = new StringBuilder(); + webRootBuilder.append("/"); + int webClientPort = 0; + String webClientHost = "localhost"; + if (forwardedHost != null && !forwardedHost.isEmpty()) + { + int freePort = getFreeLocalPort(); + + webRootBuilder.append("server"); + //TODO to use provided port to name mappings + webRootBuilder.append("/").append(freePort); + + webClientPort = freePort; + } + WebClientConfig webClient = new WebClientConfig(); + webClient.setHost(webClientHost); + webClient.setPort(webClientPort); + webClient.setWebRoot(webRootBuilder.toString()); + webClient.setForwardedHost(forwardedHost); + webClient.setForwardedProto(forwardedProto); + + return webClient; + } + + private int getFreeLocalPort() + { + int freePort = port; + + while(usedLocalPorts.contains(freePort)) + { + freePort++; + } + + return freePort; + } + + @Override + public void registerWebClientSession(String webClientId, int peerNode) + { + System.err.println("register web client session: uuid=" + webClientId + + " peerNode=" + peerNode); + synchronized (this) + { + WebClientConfig config = webClientConfigs.get(webClientId); + if (config != null) + { + webClientSessions.put(peerNode, config); + usedLocalPorts.add(config.getPort()); + } + } + } + + public void registerWebClientUuid(String webClientId, WebClientConfig config) + { + System.err.println("register web client uuid=" + webClientId + + " port=" + config.getPort()); + synchronized (this) + { + config.setUuid(webClientId); + webClientConfigs.put(webClientId, config); + } + } + + @Override + public void terminate(Session session) + { + System.err.println("server: terminate: node=" + session.getNodeAddress() + + " remote=" + session.getRemoteAddress()); + int peerNode = session.getRemoteAddress(); + synchronized (this) + { + WebClientConfig config = webClientSessions.remove(peerNode); + if (config != null) + { + System.err.println("terminate web client port=" + config.getPort() + " uuid=" + config.getUuid()); + usedLocalPorts.remove(config.getPort()); + } + } + } + + @Override + public void initialize(Session session) + { + System.err.println("server: initialize: node=" + session.getNodeAddress() + + " remote=" + session.getRemoteAddress()); + } +} === modified file 'src/com/goldencode/p2j/main/WebHandler.java' --- src/com/goldencode/p2j/main/WebHandler.java 2017-04-01 23:33:34 +0000 +++ src/com/goldencode/p2j/main/WebHandler.java 2017-06-21 18:39:15 +0000 @@ -80,6 +80,8 @@ package com.goldencode.p2j.main; import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; import java.util.*; import java.util.logging.*; @@ -91,7 +93,8 @@ import org.eclipse.jetty.server.handler.*; import com.goldencode.p2j.directory.*; -import com.goldencode.p2j.security.*; +import com.goldencode.p2j.main.WebClientConfig; +import com.goldencode.p2j.main.WebClientAllocator; import com.goldencode.p2j.security.SecurityManager; import com.goldencode.p2j.util.*; @@ -129,6 +132,8 @@ /** Flag that denotes client type (true for GUI, false for ChUI). */ private boolean isGui = false; + private final WebClientAllocator webClientAllocator = WebClientsManager.getInstance(); + /** * Spawn a new web client process, returning the redirect URL if successful. *

@@ -301,7 +306,7 @@ LogHelper.generate("Attempting spawn for user %s.", user)); } - SpawnerResult result = spawnWorker(user, pw, gui, options, trusted, referrer); + SpawnerResult result = spawnWorker(user, pw, gui, options, trusted, referrer, null); String retval = null; @@ -465,15 +470,20 @@ // get referrer URL String referrer = base.getHeader(HttpHeader.REFERER.asString()); + String forwardedHost = base.getHeader(HttpHeader.X_FORWARDED_HOST.asString()); + String forwardedProto = "https"; //base.getHeader(HttpHeader.X_FORWARDED_PROTO.asString()); // get form parameters; String user = base.getParameter("usr"); String pw = base.getParameter("psw"); + WebClientConfig webClientConfig = webClientAllocator.allocateClient(forwardedHost, forwardedProto); + // spawn a new web client (trusted mode should not be enabled here unless the proper // security plugin checks are added (see spawn() above) - SpawnerResult result = spawnWorker(user, pw, isGui, null, false, referrer); - + + SpawnerResult result = spawnWorker(user, pw, isGui, null, false, referrer, webClientConfig); + if (result.uri != null) { String type = isGui ? "GUI" : "ChUI"; @@ -641,7 +651,8 @@ boolean gui, String[] options, boolean trusted, - String referrer) + String referrer, + WebClientConfig webClient) { if (!verifySpawnOptions(user, options)) { @@ -651,13 +662,24 @@ return sr; } - WebClientSpawner spawner = new WebClientSpawner(gui, trusted, referrer, options); + WebClientSpawner spawner = new WebClientSpawner(webClient, gui, trusted, referrer, options); spawner.spawn(user, pw, null); SpawnerResult result = new SpawnerResult(); - result.uri = spawner.getRemoteUri(); + try + { + result.uri = spawner.getRemoteUri(); + } + catch (URISyntaxException e) + { + LOG.logp(Level.SEVERE, + "WebClientSpawner", + "getRemoteUri", + LogHelper.generate("Rewrite failed: X_FORWARDED_HOST=%s X_FORWARDED_PROTO=%s", + webClient.getForwardedHost(), webClient.getForwardedProto())); + } if (result.uri == null) { === modified file 'src/com/goldencode/p2j/ui/client/driver/web/WebPageHandler.java' --- src/com/goldencode/p2j/ui/client/driver/web/WebPageHandler.java 2017-04-01 23:33:34 +0000 +++ src/com/goldencode/p2j/ui/client/driver/web/WebPageHandler.java 2017-06-20 06:10:28 +0000 @@ -190,9 +190,10 @@ */ protected String handleReplacements(Request base, String text) { - if (text.contains("${context}")) + String webRoot = config.getString("client", "web", "webRoot", ""); + if (text.contains("${webRoot}")) { - text = text.replace("${context}", base.getContextPath()); + text = text.replace("${webRoot}", webRoot); } if (text.contains("${referrer}")) === modified file 'src/com/goldencode/p2j/ui/client/driver/web/index.html' --- src/com/goldencode/p2j/ui/client/driver/web/index.html 2017-04-01 23:33:34 +0000 +++ src/com/goldencode/p2j/ui/client/driver/web/index.html 2017-06-20 06:10:28 +0000 @@ -55,7 +55,7 @@ FWD ${client_title} Web Client - + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + +