mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Allow to delegate requests to dev server to another server
This commit is contained in:
parent
c2697dce88
commit
97a1db1b79
10
pom.xml
10
pom.xml
|
@ -177,6 +177,16 @@
|
||||||
<artifactId>javax-websocket-server-impl</artifactId>
|
<artifactId>javax-websocket-server-impl</artifactId>
|
||||||
<version>${jetty.version}</version>
|
<version>${jetty.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-client</artifactId>
|
||||||
|
<version>${jetty.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||||
|
<artifactId>websocket-client</artifactId>
|
||||||
|
<version>${jetty.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.teavm.tooling.ConsoleTeaVMToolLog;
|
||||||
|
|
||||||
public final class TeaVMDevServerRunner {
|
public final class TeaVMDevServerRunner {
|
||||||
private static Options options = new Options();
|
private static Options options = new Options();
|
||||||
private ConsoleTeaVMToolLog log = new ConsoleTeaVMToolLog(false);
|
|
||||||
private DevServer devServer;
|
private DevServer devServer;
|
||||||
private CommandLine commandLine;
|
private CommandLine commandLine;
|
||||||
|
|
||||||
|
@ -71,7 +70,7 @@ public final class TeaVMDevServerRunner {
|
||||||
options.addOption(OptionBuilder
|
options.addOption(OptionBuilder
|
||||||
.withDescription("display indicator on web page")
|
.withDescription("display indicator on web page")
|
||||||
.withLongOpt("indicator")
|
.withLongOpt("indicator")
|
||||||
.create());
|
.create('i'));
|
||||||
options.addOption(OptionBuilder
|
options.addOption(OptionBuilder
|
||||||
.withDescription("automatically reload page when compilation completes")
|
.withDescription("automatically reload page when compilation completes")
|
||||||
.withLongOpt("auto-reload")
|
.withLongOpt("auto-reload")
|
||||||
|
@ -80,6 +79,18 @@ public final class TeaVMDevServerRunner {
|
||||||
.withDescription("display more messages on server log")
|
.withDescription("display more messages on server log")
|
||||||
.withLongOpt("verbose")
|
.withLongOpt("verbose")
|
||||||
.create('v'));
|
.create('v'));
|
||||||
|
options.addOption(OptionBuilder
|
||||||
|
.withArgName("URL")
|
||||||
|
.hasArg()
|
||||||
|
.withDescription("delegate requests to URL")
|
||||||
|
.withLongOpt("proxy-url")
|
||||||
|
.create());
|
||||||
|
options.addOption(OptionBuilder
|
||||||
|
.withArgName("path")
|
||||||
|
.hasArg()
|
||||||
|
.withDescription("delegate requests from path")
|
||||||
|
.withLongOpt("proxy-path")
|
||||||
|
.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
private TeaVMDevServerRunner(CommandLine commandLine) {
|
private TeaVMDevServerRunner(CommandLine commandLine) {
|
||||||
|
@ -103,7 +114,6 @@ public final class TeaVMDevServerRunner {
|
||||||
|
|
||||||
TeaVMDevServerRunner runner = new TeaVMDevServerRunner(commandLine);
|
TeaVMDevServerRunner runner = new TeaVMDevServerRunner(commandLine);
|
||||||
runner.parseArguments();
|
runner.parseArguments();
|
||||||
runner.setUp();
|
|
||||||
runner.runAll();
|
runner.runAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +134,13 @@ public final class TeaVMDevServerRunner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (commandLine.hasOption("proxy-url")) {
|
||||||
|
devServer.setProxyUrl(commandLine.getOptionValue("proxy-url"));
|
||||||
|
}
|
||||||
|
if (commandLine.hasOption("proxy-path")) {
|
||||||
|
devServer.setProxyPath(commandLine.getOptionValue("proxy-path"));
|
||||||
|
}
|
||||||
|
|
||||||
String[] args = commandLine.getArgs();
|
String[] args = commandLine.getArgs();
|
||||||
if (args.length != 1) {
|
if (args.length != 1) {
|
||||||
System.err.println("Unexpected arguments");
|
System.err.println("Unexpected arguments");
|
||||||
|
@ -154,10 +171,6 @@ public final class TeaVMDevServerRunner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUp() {
|
|
||||||
devServer.setLog(log);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runAll() {
|
private void runAll() {
|
||||||
devServer.start();
|
devServer.start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,10 @@
|
||||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||||
<artifactId>javax-websocket-server-impl</artifactId>
|
<artifactId>javax-websocket-server-impl</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||||
|
<artifactId>websocket-client</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.servlet</groupId>
|
<groupId>javax.servlet</groupId>
|
||||||
<artifactId>javax.servlet-api</artifactId>
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
@ -86,6 +90,12 @@
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>4.5.6</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -25,13 +25,19 @@ import java.io.OutputStreamWriter;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -40,11 +46,26 @@ import java.util.function.Supplier;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
import javax.servlet.AsyncContext;
|
||||||
|
import javax.servlet.ServletConfig;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
import org.eclipse.jetty.client.util.InputStreamContentProvider;
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
|
import org.eclipse.jetty.websocket.api.UpgradeRequest;
|
||||||
|
import org.eclipse.jetty.websocket.api.UpgradeResponse;
|
||||||
|
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
|
||||||
|
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||||
|
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||||
|
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||||
|
import org.eclipse.jetty.websocket.client.io.UpgradeListener;
|
||||||
|
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||||
import org.teavm.backend.javascript.JavaScriptTarget;
|
import org.teavm.backend.javascript.JavaScriptTarget;
|
||||||
import org.teavm.cache.InMemoryMethodNodeCache;
|
import org.teavm.cache.InMemoryMethodNodeCache;
|
||||||
import org.teavm.cache.InMemoryProgramCache;
|
import org.teavm.cache.InMemoryProgramCache;
|
||||||
|
@ -69,17 +90,25 @@ import org.teavm.vm.TeaVMProgressListener;
|
||||||
|
|
||||||
public class CodeServlet extends HttpServlet {
|
public class CodeServlet extends HttpServlet {
|
||||||
private static final Supplier<InputStream> EMPTY_CONTENT = () -> null;
|
private static final Supplier<InputStream> EMPTY_CONTENT = () -> null;
|
||||||
|
private WebSocketServletFactory wsFactory;
|
||||||
|
|
||||||
private String mainClass;
|
private String mainClass;
|
||||||
private String[] classPath;
|
private String[] classPath;
|
||||||
private String fileName = "classes.js";
|
private String fileName = "classes.js";
|
||||||
private String pathToFile = "/";
|
private String pathToFile = "/";
|
||||||
|
private String indicatorWsPath;
|
||||||
private List<String> sourcePath = new ArrayList<>();
|
private List<String> sourcePath = new ArrayList<>();
|
||||||
private TeaVMToolLog log = new EmptyTeaVMToolLog();
|
private TeaVMToolLog log = new EmptyTeaVMToolLog();
|
||||||
private boolean indicator;
|
private boolean indicator;
|
||||||
private boolean automaticallyReloaded;
|
private boolean automaticallyReloaded;
|
||||||
private int port;
|
private int port;
|
||||||
private int debugPort;
|
private int debugPort;
|
||||||
|
private String proxyUrl;
|
||||||
|
private String proxyPath = "/";
|
||||||
|
private String proxyHost;
|
||||||
|
private String proxyProtocol;
|
||||||
|
private int proxyPort;
|
||||||
|
private String proxyBaseUrl;
|
||||||
|
|
||||||
private Map<String, Supplier<InputStream>> sourceFileCache = new HashMap<>();
|
private Map<String, Supplier<InputStream>> sourceFileCache = new HashMap<>();
|
||||||
|
|
||||||
|
@ -95,7 +124,7 @@ public class CodeServlet extends HttpServlet {
|
||||||
private final Map<String, byte[]> content = new HashMap<>();
|
private final Map<String, byte[]> content = new HashMap<>();
|
||||||
private MemoryBuildTarget buildTarget = new MemoryBuildTarget();
|
private MemoryBuildTarget buildTarget = new MemoryBuildTarget();
|
||||||
|
|
||||||
private final Set<CodeWsEndpoint> wsEndpoints = new LinkedHashSet<>();
|
private final Set<ProgressHandler> progressHandlers = new LinkedHashSet<>();
|
||||||
private final Object statusLock = new Object();
|
private final Object statusLock = new Object();
|
||||||
private volatile boolean cancelRequested;
|
private volatile boolean cancelRequested;
|
||||||
private boolean compiling;
|
private boolean compiling;
|
||||||
|
@ -103,10 +132,15 @@ public class CodeServlet extends HttpServlet {
|
||||||
private boolean waiting;
|
private boolean waiting;
|
||||||
private Thread buildThread;
|
private Thread buildThread;
|
||||||
private List<DevServerListener> listeners = new ArrayList<>();
|
private List<DevServerListener> listeners = new ArrayList<>();
|
||||||
|
private HttpClient httpClient;
|
||||||
|
private WebSocketClient wsClient = new WebSocketClient();
|
||||||
|
|
||||||
public CodeServlet(String mainClass, String[] classPath) {
|
public CodeServlet(String mainClass, String[] classPath) {
|
||||||
this.mainClass = mainClass;
|
this.mainClass = mainClass;
|
||||||
this.classPath = classPath.clone();
|
this.classPath = classPath.clone();
|
||||||
|
|
||||||
|
httpClient = new HttpClient();
|
||||||
|
httpClient.setFollowRedirects(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFileName(String fileName) {
|
public void setFileName(String fileName) {
|
||||||
|
@ -114,13 +148,7 @@ public class CodeServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPathToFile(String pathToFile) {
|
public void setPathToFile(String pathToFile) {
|
||||||
if (!pathToFile.endsWith("/")) {
|
this.pathToFile = normalizePath(pathToFile);
|
||||||
pathToFile += "/";
|
|
||||||
}
|
|
||||||
if (!pathToFile.startsWith("/")) {
|
|
||||||
pathToFile = "/" + pathToFile;
|
|
||||||
}
|
|
||||||
this.pathToFile = pathToFile;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getSourcePath() {
|
public List<String> getSourcePath() {
|
||||||
|
@ -147,9 +175,17 @@ public class CodeServlet extends HttpServlet {
|
||||||
this.automaticallyReloaded = automaticallyReloaded;
|
this.automaticallyReloaded = automaticallyReloaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addWsEndpoint(CodeWsEndpoint endpoint) {
|
public void setProxyUrl(String proxyUrl) {
|
||||||
synchronized (wsEndpoints) {
|
this.proxyUrl = proxyUrl;
|
||||||
wsEndpoints.add(endpoint);
|
}
|
||||||
|
|
||||||
|
public void setProxyPath(String proxyPath) {
|
||||||
|
this.proxyPath = normalizePath(proxyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addProgressHandler(ProgressHandler handler) {
|
||||||
|
synchronized (progressHandlers) {
|
||||||
|
progressHandlers.add(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
double progress;
|
double progress;
|
||||||
|
@ -160,12 +196,12 @@ public class CodeServlet extends HttpServlet {
|
||||||
progress = this.progress;
|
progress = this.progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoint.progress(progress);
|
handler.progress(progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeWsEndpoint(CodeWsEndpoint endpoint) {
|
public void removeProgressHandler(ProgressHandler handler) {
|
||||||
synchronized (wsEndpoints) {
|
synchronized (progressHandlers) {
|
||||||
wsEndpoints.remove(endpoint);
|
progressHandlers.remove(handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,20 +237,73 @@ public class CodeServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
public void init(ServletConfig config) throws ServletException {
|
||||||
|
super.init(config);
|
||||||
|
|
||||||
|
if (proxyUrl != null) {
|
||||||
|
try {
|
||||||
|
httpClient.start();
|
||||||
|
wsClient.start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL url = new URL(proxyUrl);
|
||||||
|
proxyPort = url.getPort();
|
||||||
|
proxyHost = proxyPort != 80 ? url.getHost() + ":" + proxyPort : url.getHost();
|
||||||
|
proxyProtocol = url.getProtocol();
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(proxyProtocol).append("://").append(proxyHost);
|
||||||
|
proxyBaseUrl = sb.toString();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
log.warning("Could not extract host from URL: " + proxyUrl, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
indicatorWsPath = pathToFile + fileName + ".ws";
|
||||||
|
WebSocketPolicy wsPolicy = new WebSocketPolicy(WebSocketBehavior.SERVER);
|
||||||
|
wsFactory = WebSocketServletFactory.Loader.load(config.getServletContext(), wsPolicy);
|
||||||
|
wsFactory.setCreator((req, resp) -> {
|
||||||
|
ProxyWsClient proxyClient = (ProxyWsClient) req.getHttpServletRequest().getAttribute("teavm.ws.client");
|
||||||
|
if (proxyClient == null) {
|
||||||
|
return new CodeWsEndpoint(this);
|
||||||
|
} else {
|
||||||
|
ProxyWsClient proxy = new ProxyWsClient();
|
||||||
|
proxy.setTarget(proxyClient);
|
||||||
|
proxyClient.setTarget(proxy);
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
wsFactory.start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServletException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||||
String path = req.getPathInfo();
|
String path = req.getPathInfo();
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
log.debug("Serving " + path);
|
log.debug("Serving " + path);
|
||||||
if (!path.startsWith("/")) {
|
if (!path.startsWith("/")) {
|
||||||
path = "/" + path;
|
path = "/" + path;
|
||||||
}
|
}
|
||||||
if (path.startsWith(pathToFile) && path.length() > pathToFile.length()) {
|
if (req.getMethod().equals("GET") && path.startsWith(pathToFile) && path.length() > pathToFile.length()) {
|
||||||
String fileName = path.substring(pathToFile.length());
|
String fileName = path.substring(pathToFile.length());
|
||||||
if (fileName.startsWith("src/")) {
|
if (fileName.startsWith("src/")) {
|
||||||
if (serveSourceFile(fileName.substring("src/".length()), resp)) {
|
if (serveSourceFile(fileName.substring("src/".length()), resp)) {
|
||||||
log.debug("File " + path + " served as source file");
|
log.debug("File " + path + " served as source file");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (path.equals(indicatorWsPath)) {
|
||||||
|
if (wsFactory.isUpgradeRequest(req, resp)) {
|
||||||
|
if (wsFactory.acceptWebSocket(req, resp) || resp.isCommitted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
byte[] fileContent;
|
byte[] fileContent;
|
||||||
boolean firstTime;
|
boolean firstTime;
|
||||||
|
@ -236,15 +325,216 @@ public class CodeServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (proxyUrl != null && path.startsWith(proxyPath)) {
|
||||||
|
if (wsFactory.isUpgradeRequest(req, resp)) {
|
||||||
|
proxyWebSocket(req, resp, path);
|
||||||
|
} else {
|
||||||
|
proxy(req, resp, path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("File " + path + " not found");
|
log.debug("File " + path + " not found");
|
||||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void proxy(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
|
||||||
|
AsyncContext async = req.startAsync();
|
||||||
|
|
||||||
|
String relPath = path.substring(proxyPath.length());
|
||||||
|
StringBuilder sb = new StringBuilder(proxyUrl);
|
||||||
|
if (!relPath.isEmpty() && !proxyUrl.endsWith("/")) {
|
||||||
|
sb.append("/");
|
||||||
|
}
|
||||||
|
sb.append(relPath);
|
||||||
|
|
||||||
|
if (req.getQueryString() != null) {
|
||||||
|
sb.append("?").append(req.getQueryString());
|
||||||
|
}
|
||||||
|
log.debug("Trying to serve '" + relPath + "' from '" + sb + "'");
|
||||||
|
|
||||||
|
Request proxyReq = httpClient.newRequest(sb.toString());
|
||||||
|
proxyReq.method(req.getMethod());
|
||||||
|
copyRequestHeaders(req, proxyReq::header);
|
||||||
|
|
||||||
|
proxyReq.content(new InputStreamContentProvider(req.getInputStream()));
|
||||||
|
HeaderSender headerSender = new HeaderSender(resp);
|
||||||
|
|
||||||
|
proxyReq.onResponseContent((response, responseContent) -> {
|
||||||
|
headerSender.send(response);
|
||||||
|
try {
|
||||||
|
WritableByteChannel channel = Channels.newChannel(resp.getOutputStream());
|
||||||
|
while (responseContent.remaining() > 0) {
|
||||||
|
channel.write(responseContent);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
proxyReq.send(result -> {
|
||||||
|
headerSender.send(result.getResponse());
|
||||||
|
async.complete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class HeaderSender {
|
||||||
|
final HttpServletResponse resp;
|
||||||
|
boolean sent;
|
||||||
|
|
||||||
|
HeaderSender(HttpServletResponse resp) {
|
||||||
|
this.resp = resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(Response response) {
|
||||||
|
if (sent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sent = true;
|
||||||
|
resp.setStatus(response.getStatus());
|
||||||
|
|
||||||
|
for (HttpField field : response.getHeaders()) {
|
||||||
|
if (field.getName().toLowerCase().equals("location")) {
|
||||||
|
String value = field.getValue();
|
||||||
|
if (value.startsWith(proxyUrl)) {
|
||||||
|
String relLocation = value.substring(proxyUrl.length());
|
||||||
|
resp.addHeader(field.getName(), "http://localhost:" + port + proxyPath + relLocation);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp.addHeader(field.getName(), field.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void proxyWebSocket(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
|
||||||
|
AsyncContext async = req.startAsync();
|
||||||
|
|
||||||
|
String relPath = path.substring(proxyPath.length());
|
||||||
|
StringBuilder sb = new StringBuilder(proxyProtocol.equals("http") ? "ws" : "wss").append("://");
|
||||||
|
sb.append(proxyHost);
|
||||||
|
if (!relPath.isEmpty()) {
|
||||||
|
sb.append("/");
|
||||||
|
}
|
||||||
|
sb.append(relPath);
|
||||||
|
if (req.getQueryString() != null) {
|
||||||
|
sb.append("?").append(req.getQueryString());
|
||||||
|
}
|
||||||
|
URI uri;
|
||||||
|
try {
|
||||||
|
uri = new URI(sb.toString());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProxyWsClient client = new ProxyWsClient();
|
||||||
|
req.setAttribute("teavm.ws.client", client);
|
||||||
|
ClientUpgradeRequest proxyReq = new ClientUpgradeRequest();
|
||||||
|
proxyReq.setMethod(req.getMethod());
|
||||||
|
Map<String, List<String>> headers = new LinkedHashMap<>();
|
||||||
|
copyRequestHeaders(req, (key, value) -> headers.computeIfAbsent(key, k -> new ArrayList<>()).add(value));
|
||||||
|
proxyReq.setHeaders(headers);
|
||||||
|
|
||||||
|
wsClient.connect(client, uri, proxyReq, new UpgradeListener() {
|
||||||
|
@Override
|
||||||
|
public void onHandshakeRequest(UpgradeRequest request) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHandshakeResponse(UpgradeResponse response) {
|
||||||
|
resp.setStatus(response.getStatusCode());
|
||||||
|
for (String header : response.getHeaderNames()) {
|
||||||
|
switch (header.toLowerCase()) {
|
||||||
|
case "connection":
|
||||||
|
case "date":
|
||||||
|
case "sec-websocket-accept":
|
||||||
|
case "upgrade":
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (String value : response.getHeaders(header)) {
|
||||||
|
resp.addHeader(header, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
wsFactory.acceptWebSocket(req, resp);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
async.complete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyRequestHeaders(HttpServletRequest req, HeaderConsumer proxyReq) {
|
||||||
|
Enumeration<String> headers = req.getHeaderNames();
|
||||||
|
while (headers.hasMoreElements()) {
|
||||||
|
String header = headers.nextElement();
|
||||||
|
String headerLower = header.toLowerCase();
|
||||||
|
switch (headerLower) {
|
||||||
|
case "host":
|
||||||
|
if (proxyHost != null) {
|
||||||
|
proxyReq.header(header, proxyHost);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "origin":
|
||||||
|
if (proxyBaseUrl != null) {
|
||||||
|
String origin = req.getHeader(header);
|
||||||
|
if (origin.equals("http://localhost:" + port)) {
|
||||||
|
proxyReq.header(header, proxyBaseUrl);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "referer": {
|
||||||
|
String referer = req.getHeader(header);
|
||||||
|
String localUrl = "http://localhost:" + port + "/";
|
||||||
|
if (referer.startsWith(localUrl)) {
|
||||||
|
String relReferer = referer.substring(localUrl.length());
|
||||||
|
proxyReq.header(header, proxyUrl + relReferer);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "connection":
|
||||||
|
case "upgrade":
|
||||||
|
case "user-agent":
|
||||||
|
case "sec-websocket-key":
|
||||||
|
case "sec-websocket-version":
|
||||||
|
case "sec-websocket-extensions":
|
||||||
|
case "accept-encoding":
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Enumeration<String> values = req.getHeaders(header);
|
||||||
|
while (values.hasMoreElements()) {
|
||||||
|
proxyReq.header(header, values.nextElement());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
super.destroy();
|
super.destroy();
|
||||||
|
try {
|
||||||
|
wsFactory.stop();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warning("Error stopping WebSocket server", e);
|
||||||
|
}
|
||||||
|
if (proxyUrl != null) {
|
||||||
|
try {
|
||||||
|
httpClient.stop();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warning("Error stopping HTTP client", e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
wsClient.stop();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warning("Error stopping WebSocket client", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
stopped = true;
|
stopped = true;
|
||||||
synchronized (statusLock) {
|
synchronized (statusLock) {
|
||||||
if (waiting) {
|
if (waiting) {
|
||||||
|
@ -602,13 +892,13 @@ public class CodeServlet extends HttpServlet {
|
||||||
this.progress = progress;
|
this.progress = progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeWsEndpoint[] endpoints;
|
ProgressHandler[] handlers;
|
||||||
synchronized (wsEndpoints) {
|
synchronized (progressHandlers) {
|
||||||
endpoints = wsEndpoints.toArray(new CodeWsEndpoint[0]);
|
handlers = progressHandlers.toArray(new ProgressHandler[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (CodeWsEndpoint endpoint : endpoints) {
|
for (ProgressHandler handler : handlers) {
|
||||||
endpoint.progress(progress);
|
handler.progress(progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (DevServerListener listener : listeners) {
|
for (DevServerListener listener : listeners) {
|
||||||
|
@ -624,13 +914,13 @@ public class CodeServlet extends HttpServlet {
|
||||||
compiling = false;
|
compiling = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeWsEndpoint[] endpoints;
|
ProgressHandler[] handlers;
|
||||||
synchronized (wsEndpoints) {
|
synchronized (progressHandlers) {
|
||||||
endpoints = wsEndpoints.toArray(new CodeWsEndpoint[0]);
|
handlers = progressHandlers.toArray(new ProgressHandler[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (CodeWsEndpoint endpoint : endpoints) {
|
for (ProgressHandler handler : handlers) {
|
||||||
endpoint.complete(success);
|
handler.complete(success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,4 +1015,14 @@ public class CodeServlet extends HttpServlet {
|
||||||
return TeaVMProgressFeedback.CONTINUE;
|
return TeaVMProgressFeedback.CONTINUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String normalizePath(String path) {
|
||||||
|
if (!path.endsWith("/")) {
|
||||||
|
path += "/";
|
||||||
|
}
|
||||||
|
if (!path.startsWith("/")) {
|
||||||
|
path = "/" + path;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,43 +15,50 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.devserver;
|
package org.teavm.devserver;
|
||||||
|
|
||||||
import javax.websocket.OnClose;
|
import java.util.HashMap;
|
||||||
import javax.websocket.OnOpen;
|
import java.util.Map;
|
||||||
import javax.websocket.Session;
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
import javax.websocket.server.ServerEndpoint;
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||||
|
|
||||||
@ServerEndpoint("/")
|
@WebSocket
|
||||||
public class CodeWsEndpoint {
|
public class CodeWsEndpoint {
|
||||||
private Session session;
|
private Map<Session, ProgressHandlerImpl> progressHandlerMap = new HashMap<>();
|
||||||
private CodeServlet servlet;
|
private CodeServlet servlet;
|
||||||
|
|
||||||
@OnOpen
|
public CodeWsEndpoint(CodeServlet servlet) {
|
||||||
|
this.servlet = servlet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnWebSocketConnect
|
||||||
public void open(Session session) {
|
public void open(Session session) {
|
||||||
|
ProgressHandlerImpl progressHandler = new ProgressHandlerImpl(session);
|
||||||
|
progressHandlerMap.put(session, progressHandler);
|
||||||
|
servlet.addProgressHandler(progressHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnWebSocketClose
|
||||||
|
public void close(Session session, int code, String reason) {
|
||||||
|
ProgressHandlerImpl handler = progressHandlerMap.remove(session);
|
||||||
|
servlet.removeProgressHandler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ProgressHandlerImpl implements ProgressHandler {
|
||||||
|
Session session;
|
||||||
|
|
||||||
|
ProgressHandlerImpl(Session session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
servlet = (CodeServlet) session.getUserProperties().get("teavm.servlet");
|
|
||||||
if (servlet != null) {
|
|
||||||
servlet.addWsEndpoint(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClose
|
|
||||||
public void close() {
|
|
||||||
if (servlet != null) {
|
|
||||||
servlet.removeWsEndpoint(this);
|
|
||||||
}
|
|
||||||
servlet = null;
|
|
||||||
session = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void progress(double value) {
|
public void progress(double value) {
|
||||||
if (session != null) {
|
session.getRemote().sendStringByFuture("{ \"command\": \"compiling\", \"progress\": " + value + " }");
|
||||||
session.getAsyncRemote().sendText("{ \"command\": \"compiling\", \"progress\": " + value + " }");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void complete(boolean success) {
|
public void complete(boolean success) {
|
||||||
if (session != null) {
|
session.getRemote().sendStringByFuture("{ \"command\": \"complete\", \"success\": " + success + " }");
|
||||||
session.getAsyncRemote().sendText("{ \"command\": \"complete\", \"success\": " + success + " }");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,20 +16,11 @@
|
||||||
package org.teavm.devserver;
|
package org.teavm.devserver;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import javax.websocket.Decoder;
|
|
||||||
import javax.websocket.Encoder;
|
|
||||||
import javax.websocket.Extension;
|
|
||||||
import javax.websocket.server.ServerContainer;
|
|
||||||
import javax.websocket.server.ServerEndpointConfig;
|
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
|
|
||||||
import org.teavm.tooling.TeaVMToolLog;
|
import org.teavm.tooling.TeaVMToolLog;
|
||||||
|
|
||||||
public class DevServer {
|
public class DevServer {
|
||||||
|
@ -47,6 +38,8 @@ public class DevServer {
|
||||||
private Server server;
|
private Server server;
|
||||||
private int port = 9090;
|
private int port = 9090;
|
||||||
private int debugPort;
|
private int debugPort;
|
||||||
|
private String proxyUrl;
|
||||||
|
private String proxyPath = "/";
|
||||||
|
|
||||||
public void setMainClass(String mainClass) {
|
public void setMainClass(String mainClass) {
|
||||||
this.mainClass = mainClass;
|
this.mainClass = mainClass;
|
||||||
|
@ -90,6 +83,14 @@ public class DevServer {
|
||||||
this.reloadedAutomatically = reloadedAutomatically;
|
this.reloadedAutomatically = reloadedAutomatically;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setProxyUrl(String proxyUrl) {
|
||||||
|
this.proxyUrl = proxyUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProxyPath(String proxyPath) {
|
||||||
|
this.proxyPath = proxyPath;
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getSourcePath() {
|
public List<String> getSourcePath() {
|
||||||
return sourcePath;
|
return sourcePath;
|
||||||
}
|
}
|
||||||
|
@ -128,14 +129,16 @@ public class DevServer {
|
||||||
servlet.setAutomaticallyReloaded(reloadedAutomatically);
|
servlet.setAutomaticallyReloaded(reloadedAutomatically);
|
||||||
servlet.setPort(port);
|
servlet.setPort(port);
|
||||||
servlet.setDebugPort(debugPort);
|
servlet.setDebugPort(debugPort);
|
||||||
|
servlet.setProxyUrl(proxyUrl);
|
||||||
|
servlet.setProxyPath(proxyPath);
|
||||||
for (DevServerListener listener : listeners) {
|
for (DevServerListener listener : listeners) {
|
||||||
servlet.addListener(listener);
|
servlet.addListener(listener);
|
||||||
}
|
}
|
||||||
context.addServlet(new ServletHolder(servlet), "/*");
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
servletHolder.setAsyncSupported(true);
|
||||||
|
context.addServlet(servletHolder, "/*");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
|
|
||||||
wscontainer.addEndpoint(new DevServerEndpointConfig(servlet));
|
|
||||||
server.start();
|
server.start();
|
||||||
server.join();
|
server.join();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -152,52 +155,4 @@ public class DevServer {
|
||||||
server = null;
|
server = null;
|
||||||
servlet = null;
|
servlet = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DevServerEndpointConfig implements ServerEndpointConfig {
|
|
||||||
private Map<String, Object> userProperties = new HashMap<>();
|
|
||||||
|
|
||||||
public DevServerEndpointConfig(CodeServlet servlet) {
|
|
||||||
userProperties.put("teavm.servlet", servlet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Class<? extends Decoder>> getDecoders() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Class<? extends Encoder>> getEncoders() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> getUserProperties() {
|
|
||||||
return userProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Configurator getConfigurator() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getEndpointClass() {
|
|
||||||
return CodeWsEndpoint.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Extension> getExtensions() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPath() {
|
|
||||||
return pathToFile + fileName + ".ws";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getSubprotocols() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.devserver;
|
||||||
|
|
||||||
|
interface HeaderConsumer {
|
||||||
|
void header(String key, String value);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.devserver;
|
||||||
|
|
||||||
|
public interface ProgressHandler {
|
||||||
|
void complete(boolean success);
|
||||||
|
|
||||||
|
void progress(double value);
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.devserver;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||||
|
|
||||||
|
@WebSocket
|
||||||
|
public class ProxyWsClient {
|
||||||
|
private Session session;
|
||||||
|
private ProxyWsClient target;
|
||||||
|
private boolean closed;
|
||||||
|
private List<Consumer<ProxyWsClient>> pendingMessages = new ArrayList<>();
|
||||||
|
|
||||||
|
public void setTarget(ProxyWsClient target) {
|
||||||
|
if (this.target != null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
this.target = target;
|
||||||
|
flush();
|
||||||
|
target.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnWebSocketConnect
|
||||||
|
public void connect(Session session) {
|
||||||
|
this.session = session;
|
||||||
|
flush();
|
||||||
|
if (target != null) {
|
||||||
|
target.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnWebSocketClose
|
||||||
|
public void close(int code, String reason) {
|
||||||
|
closed = true;
|
||||||
|
if (!target.closed) {
|
||||||
|
target.closed = true;
|
||||||
|
session.close(code, reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnWebSocketMessage
|
||||||
|
public void onMessage(byte[] buf, int offset, int length) {
|
||||||
|
send(t -> t.session.getRemote().sendBytesByFuture(ByteBuffer.wrap(buf, offset, length)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnWebSocketMessage
|
||||||
|
public void onMessage(String text) {
|
||||||
|
send(t -> t.session.getRemote().sendStringByFuture(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void send(Consumer<ProxyWsClient> message) {
|
||||||
|
if (target == null || target.session == null || !target.session.isOpen()) {
|
||||||
|
if (pendingMessages != null) {
|
||||||
|
pendingMessages.add(message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.accept(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flush() {
|
||||||
|
if (pendingMessages == null || target == null || target.session == null || !target.session.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Consumer<ProxyWsClient> message : pendingMessages) {
|
||||||
|
message.accept(target);
|
||||||
|
}
|
||||||
|
pendingMessages = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,4 +27,6 @@ public class DevServerConfiguration {
|
||||||
public String pathToFile;
|
public String pathToFile;
|
||||||
public String fileName;
|
public String fileName;
|
||||||
public int debugPort;
|
public int debugPort;
|
||||||
|
public String proxyUrl;
|
||||||
|
public String proxyPath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ public class DevServerRunner extends UnicastRemoteObject implements DevServerMan
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
DevServer server = new DevServer();
|
DevServer server = new DevServer();
|
||||||
server.setLog(new ConsoleTeaVMToolLog(true));
|
server.setLog(new ConsoleTeaVMToolLog(false));
|
||||||
server.setMainClass(args[0]);
|
server.setMainClass(args[0]);
|
||||||
List<String> classPath = new ArrayList<>();
|
List<String> classPath = new ArrayList<>();
|
||||||
for (int i = 1; i < args.length; ++i) {
|
for (int i = 1; i < args.length; ++i) {
|
||||||
|
@ -137,6 +137,12 @@ public class DevServerRunner extends UnicastRemoteObject implements DevServerMan
|
||||||
case "-P":
|
case "-P":
|
||||||
server.setDebugPort(Integer.parseInt(args[++i]));
|
server.setDebugPort(Integer.parseInt(args[++i]));
|
||||||
break;
|
break;
|
||||||
|
case "-proxy-url":
|
||||||
|
server.setProxyUrl(args[++i]);
|
||||||
|
break;
|
||||||
|
case "-proxy-path":
|
||||||
|
server.setProxyPath(args[++i]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
server.setClassPath(classPath.toArray(new String[0]));
|
server.setClassPath(classPath.toArray(new String[0]));
|
||||||
|
@ -196,6 +202,15 @@ public class DevServerRunner extends UnicastRemoteObject implements DevServerMan
|
||||||
arguments.add(Integer.toString(options.debugPort));
|
arguments.add(Integer.toString(options.debugPort));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.proxyUrl != null && !options.proxyUrl.isEmpty()) {
|
||||||
|
arguments.add("-proxy-url");
|
||||||
|
arguments.add(options.proxyUrl);
|
||||||
|
}
|
||||||
|
if (options.proxyPath != null) {
|
||||||
|
arguments.add("-proxy-path");
|
||||||
|
arguments.add(options.proxyPath);
|
||||||
|
}
|
||||||
|
|
||||||
ProcessBuilder builder = new ProcessBuilder(arguments.toArray(new String[0]));
|
ProcessBuilder builder = new ProcessBuilder(arguments.toArray(new String[0]));
|
||||||
Process process = builder.start();
|
Process process = builder.start();
|
||||||
BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream(),
|
BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream(),
|
||||||
|
|
|
@ -46,6 +46,8 @@ public class TeaVMDevServerConfiguration extends ModuleBasedConfiguration<RunCon
|
||||||
private boolean indicator = true;
|
private boolean indicator = true;
|
||||||
private boolean automaticallyReloaded;
|
private boolean automaticallyReloaded;
|
||||||
private int maxHeap = 1024;
|
private int maxHeap = 1024;
|
||||||
|
private String proxyUrl = "";
|
||||||
|
private String proxyPath = "";
|
||||||
|
|
||||||
public TeaVMDevServerConfiguration(
|
public TeaVMDevServerConfiguration(
|
||||||
@NotNull RunConfigurationModule configurationModule,
|
@NotNull RunConfigurationModule configurationModule,
|
||||||
|
@ -172,4 +174,24 @@ public class TeaVMDevServerConfiguration extends ModuleBasedConfiguration<RunCon
|
||||||
public void setMaxHeap(int maxHeap) {
|
public void setMaxHeap(int maxHeap) {
|
||||||
this.maxHeap = maxHeap;
|
this.maxHeap = maxHeap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Property
|
||||||
|
@Tag
|
||||||
|
public String getProxyUrl() {
|
||||||
|
return proxyUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProxyUrl(String proxyUrl) {
|
||||||
|
this.proxyUrl = proxyUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Property
|
||||||
|
@Tag
|
||||||
|
public String getProxyPath() {
|
||||||
|
return proxyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProxyPath(String proxyPath) {
|
||||||
|
this.proxyPath = proxyPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,8 @@ public class TeaVMDevServerRunState implements RunProfileState {
|
||||||
config.autoReload = configuration.isAutomaticallyReloaded();
|
config.autoReload = configuration.isAutomaticallyReloaded();
|
||||||
config.mainClass = configuration.getMainClass();
|
config.mainClass = configuration.getMainClass();
|
||||||
config.maxHeap = configuration.getMaxHeap();
|
config.maxHeap = configuration.getMaxHeap();
|
||||||
|
config.proxyUrl = configuration.getProxyUrl();
|
||||||
|
config.proxyPath = configuration.getProxyPath();
|
||||||
|
|
||||||
if (executor.getId().equals(DefaultDebugExecutor.EXECUTOR_ID)) {
|
if (executor.getId().equals(DefaultDebugExecutor.EXECUTOR_ID)) {
|
||||||
config.debugPort = choosePort();
|
config.debugPort = choosePort();
|
||||||
|
|
|
@ -50,6 +50,8 @@ public class TeaVMDevServerSettingsPanel extends JPanel {
|
||||||
private JCheckBox indicatorField;
|
private JCheckBox indicatorField;
|
||||||
private JCheckBox autoReloadField;
|
private JCheckBox autoReloadField;
|
||||||
private JFormattedTextField maxHeapField;
|
private JFormattedTextField maxHeapField;
|
||||||
|
private JTextField proxyUrlField;
|
||||||
|
private JTextField proxyPathField;
|
||||||
|
|
||||||
public TeaVMDevServerSettingsPanel(Project project) {
|
public TeaVMDevServerSettingsPanel(Project project) {
|
||||||
moduleField = new ModuleDescriptionsComboBox();
|
moduleField = new ModuleDescriptionsComboBox();
|
||||||
|
@ -77,6 +79,9 @@ public class TeaVMDevServerSettingsPanel extends JPanel {
|
||||||
autoReloadField = new JCheckBox("Reload page automatically:");
|
autoReloadField = new JCheckBox("Reload page automatically:");
|
||||||
maxHeapField = new JFormattedTextField(new DecimalFormat("#0"));
|
maxHeapField = new JFormattedTextField(new DecimalFormat("#0"));
|
||||||
|
|
||||||
|
proxyUrlField = new JTextField();
|
||||||
|
proxyPathField = new JTextField();
|
||||||
|
|
||||||
initLayout();
|
initLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +122,12 @@ public class TeaVMDevServerSettingsPanel extends JPanel {
|
||||||
|
|
||||||
add(new JLabel("Server heap limit:"), labelConstraints);
|
add(new JLabel("Server heap limit:"), labelConstraints);
|
||||||
add(maxHeapField, constraints);
|
add(maxHeapField, constraints);
|
||||||
|
|
||||||
|
add(new JLabel("Proxy URL:"), labelConstraints);
|
||||||
|
add(proxyUrlField, constraints);
|
||||||
|
|
||||||
|
add(new JLabel("Proxy from path:"), labelConstraints);
|
||||||
|
add(proxyPathField, constraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void load(TeaVMDevServerConfiguration configuration) {
|
public void load(TeaVMDevServerConfiguration configuration) {
|
||||||
|
@ -129,21 +140,25 @@ public class TeaVMDevServerSettingsPanel extends JPanel {
|
||||||
autoReloadField.setSelected(configuration.isAutomaticallyReloaded());
|
autoReloadField.setSelected(configuration.isAutomaticallyReloaded());
|
||||||
maxHeapField.setText(Integer.toString(configuration.getMaxHeap()));
|
maxHeapField.setText(Integer.toString(configuration.getMaxHeap()));
|
||||||
portField.setText(Integer.toString(configuration.getPort()));
|
portField.setText(Integer.toString(configuration.getPort()));
|
||||||
|
proxyUrlField.setText(configuration.getProxyUrl());
|
||||||
|
proxyPathField.setText(configuration.getProxyPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save(TeaVMDevServerConfiguration configuration) {
|
public void save(TeaVMDevServerConfiguration configuration) {
|
||||||
configuration.setMainClass(mainClassField.getText());
|
configuration.setMainClass(mainClassField.getText().trim());
|
||||||
moduleSelector.applyTo(configuration);
|
moduleSelector.applyTo(configuration);
|
||||||
configuration.setJdkPath(jrePathEditor.getJrePathOrName());
|
configuration.setJdkPath(jrePathEditor.getJrePathOrName());
|
||||||
configuration.setFileName(fileNameField.getText());
|
configuration.setFileName(fileNameField.getText().trim());
|
||||||
configuration.setPathToFile(pathToFileField.getText());
|
configuration.setPathToFile(pathToFileField.getText().trim());
|
||||||
configuration.setIndicator(indicatorField.isSelected());
|
configuration.setIndicator(indicatorField.isSelected());
|
||||||
configuration.setAutomaticallyReloaded(autoReloadField.isSelected());
|
configuration.setAutomaticallyReloaded(autoReloadField.isSelected());
|
||||||
if (!maxHeapField.getText().isEmpty()) {
|
if (!maxHeapField.getText().trim().isEmpty()) {
|
||||||
configuration.setMaxHeap(Integer.parseInt(maxHeapField.getText()));
|
configuration.setMaxHeap(Integer.parseInt(maxHeapField.getText()));
|
||||||
}
|
}
|
||||||
if (!portField.getText().isEmpty()) {
|
if (!portField.getText().trim().isEmpty()) {
|
||||||
configuration.setPort(Integer.parseInt(portField.getText()));
|
configuration.setPort(Integer.parseInt(portField.getText()));
|
||||||
}
|
}
|
||||||
|
configuration.setProxyUrl(proxyUrlField.getText().trim());
|
||||||
|
configuration.setProxyPath(proxyPathField.getText().trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user