Поддержка сокетов в прокси

Теперь проксирование поддерживает сокеты.
This commit is contained in:
2026-02-02 04:17:11 +07:00
parent 50c1c09343
commit 4a0cfe316d

View File

@@ -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 {