feat: ASW-74 新增 adb shell websocket 接口
This commit is contained in:
8
pom.xml
8
pom.xml
@@ -60,6 +60,14 @@
|
||||
<artifactId>Java-WebSocket</artifactId>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.jetbrains.pty4j/pty4j -->
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.pty4j</groupId>
|
||||
<artifactId>pty4j</artifactId>
|
||||
<version>0.12.35</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
131
src/main/java/net/geedge/api/config/AdbShellProxyHandler.java
Normal file
131
src/main/java/net/geedge/api/config/AdbShellProxyHandler.java
Normal file
@@ -0,0 +1,131 @@
|
||||
package net.geedge.api.config;
|
||||
|
||||
import cn.hutool.log.Log;
|
||||
import com.pty4j.PtyProcessBuilder;
|
||||
import net.geedge.api.entity.EnvApiYml;
|
||||
import net.geedge.api.util.AdbCommandBuilder;
|
||||
import net.geedge.api.util.AdbUtil;
|
||||
import net.geedge.common.T;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class AdbShellProxyHandler extends TextWebSocketHandler {
|
||||
|
||||
private static final Log log = Log.get();
|
||||
|
||||
private EnvApiYml.Adb adb;
|
||||
|
||||
private Process process = null;
|
||||
private InputStream inputStream = null;
|
||||
private OutputStream outputStream = null;
|
||||
private ExecutorService executorService = Executors.newFixedThreadPool(2);
|
||||
|
||||
public AdbShellProxyHandler(EnvApiYml.Adb adb) {
|
||||
this.adb = adb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
log.info("[afterConnectionEstablished] [WebSocket connection established] [websocket uri: {}]", session.getUri());
|
||||
super.afterConnectionEstablished(session);
|
||||
|
||||
List<String> cmd = AdbCommandBuilder.builder()
|
||||
.serial(AdbUtil.getInstance(adb).getSerial())
|
||||
.buildShellCommand("shell")
|
||||
.build();
|
||||
|
||||
Map<String, String> env = new HashMap<>(System.getenv());
|
||||
env.put("TERM", "xterm");
|
||||
|
||||
// start process
|
||||
process = new PtyProcessBuilder()
|
||||
.setCommand(cmd.toArray(new String[]{}))
|
||||
.setEnvironment(env)
|
||||
.setRedirectErrorStream(true)
|
||||
.start();
|
||||
|
||||
// process = new PtyProcessBuilder()
|
||||
// .setCommand(new String[]{"cmd.exe", "/C", "D:\\softwares\\platform-tools\\platform-tools\\adb.exe shell"})
|
||||
// .setEnvironment(env)
|
||||
// .setRedirectErrorStream(true)
|
||||
// .start();
|
||||
|
||||
// stream
|
||||
inputStream = process.getInputStream();
|
||||
outputStream = process.getOutputStream();
|
||||
|
||||
// server to client
|
||||
executorService.submit(() -> {
|
||||
byte[] buffer = new byte[1024 * 4];
|
||||
try {
|
||||
while (session != null && session.isOpen()) {
|
||||
int size = inputStream.read(buffer);
|
||||
if (size == -1) {
|
||||
try {
|
||||
if (session.isOpen()) {
|
||||
session.sendMessage(new TextMessage("Connection closed \r\n"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < size; i++) {
|
||||
char chr = (char) (buffer[i] & 0xff);
|
||||
sb.append(chr);
|
||||
}
|
||||
|
||||
String message = sb.toString();
|
||||
message = T.StrUtil.str(message.getBytes(T.DigestUtils.getEncoding(message)), "UTF-8");
|
||||
|
||||
session.sendMessage(new TextMessage(message));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e, "[serverToClient] [error]");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
|
||||
super.handleTextMessage(session, message);
|
||||
if (T.ObjectUtil.isNotNull(outputStream)) {
|
||||
// write cmd byte
|
||||
T.IoUtil.write(outputStream, false, message.getPayload().getBytes());
|
||||
T.IoUtil.flush(outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
|
||||
log.info("[afterConnectionClosed] [WebSocket connection closed] [websocket uri: {}]", session.getUri());
|
||||
super.afterConnectionClosed(session, status);
|
||||
|
||||
// close resources
|
||||
this.closeResources();
|
||||
}
|
||||
|
||||
/**
|
||||
* close resources
|
||||
*/
|
||||
private void closeResources() {
|
||||
try {
|
||||
T.IoUtil.close(outputStream);
|
||||
T.IoUtil.close(inputStream);
|
||||
if (process != null)
|
||||
process.destroy();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,5 +17,6 @@ public class WebSocketConfig implements WebSocketConfigurer {
|
||||
@Override
|
||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
||||
registry.addHandler(new VncProxyHandler(envApiYml.getVnc()), "/api/v1/env/novnc").setAllowedOrigins("*");
|
||||
registry.addHandler(new AdbShellProxyHandler(envApiYml.getAdb()), "/api/v1/env/terminal").setAllowedOrigins("*");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class T {
|
||||
@@ -127,6 +129,14 @@ public class T {
|
||||
public static class URLUtil extends cn.hutool.core.util.URLUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象工具类,包括判空、克隆、序列化等操作
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public static class ObjectUtil extends cn.hutool.core.util.ObjectUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* CommandLineUtil
|
||||
*
|
||||
@@ -244,4 +254,24 @@ public class T {
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
public class DigestUtils {
|
||||
private static final List<String> ENCODE_LIST = Arrays.asList("ISO-8859-1", "GB2312", "UTF-8", "GBK");
|
||||
|
||||
public static String getEncoding(String text) {
|
||||
for (String enc : ENCODE_LIST) {
|
||||
try {
|
||||
byte[] bytes = text.getBytes(enc);
|
||||
String str = cn.hutool.core.util.StrUtil.str(bytes, enc);
|
||||
Arrays.fill(bytes, (byte) 0);
|
||||
if (cn.hutool.core.util.StrUtil.equals(text, str)) {
|
||||
return enc;
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user