proxy progress

This commit is contained in:
rusefi 2020-07-12 14:33:18 -04:00
parent 555e0b5f26
commit 2bb99ab387
9 changed files with 243 additions and 33 deletions

View File

@ -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();
}

View File

@ -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());

View File

@ -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>

View File

@ -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);

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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 +
'}';
}
}

View File

@ -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);
}
}