обновляю сетевые хэндлеры

This commit is contained in:
AidarKC 2025-12-04 13:34:04 +03:00
parent 6276f3868b
commit 5d8dd86c96
7 changed files with 240 additions and 184 deletions

View File

@ -5,7 +5,7 @@ import server.logic.ws_protocol.JSON.entyties.Auth.NetAuthSessionNewStep1Request
import server.logic.ws_protocol.JSON.entyties.Auth.NetSessionRefreshRequest;
import server.logic.ws_protocol.JSON.handlers.*;
import server.logic.ws_protocol.JSON.entyties.tempToTest.NetAddUserRequest;
import server.logic.ws_protocol.JSON.handlers.auth.NetAddUserHandler;
import server.logic.ws_protocol.JSON.handlers.tempToTest.NetAddUserHandler;
import server.logic.ws_protocol.JSON.handlers.auth.NetAuthSessionNewStep1Handler;
import server.logic.ws_protocol.JSON.handlers.auth.NetSessionRefreshHandler;

View File

@ -55,10 +55,10 @@ public final class JsonInboundProcessor {
"EMPTY_JSON", "Пустое JSON-сообщение");
}
// 1. Парсим общий пакет как дерево
// 1. Парсим общий пакет
JsonNode root = JSON_MAPPER.readTree(json);
// 2. Берём op и requestId
// 2. op и requestId
String op = getTextOrNull(root, "op");
if (op == null || op.isEmpty()) {
return buildErrorJson(null, null, WireCodes.Status.BAD_REQUEST,
@ -75,21 +75,27 @@ public final class JsonInboundProcessor {
"UNKNOWN_OP", "Неизвестная операция: " + op);
}
// 3. Маппим весь JSON в конкретный класс запроса
// 3. Маппим JSON нужный NetRequest
NetRequest request = JSON_MAPPER.treeToValue(root, reqClass);
// 4. Вызываем хэндлер, передавая контекст
NetResponse response = handler.handle(request, ctx);
NetResponse response;
// На всякий случай: если хэндлер не выставил op/requestId
if (response.getOp() == null) {
response.setOp(op);
}
if (response.getRequestId() == null) {
response.setRequestId(requestId);
// 4. Трай-кэтч вокруг хэндлера (важно!)
try {
response = handler.handle(request, ctx);
} catch (Exception handlerError) {
log.error("💥 Ошибка внутри хэндлера '{}'", op, handlerError);
return buildErrorJson(op, requestId,
WireCodes.Status.INTERNAL_ERROR,
"INTERNAL_HANDLER_ERROR",
"Неожиданная ошибка при обработке операции: " + op);
}
// 5. Собираем JSON-ответ
// Если хэндлер не выставил op/requestId
if (response.getOp() == null) response.setOp(op);
if (response.getRequestId() == null) response.setRequestId(requestId);
// 5. Формируем JSON
ObjectNode out = JSON_MAPPER.createObjectNode();
out.put("op", response.getOp());
out.put("requestId", response.getRequestId());
@ -118,16 +124,7 @@ public final class JsonInboundProcessor {
}
/**
* Генерация JSON-ошибки в формате ответа:
* {
* "op": op,
* "requestId": requestId,
* "status": status,
* "payload": {
* "code": errorCode,
* "message": errorMessage
* }
* }
* Генерация JSON-ошибки
*/
private static String buildErrorJson(String op,
String requestId,

View File

@ -5,6 +5,7 @@ import server.logic.ws_protocol.JSON.entyties.*;
import server.logic.ws_protocol.JSON.entyties.Auth.NetAuthSessionNewStep1Request;
import server.logic.ws_protocol.JSON.entyties.Auth.NetAuthSessionNewStep1Response;
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
import server.logic.ws_protocol.WireCodes;
import shine.db.dao.SolanaUsersDAO;
import shine.db.entities.SolanaUser;
@ -23,15 +24,22 @@ public class NetAuthSessionNewStep1Handler implements JsonMessageHandler {
String login = req.getLogin();
if (login == null || login.isBlank()) {
return error(req, WireCodes.Status.BAD_REQUEST,
"EMPTY_LOGIN", "Пустой логин");
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"EMPTY_LOGIN",
"Пустой логин"
);
}
// 1) Проверка: в контексте никто не авторизован
if (ctx.getLogin() != null) {
return error(req, WireCodes.Status.BAD_REQUEST,
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"ALREADY_AUTHED",
"Попытка повторной авторификации для уже заданного login=" + ctx.getLogin());
"Попытка повторной авторификации для уже заданного login=" + ctx.getLogin()
);
}
// 2) Ищем пользователя в локальной БД
@ -39,8 +47,12 @@ public class NetAuthSessionNewStep1Handler implements JsonMessageHandler {
if (solanaUser == null) {
// TODO позже запрос в Solana, если не нашли локально
return error(req, WireCodes.Status.UNVERIFIED,
"UNKNOWN_USER", "Пользователь с таким логином не найден");
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.UNVERIFIED,
"UNKNOWN_USER",
"Пользователь с таким логином не найден"
);
}
// 3) Заполняем контекст полями пользователя
@ -67,13 +79,4 @@ public class NetAuthSessionNewStep1Handler implements JsonMessageHandler {
return resp;
}
private NetExceptionResponse error(NetRequest req, int status, String code, String msg) {
NetExceptionResponse resp = new NetExceptionResponse();
resp.setOp(req.getOp());
resp.setRequestId(req.getRequestId());
resp.setStatus(status);
resp.setPayload(Map.of("code", code, "message", msg));
return resp;
}
}

View File

@ -1,18 +1,17 @@
package server.logic.ws_protocol.JSON.handlers.auth;
import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.NetExceptionResponse;
import server.logic.ws_protocol.JSON.entyties.NetRequest;
import server.logic.ws_protocol.JSON.entyties.NetResponse;
import server.logic.ws_protocol.JSON.entyties.Auth.NetSessionRefreshRequest;
import server.logic.ws_protocol.JSON.entyties.Auth.NetSessionRefreshResponse;
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
import server.logic.ws_protocol.WireCodes;
import shine.db.dao.ActiveSessionsDAO;
import shine.db.entities.ActiveSession;
import java.sql.SQLException;
import java.util.Map;
/**
* Хэндлер SessionRefresh.
@ -35,8 +34,12 @@ public class NetSessionRefreshHandler implements JsonMessageHandler {
String sessionPwd = req.getSessionPwd();
if (sessionPwd == null || sessionPwd.isEmpty()) {
return buildError(req, WireCodes.Status.BAD_REQUEST,
"BAD_SESSION_PWD", "Пустой пароль сессии");
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"BAD_SESSION_PWD",
"Пустой пароль сессии"
);
}
ActiveSessionsDAO dao = ActiveSessionsDAO.getInstance();
@ -45,19 +48,31 @@ public class NetSessionRefreshHandler implements JsonMessageHandler {
session = dao.getBySessionId(sessionId);
} catch (SQLException e) {
// Ошибка БД внутренняя ошибка сервера
return buildError(req, WireCodes.Status.SERVER_DATA_ERROR,
"DB_ERROR", "Ошибка доступа к базе данных");
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.SERVER_DATA_ERROR,
"DB_ERROR",
"Ошибка доступа к базе данных"
);
}
if (session == null) {
return buildError(req, WireCodes.Status.UNVERIFIED,
"SESSION_NOT_FOUND", "Сессия не найдена");
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.UNVERIFIED,
"SESSION_NOT_FOUND",
"Сессия не найдена"
);
}
String dbPwd = session.getSessionPwd();
if (dbPwd == null || !dbPwd.equals(sessionPwd)) {
return buildError(req, WireCodes.Status.UNVERIFIED,
"SESSION_PWD_MISMATCH", "Неверный пароль сессии");
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.UNVERIFIED,
"SESSION_PWD_MISMATCH",
"Неверный пароль сессии"
);
}
// Всё хорошо обновляем контекст соединения
@ -76,19 +91,4 @@ public class NetSessionRefreshHandler implements JsonMessageHandler {
resp.setPayload(null); // или Map.of("ok", true)
return resp;
}
private NetExceptionResponse buildError(NetRequest req,
int status,
String code,
String message) {
NetExceptionResponse resp = new NetExceptionResponse();
resp.setOp(req.getOp());
resp.setRequestId(req.getRequestId());
resp.setStatus(status);
resp.setPayload(Map.of(
"code", code,
"message", message
));
return resp;
}
}

View File

@ -1,20 +1,19 @@
package server.logic.ws_protocol.JSON.handlers.auth;
package server.logic.ws_protocol.JSON.handlers.tempToTest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.tempToTest.NetAddUserRequest;
import server.logic.ws_protocol.JSON.entyties.tempToTest.NetAddUserResponse;
import server.logic.ws_protocol.JSON.entyties.NetExceptionResponse;
import server.logic.ws_protocol.JSON.entyties.NetRequest;
import server.logic.ws_protocol.JSON.entyties.NetResponse;
import server.logic.ws_protocol.JSON.entyties.tempToTest.NetAddUserRequest;
import server.logic.ws_protocol.JSON.entyties.tempToTest.NetAddUserResponse;
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
import server.logic.ws_protocol.WireCodes;
import shine.db.dao.SolanaUsersDAO;
import shine.db.entities.SolanaUser;
import java.sql.SQLException;
import java.util.Map;
/**
* Временный Хэндлер AddUser. Используется для тестовой регистрации!!!!!!!!
@ -33,19 +32,18 @@ public class NetAddUserHandler implements JsonMessageHandler {
public NetResponse handle(NetRequest baseRequest, ConnectionContext ctx) throws Exception {
NetAddUserRequest req = (NetAddUserRequest) baseRequest;
// Минимальная валидация входных данных
if (req.getLogin() == null || req.getLogin().isBlank()) {
return buildError(req, WireCodes.Status.BAD_REQUEST,
"BAD_LOGIN", "Пустой логин");
}
if (req.getPubkey0() == null || req.getPubkey0().isBlank()
|| req.getPubkey1() == null || req.getPubkey1().isBlank()) {
return buildError(req, WireCodes.Status.BAD_REQUEST,
"BAD_PUBKEY", "Публичные ключи не указаны");
}
if (req.getBchLimit() == null) {
return buildError(req, WireCodes.Status.BAD_REQUEST,
"BAD_BCH_LIMIT", "Не указан лимит блокчейна");
// Одна общая проверка всех ключевых полей
if (req.getLogin() == null || req.getLogin().isBlank()
|| req.getPubkey0() == null || req.getPubkey0().isBlank()
|| req.getPubkey1() == null || req.getPubkey1().isBlank()
|| req.getBchLimit() == null) {
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"BAD_FIELDS",
"Некорректные или пустые поля: login, pubkey0, pubkey1, bchLimit"
);
}
try {
@ -72,27 +70,20 @@ public class NetAddUserHandler implements JsonMessageHandler {
} catch (SQLException e) {
log.error("❌ Ошибка при вставке пользователя в БД", e);
return buildError(req, WireCodes.Status.SERVER_DATA_ERROR,
"DB_ERROR", "Ошибка доступа к базе данных");
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.SERVER_DATA_ERROR,
"DB_ERROR",
"Ошибка доступа к базе данных"
);
} catch (Exception e) {
log.error("❌ Неожиданная ошибка в AddUser", e);
return buildError(req, WireCodes.Status.INTERNAL_ERROR,
"INTERNAL_ERROR", "Внутренняя ошибка сервера");
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.INTERNAL_ERROR,
"INTERNAL_ERROR",
"Внутренняя ошибка сервера"
);
}
}
private NetExceptionResponse buildError(NetRequest req,
int status,
String code,
String message) {
NetExceptionResponse resp = new NetExceptionResponse();
resp.setOp(req.getOp());
resp.setRequestId(req.getRequestId());
resp.setStatus(status);
resp.setPayload(Map.of(
"code", code,
"message", message
));
return resp;
}
}

View File

@ -0,0 +1,36 @@
package server.logic.ws_protocol.JSON.utils;
import server.logic.ws_protocol.JSON.entyties.NetExceptionResponse;
import server.logic.ws_protocol.JSON.entyties.NetRequest;
import java.util.Map;
/**
* Фабрика ошибок для JSON-протокола.
* Создаёт единообразные NetExceptionResponse.
*/
public final class NetExceptionResponseFactory {
private NetExceptionResponseFactory() {
// запрет на создание объектов
}
public static NetExceptionResponse error(NetRequest req,
int status,
String code,
String message) {
NetExceptionResponse resp = new NetExceptionResponse();
resp.setOp(req.getOp());
resp.setRequestId(req.getRequestId());
resp.setStatus(status);
resp.setPayload(Map.of(
"code", code,
"message", message
));
return resp;
}
}

View File

@ -2,105 +2,134 @@ import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.net.http.WebSocket.Listener;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CountDownLatch;
public class TestJsonWsClient {
// Адрес сервера
private static final String WS_URI = "ws://localhost:7070/ws";
// Отдельные запросы
private static final String JSON_REQUEST_SESSION_REFRESH = """
{
"op": "SessionRefresh",
"requestId": "test-1",
"sessionId": 123,
"sessionPwd": "test-password"
}
""";
private static final String JSON_REQUEST_ADD_USER = """
{
"op": "AddUser",
"requestId": "test-add-1",
"login": "anya1111",
"loginId": 100211,
"bchId": 4222,
"pubkey0": "PUB0",
"pubkey1": "PUB1",
"bchLimit": 1000000
}
""";
private static final String JSON_REQUEST_AUTH_SESSION_NEW_STEP1 = """
{
"op": "AuthSessionNewStep1",
"requestId": "test-auth-1",
"login": "anya1111"
}
""";
// МАССИВ КОНСТАНТА с запросами добавляешь сюда любые свои JSON
private static final String[] JSON_REQUESTS = {
JSON_REQUEST_SESSION_REFRESH,
JSON_REQUEST_ADD_USER,
JSON_REQUEST_AUTH_SESSION_NEW_STEP1
};
public static void main(String[] args) throws Exception {
String uri = "ws://localhost:7070/ws";
String jsonRequestSessionRefresh = """
{
"op": "SessionRefresh",
"requestId": "test-1",
"sessionId": 123,
"sessionPwd": "test-password"
}
""";
String jsonRequestAddUser = """
{
"op": "AddUser",
"requestId": "test-add-1",
"login": "anya1111",
"loginId": 100211,
"bchId": 4222,
"pubkey0": "PUB0",
"pubkey1": "PUB1",
"bchLimit": 1000000
}
""";
String jsonRequestAuthSessionNewStep1 = """
{
"op": "AuthSessionNewStep1",
"requestId": "test-auth-1",
"login": "anya1111"
}
""";
// Тестовый JSON-пакет SessionRefresh
String jsonRequest = jsonRequestAuthSessionNewStep1;
System.out.println("Подключаемся к " + uri);
System.out.println("Подключаемся к " + WS_URI);
CountDownLatch latch = new CountDownLatch(1);
HttpClient client = HttpClient.newHttpClient();
WebSocket webSocket = client.newWebSocketBuilder()
.buildAsync(URI.create(uri), new Listener() {
@Override
public void onOpen(WebSocket webSocket) {
System.out.println("✅ WebSocket подключен");
ClientListener listener = new ClientListener(JSON_REQUESTS, latch);
// Отправляем JSON сразу после подключения
System.out.println("📤 Отправляем JSON-запрос:");
System.out.println(jsonRequest);
client.newWebSocketBuilder()
.buildAsync(URI.create(WS_URI), listener)
.join();
webSocket.sendText(jsonRequest, true);
Listener.super.onOpen(webSocket);
}
@Override
public CompletionStage<?> onText(WebSocket webSocket,
CharSequence data,
boolean last) {
String message = data.toString();
System.out.println("📥 Получен TEXT-ответ от сервера:");
System.out.println(message);
// После получения первого ответа закрываем соединение
webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "test done");
latch.countDown();
return Listener.super.onText(webSocket, data, last);
}
@Override
public void onError(WebSocket webSocket, Throwable error) {
System.out.println("❌ Ошибка WebSocket-клиента: " + error.getMessage());
error.printStackTrace(System.out);
latch.countDown();
}
@Override
public CompletionStage<?> onClose(WebSocket webSocket,
int statusCode,
String reason) {
System.out.println("🔚 Соединение закрыто. Код=" + statusCode + ", причина=" + reason);
latch.countDown();
return Listener.super.onClose(webSocket, statusCode, reason);
}
}).join();
// Ждём, пока получим ответ/ошибку/закрытие
// Ждём, пока всё не завершится (успех/ошибка/закрытие)
latch.await();
System.out.println("Тест завершён, выходим.");
}
// Внутренний Listener, который сам по очереди шлёт запросы и печатает ответы
private static class ClientListener implements Listener {
private final String[] requests;
private final CountDownLatch latch;
private int index = 0; // какой запрос сейчас отправляем/ждём ответ
ClientListener(String[] requests, CountDownLatch latch) {
this.requests = requests;
this.latch = latch;
}
@Override
public void onOpen(WebSocket webSocket) {
System.out.println("✅ WebSocket подключен");
sendNextRequest(webSocket);
Listener.super.onOpen(webSocket);
}
// Отправка следующего запроса из массива
private void sendNextRequest(WebSocket webSocket) {
if (index < requests.length) {
String json = requests[index];
System.out.println();
System.out.println("📤 Отправляем запрос " + (index + 1) + " из " + requests.length + ":");
System.out.println(json);
webSocket.sendText(json, true);
} else {
System.out.println("Все запросы отправлены, закрываем соединение");
webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "all tests done");
}
}
@Override
public CompletionStage<?> onText(WebSocket webSocket,
CharSequence data,
boolean last) {
// Ответ на текущий запрос (с индексом index)
System.out.println("📥 Ответ на запрос " + (index + 1) + ":");
System.out.println(data.toString());
System.out.println("-----------------------------------------------------");
// Переходим к следующему запросу
index++;
sendNextRequest(webSocket);
return CompletableFuture.completedFuture(null);
}
@Override
public void onError(WebSocket webSocket, Throwable error) {
System.out.println("❌ Ошибка WebSocket-клиента: " + error.getMessage());
error.printStackTrace(System.out);
latch.countDown();
}
@Override
public CompletionStage<?> onClose(WebSocket webSocket,
int statusCode,
String reason) {
System.out.println("🔚 Соединение закрыто. Код=" + statusCode + ", причина=" + reason);
latch.countDown();
return CompletableFuture.completedFuture(null);
}
}
}