/
dixsu
/
websocket-client
Обзор
Документация
Войти
/
dixsu
/
websocket-client
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
master
src/main/java/HttpResponse.java
297 строк
14 KB
Сахно Роман Александрович
начало работы с POST как с потоками
19 фев 2025, 20:34
19 фев 2025, 20:34
1b600f0
Код
Авторство
О чём код?
import com.google.gson.JsonObject; import org.glassfish.grizzly.http.HttpHeader; import org.jetbrains.annotations.NotNull; import javax.websocket.Session; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Base64; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; //public class ResponseDataService { // private final HashMap<String,String> responseHeaders = new HashMap<>(); // // public void addRespHeaders(String cookie){ // this.responseHeaders.put("Set-Cookie",cookie); // } // Вспомогательный класс для хранения HTTP-ответа public class HttpResponse { private final int statusCode; private final Map<String, String> headers; private final Object body; // Может быть String или byte[] private InputStream bodyStream; // Поток для чтения тела ответа // public HttpResponse(int statusCode, Map<String, String> headers, Object body) { // this.statusCode = statusCode; // this.headers = headers; // this.body = body; // } public HttpResponse(int statusCode, Map<String, String> headers, String body) { this.statusCode = statusCode; this.headers = headers; this.body = body; } public HttpResponse(int statusCode, Map<String, String> headers, byte[] body) { this.statusCode = statusCode; this.headers = headers; this.body = body; } // public HttpResponse(int statusCode, Map<String, String> headers, Object body, InputStream bodyStream) { // this.statusCode = statusCode; // this.headers = headers; // this.body = body; // this.bodyStream = bodyStream; // } public InputStream getBodyAsStream() { if (bodyStream != null) { return bodyStream; } else if (body instanceof byte[]) { return new ByteArrayInputStream((byte[]) body); } else if (body instanceof String) { return new ByteArrayInputStream(((String) body).getBytes(StandardCharsets.UTF_8)); } return null; } protected static boolean isTextResponse(String contentType) { // String contentType = this.headers.get("Content-Type"); // Проверяем, текстовые ли это данные boolean isTextResponse = contentType != null && contentType.startsWith("text/") || contentType != null && contentType.contains("javascript") || contentType != null && contentType.contains("json") || contentType != null && contentType.contains("xml"); UtilsLayer.logClient("Контент тип ресурса определены как Текстовые данные? = ["+isTextResponse+"]", true); return isTextResponse; } protected static boolean isGzipped(Map<String, String> responseHeaders) { // Проверяем, сжат ли контент boolean isGzipped = responseHeaders.containsKey("Content-Encoding") && "gzip".equalsIgnoreCase(responseHeaders.get("Content-Encoding")); UtilsLayer.logClient("Контент ресурса сжат? = ["+isGzipped+"]", true); return isGzipped; } //здесь и заголовки и данные едины для текстовых сообщений. для бинарных этим методом мы отправляем только заголовки. связать их можно будет только по requestId public static void sendTextMessage(Session session, String requestId, String message) { try { // logClient("Отправка текстового сообщения для запроса [" + requestId + "]"); int length = message.length(); int offset = 0; // Извлекаем заголовки для логирования String headers = ""; int index = message.indexOf("\r\n\r\n"); if (index != -1) { headers = message.substring(0, index); } else { UtilsLayer.logClient("заголовки на отправку на прокси!!! неверный формат",true); } //полное сырое сообщение(с заголовками) отправляем частями по сокету в json формате while (offset < length) { int chunkSize = Math.min(UtilsLayer.BUFFER_SIZE, length - offset); String chunk = message.substring(offset, offset + chunkSize); boolean isLast = (offset + chunkSize >= length); // Формируем JSON-объект для отправки JsonObject json = new JsonObject(); json.addProperty("requestId", requestId); json.addProperty("data", chunk); json.addProperty("isLast", isLast); // Преобразуем объект в строку JSON String jsonChunk = json.toString(); // Отправляем часть сообщения/ сейчас отправляется вместе с заголовком нужно сделать отдельно чтобы заголовки отправлялись и для текстовых и для данных и сохранялись в мапе на сервере по requestId session.getBasicRemote().sendText(jsonChunk); // UtilsLayer.logClient("Отправка части для запроса [" + requestId + "]: " + chunkSize + " символов",true); offset += chunkSize; } UtilsLayer.logClient("Текстовое сообщение для запроса [" + requestId + "] отправлено полностью",true); // UtilsLayer.logClient(" с такими заголовками:"+headers,true); } catch (Exception e) { UtilsLayer.logClient("Ошибка при отправке текстового сообщения для запроса [" + requestId + "]: " + e.getMessage()); e.printStackTrace(); } } private static String calculateHash(byte[] data) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(data); return Base64.getEncoder().encodeToString(hash); } catch (Exception e) { return "Ошибка вычисления хэша"; } } //Добавьте requestId в начало данных. первые байты содержат requestId (UUID), а остальные — данные. //т.е. в каждой порции ответа содержится requestId, чтобы на сервере собрать всё до кучи правильно. public static void sendBinaryMessage(Session session, String requestId, byte[] data) { try { // Вычисление хэша отправляемых данных // String dataHash = calculateHash(data); // // logClient("Получены данные для запроса [" + requestId + "]: " + Base64.getEncoder().encodeToString(data)); // UtilsLayer.logClient("Хэш данных для бинарного запроса [" + requestId + "]: " + dataHash,true); // int length = data.length; int offset = 0; while (offset < length) { int chunkSize = Math.min(UtilsLayer.BUFFER_SIZE, length - offset); byte[] chunkData = new byte[chunkSize]; System.arraycopy(data, offset, chunkData, 0, chunkSize); boolean isLast = (offset + chunkSize >= length); // Вычисляем размер буфера (4 байта для длины requestId, длина самого requestId, плюс данные) int bufferSize = Integer.BYTES + requestId.length() + chunkSize; ByteBuffer chunk = ByteBuffer.allocate(bufferSize); // Формируем полный пакет с метаданными //ByteBuffer chunk = ByteBuffer.allocate(requestId.length() + chunkSize); // requestId length + данные chunk.putInt(requestId.length()); // Длина requestId chunk.put(requestId.getBytes(StandardCharsets.UTF_8)); // Сам requestId chunk.put(chunkData); // Данные chunk.flip(); // Отправляем текущий фрагмент session.getBasicRemote().sendBinary(chunk, isLast); // UtilsLayer.logClient("Отправка части для запроса [" + requestId + "]: " + chunkSize + " байт",true); offset += chunkSize; } UtilsLayer.logClient("Бинарное сообщение для запроса [" + requestId + "] отправлено полностью",true); } catch (Exception e) { e.printStackTrace(); } } public int getStatusCode() { return statusCode; } public Map<String, String> getHeaders() { return headers; } public Object getBody() { return body; } /** * Модифицирует пути в HTML, CSS и JS контенте, добавляя префикс "/p/{deviceid}/" к ссылкам. */ protected String modifyHtmlPaths(String content, String contentType) { // Определяем, какой тип контента обрабатываем boolean isHtml = contentType.startsWith("text/html"); boolean isCss = contentType.startsWith("text/css"); boolean isJs = contentType.contains("javascript"); // Регулярные выражения для поиска путей // String htmlRegex = "(href|src|background-image)\\s*=\\s*[\"']([^\"']+)[\"']"; String htmlRegex = "(href|src|background-image|action)\\s*=\\s*[\"']([^\"']+)[\"']"; String cssRegex = "url\\(\\s*['\"]?([^'\")]+)['\"]?\\s*\\)"; String jsRegex = "['\"](/[^'\"]+)['\"]"; String jsVarRegex = "(const|let|var)\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*=\\s*[\"'](/[^\"']+)[\"'];"; // Если тип контента не поддерживается, возвращаем контент без изменений if (!isHtml && !isCss && !isJs) { UtilsLayer.logClient("неизвестный тип контента"); return content; } // Если это HTML, обрабатываем его как HTML, CSS и JS if (isHtml) { content = applyRegex(content, htmlRegex, "html"); content = applyRegex(content, cssRegex, "css"); content = applyRegex(content, jsVarRegex, "js-var"); // content = applyRegex(content, jsRegex, "js"); } else if (isCss) { // Если это CSS, обрабатываем только CSS content = applyRegex(content, cssRegex, "css"); } else if (isJs) { // Если это JS, обрабатываем только JS content = applyRegex(content, jsRegex, "js"); } return content; } private String applyRegex(String content, String regex, String type) { Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(content); StringBuffer modifiedContent = new StringBuffer(); while (matcher.find()) { String path; if ("html".equals(type)) { // Для HTML извлекаем путь из атрибутов href, src, background-image и action path = matcher.group(2); } else if ("js-var".equals(type)) { // Для JS-переменных извлекаем путь из группы 3 path = matcher.group(3); } else { // Для CSS и JS извлекаем путь из группы 1 path = matcher.group(1); } // Если путь начинается с "/" и не является абсолютным URL if (path.startsWith("/") && !path.startsWith("http://") && !path.startsWith("https://") && !path.startsWith("//")) { // Добавляем префикс String newPath = "/p/" + WebSocketLayer.DEVICEID + path; // Заменяем старое значение на новое if ("html".equals(type)) { matcher.appendReplacement(modifiedContent, matcher.group(1) + "=\"" + newPath + "\""); } else if ("css".equals(type)) { matcher.appendReplacement(modifiedContent, "url('" + newPath + "')"); } else if ("js".equals(type)) { matcher.appendReplacement(modifiedContent, "'" + newPath + "'"); } else if ("js-var".equals(type)) { matcher.appendReplacement(modifiedContent, matcher.group(1) + " " + matcher.group(2) + " = \"" + newPath + "\";"); } } else { // Оставляем путь без изменений matcher.appendReplacement(modifiedContent, matcher.group(0)); } } // Добавляем оставшуюся часть строки matcher.appendTail(modifiedContent); return modifiedContent.toString(); } } //}