diff --git a/Backend/WebServer/proxy_server.go b/Backend/WebServer/proxy_server.go index ccc9bca..62eaa88 100644 --- a/Backend/WebServer/proxy_server.go +++ b/Backend/WebServer/proxy_server.go @@ -1,13 +1,16 @@ package webserver import ( + "bufio" "bytes" "crypto/tls" "io" "log" + "net" "net/http" "strings" "sync" + "time" "vServer/Backend/config" tools "vServer/Backend/tools" ) @@ -16,6 +19,162 @@ var ( configMutex sync.RWMutex ) +// Проверяет, является ли запрос WebSocket +func isWebSocketRequest(r *http.Request) bool { + connection := strings.ToLower(r.Header.Get("Connection")) + upgrade := strings.ToLower(r.Header.Get("Upgrade")) + return strings.Contains(connection, "upgrade") && upgrade == "websocket" +} + +// Проксирует WebSocket соединение +func handleWebSocketProxy(w http.ResponseWriter, r *http.Request, proxyConfig config.Proxy_Service) bool { + // Определяем протокол для локального соединения + protocol := "ws" + networkProtocol := "tcp" + if proxyConfig.ServiceHTTPSuse { + protocol = "wss" + } + + targetAddr := proxyConfig.LocalAddress + ":" + proxyConfig.LocalPort + + tools.Logs_file(0, "WS-PROXY", "🔌 WebSocket: "+r.RemoteAddr+" → "+protocol+"://"+targetAddr+r.URL.Path, "logs_proxy.log", false) + + // Захватываем клиентское соединение через Hijacker + hijacker, ok := w.(http.Hijacker) + if !ok { + http.Error(w, "WebSocket не поддерживается", http.StatusInternalServerError) + tools.Logs_file(1, "WS-PROXY", "❌ Hijacker не поддерживается", "logs_proxy.log", false) + return true + } + + // Подключаемся к бэкенд серверу + var backendConn net.Conn + var err error + + if proxyConfig.ServiceHTTPSuse { + // TLS соединение для wss:// + backendConn, err = tls.Dial(networkProtocol, targetAddr, &tls.Config{ + InsecureSkipVerify: true, + }) + } else { + // Обычное TCP соединение для ws:// + backendConn, err = net.DialTimeout(networkProtocol, targetAddr, 10*time.Second) + } + + if err != nil { + http.Error(w, "Ошибка подключения к бэкенду", http.StatusBadGateway) + tools.Logs_file(1, "WS-PROXY", "❌ Ошибка подключения к бэкенду: "+err.Error(), "logs_proxy.log", false) + return true + } + defer backendConn.Close() + + // Формируем WebSocket handshake запрос к бэкенду + var handshakeReq bytes.Buffer + handshakeReq.WriteString(r.Method + " " + r.URL.RequestURI() + " HTTP/1.1\r\n") + handshakeReq.WriteString("Host: " + r.Host + "\r\n") + + // Копируем все заголовки от клиента (включая WebSocket заголовки!) + for name, values := range r.Header { + for _, value := range values { + handshakeReq.WriteString(name + ": " + value + "\r\n") + } + } + + // Добавляем X-Forwarded заголовки + clientIP := r.RemoteAddr + if colonIndex := strings.LastIndex(clientIP, ":"); colonIndex != -1 { + clientIP = clientIP[:colonIndex] + } + handshakeReq.WriteString("X-Real-IP: " + clientIP + "\r\n") + handshakeReq.WriteString("X-Forwarded-For: " + clientIP + "\r\n") + handshakeReq.WriteString("\r\n") + + // Отправляем handshake на бэкенд + if _, err := backendConn.Write(handshakeReq.Bytes()); err != nil { + http.Error(w, "Ошибка отправки handshake", http.StatusBadGateway) + tools.Logs_file(1, "WS-PROXY", "❌ Ошибка отправки handshake: "+err.Error(), "logs_proxy.log", false) + return true + } + + // Читаем ответ от бэкенда + backendReader := bufio.NewReader(backendConn) + resp, err := http.ReadResponse(backendReader, r) + if err != nil { + http.Error(w, "Ошибка чтения ответа бэкенда", http.StatusBadGateway) + tools.Logs_file(1, "WS-PROXY", "❌ Ошибка чтения ответа: "+err.Error(), "logs_proxy.log", false) + return true + } + + // Проверяем, что бэкенд принял WebSocket (101 Switching Protocols) + if resp.StatusCode != http.StatusSwitchingProtocols { + http.Error(w, "Бэкенд отклонил WebSocket", resp.StatusCode) + tools.Logs_file(1, "WS-PROXY", "❌ Бэкенд отклонил WebSocket: "+resp.Status, "logs_proxy.log", false) + return true + } + + // Захватываем клиентское соединение + clientConn, clientBuf, err := hijacker.Hijack() + if err != nil { + tools.Logs_file(1, "WS-PROXY", "❌ Ошибка Hijack: "+err.Error(), "logs_proxy.log", false) + return true + } + defer clientConn.Close() + + // Отправляем ответ handshake клиенту + var handshakeResp bytes.Buffer + handshakeResp.WriteString("HTTP/1.1 101 Switching Protocols\r\n") + for name, values := range resp.Header { + for _, value := range values { + handshakeResp.WriteString(name + ": " + value + "\r\n") + } + } + handshakeResp.WriteString("\r\n") + + if _, err := clientConn.Write(handshakeResp.Bytes()); err != nil { + tools.Logs_file(1, "WS-PROXY", "❌ Ошибка отправки ответа клиенту: "+err.Error(), "logs_proxy.log", false) + return true + } + + tools.Logs_file(0, "WS-PROXY", "✅ WebSocket установлен: "+r.Host+r.URL.Path, "logs_proxy.log", false) + + // Двунаправленное проксирование данных + done := make(chan struct{}, 2) + + // Клиент → Бэкенд + go func() { + defer func() { done <- struct{}{} }() + + // Сначала отправляем буферизированные данные если есть + if clientBuf.Reader.Buffered() > 0 { + buffered := make([]byte, clientBuf.Reader.Buffered()) + clientBuf.Read(buffered) + backendConn.Write(buffered) + } + + io.Copy(backendConn, clientConn) + }() + + // Бэкенд → Клиент + go func() { + defer func() { done <- struct{}{} }() + + // Сначала отправляем буферизированные данные если есть + if backendReader.Buffered() > 0 { + buffered := make([]byte, backendReader.Buffered()) + backendReader.Read(buffered) + clientConn.Write(buffered) + } + + io.Copy(clientConn, backendConn) + }() + + // Ждём завершения одного из направлений + <-done + + tools.Logs_file(0, "WS-PROXY", "🔌 WebSocket закрыт: "+r.Host+r.URL.Path, "logs_proxy.log", false) + return true +} + func StartHandlerProxy(w http.ResponseWriter, r *http.Request) (valid bool) { valid = false @@ -49,6 +208,11 @@ func StartHandlerProxy(w http.ResponseWriter, r *http.Request) (valid bool) { return valid } + // Проверяем WebSocket запрос — обрабатываем отдельно + if isWebSocketRequest(r) { + return handleWebSocketProxy(w, r, proxyConfig) + } + // Проверяем AutoHTTPS - редирект с HTTP на HTTPS https_check := !(r.TLS == nil) if !https_check && proxyConfig.AutoHTTPS {