/
dixsu
/
websocket-client
Обзор
Документация
Войти
/
dixsu
/
websocket-client
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
master
src/main/java/BusinessLogicLayer.java
341 строка
17 KB
Сахно Роман Александрович
исправлена отправка заголовков отдельно только для бинарных. и целиком для просто текста без сжатия в onMessage
20 фев 2025, 07:25
20 фев 2025, 07:25
a314a55
Код
Авторство
О чём код?
//import com.google.gson.JsonObject; //import netscape.javascript.JSObject; import org.json.JSONObject; import javax.websocket.Session; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class BusinessLogicLayer { private HttpResponse httpResponse; private HttpRequest httpRequest=new HttpRequest(); // Хранилище потоков для каждого requestId private final Map<String, WebSocketInputStream> inputStreamMap = new ConcurrentHashMap<>(); //с прокси сервера все запросы приходят сюда public void onOpen(Session session) { UtilsLayer.logClient("Connected to server. SessionID ["+session.getId().toString()+"]"); } //с прокси сервера все запросы приходят сюда public void onClose(Session session) { UtilsLayer.logClient("Not Connected to server. SessionID ["+session.getId().toString()+"]"); } //получаем все поля заголовка только без первой строки с методом( GET/POST ) и url который мы исправим при вызове public static String parseHeadersToJson(String input) { JSONObject json = new JSONObject(); String[] lines = input.split("\\r?\\n"); for (int i = 1; i < lines.length; i++) { // Пропускаем первую строку String line = lines[i].trim(); if (line.isEmpty()) continue; // UtilsLayer.logClient(i+" line = "+line,true); int colonIndex = line.indexOf(':'); if (colonIndex == -1) continue; // Пропускаем строки без двоеточия String key = line.substring(0, colonIndex).trim(); String value = "";// line.substring(colonIndex + 1).trim(); if (key.toLowerCase().startsWith("host")) { value = "localhost:"+WebSocketLayer.LOCALWEBPORT; } else { value = line.substring(colonIndex + 1).trim(); } json.put(key, value); } json.remove("X-Request-Id"); // Удаляем чтобы не дублировалось StringBuilder headers = new StringBuilder(); for (String key : json.keySet()) { headers.append(key) .append(": ") .append(json.getString(key)) .append("\r\n"); } String updateHeaders = headers.toString(); return updateHeaders; } // // // Метод для обработки входящих бинарных сообщений // public void onBinaryMessage(byte[] data, Session session) { // // Извлекаем requestId из первых N байт (например, первые 36 байт для UUID) // String requestId = new String(data, 0, 36, StandardCharsets.UTF_8); // byte[] chunkData = Arrays.copyOfRange(data, 36, data.length); // Остальные байты — это данные // // // Получаем поток для этого requestId // WebSocketInputStream inputStream = inputStreamMap.get(requestId); // if (inputStream != null) { // inputStream.addData(chunkData); // Добавляем данные в поток // } // } // // // Метод для завершения потока // public void finishStream(String requestId) { // WebSocketInputStream inputStream = inputStreamMap.get(requestId); // if (inputStream != null) { // inputStream.finish(); // Сигнализируем о завершении потока // inputStreamMap.remove(requestId); // Удаляем поток из хранилища // } // } // // // Метод для получения InputStream по requestId // public InputStream getInputStream(String requestId) { // WebSocketInputStream inputStream = new WebSocketInputStream(); // inputStreamMap.put(requestId, inputStream); // return inputStream; // } // Событие при получении сообщения с прокси-сервера по сокету public void onMessage(String message, Session session) throws IOException { UtilsLayer.logClient("Received message: " + message, true); // Идём внутрь условия, если это отправляемый с прокси request if (message.startsWith("GET") || message.startsWith("POST")) { // Извлекаем requestId из заголовков String requestId = HttpRequest.extractRequestId(message); UtilsLayer.logClient("requestId = " + requestId, true); // Извлекаем метод (GET или POST) String method = message.split(" ")[0]; UtilsLayer.logClient("method = " + method, true); // Извлекаем путь и query parameters String resourcePath = message.split(" ")[1]; if (resourcePath.isEmpty()) { resourcePath = "/"; // Путь по умолчанию } UtilsLayer.logClient("resourcePath = " + resourcePath, true); // Формируем URL для локального сервера String localServerUrl = "http://localhost:" + WebSocketLayer.LOCALWEBPORT + resourcePath; UtilsLayer.logClient("localServerUrl = " + localServerUrl, true); // Обеспечиваем непрерывную сохранность значений в заголовках, в том числе и кук String updateHeaders = parseHeadersToJson(message); String requestWithCookies = method + " " + resourcePath + " HTTP/1.1\r\n" + updateHeaders + "\r\n"; // Пустая строка в конце заголовков UtilsLayer.logClient("requestWithCookies:" + requestWithCookies, true); // Создаем InputStream для потоковой передачи данных WebSocketInputStream inputStream = new WebSocketInputStream(); inputStreamMap.put(requestId, inputStream); HttpResponse httpResponse; // Получаем данные с локального сервера через потоки если это POST может быть и большой if (message.startsWith("POST")) { httpResponse = fetchResourceFromLocalServer(localServerUrl, method, requestWithCookies, requestId, inputStream); } //либо если GET никаких потоков там быть не может в запросе else httpResponse = fetchResourceFromLocalServer(localServerUrl, method, requestWithCookies,requestId); if (httpResponse != null) { try { StringBuilder responseBuilder = new StringBuilder(); responseBuilder.append("HTTP/1.1 ").append(httpResponse.getStatusCode()).append(" OK\r\n"); // Добавляем заголовки for (Map.Entry<String, String> header : httpResponse.getHeaders().entrySet()) { responseBuilder.append(header.getKey()).append(": ").append(header.getValue()).append("\r\n"); } responseBuilder.append("\r\n"); //если данные не сжаты и являются типом текстового контента if (httpResponse.isTextResponse(httpResponse.getHeaders().get("Content-Type")) && !httpResponse.isGzipped(httpResponse.getHeaders())) { // Текстовые данные String body = (String) httpResponse.getBody(); responseBuilder.append(body); httpResponse.sendTextMessage(session, requestId, responseBuilder.toString()); } // Если это бинарные данные, передаем их потоково else { //отправляем заголовки HttpResponse.sendTextMessage(session, requestId, responseBuilder.toString()); try (InputStream bodyStream = httpResponse.getBodyAsStream()) { byte[] buffer = new byte[UtilsLayer.BUFFER_SIZE]; int bytesRead; while ((bytesRead = bodyStream.read(buffer)) != -1) { httpResponse.sendBinaryMessage(session, requestId, Arrays.copyOf(buffer, bytesRead)); } } } } catch (Exception e) { e.printStackTrace(); } finally { // Завершаем поток и удаляем его из хранилища inputStream.finish(); inputStreamMap.remove(requestId); } } } } // для POST запросов protected HttpResponse fetchResourceFromLocalServer(String urlString, String method, String requestWithCookies, String requestId, InputStream inputStream) { try { URL url = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod(method); // Устанавливаем заголовки String[] headers = requestWithCookies.split("\r\n"); for (String header : headers) { if (header.contains(":")) { String[] keyValue = header.split(":", 2); connection.setRequestProperty(keyValue[0].trim(), keyValue[1].trim()); } } if ("POST".equalsIgnoreCase(method)) { connection.setDoOutput(true); // Разрешаем отправку тела connection.setChunkedStreamingMode(UtilsLayer.BUFFER_SIZE); // Включаем потоковую передачу // Потоковая передача данных try (OutputStream os = connection.getOutputStream()) { byte[] buffer = new byte[UtilsLayer.BUFFER_SIZE]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } } } // Получаем статус-код int statusCode = connection.getResponseCode(); // Получаем заголовки ответа Map<String, String> responseHeaders = new HashMap<>(); for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) { if (entry.getKey() != null) { responseHeaders.put(entry.getKey(), String.join("; ", entry.getValue())); } } // Обрабатываем ответ if (HttpResponse.isTextResponse(responseHeaders.get("Content-Type")) && !HttpResponse.isGzipped(responseHeaders)) { // Текстовые данные StringBuilder bodyBuilder = new StringBuilder(); try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { String inputLine; while ((inputLine = in.readLine()) != null) { bodyBuilder.append(inputLine).append("\n"); } } return new HttpResponse(statusCode, responseHeaders, bodyBuilder.toString()); } else { // Бинарные данные ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try (InputStream inputStreamResponse = connection.getInputStream()) { byte[] buffer = new byte[UtilsLayer.BUFFER_SIZE]; int bytesRead; while ((bytesRead = inputStreamResponse.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } } return new HttpResponse(statusCode, responseHeaders, outputStream.toByteArray()); } } catch (Exception e) { e.printStackTrace(); return null; } } //Для GET запросов protected HttpResponse fetchResourceFromLocalServer(String urlString, String method, String requestWithCookies, String requestId) { try { URL url = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod(method); // // Устанавливаем в connection заголовки(от прокси) полученные с параметрами при вызове этого метода String[] headers = requestWithCookies.split("\r\n"); for (String header : headers) { if (header.contains(":")) { String[] keyValue = header.split(":", 2); connection.setRequestProperty(keyValue[0].trim(), keyValue[1].trim()); } } if ("POST".equalsIgnoreCase(method)) { connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.setDoOutput(true); // Разрешаем отправку тела } // Если это POST-запрос, отправляем тело if ("POST".equalsIgnoreCase(method) && requestWithCookies.contains("\r\n\r\n")) { UtilsLayer.logClient("requestWithCookies:"+requestWithCookies); String body = requestWithCookies.split("\r\n\r\n")[1]; try (OutputStream os = connection.getOutputStream()) { byte[] input = body.getBytes(StandardCharsets.UTF_8); os.write(input, 0, input.length); } } // Получаем статус-код int statusCode = connection.getResponseCode(); // Получаем заголовки ответа Map<String, String> responseHeaders = new HashMap<>(); for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) { if (entry.getKey() != null) { responseHeaders.put(entry.getKey(), String.join("; ", entry.getValue())); if (HttpRequest.isHtmlPure(urlString)) UtilsLayer.logClient("responseHeaders=[" + entry.getKey() + "]:{" + entry.getValue() + "}", true); } } String contentType = responseHeaders.get("Content-Type"); // Обрабатываем ответ в зависимости от типа контента if (contentType != null && (HttpResponse.isTextResponse(contentType)) && !HttpResponse.isGzipped(responseHeaders)) { // if (contentType != null && (contentType.startsWith("text/") || contentType.contains("javascript") || contentType.contains("css"))) { // Текстовые данные (HTML, CSS, JS) StringBuilder bodyBuilder = new StringBuilder(); try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { String inputLine; while ((inputLine = in.readLine()) != null) { bodyBuilder.append(inputLine).append("\n"); } } return new HttpResponse(statusCode, responseHeaders, bodyBuilder.toString()); } else { // Бинарные данные (изображения, шрифты и т.д.) ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try (InputStream inputStream = connection.getInputStream()) { byte[] buffer = new byte[UtilsLayer.BUFFER_SIZE]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } } return new HttpResponse(statusCode, responseHeaders, outputStream.toByteArray()); } } catch (Exception e) { e.printStackTrace(); return null; } } }