caploon是基于unix。为了实现在web下面实现运行unix命令终端,api deamon 提高了websocket的接口:ws://127.0.0.1:7703,这是Rust实现的wspty。前端使用Xterm库显示命令行。
前端
websockect
var websocket = new WebSocket("ws://127.0.0.1:7703");
websocket.binaryType = "arraybuffer";
// websocket event
// onopen
// 默认安卓的sh
let command = "/system/bin/sh";
if (location.port == 8081) {
// use /usr/bin/bash on Linux and /bin/zsh on Mac
if (navigator.platform.startsWith("Linux")) {
command = "/usr/bin/bash";
} else if (navigator.platform.startsWith("Mac")) {
command = "/bin/zsh";
} else {
console.error(`Unsupported platform: ${navigator.platform}`);
return;
}
}
// 发送
websocket.send(command);
// 初始化 xterm
term = new Terminal({
screenKeys: true,
useStyle: true,
cursorBlink: true,
fontFamily: "Droid Sans Mono, mono, monospace",
windowsMode: true,
});
// onmessage
websocket.onmessage = (event) => {
let data = event.data;
term.write(
typeof data === "string" ? data : new Uint8Array(data.slice(1))
);
};
Xterm
//string 命令数据
term.onData(function (data) {
websocket.send(new TextEncoder().encode("\x00" + data));
});
// Uint8Array数据
term.onBinary((data) => {
if (websocket.readyState !== 1) {
return;
}
const buffer = new Uint8Array(data.length + 1);
buffer[0] = 0;
for (let i = 0; i < data.length; ++i) {
buffer[i + 1] = data.charCodeAt(i) & 255;
}
websocket.send(buffer);
});
term.onResize((event) => {
websocket.send(
new TextEncoder().encode(
"\x01" + JSON.stringify({ cols: event.cols, rows: event.rows })
)
);
});
term.onTitleChange(function (title) {
document.title = `webPTY | ${title}`;
});
后端 api-daemon 接口
wspty库: capyloon/wspty (github.com)
api-daemon 在main开辟进程给wspty:api-daemon/daemon/src/main.rs
fn start_wspty() {
let _ = thread::Builder::new()
.name("wspty server".into())
.spawn(move || {
debug!("start_wspty start");
tokio::runtime::Runtime::new().unwrap().block_on(async {
let _ = wspty::start_server()
.await
.map_err(|e| error!("wspty server exit with error: {:?}", e));
});
debug!("start_wspty end");
});
}