proxy progress
This commit is contained in:
parent
b7c994daaf
commit
e696dc6890
|
@ -6,6 +6,8 @@ import com.rusefi.config.generated.Fields;
|
|||
import com.rusefi.io.commands.HelloCommand;
|
||||
import com.rusefi.io.tcp.BinaryProtocolServer;
|
||||
import com.rusefi.io.tcp.TcpIoStream;
|
||||
import com.rusefi.server.ControllerInfo;
|
||||
import com.rusefi.server.SessionDetails;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
|
@ -15,13 +17,14 @@ import static com.rusefi.io.tcp.BinaryProtocolServer.getPacketLength;
|
|||
import static com.rusefi.io.tcp.BinaryProtocolServer.readPromisedBytes;
|
||||
|
||||
public class MockRusEfiDevice {
|
||||
private final String authToken;
|
||||
private final String signature;
|
||||
private SessionDetails sessionDetails;
|
||||
private final Logger logger;
|
||||
|
||||
public MockRusEfiDevice(String authToken, String signature, Logger logger) {
|
||||
this.authToken = authToken;
|
||||
this.signature = signature;
|
||||
ControllerInfo ci = new ControllerInfo("vehicle", "make", "code", signature);
|
||||
|
||||
sessionDetails = new SessionDetails(ci, authToken, SessionDetails.createOneTimeCode());
|
||||
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
@ -40,9 +43,8 @@ public class MockRusEfiDevice {
|
|||
|
||||
byte command = payload[0];
|
||||
|
||||
|
||||
if (command == Fields.TS_HELLO_COMMAND) {
|
||||
new HelloCommand(logger, authToken + signature).handle(packet, stream);
|
||||
new HelloCommand(logger, sessionDetails.toJson()).handle(packet, stream);
|
||||
} else {
|
||||
handleCommand();
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import java.util.concurrent.CountDownLatch;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
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.assertTrue;
|
||||
|
@ -49,9 +48,16 @@ public class ServerTest {
|
|||
Function<String, UserDetails> userDetailsResolver = authToken -> new UserDetails(authToken.substring(0, 5), authToken.charAt(6));
|
||||
|
||||
CountDownLatch allClientsDisconnected = new CountDownLatch(1);
|
||||
CountDownLatch onConnected = new CountDownLatch(2);
|
||||
|
||||
int httpPort = 8000;
|
||||
Backend backend = new Backend(userDetailsResolver, httpPort) {
|
||||
@Override
|
||||
public void register(ClientConnectionState clientConnectionState) {
|
||||
super.register(clientConnectionState);
|
||||
onConnected.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(ClientConnectionState inactiveClient) {
|
||||
super.close(inactiveClient);
|
||||
|
@ -68,7 +74,7 @@ public class ServerTest {
|
|||
public void run() {
|
||||
ClientConnectionState clientConnectionState = new ClientConnectionState(clientSocket, logger, backend.getUserDetailsResolver());
|
||||
try {
|
||||
clientConnectionState.sayHello();
|
||||
clientConnectionState.requestControllerInfo();
|
||||
|
||||
backend.register(clientConnectionState);
|
||||
clientConnectionState.runEndlessLoop();
|
||||
|
@ -87,9 +93,7 @@ public class ServerTest {
|
|||
new MockRusEfiDevice("00000000-1234-1234-1234-123456789012", "rusEFI 2020.07.06.frankenso_na6.2468827536", logger).connect(serverPort);
|
||||
new MockRusEfiDevice("12345678-1234-1234-1234-123456789012", "rusEFI 2020.07.11.proteus_f4.1986715563", logger).connect(serverPort);
|
||||
|
||||
|
||||
// todo: technically we should have callbacks for 'connect', will make this better if this would be failing
|
||||
sleep(Timeouts.SECOND);
|
||||
assertTrue(onConnected.await(30, TimeUnit.SECONDS));
|
||||
|
||||
List<ClientConnectionState> clients = backend.getClients();
|
||||
assertEquals(2, clients.size());
|
||||
|
|
|
@ -14,5 +14,6 @@
|
|||
<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" />
|
||||
<orderEntry type="library" scope="TEST" name="junit" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -19,8 +19,9 @@ import java.util.Set;
|
|||
import java.util.function.Function;
|
||||
|
||||
public class Backend {
|
||||
|
||||
public static final String LIST_PATH = "/list_online";
|
||||
public static final String VERSION_PATH = "/version";
|
||||
public static final String BACKEND_VERSION = "0.0001";
|
||||
|
||||
private final FkRegex showOnlineUsers = new FkRegex(LIST_PATH,
|
||||
(Take) req -> getUsersOnline()
|
||||
|
@ -33,9 +34,9 @@ public class Backend {
|
|||
for (ClientConnectionState client : clients) {
|
||||
|
||||
JsonObject clientObject = Json.createObjectBuilder()
|
||||
.add(UserDetails.USER_ID, client.getUserDetails().getId())
|
||||
.add(UserDetails.USER_ID, client.getUserDetails().getUserId())
|
||||
.add(UserDetails.USERNAME, client.getUserDetails().getUserName())
|
||||
.add("signature", client.getSignature())
|
||||
.add(ControllerInfo.SIGNATURE, client.getSessionDetails().getControllerInfo().getSignature())
|
||||
.build();
|
||||
builder.add(clientObject);
|
||||
}
|
||||
|
@ -58,6 +59,7 @@ public class Backend {
|
|||
try {
|
||||
new FtBasic(
|
||||
new TkFork(showOnlineUsers,
|
||||
new FkRegex(VERSION_PATH, BACKEND_VERSION),
|
||||
new FkRegex("/", "<a href='https://rusefi.com/online/'>rusEFI Online</a>")
|
||||
), httpPort
|
||||
).start(Exit.NEVER);
|
||||
|
|
|
@ -10,7 +10,6 @@ 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;
|
||||
|
@ -22,12 +21,17 @@ public class ClientConnectionState {
|
|||
private final Logger logger;
|
||||
private final Function<String, UserDetails> userDetailsResolver;
|
||||
|
||||
private long lastActivityTimestamp;
|
||||
private boolean isClosed;
|
||||
private IoStream stream;
|
||||
private IncomingDataBuffer incomingData;
|
||||
/**
|
||||
* Data from controller
|
||||
*/
|
||||
private SessionDetails sessionDetails;
|
||||
/**
|
||||
* user info from rusEFI database based on auth token
|
||||
*/
|
||||
private UserDetails userDetails;
|
||||
private String signature;
|
||||
|
||||
public ClientConnectionState(Socket clientSocket, Logger logger, Function<String, UserDetails> userDetailsResolver) {
|
||||
this.clientSocket = clientSocket;
|
||||
|
@ -55,19 +59,18 @@ public class ClientConnectionState {
|
|||
close(clientSocket);
|
||||
}
|
||||
|
||||
public void sayHello() throws IOException {
|
||||
public void requestControllerInfo() 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);
|
||||
String jsonString = new String(response, 1, response.length - 1);
|
||||
sessionDetails = SessionDetails.valueOf(jsonString);
|
||||
if (!AutoTokenUtil.isToken(sessionDetails.getAuthToken()))
|
||||
throw new IOException("Invalid token in " + jsonString);
|
||||
|
||||
logger.info(authToken + " New client: " + signature);
|
||||
userDetails = userDetailsResolver.apply(authToken);
|
||||
logger.info(sessionDetails.getAuthToken() + " New client: " + sessionDetails.getControllerInfo());
|
||||
userDetails = userDetailsResolver.apply(sessionDetails.getAuthToken());
|
||||
logger.info("User " + userDetails);
|
||||
}
|
||||
|
||||
|
@ -75,8 +78,8 @@ public class ClientConnectionState {
|
|||
return userDetails;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
public SessionDetails getSessionDetails() {
|
||||
return sessionDetails;
|
||||
}
|
||||
|
||||
private static void close(Closeable closeable) {
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
package com.rusefi.server;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonReader;
|
||||
import java.io.StringReader;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Controller description without any sensitive information
|
||||
*/
|
||||
public class ControllerInfo {
|
||||
public static final String VEHICLE_NAME = "vehicleName";
|
||||
public static final String ENGINE_MAKE = "engineMake";
|
||||
public static final String ENGINE_CODE = "engineCode";
|
||||
public static final String SIGNATURE = "signature";
|
||||
|
||||
private final String vehicleName;
|
||||
private final String engineMake;
|
||||
private final String engineCode;
|
||||
private final String signature;
|
||||
|
||||
public ControllerInfo(String vehicleName, String engineCode, String engineMake, String signature) {
|
||||
Objects.requireNonNull(vehicleName);
|
||||
Objects.requireNonNull(engineMake);
|
||||
Objects.requireNonNull(engineCode);
|
||||
|
||||
this.vehicleName = vehicleName;
|
||||
this.engineCode = engineCode;
|
||||
this.engineMake = engineMake;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ControllerInfo{" +
|
||||
"vehicleName='" + vehicleName + '\'' +
|
||||
", engineMake='" + engineMake + '\'' +
|
||||
", engineCode='" + engineCode + '\'' +
|
||||
", signature='" + signature + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getVehicleName() {
|
||||
return vehicleName;
|
||||
}
|
||||
|
||||
public String getEngineMake() {
|
||||
return engineMake;
|
||||
}
|
||||
|
||||
public String getEngineCode() {
|
||||
return engineCode;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public static ControllerInfo valueOf(String jsonString) {
|
||||
JsonReader reader = Json.createReader(new StringReader(jsonString));
|
||||
|
||||
JsonObject jsonObject = reader.readObject();
|
||||
String vehicleName = jsonObject.getString(VEHICLE_NAME);
|
||||
String engineMake = jsonObject.getString(ENGINE_MAKE);
|
||||
String engineCode = jsonObject.getString(ENGINE_CODE);
|
||||
String signature = jsonObject.getString(SIGNATURE);
|
||||
|
||||
return new ControllerInfo(vehicleName, engineCode, engineMake, signature);
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
JsonObject jsonObject = Json.createObjectBuilder()
|
||||
.add(ENGINE_MAKE, engineMake)
|
||||
.add(ENGINE_CODE, engineCode)
|
||||
.add(VEHICLE_NAME, vehicleName)
|
||||
.add(SIGNATURE, signature)
|
||||
.build();
|
||||
return jsonObject.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ControllerInfo that = (ControllerInfo) o;
|
||||
return vehicleName.equals(that.vehicleName) &&
|
||||
engineMake.equals(that.engineMake) &&
|
||||
engineCode.equals(that.engineCode) &&
|
||||
signature.equals(that.signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(vehicleName, engineMake, engineCode, signature);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package com.rusefi.server;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonReader;
|
||||
import java.io.StringReader;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
|
||||
public class SessionDetails {
|
||||
public static final String ONE_TIME_TOKEN = "oneTime";
|
||||
public static final String AUTH_TOKEN = "authToken";
|
||||
public static final String CONTROLLER = "controller";
|
||||
|
||||
private final ControllerInfo controllerInfo;
|
||||
|
||||
private final int oneTimeToken;
|
||||
private final String authToken;
|
||||
|
||||
public SessionDetails(ControllerInfo controllerInfo, String authToken, int oneTimeCode) {
|
||||
Objects.requireNonNull(controllerInfo);
|
||||
Objects.requireNonNull(authToken);
|
||||
this.controllerInfo = controllerInfo;
|
||||
this.oneTimeToken = oneTimeCode;
|
||||
this.authToken = authToken;
|
||||
}
|
||||
|
||||
public static int createOneTimeCode() {
|
||||
return new Random().nextInt(60000);
|
||||
}
|
||||
|
||||
public int getOneTimeToken() {
|
||||
return oneTimeToken;
|
||||
}
|
||||
|
||||
public ControllerInfo getControllerInfo() {
|
||||
return controllerInfo;
|
||||
}
|
||||
|
||||
public String getAuthToken() {
|
||||
return authToken;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
JsonObject jsonObject = Json.createObjectBuilder()
|
||||
.add(CONTROLLER, controllerInfo.toJson())
|
||||
.add(ONE_TIME_TOKEN, oneTimeToken)
|
||||
.add(AUTH_TOKEN, authToken)
|
||||
.build();
|
||||
return jsonObject.toString();
|
||||
}
|
||||
|
||||
public static SessionDetails valueOf(String jsonString) {
|
||||
JsonReader reader = Json.createReader(new StringReader(jsonString));
|
||||
|
||||
JsonObject jsonObject = reader.readObject();
|
||||
String authToken = jsonObject.getString(AUTH_TOKEN);
|
||||
int oneTimeCode = jsonObject.getInt(ONE_TIME_TOKEN);
|
||||
|
||||
ControllerInfo controllerInfo = ControllerInfo.valueOf(jsonObject.getString(CONTROLLER));
|
||||
|
||||
return new SessionDetails(controllerInfo, authToken, oneTimeCode);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SessionDetails that = (SessionDetails) o;
|
||||
return oneTimeToken == that.oneTimeToken &&
|
||||
controllerInfo.equals(that.controllerInfo) &&
|
||||
authToken.equals(that.authToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(controllerInfo, oneTimeToken, authToken);
|
||||
}
|
||||
}
|
|
@ -8,11 +8,11 @@ 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;
|
||||
private final int userId;
|
||||
|
||||
public UserDetails(String userName, int id) {
|
||||
public UserDetails(String userName, int userId) {
|
||||
this.userName = userName;
|
||||
this.id = id;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public static UserDetails valueOf(JSONObject element) {
|
||||
|
@ -25,15 +25,15 @@ public class UserDetails {
|
|||
return userName;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
public int getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UserDetails{" +
|
||||
"userName='" + userName + '\'' +
|
||||
", id=" + id +
|
||||
", id=" + userId +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package com.rusefi.server;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class SessionDetailsTest {
|
||||
@Test
|
||||
public void testSerialization() {
|
||||
ControllerInfo ci = new ControllerInfo("name", "make", "code", "sign");
|
||||
|
||||
SessionDetails sd = new SessionDetails(ci, "auth", 123);
|
||||
|
||||
String json = sd.toJson();
|
||||
|
||||
SessionDetails fromJson = SessionDetails.valueOf(json);
|
||||
|
||||
assertEquals(sd, fromJson);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue