feat: ASW-52 device novnc websocket 代理接口开发

This commit is contained in:
zhangshuai
2024-09-03 14:12:38 +08:00
parent 96c9b992af
commit 7a936b6187
4 changed files with 191 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
package net.geedge.asw.common.config.websocket;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.device.entity.DeviceEntity;
import net.geedge.asw.module.device.service.IDeviceService;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletionStage;
@Component
public class DeviceWebSocketHandler extends TextWebSocketHandler {
private static final Log log = Log.get();
/**
* device id
*/
private String id;
/**
* device token
*/
private String token;
private IDeviceService deviceService;
public DeviceWebSocketHandler(IDeviceService deviceService) {
this.deviceService = deviceService;
}
private void initFieldVal(WebSocketSession session) {
this.id = (String) session.getAttributes().get("id");
this.token = (String) session.getAttributes().get("token");
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
this.initFieldVal(session);
log.info("WebSocket connectioned. after connection established open device begin... device id: {}", id);
DeviceEntity deviceEntity = deviceService.queryInfo(id);
JSONObject paramJSONObject = deviceEntity.getParamJSONObject();
if (T.StrUtil.equals(token, paramJSONObject.getStr("token"))) {
log.warn("WebSocket connectioned error. device token exception. device id: {}, token: {}", id, token);
session.sendMessage(new TextMessage("Token error, Please config device token"));
session.close();
return;
}
String urlStr = String.format("ws://%s:%s%s", paramJSONObject.getStr("host"), paramJSONObject.getStr("port"), Constants.DEVICE_API_WEBSOCKET_PATH);
HttpClient client = HttpClient.newHttpClient();
WebSocket webSocket = client.newWebSocketBuilder()
.buildAsync(URI.create(urlStr), new WebSocketListener(session))
.get();
log.info("[afterConnectionEstablished] [device server: {}]", T.JSONUtil.toJsonStr(paramJSONObject));
session.getAttributes().put("deviceWebsocket", webSocket);
}
// WebSocket 监听器实现
private static class WebSocketListener implements WebSocket.Listener {
private WebSocketSession session;
public WebSocketListener(WebSocketSession session) {
this.session = session;
}
@Override
public CompletionStage<?> onBinary(WebSocket webSocket, ByteBuffer data, boolean last) {
try {
// device -> asw
session.sendMessage(new BinaryMessage(data, true));
} catch (IOException e) {
throw new RuntimeException(e);
}
return WebSocket.Listener.super.onBinary(webSocket, data, last);
}
@Override
public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) {
log.info("Device webSocket connection closed, Status: " + statusCode + ", Reason: " + reason);
return WebSocket.Listener.super.onClose(webSocket, statusCode, reason);
}
}
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
try {
// asw -> device api
WebSocket vncSocket = (WebSocket) session.getAttributes().get("deviceWebsocket");
if (vncSocket != null) {
vncSocket.sendBinary(message.getPayload(), true);
}
} catch (Exception e) {
log.error(e, "[handleBinaryMessage] [error]");
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
log.info("[afterConnectionClosed] [WebSocket connection closed] [websocket uri: {}]", session.getUri());
WebSocket deviceWebsocket = (WebSocket) session.getAttributes().get("deviceWebsocket");
if (deviceWebsocket != null) {
deviceWebsocket.sendClose(WebSocket.NORMAL_CLOSURE, "Normal closure");
}
super.afterConnectionClosed(session, status);
}
}

View File

@@ -0,0 +1,23 @@
package net.geedge.asw.common.config.websocket;
import net.geedge.asw.module.device.service.IDeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
private IDeviceService deviceService;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new DeviceWebSocketHandler(deviceService), "/api/v1/device/{id}/novnc")
.addInterceptors(new WebSocketInterceptor())
.setAllowedOrigins("*");
}
}

View File

@@ -0,0 +1,36 @@
package net.geedge.asw.common.config.websocket;
import jakarta.servlet.http.HttpSession;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import org.springframework.web.util.UriTemplate;
import java.util.Map;
@Component
public class WebSocketInterceptor extends HttpSessionHandshakeInterceptor {
@Override
public synchronized boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
String servletPath = servletRequest.getServletRequest().getServletPath();
UriTemplate template = new UriTemplate("/api/v1/device/{id}/novnc");
Map<String, String> variables = template.match(servletPath);
attributes.put("id", variables.get("id"));
attributes.put("token", servletRequest.getServletRequest().getParameter("token"));
}
return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
super.afterHandshake(request, response, wsHandler, exception);
}
}

View File

@@ -69,4 +69,8 @@ public class Constants {
.put("Access-Control-Max-Age", "18000").put("Access-Control-Allow-Origin", "*").build();
/**
* device api websocket path
*/
public static final String DEVICE_API_WEBSOCKET_PATH = "/api/v1/device/websocket";
}