Поддержка сокетов в прокси
Теперь проксирование поддерживает сокеты.
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user