mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-07-03 18:28:52 +00:00
refactor: simplify browser dialer static state and remove sockopt browserDialer parsing
Agent-Logs-Url: https://github.com/XTLS/Xray-core/sessions/4875f50c-9a90-4d34-afbe-2e629296faa0 Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
1cc7349529
commit
266ae17654
@@ -1075,15 +1075,10 @@ type SocketConfig struct {
|
|||||||
AddressPortStrategy string `json:"addressPortStrategy"`
|
AddressPortStrategy string `json:"addressPortStrategy"`
|
||||||
HappyEyeballsSettings *HappyEyeballsConfig `json:"happyEyeballs"`
|
HappyEyeballsSettings *HappyEyeballsConfig `json:"happyEyeballs"`
|
||||||
TrustedXForwardedFor []string `json:"trustedXForwardedFor"`
|
TrustedXForwardedFor []string `json:"trustedXForwardedFor"`
|
||||||
BrowserDialer string `json:"browserDialer"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
||||||
if c.BrowserDialer != "" {
|
|
||||||
return nil, errors.PrintRemovedFeatureError("sockopt.browserDialer", "root browserDialers + sockopt.dialerProxy")
|
|
||||||
}
|
|
||||||
|
|
||||||
tfo := int32(0) // don't invoke setsockopt() for TFO
|
tfo := int32(0) // don't invoke setsockopt() for TFO
|
||||||
if c.TFO != nil {
|
if c.TFO != nil {
|
||||||
switch v := c.TFO.(type) {
|
switch v := c.TFO.(type) {
|
||||||
@@ -1976,9 +1971,6 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
|||||||
}
|
}
|
||||||
config.ProtocolName = protocol
|
config.ProtocolName = protocol
|
||||||
}
|
}
|
||||||
if c.SocketSettings != nil && c.SocketSettings.BrowserDialer != "" {
|
|
||||||
return nil, errors.PrintRemovedFeatureError("sockopt.browserDialer", "root browserDialers + sockopt.dialerProxy")
|
|
||||||
}
|
|
||||||
if c.SocketSettings != nil && c.SocketSettings.DialerProxy != "" {
|
if c.SocketSettings != nil && c.SocketSettings.DialerProxy != "" {
|
||||||
if _, ok := browser_dialer.GetAddressByTag(c.SocketSettings.DialerProxy); ok {
|
if _, ok := browser_dialer.GetAddressByTag(c.SocketSettings.DialerProxy); ok {
|
||||||
if config.ProtocolName != "websocket" && config.ProtocolName != "splithttp" {
|
if config.ProtocolName != "websocket" && config.ProtocolName != "splithttp" {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
pathlib "path"
|
pathlib "path"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
@@ -31,10 +32,11 @@ type task struct {
|
|||||||
StreamResponse bool `json:"streamResponse"`
|
StreamResponse bool `json:"streamResponse"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var sockoptDialers map[string]*dialerInstance
|
var dialersByAddress = map[string]*dialerInstance{}
|
||||||
var dialerServers map[string]*dialerServer
|
var serversByListenAddr = map[string]*dialerServer{}
|
||||||
var dialerTags map[string]string
|
var addressByTag atomic.Value
|
||||||
var mu sync.RWMutex
|
var initMu sync.Mutex
|
||||||
|
var initialized bool
|
||||||
|
|
||||||
const browserDialerSubprotocol = "browser-dialer"
|
const browserDialerSubprotocol = "browser-dialer"
|
||||||
|
|
||||||
@@ -47,18 +49,12 @@ var upgrader = &websocket.Upgrader{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func HasBrowserDialerWithAddress(addr string) bool {
|
|
||||||
_, _, ok := parseBrowserDialerAddress(addr)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAddressByTag(tag string) (string, bool) {
|
func GetAddressByTag(tag string) (string, bool) {
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
mu.RLock()
|
tags, _ := addressByTag.Load().(map[string]string)
|
||||||
defer mu.RUnlock()
|
addr, ok := tags[tag]
|
||||||
addr, ok := dialerTags[tag]
|
|
||||||
return addr, ok
|
return addr, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +67,13 @@ func CheckLegacyEnv() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ConfigureDialerTags(tags map[string]string) error {
|
func ConfigureDialerTags(tags map[string]string) error {
|
||||||
|
initMu.Lock()
|
||||||
|
defer initMu.Unlock()
|
||||||
|
|
||||||
|
if initialized {
|
||||||
|
return errors.New("browserDialers does not support dynamic add/remove; restart is required after changing configuration")
|
||||||
|
}
|
||||||
|
|
||||||
if err := CheckLegacyEnv(); err != nil {
|
if err := CheckLegacyEnv(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -97,9 +100,7 @@ func ConfigureDialerTags(tags map[string]string) error {
|
|||||||
listenAddrByPort[port] = listenAddr
|
listenAddrByPort[port] = listenAddr
|
||||||
next[tag] = addr
|
next[tag] = addr
|
||||||
}
|
}
|
||||||
mu.RLock()
|
for existingAddr := range serversByListenAddr {
|
||||||
defer mu.RUnlock()
|
|
||||||
for existingAddr := range dialerServers {
|
|
||||||
_, existingPort, splitErr := net.SplitHostPort(existingAddr)
|
_, existingPort, splitErr := net.SplitHostPort(existingAddr)
|
||||||
if splitErr != nil {
|
if splitErr != nil {
|
||||||
continue
|
continue
|
||||||
@@ -113,10 +114,13 @@ func ConfigureDialerTags(tags map[string]string) error {
|
|||||||
return errors.New("failed to initialize browserDialers listener for tag ", tag).Base(err)
|
return errors.New("failed to initialize browserDialers listener for tag ", tag).Base(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for listenAddr, server := range serversByListenAddr {
|
||||||
mu.Lock()
|
if err := server.start(); err != nil {
|
||||||
dialerTags = next
|
return errors.New("failed to start browserDialers listener on ", listenAddr).Base(err)
|
||||||
mu.Unlock()
|
}
|
||||||
|
}
|
||||||
|
addressByTag.Store(next)
|
||||||
|
initialized = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +136,7 @@ type dialerInstance struct {
|
|||||||
type dialerServer struct {
|
type dialerServer struct {
|
||||||
server *http.Server
|
server *http.Server
|
||||||
pageRoutes map[string]*dialerInstance
|
pageRoutes map[string]*dialerInstance
|
||||||
|
started bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseBrowserDialerAddress(addr string) (string, string, bool) {
|
func parseBrowserDialerAddress(addr string) (string, string, bool) {
|
||||||
@@ -178,9 +183,7 @@ func newDialerServer(listenAddr string) (*dialerServer, error) {
|
|||||||
dialer.server = &http.Server{
|
dialer.server = &http.Server{
|
||||||
Addr: listenAddr,
|
Addr: listenAddr,
|
||||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
mu.RLock()
|
|
||||||
pageDialer := dialer.pageRoutes[r.URL.Path]
|
pageDialer := dialer.pageRoutes[r.URL.Path]
|
||||||
mu.RUnlock()
|
|
||||||
|
|
||||||
if pageDialer != nil && websocket.IsWebSocketUpgrade(r) {
|
if pageDialer != nil && websocket.IsWebSocketUpgrade(r) {
|
||||||
ok := false
|
ok := false
|
||||||
@@ -213,16 +216,24 @@ func newDialerServer(listenAddr string) (*dialerServer, error) {
|
|||||||
closeConnection(w)
|
closeConnection(w)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
listener, err := net.Listen("tcp", listenAddr)
|
return dialer, nil
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
|
||||||
|
func (d *dialerServer) start() error {
|
||||||
|
if d.started {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
listener, err := net.Listen("tcp", d.server.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.started = true
|
||||||
go func() {
|
go func() {
|
||||||
if err := dialer.server.Serve(listener); err != nil && !stderrors.Is(err, http.ErrServerClosed) {
|
if err := d.server.Serve(listener); err != nil && !stderrors.Is(err, http.ErrServerClosed) {
|
||||||
errors.LogError(context.Background(), "Browser dialer http server unexpected error on ", dialer.server.Addr, ": ", err)
|
errors.LogError(context.Background(), "Browser dialer http server unexpected error on ", d.server.Addr, ": ", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return dialer, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func closeConnection(w http.ResponseWriter) {
|
func closeConnection(w http.ResponseWriter) {
|
||||||
@@ -238,6 +249,18 @@ func closeConnection(w http.ResponseWriter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getDialerByAddress(addr string) (*dialerInstance, error) {
|
func getDialerByAddress(addr string) (*dialerInstance, error) {
|
||||||
|
listenAddr, path, ok := parseBrowserDialerAddress(addr)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("invalid browserDialers url: ", addr)
|
||||||
|
}
|
||||||
|
key := listenAddr + path
|
||||||
|
if dialer, found := dialersByAddress[key]; found {
|
||||||
|
return dialer, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("browser dialer is not configured for browserDialers url: ", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureDialerWithAddress(addr string) (*dialerInstance, error) {
|
||||||
listenAddr, path, ok := parseBrowserDialerAddress(addr)
|
listenAddr, path, ok := parseBrowserDialerAddress(addr)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("invalid browserDialers url: ", addr)
|
return nil, errors.New("invalid browserDialers url: ", addr)
|
||||||
@@ -248,23 +271,13 @@ func getDialerByAddress(addr string) (*dialerInstance, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
key := listenAddr + path
|
key := listenAddr + path
|
||||||
|
if dialer, found := dialersByAddress[key]; found {
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
if sockoptDialers == nil {
|
|
||||||
sockoptDialers = make(map[string]*dialerInstance)
|
|
||||||
}
|
|
||||||
if dialerServers == nil {
|
|
||||||
dialerServers = make(map[string]*dialerServer)
|
|
||||||
}
|
|
||||||
if dialer, found := sockoptDialers[key]; found {
|
|
||||||
return dialer, nil
|
return dialer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
server, found := dialerServers[listenAddr]
|
server, found := serversByListenAddr[listenAddr]
|
||||||
if !found {
|
if !found {
|
||||||
for existingAddr := range dialerServers {
|
for existingAddr := range serversByListenAddr {
|
||||||
_, existingPort, splitErr := net.SplitHostPort(existingAddr)
|
_, existingPort, splitErr := net.SplitHostPort(existingAddr)
|
||||||
if splitErr == nil && existingPort == port {
|
if splitErr == nil && existingPort == port {
|
||||||
return nil, errors.New("browserDialers cannot use the same port with a different listen address: ", existingAddr, " and ", listenAddr)
|
return nil, errors.New("browserDialers cannot use the same port with a different listen address: ", existingAddr, " and ", listenAddr)
|
||||||
@@ -275,14 +288,14 @@ func getDialerByAddress(addr string) (*dialerInstance, error) {
|
|||||||
return nil, serverErr
|
return nil, serverErr
|
||||||
}
|
}
|
||||||
server = newServer
|
server = newServer
|
||||||
dialerServers[listenAddr] = server
|
serversByListenAddr[listenAddr] = server
|
||||||
}
|
}
|
||||||
|
|
||||||
dialer := &dialerInstance{
|
dialer := &dialerInstance{
|
||||||
conns: make(chan *websocket.Conn, 256),
|
conns: make(chan *websocket.Conn, 256),
|
||||||
page: bytes.ReplaceAll(webpage, []byte("dialerPath"), []byte(strings.TrimPrefix(path, "/"))),
|
page: bytes.ReplaceAll(webpage, []byte("dialerPath"), []byte(strings.TrimPrefix(path, "/"))),
|
||||||
}
|
}
|
||||||
sockoptDialers[key] = dialer
|
dialersByAddress[key] = dialer
|
||||||
server.pageRoutes[path] = dialer
|
server.pageRoutes[path] = dialer
|
||||||
return dialer, nil
|
return dialer, nil
|
||||||
}
|
}
|
||||||
@@ -291,7 +304,7 @@ func EnsureDialerWithAddress(addr string) error {
|
|||||||
if addr == "" {
|
if addr == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
_, err := getDialerByAddress(addr)
|
_, err := ensureDialerWithAddress(addr)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,7 +406,10 @@ func dialTaskWithAddress(addr string, task task) (*websocket.Conn, error) {
|
|||||||
return nil, errors.New("browser dialer is not configured; set root browserDialers and use sockopt.dialerProxy tag")
|
return nil, errors.New("browser dialer is not configured; set root browserDialers and use sockopt.dialerProxy tag")
|
||||||
}
|
}
|
||||||
dialer, err := getDialerByAddress(addr)
|
dialer, err := getDialerByAddress(addr)
|
||||||
if err != nil || dialer == nil {
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dialer == nil {
|
||||||
return nil, errors.New("browser dialer is not configured for browserDialers url: ", addr)
|
return nil, errors.New("browser dialer is not configured for browserDialers url: ", addr)
|
||||||
}
|
}
|
||||||
conns := dialer.conns
|
conns := dialer.conns
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if browser_dialer.HasBrowserDialerWithAddress(browserDialer) && realityConfig == nil {
|
if browserDialer != "" && realityConfig == nil {
|
||||||
transportConfig := streamSettings.ProtocolSettings.(*Config)
|
transportConfig := streamSettings.ProtocolSettings.(*Config)
|
||||||
if transportConfig.Mode != "auto" && transportConfig.Mode != "packet-up" {
|
if transportConfig.Mode != "auto" && transportConfig.Mode != "packet-up" {
|
||||||
return &errorDialerClient{
|
return &errorDialerClient{
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
browserDialer = taggedDialer
|
browserDialer = taggedDialer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if browser_dialer.HasBrowserDialerWithAddress(browserDialer) {
|
if browserDialer != "" {
|
||||||
conn, err := browser_dialer.DialWSWithAddress(browserDialer, uri, ed)
|
conn, err := browser_dialer.DialWSWithAddress(browserDialer, uri, ed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
Reference in New Issue
Block a user