proxy progress
This commit is contained in:
parent
15d0e29436
commit
439dbaff55
|
@ -0,0 +1,9 @@
|
||||||
|
<component name="libraryTable">
|
||||||
|
<library name="javax.json">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/server/javax.json.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<component name="libraryTable">
|
||||||
|
<library name="takes">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/server/cactoos.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/server/takes.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/server/takes-sources.jar!/" />
|
||||||
|
</SOURCES>
|
||||||
|
</library>
|
||||||
|
</component>
|
|
@ -11,6 +11,7 @@
|
||||||
<module fileurl="file://$PROJECT_DIR$/logging/logging.iml" filepath="$PROJECT_DIR$/logging/logging.iml" />
|
<module fileurl="file://$PROJECT_DIR$/logging/logging.iml" filepath="$PROJECT_DIR$/logging/logging.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/logging-api/logging-api.iml" filepath="$PROJECT_DIR$/logging-api/logging-api.iml" />
|
<module fileurl="file://$PROJECT_DIR$/logging-api/logging-api.iml" filepath="$PROJECT_DIR$/logging-api/logging-api.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/models/models.iml" filepath="$PROJECT_DIR$/models/models.iml" />
|
<module fileurl="file://$PROJECT_DIR$/models/models.iml" filepath="$PROJECT_DIR$/models/models.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/../java_tools/proxy_server/proxy_server.iml" filepath="$PROJECT_DIR$/../java_tools/proxy_server/proxy_server.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/romraider/romraider.iml" filepath="$PROJECT_DIR$/romraider/romraider.iml" />
|
<module fileurl="file://$PROJECT_DIR$/romraider/romraider.iml" filepath="$PROJECT_DIR$/romraider/romraider.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/shared_io/shared_io.iml" filepath="$PROJECT_DIR$/shared_io/shared_io.iml" />
|
<module fileurl="file://$PROJECT_DIR$/shared_io/shared_io.iml" filepath="$PROJECT_DIR$/shared_io/shared_io.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/shared_ui/shared_ui.iml" filepath="$PROJECT_DIR$/shared_ui/shared_ui.iml" />
|
<module fileurl="file://$PROJECT_DIR$/shared_ui/shared_ui.iml" filepath="$PROJECT_DIR$/shared_ui/shared_ui.iml" />
|
||||||
|
|
|
@ -10,5 +10,6 @@
|
||||||
<orderEntry type="module" module-name="io" />
|
<orderEntry type="module" module-name="io" />
|
||||||
<orderEntry type="module" module-name="models" />
|
<orderEntry type="module" module-name="models" />
|
||||||
<orderEntry type="module" module-name="logging" />
|
<orderEntry type="module" module-name="logging" />
|
||||||
|
<orderEntry type="library" name="javax.json" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
|
@ -1,6 +1,7 @@
|
||||||
<project default="jar">
|
<project default="jar">
|
||||||
<property name="jar_file_folder" value="../java_console_binary"/>
|
<property name="jar_file_folder" value="../java_console_binary"/>
|
||||||
<property name="jar_file" value="${jar_file_folder}/rusefi_console.jar"/>
|
<property name="jar_file" value="${jar_file_folder}/rusefi_console.jar"/>
|
||||||
|
<property name="lib_list" value="../java_tools/configuration_definition/lib/snakeyaml.jar:lib/json-simple-1.1.1.jar:lib/server/javax.json.jar:lib/server/cactoos.jar:lib/server/takes.jar:lib/json-simple-1.1.1.jar:lib/jaxb-api.jar:lib/httpclient.jar:lib/httpmime.jar:lib/httpcore.jar:lib/jSerialComm.jar:lib/jcip-annotations-1.0.jar:lib/jlatexmath-1.0.6.jar:lib/swing-layout-1.0.jar:lib/jep.jar:lib/log4j.jar:lib/junit.jar:lib/SteelSeries-3.9.30.jar:lib/annotations.jar:lib/miglayout-4.0.jar:lib/surfaceplotter-2.0.1.jar"/>
|
||||||
|
|
||||||
<target name="clean">
|
<target name="clean">
|
||||||
<delete dir="build"/>
|
<delete dir="build"/>
|
||||||
|
@ -39,11 +40,13 @@
|
||||||
<target name="compile">
|
<target name="compile">
|
||||||
<mkdir dir="build/classes"/>
|
<mkdir dir="build/classes"/>
|
||||||
<javac debug="yes" destdir="build/classes"
|
<javac debug="yes" destdir="build/classes"
|
||||||
classpath="../java_tools/configuration_definition/lib/snakeyaml.jar:lib/json-simple-1.1.1.jar:lib/jaxb-api.jar:lib/httpclient.jar:lib/httpmime.jar:lib/httpcore.jar:lib/jSerialComm.jar:lib/jcip-annotations-1.0.jar:lib/jlatexmath-1.0.6.jar:lib/swing-layout-1.0.jar:lib/jep.jar:lib/log4j.jar:lib/junit.jar:lib/SteelSeries-3.9.30.jar:lib/annotations.jar:lib/miglayout-4.0.jar:lib/surfaceplotter-2.0.1.jar">
|
classpath="${lib_list}">
|
||||||
<src path="autotest/src"/>
|
<src path="autotest/src"/>
|
||||||
<src path="autoupdate/src"/>
|
<src path="autoupdate/src"/>
|
||||||
<src path="../java_tools/configuration_definition/src"/>
|
<src path="../java_tools/configuration_definition/src"/>
|
||||||
<src path="../java_tools/enum_to_string/src"/>
|
<src path="../java_tools/enum_to_string/src"/>
|
||||||
|
<src path="../java_tools/proxy_server/src/main/java"/>
|
||||||
|
<src path="../java_tools/proxy_server/src/test/java"/>
|
||||||
<src path="io/src/main/java"/>
|
<src path="io/src/main/java"/>
|
||||||
<src path="io/src/test/java"/>
|
<src path="io/src/test/java"/>
|
||||||
<src path="models/src/main/java"/>
|
<src path="models/src/main/java"/>
|
||||||
|
@ -76,7 +79,7 @@
|
||||||
<jvmarg value="-XX:+HeapDumpOnOutOfMemoryError"/>
|
<jvmarg value="-XX:+HeapDumpOnOutOfMemoryError"/>
|
||||||
<formatter type="brief"/>
|
<formatter type="brief"/>
|
||||||
<classpath
|
<classpath
|
||||||
path="build/classes:lib/junit.jar:lib/SteelSeries-3.9.30.jar:lib/miglayout-4.0.jar"/>
|
path="build/classes:lib/junit.jar:${lib_list}:lib/commons-logging.jar"/>
|
||||||
<batchtest todir="build">
|
<batchtest todir="build">
|
||||||
<fileset dir="autotest/src" includes="**/test/**/*Test.java"/>
|
<fileset dir="autotest/src" includes="**/test/**/*Test.java"/>
|
||||||
<fileset dir="autoupdate/src" includes="**/test/**/*Test.java"/>
|
<fileset dir="autoupdate/src" includes="**/test/**/*Test.java"/>
|
||||||
|
|
|
@ -288,7 +288,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
|
||||||
setController(newVersion);
|
setController(newVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] receivePacket(String msg, boolean allowLongResponse) throws InterruptedException, EOFException {
|
private byte[] receivePacket(String msg, boolean allowLongResponse) throws EOFException {
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
synchronized (ioLock) {
|
synchronized (ioLock) {
|
||||||
return incomingData.getPacket(logger, msg, allowLongResponse, start);
|
return incomingData.getPacket(logger, msg, allowLongResponse, start);
|
||||||
|
@ -408,7 +408,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
|
||||||
|
|
||||||
sendPacket(packet);
|
sendPacket(packet);
|
||||||
return receivePacket(msg, allowLongResponse);
|
return receivePacket(msg, allowLongResponse);
|
||||||
} catch (InterruptedException | IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error(msg + ": executeCommand failed: " + e);
|
logger.error(msg + ": executeCommand failed: " + e);
|
||||||
close();
|
close();
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
package com.rusefi.server;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static com.rusefi.binaryprotocol.BinaryProtocol.sleep;
|
|
||||||
|
|
||||||
public class Backend {
|
|
||||||
|
|
||||||
// guarded by own monitor
|
|
||||||
private final Set<ClientConnectionState> clients = new HashSet<>();
|
|
||||||
private final int clientTimeout;
|
|
||||||
|
|
||||||
public Backend(int clientTimeout) {
|
|
||||||
this.clientTimeout = clientTimeout;
|
|
||||||
|
|
||||||
new Thread(() -> {
|
|
||||||
while (true) {
|
|
||||||
runCleanup();
|
|
||||||
sleep(clientTimeout);
|
|
||||||
}
|
|
||||||
}, "rusEFI Server Cleanup").start();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runCleanup() {
|
|
||||||
List<ClientConnectionState> inactiveClients = new ArrayList<>();
|
|
||||||
|
|
||||||
synchronized (clients) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
for (ClientConnectionState client : clients) {
|
|
||||||
if (now - client.getLastActivityTimestamp() > clientTimeout)
|
|
||||||
inactiveClients.add(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ClientConnectionState inactiveClient : inactiveClients) {
|
|
||||||
close(inactiveClient);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void register(ClientConnectionState clientConnectionState) {
|
|
||||||
synchronized (clients) {
|
|
||||||
clients.add(clientConnectionState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void close(ClientConnectionState inactiveClient) {
|
|
||||||
inactiveClient.close();
|
|
||||||
synchronized (clients) {
|
|
||||||
clients.remove(inactiveClient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ClientConnectionState> getClients() {
|
|
||||||
synchronized (clients) {
|
|
||||||
return new ArrayList<>(clients);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCount() {
|
|
||||||
synchronized (clients) {
|
|
||||||
return clients.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
package com.rusefi.server;
|
|
||||||
|
|
||||||
import com.opensr5.Logger;
|
|
||||||
import com.rusefi.auth.AutoTokenUtil;
|
|
||||||
import com.rusefi.binaryprotocol.BinaryProtocolCommands;
|
|
||||||
import com.rusefi.binaryprotocol.IncomingDataBuffer;
|
|
||||||
import com.rusefi.io.IoStream;
|
|
||||||
import com.rusefi.io.commands.HelloCommand;
|
|
||||||
import com.rusefi.io.tcp.TcpIoStream;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
import static com.rusefi.binaryprotocol.IoHelper.checkResponseCode;
|
|
||||||
|
|
||||||
public class ClientConnectionState {
|
|
||||||
private final Socket clientSocket;
|
|
||||||
private final Logger logger;
|
|
||||||
|
|
||||||
private long lastActivityTimestamp;
|
|
||||||
private boolean isClosed;
|
|
||||||
private IoStream stream;
|
|
||||||
private IncomingDataBuffer incomingData;
|
|
||||||
|
|
||||||
public ClientConnectionState(Socket clientSocket, Logger logger) {
|
|
||||||
this.clientSocket = clientSocket;
|
|
||||||
this.logger = logger;
|
|
||||||
try {
|
|
||||||
stream = new TcpIoStream(logger, clientSocket);
|
|
||||||
incomingData = stream.getDataBuffer();
|
|
||||||
} catch (IOException e) {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLastActivityTimestamp() {
|
|
||||||
return lastActivityTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
isClosed = true;
|
|
||||||
close(clientSocket);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sayHello() {
|
|
||||||
try {
|
|
||||||
HelloCommand.send(stream, logger);
|
|
||||||
byte[] response = incomingData.getPacket(logger, "", false);
|
|
||||||
if (!checkResponseCode(response, BinaryProtocolCommands.RESPONSE_OK))
|
|
||||||
return;
|
|
||||||
String tokenAndSignature = new String(response, 1, response.length - 1);
|
|
||||||
String token = tokenAndSignature.length() > AutoTokenUtil.TOKEN_LENGTH ? tokenAndSignature.substring(0, AutoTokenUtil.TOKEN_LENGTH) : null;
|
|
||||||
if (!AutoTokenUtil.isToken(token))
|
|
||||||
throw new IOException("Invalid token");
|
|
||||||
String signature = tokenAndSignature.substring(AutoTokenUtil.TOKEN_LENGTH);
|
|
||||||
|
|
||||||
logger.info(token + " New client: " + signature);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void close(Closeable closeable) {
|
|
||||||
if (closeable != null) {
|
|
||||||
try {
|
|
||||||
closeable.close();
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,24 +1,40 @@
|
||||||
package com.rusefi;
|
package com.rusefi;
|
||||||
|
|
||||||
import com.opensr5.Logger;
|
import com.opensr5.Logger;
|
||||||
|
import com.rusefi.io.TcpCommunicationIntegrationTest;
|
||||||
import com.rusefi.io.tcp.BinaryProtocolServer;
|
import com.rusefi.io.tcp.BinaryProtocolServer;
|
||||||
import com.rusefi.server.Backend;
|
import com.rusefi.server.Backend;
|
||||||
import com.rusefi.server.ClientConnectionState;
|
import com.rusefi.server.ClientConnectionState;
|
||||||
|
import com.rusefi.server.UserDetails;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.json.simple.JSONArray;
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
import org.json.simple.parser.JSONParser;
|
||||||
|
import org.json.simple.parser.ParseException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static com.rusefi.binaryprotocol.BinaryProtocol.sleep;
|
import static com.rusefi.binaryprotocol.BinaryProtocol.sleep;
|
||||||
|
import static com.rusefi.server.Backend.LIST_PATH;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* integration test of the rusEFI online backend process
|
* integration test of the rusEFI online backend process
|
||||||
|
* At the moment this test is very loose with timing it must be unreliable?
|
||||||
*/
|
*/
|
||||||
public class ServerTest {
|
public class ServerTest {
|
||||||
private final static Logger logger = Logger.CONSOLE;
|
private final static Logger logger = Logger.CONSOLE;
|
||||||
|
@ -30,8 +46,19 @@ public class ServerTest {
|
||||||
CountDownLatch serverCreated = new CountDownLatch(1);
|
CountDownLatch serverCreated = new CountDownLatch(1);
|
||||||
|
|
||||||
|
|
||||||
Backend backend = new Backend(5 * Timeouts.SECOND);
|
Function<String, UserDetails> userDetailsResolver = authToken -> new UserDetails(authToken.substring(0, 5), authToken.charAt(6));
|
||||||
|
|
||||||
|
CountDownLatch allClientsDisconnected = new CountDownLatch(1);
|
||||||
|
|
||||||
|
int httpPort = 8000;
|
||||||
|
Backend backend = new Backend(userDetailsResolver, httpPort) {
|
||||||
|
@Override
|
||||||
|
public void close(ClientConnectionState inactiveClient) {
|
||||||
|
super.close(inactiveClient);
|
||||||
|
if (getCount() == 0)
|
||||||
|
allClientsDisconnected.countDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
BinaryProtocolServer.tcpServerSocket(serverPort, "Server", new Function<Socket, Runnable>() {
|
BinaryProtocolServer.tcpServerSocket(serverPort, "Server", new Function<Socket, Runnable>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -39,14 +66,15 @@ public class ServerTest {
|
||||||
return new Runnable() {
|
return new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
ClientConnectionState clientConnectionState = new ClientConnectionState(clientSocket, logger);
|
ClientConnectionState clientConnectionState = new ClientConnectionState(clientSocket, logger, backend.getUserDetailsResolver());
|
||||||
clientConnectionState.sayHello();
|
try {
|
||||||
backend.register(clientConnectionState);
|
clientConnectionState.sayHello();
|
||||||
|
|
||||||
while(true) {
|
|
||||||
|
|
||||||
|
backend.register(clientConnectionState);
|
||||||
|
clientConnectionState.runEndlessLoop();
|
||||||
|
} catch (IOException e) {
|
||||||
|
backend.close(clientConnectionState);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -66,6 +94,38 @@ public class ServerTest {
|
||||||
List<ClientConnectionState> clients = backend.getClients();
|
List<ClientConnectionState> clients = backend.getClients();
|
||||||
assertEquals(2, clients.size());
|
assertEquals(2, clients.size());
|
||||||
|
|
||||||
|
List<UserDetails> onlineUsers = getOnlineUsers(httpPort);
|
||||||
|
assertEquals(2, onlineUsers.size());
|
||||||
|
|
||||||
|
assertTrue(allClientsDisconnected.await(30, TimeUnit.SECONDS));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private List<UserDetails> getOnlineUsers(int httpPort) throws IOException {
|
||||||
|
HttpClient httpclient = new DefaultHttpClient();
|
||||||
|
String url = "http://" + TcpCommunicationIntegrationTest.LOCALHOST + ":" + httpPort + LIST_PATH;
|
||||||
|
System.out.println("Connecting to " + url);
|
||||||
|
HttpGet httpget = new HttpGet(url);
|
||||||
|
HttpResponse httpResponse = httpclient.execute(httpget);
|
||||||
|
|
||||||
|
HttpEntity entity = httpResponse.getEntity();
|
||||||
|
String responseString = EntityUtils.toString(entity, "UTF-8");
|
||||||
|
JSONParser parser = new JSONParser();
|
||||||
|
List<UserDetails> userLists = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
JSONArray array = (JSONArray) parser.parse(responseString);
|
||||||
|
|
||||||
|
for (int i = 0; i < array.size(); i++) {
|
||||||
|
JSONObject element = (JSONObject) array.get(i);
|
||||||
|
|
||||||
|
userLists.add(UserDetails.valueOf(element));
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("object=" + array);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
return userLists;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,5 +28,7 @@
|
||||||
<orderEntry type="library" name="jSerialComm" level="project" />
|
<orderEntry type="library" name="jSerialComm" level="project" />
|
||||||
<orderEntry type="library" name="json-simple" level="project" />
|
<orderEntry type="library" name="json-simple" level="project" />
|
||||||
<orderEntry type="module" module-name="models" />
|
<orderEntry type="module" module-name="models" />
|
||||||
|
<orderEntry type="module" module-name="proxy_server" scope="TEST" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="takes" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="takes" level="project" />
|
||||||
|
<orderEntry type="module" module-name="io" />
|
||||||
|
<orderEntry type="module" module-name="shared_io" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="javax.json" level="project" />
|
||||||
|
<orderEntry type="library" name="javax.json" level="project" />
|
||||||
|
<orderEntry type="library" name="json-simple" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,126 @@
|
||||||
|
package com.rusefi.server;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.takes.Take;
|
||||||
|
import org.takes.facets.fork.FkRegex;
|
||||||
|
import org.takes.facets.fork.TkFork;
|
||||||
|
import org.takes.http.Exit;
|
||||||
|
import org.takes.http.FtBasic;
|
||||||
|
import org.takes.rs.RsJson;
|
||||||
|
|
||||||
|
import javax.json.Json;
|
||||||
|
import javax.json.JsonArrayBuilder;
|
||||||
|
import javax.json.JsonObject;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class Backend {
|
||||||
|
|
||||||
|
public static final String LIST_PATH = "/list_online";
|
||||||
|
|
||||||
|
private final FkRegex showOnlineUsers = new FkRegex(LIST_PATH,
|
||||||
|
(Take) req -> getUsersOnline()
|
||||||
|
);
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private RsJson getUsersOnline() throws IOException {
|
||||||
|
JsonArrayBuilder builder = Json.createArrayBuilder();
|
||||||
|
List<ClientConnectionState> clients = getClients();
|
||||||
|
for (ClientConnectionState client : clients) {
|
||||||
|
|
||||||
|
JsonObject clientObject = Json.createObjectBuilder()
|
||||||
|
.add(UserDetails.USER_ID, client.getUserDetails().getId())
|
||||||
|
.add(UserDetails.USERNAME, client.getUserDetails().getUserName())
|
||||||
|
.add("signature", client.getSignature())
|
||||||
|
.build();
|
||||||
|
builder.add(clientObject);
|
||||||
|
}
|
||||||
|
return new RsJson(builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// guarded by own monitor
|
||||||
|
private final Set<ClientConnectionState> clients = new HashSet<>();
|
||||||
|
// private final int clientTimeout;
|
||||||
|
private final Function<String, UserDetails> userDetailsResolver;
|
||||||
|
|
||||||
|
public Backend(Function<String, UserDetails> userDetailsResolver, int httpPort) {
|
||||||
|
// this.clientTimeout = clientTimeout;
|
||||||
|
this.userDetailsResolver = userDetailsResolver;
|
||||||
|
|
||||||
|
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
new FtBasic(
|
||||||
|
new TkFork(showOnlineUsers,
|
||||||
|
new FkRegex("/", "<a href='https://rusefi.com/online/'>rusEFI Online</a>")
|
||||||
|
), httpPort
|
||||||
|
).start(Exit.NEVER);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
|
||||||
|
// new Thread(() -> {
|
||||||
|
// while (true) {
|
||||||
|
// runCleanup();
|
||||||
|
// sleep(clientTimeout);
|
||||||
|
// }
|
||||||
|
// }, "rusEFI Server Cleanup").start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Function<String, UserDetails> getUserDetailsResolver() {
|
||||||
|
return userDetailsResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private void runCleanup() {
|
||||||
|
// List<ClientConnectionState> inactiveClients = new ArrayList<>();
|
||||||
|
//
|
||||||
|
// synchronized (clients) {
|
||||||
|
// long now = System.currentTimeMillis();
|
||||||
|
// for (ClientConnectionState client : clients) {
|
||||||
|
// if (now - client.getLastActivityTimestamp() > clientTimeout)
|
||||||
|
// inactiveClients.add(client);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for (ClientConnectionState inactiveClient : inactiveClients) {
|
||||||
|
// close(inactiveClient);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void register(ClientConnectionState clientConnectionState) {
|
||||||
|
synchronized (clients) {
|
||||||
|
clients.add(clientConnectionState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close(ClientConnectionState inactiveClient) {
|
||||||
|
inactiveClient.close();
|
||||||
|
synchronized (clients) {
|
||||||
|
// in case of exception in the initialization phase we do not even add client into the the collection
|
||||||
|
clients.remove(inactiveClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ClientConnectionState> getClients() {
|
||||||
|
synchronized (clients) {
|
||||||
|
return new ArrayList<>(clients);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCount() {
|
||||||
|
synchronized (clients) {
|
||||||
|
return clients.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package com.rusefi.server;
|
||||||
|
|
||||||
|
import com.opensr5.Logger;
|
||||||
|
import com.rusefi.auth.AutoTokenUtil;
|
||||||
|
import com.rusefi.binaryprotocol.BinaryProtocolCommands;
|
||||||
|
import com.rusefi.binaryprotocol.IncomingDataBuffer;
|
||||||
|
import com.rusefi.io.IoStream;
|
||||||
|
import com.rusefi.io.commands.GetOutputsCommand;
|
||||||
|
import com.rusefi.io.commands.HelloCommand;
|
||||||
|
import com.rusefi.io.tcp.TcpIoStream;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static com.rusefi.binaryprotocol.IoHelper.checkResponseCode;
|
||||||
|
|
||||||
|
public class ClientConnectionState {
|
||||||
|
private final Socket clientSocket;
|
||||||
|
private final Logger logger;
|
||||||
|
private final Function<String, UserDetails> userDetailsResolver;
|
||||||
|
|
||||||
|
private long lastActivityTimestamp;
|
||||||
|
private boolean isClosed;
|
||||||
|
private IoStream stream;
|
||||||
|
private IncomingDataBuffer incomingData;
|
||||||
|
private UserDetails userDetails;
|
||||||
|
private String signature;
|
||||||
|
|
||||||
|
public ClientConnectionState(Socket clientSocket, Logger logger, Function<String, UserDetails> userDetailsResolver) {
|
||||||
|
this.clientSocket = clientSocket;
|
||||||
|
this.logger = logger;
|
||||||
|
this.userDetailsResolver = userDetailsResolver;
|
||||||
|
try {
|
||||||
|
stream = new TcpIoStream(logger, clientSocket);
|
||||||
|
incomingData = stream.getDataBuffer();
|
||||||
|
} catch (IOException e) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// public long getLastActivityTimestamp() {
|
||||||
|
// return lastActivityTimestamp;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isClosed() {
|
||||||
|
return isClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
isClosed = true;
|
||||||
|
close(clientSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sayHello() throws IOException {
|
||||||
|
HelloCommand.send(stream, logger);
|
||||||
|
byte[] response = incomingData.getPacket(logger, "", false);
|
||||||
|
if (!checkResponseCode(response, BinaryProtocolCommands.RESPONSE_OK))
|
||||||
|
return;
|
||||||
|
String tokenAndSignature = new String(response, 1, response.length - 1);
|
||||||
|
String authToken = tokenAndSignature.length() > AutoTokenUtil.TOKEN_LENGTH ? tokenAndSignature.substring(0, AutoTokenUtil.TOKEN_LENGTH) : null;
|
||||||
|
if (!AutoTokenUtil.isToken(authToken))
|
||||||
|
throw new IOException("Invalid token");
|
||||||
|
signature = tokenAndSignature.substring(AutoTokenUtil.TOKEN_LENGTH);
|
||||||
|
|
||||||
|
logger.info(authToken + " New client: " + signature);
|
||||||
|
userDetails = userDetailsResolver.apply(authToken);
|
||||||
|
logger.info("User " + userDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDetails getUserDetails() {
|
||||||
|
return userDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignature() {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void close(Closeable closeable) {
|
||||||
|
if (closeable != null) {
|
||||||
|
try {
|
||||||
|
closeable.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runEndlessLoop() throws IOException {
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
byte[] commandPacket = GetOutputsCommand.createRequest();
|
||||||
|
|
||||||
|
stream.sendPacket(commandPacket, logger);
|
||||||
|
|
||||||
|
byte[] packet = incomingData.getPacket(logger, "msg", true);
|
||||||
|
if (packet == null)
|
||||||
|
throw new IOException("No response");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.rusefi.server;
|
||||||
|
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
|
||||||
|
public class UserDetails {
|
||||||
|
|
||||||
|
|
||||||
|
public static final String USER_ID = "user_id";
|
||||||
|
public static final String USERNAME = "username";
|
||||||
|
private final String userName;
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
public UserDetails(String userName, int id) {
|
||||||
|
this.userName = userName;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserDetails valueOf(JSONObject element) {
|
||||||
|
long userId = (long) element.get(USER_ID);
|
||||||
|
String userName = (String) element.get(USERNAME);
|
||||||
|
return new UserDetails(userName, (int) userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "UserDetails{" +
|
||||||
|
"userName='" + userName + '\'' +
|
||||||
|
", id=" + id +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package com.rusefi.server;
|
||||||
|
|
||||||
|
import org.takes.Request;
|
||||||
|
import org.takes.Response;
|
||||||
|
import org.takes.Take;
|
||||||
|
import org.takes.facets.fork.FkRegex;
|
||||||
|
import org.takes.facets.fork.TkFork;
|
||||||
|
import org.takes.http.Exit;
|
||||||
|
import org.takes.http.FtBasic;
|
||||||
|
import org.takes.misc.Href;
|
||||||
|
import org.takes.rq.RqHref;
|
||||||
|
import org.takes.rs.RsJson;
|
||||||
|
|
||||||
|
import javax.json.Json;
|
||||||
|
import javax.json.JsonArray;
|
||||||
|
import javax.json.JsonObject;
|
||||||
|
|
||||||
|
public class ServerSandbox {
|
||||||
|
|
||||||
|
public static void main(final String... args) throws Exception {
|
||||||
|
new FtBasic(
|
||||||
|
new TkFork(
|
||||||
|
new FkRegex("/list", new Take() {
|
||||||
|
@Override
|
||||||
|
public Response act(Request request) throws Exception {
|
||||||
|
Href href = new RqHref.Base(request).href();
|
||||||
|
// URI uri = href.uri();
|
||||||
|
Iterable<String> values = href.param("user");
|
||||||
|
String name = values.iterator().next();
|
||||||
|
|
||||||
|
|
||||||
|
JsonArray result = Json.createArrayBuilder()
|
||||||
|
.add(Json.createObjectBuilder().add("name", name))
|
||||||
|
.add(Json.createObjectBuilder().add("name", name))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return new RsJson(result);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new FkRegex("/", "hello, world!")
|
||||||
|
|
||||||
|
|
||||||
|
), 8080
|
||||||
|
).start(Exit.NEVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final class User implements RsJson.Source {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public User(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObject toJson() {
|
||||||
|
return Json.createObjectBuilder()
|
||||||
|
.add("name", name)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue