Поддержка сокетов в прокси
Теперь проксирование поддерживает сокеты.
This commit is contained in:
@@ -1,13 +1,16 @@
|
|||||||
package webserver
|
package webserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
"vServer/Backend/config"
|
"vServer/Backend/config"
|
||||||
tools "vServer/Backend/tools"
|
tools "vServer/Backend/tools"
|
||||||
)
|
)
|
||||||
@@ -16,6 +19,162 @@ var (
|
|||||||
configMutex sync.RWMutex
|
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) {
|
func StartHandlerProxy(w http.ResponseWriter, r *http.Request) (valid bool) {
|
||||||
valid = false
|
valid = false
|
||||||
|
|
||||||
@@ -49,6 +208,11 @@ func StartHandlerProxy(w http.ResponseWriter, r *http.Request) (valid bool) {
|
|||||||
return valid
|
return valid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем WebSocket запрос — обрабатываем отдельно
|
||||||
|
if isWebSocketRequest(r) {
|
||||||
|
return handleWebSocketProxy(w, r, proxyConfig)
|
||||||
|
}
|
||||||
|
|
||||||
// Проверяем AutoHTTPS - редирект с HTTP на HTTPS
|
// Проверяем AutoHTTPS - редирект с HTTP на HTTPS
|
||||||
https_check := !(r.TLS == nil)
|
https_check := !(r.TLS == nil)
|
||||||
if !https_check && proxyConfig.AutoHTTPS {
|
if !https_check && proxyConfig.AutoHTTPS {
|
||||||
|
|||||||
Reference in New Issue
Block a user