diff --git a/core/xray.go b/core/xray.go index 58135c96b800..93b3fa3a36e4 100644 --- a/core/xray.go +++ b/core/xray.go @@ -18,6 +18,7 @@ import ( "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/browser_dialer" ) // Server is an instance of Xray. At any time, there must be at most one Server instance running. @@ -262,6 +263,9 @@ func (s *Instance) Close() error { s.running = false var errs []interface{} + if err := browser_dialer.StopCollectedDialerProxyURLs(); err != nil { + errs = append(errs, err) + } for _, f := range s.features { if err := f.Close(); err != nil { errs = append(errs, err) @@ -385,9 +389,16 @@ func (s *Instance) Start() error { s.statusLock.Lock() defer s.statusLock.Unlock() + if err := browser_dialer.StartCollectedDialerProxyURLs(); err != nil { + return err + } s.running = true for _, f := range s.features { if err := f.Start(); err != nil { + s.running = false + if stopErr := browser_dialer.StopCollectedDialerProxyURLs(); stopErr != nil { + return errors.New("browser dialer cleanup after startup failure also failed: ", stopErr).Base(err) + } return err } } diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 15d5cfbd7607..cdca86426ee2 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -21,6 +21,7 @@ import ( "github.com/xtls/xray-core/common/platform/filesystem" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/browser_dialer" "github.com/xtls/xray-core/transport/internet/finalmask/fragment" "github.com/xtls/xray-core/transport/internet/finalmask/header/custom" "github.com/xtls/xray-core/transport/internet/finalmask/header/dns" @@ -1970,6 +1971,35 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) { } config.ProtocolName = protocol } + if c.SocketSettings != nil && c.SocketSettings.DialerProxy != "" { + if browser_dialer.IsBrowserDialerProxy(c.SocketSettings.DialerProxy) { + if config.ProtocolName != "websocket" && config.ProtocolName != "splithttp" { + return nil, errors.New("dialerProxy ", c.SocketSettings.DialerProxy, " only supports websocket or splithttp") + } + if strings.EqualFold(c.Security, "reality") { + return nil, errors.New("dialerProxy ", c.SocketSettings.DialerProxy, " does not support REALITY") + } + if config.ProtocolName == "splithttp" { + splitHTTPSettings := c.SplitHTTPSettings + if c.XHTTPSettings != nil { + splitHTTPSettings = c.XHTTPSettings + } + if splitHTTPSettings != nil { + splitHTTPSettingsCopy := *splitHTTPSettings + hs, err := splitHTTPSettingsCopy.Build() + if err != nil { + return nil, errors.New("failed to build XHTTP config for browser dialer validation").Base(err) + } + if splitHTTPConfig, ok := hs.(*splithttp.Config); ok && splitHTTPConfig.Mode != "auto" && splitHTTPConfig.Mode != "packet-up" { + return nil, errors.New("dialerProxy ", c.SocketSettings.DialerProxy, " only supports XHTTP modes \"auto\" or \"packet-up\", got: \"", splitHTTPConfig.Mode, "\"") + } + } + } + if err := browser_dialer.RegisterDialerProxyURL(c.SocketSettings.DialerProxy); err != nil { + return nil, errors.New("failed to collect browser dialer URL").Base(err) + } + } + } switch strings.ToLower(c.Security) { case "", "none": diff --git a/infra/conf/xray.go b/infra/conf/xray.go index d01dac2bf683..cb9367defe89 100644 --- a/infra/conf/xray.go +++ b/infra/conf/xray.go @@ -15,6 +15,7 @@ import ( "github.com/xtls/xray-core/common/serial" core "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/browser_dialer" ) var ( @@ -437,7 +438,6 @@ func (c *Config) Override(o *Config, fn string) { if o.Geodata != nil { c.Geodata = o.Geodata } - // update the Inbound in slice if the only one in override config has same tag if len(o.InboundConfigs) > 0 { for i := range o.InboundConfigs { @@ -604,6 +604,9 @@ func (c *Config) Build() (*core.Config, error) { if len(c.Transport) > 0 { return nil, errors.PrintRemovedFeatureError("Global transport config", "streamSettings in inbounds and outbounds") } + if err := browser_dialer.BeginCollectingDialerProxyURLs(); err != nil { + return nil, err + } for _, rawInboundConfig := range inbounds { ic, err := rawInboundConfig.Build() @@ -626,6 +629,9 @@ func (c *Config) Build() (*core.Config, error) { } config.Outbound = append(config.Outbound, oc) } + if err := browser_dialer.ConfigureCollectedDialerProxyURLs(); err != nil { + return nil, errors.New("failed to configure browser dialer").Base(err) + } return config, nil } diff --git a/transport/internet/browser_dialer/dialer.go b/transport/internet/browser_dialer/dialer.go index 53955bc41fda..a8da600b938f 100644 --- a/transport/internet/browser_dialer/dialer.go +++ b/transport/internet/browser_dialer/dialer.go @@ -1,101 +1,33 @@ package browser_dialer import ( - "bytes" - "context" _ "embed" "encoding/base64" "encoding/json" "net/http" - "sync" - "time" "github.com/gorilla/websocket" "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/platform" - "github.com/xtls/xray-core/common/uuid" ) //go:embed dialer.html var webpage []byte type task struct { - Method string `json:"method"` - URL string `json:"url"` - Extra any `json:"extra,omitempty"` - StreamResponse bool `json:"streamResponse"` -} - -var conns chan *websocket.Conn -var server *http.Server -var mu sync.Mutex - -var upgrader = &websocket.Upgrader{ - ReadBufferSize: 0, - WriteBufferSize: 0, - HandshakeTimeout: time.Second * 4, - CheckOrigin: func(r *http.Request) bool { - return true - }, -} - -// Used by external projects when using xray as a go module -func Reload() { - addr := platform.NewEnvFlag(platform.BrowserDialerAddress).GetValue(func() string { return "" }) - mu.Lock() - defer mu.Unlock() - - if server != nil { - server.Close() - } - if HasBrowserDialer() { - for len(conns) > 0 { - select { - case c := <-conns: - c.Close() - default: - } - } - conns = nil - } - if addr != "" { - token := uuid.New() - csrfToken := token.String() - webpage := bytes.ReplaceAll(webpage, []byte("csrfToken"), []byte(csrfToken)) - conns = make(chan *websocket.Conn, 256) - server = &http.Server{ - Addr: addr, - Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/websocket" { - if r.URL.Query().Get("token") == csrfToken { - if conn, err := upgrader.Upgrade(w, r, nil); err == nil { - conns <- conn - } else { - errors.LogError(context.Background(), "Browser dialer http upgrade unexpected error") - } - } - } else { - w.Header().Set("Access-Control-Allow-Origin", "*"); - w.Write(webpage) - } - }), - } - go server.ListenAndServe() - } -} - -func HasBrowserDialer() bool { - return conns != nil + Method string `json:"method"` + URL string `json:"url"` + Extra any `json:"extra,omitempty"` + StreamResponse bool `json:"streamResponse"` } type webSocketExtra struct { Protocol string `json:"protocol,omitempty"` } -func DialWS(uri string, ed []byte) (*websocket.Conn, error) { +func DialWSWithAddress(addr string, uri string, ed []byte) (*websocket.Conn, error) { task := task{ - Method: "WS", - URL: uri, + Method: "WS", + URL: uri, StreamResponse: true, } @@ -105,7 +37,7 @@ func DialWS(uri string, ed []byte) (*websocket.Conn, error) { } } - return dialTask(task) + return dialTaskWithAddress(addr, task) } type httpExtra struct { @@ -142,30 +74,26 @@ func httpExtraFromHeadersAndCookies(headers http.Header, cookies []*http.Cookie) return &extra } -func DialGet(uri string, headers http.Header, cookies []*http.Cookie) (*websocket.Conn, error) { +func DialGetWithAddress(addr string, uri string, headers http.Header, cookies []*http.Cookie) (*websocket.Conn, error) { task := task{ - Method: "GET", - URL: uri, - Extra: httpExtraFromHeadersAndCookies(headers, cookies), + Method: "GET", + URL: uri, + Extra: httpExtraFromHeadersAndCookies(headers, cookies), StreamResponse: true, } - return dialTask(task) + return dialTaskWithAddress(addr, task) } -func DialPacket(method string, uri string, headers http.Header, cookies []*http.Cookie, payload []byte) error { - return dialWithBody(method, uri, headers, cookies, payload) -} - -func dialWithBody(method string, uri string, headers http.Header, cookies []*http.Cookie, payload []byte) error { +func DialPacketWithAddress(addr string, method string, uri string, headers http.Header, cookies []*http.Cookie, payload []byte) error { task := task{ - Method: method, - URL: uri, - Extra: httpExtraFromHeadersAndCookies(headers, cookies), + Method: method, + URL: uri, + Extra: httpExtraFromHeadersAndCookies(headers, cookies), StreamResponse: false, } - conn, err := dialTask(task) + conn, err := dialTaskWithAddress(addr, task) if err != nil { return err } @@ -184,12 +112,21 @@ func dialWithBody(method string, uri string, headers http.Header, cookies []*htt return nil } -func dialTask(task task) (*websocket.Conn, error) { +func dialTaskWithAddress(addr string, task task) (*websocket.Conn, error) { data, err := json.Marshal(task) if err != nil { return nil, err } + if addr == "" { + return nil, errors.New("browser dialer is not configured; set sockopt.dialerProxy to browser://host:port/uuid") + } + dialer, err := getDialerByAddress(addr) + if err != nil { + return nil, err + } + conns := dialer.conns + var conn *websocket.Conn for { conn = <-conns @@ -218,8 +155,3 @@ func CheckOK(conn *websocket.Conn) error { return nil } - -func init() { - Reload() -} - diff --git a/transport/internet/browser_dialer/dialer.html b/transport/internet/browser_dialer/dialer.html index 5a0df4892b81..acef530ba540 100644 --- a/transport/internet/browser_dialer/dialer.html +++ b/transport/internet/browser_dialer/dialer.html @@ -10,7 +10,7 @@ // Enable a much more aggressive JIT for performance gains // Copyright (c) 2021 XRAY. Mozilla Public License 2.0. - let url = "ws://" + window.location.host + "/websocket?token=csrfToken"; + let url = "ws://" + window.location.host + "/dialerPath"; let clientIdleCount = 0; let upstreamGetCount = 0; let upstreamWsCount = 0; @@ -67,7 +67,7 @@ } clientIdleCount += 1; console.log("Prepare", url); - let ws = new WebSocket(url); + let ws = new WebSocket(url, "browser-dialer"); // arraybuffer is significantly faster in chrome than default // blob, tested with chrome 123 ws.binaryType = "arraybuffer"; diff --git a/transport/internet/browser_dialer/manager.go b/transport/internet/browser_dialer/manager.go new file mode 100644 index 000000000000..1e47be3e4356 --- /dev/null +++ b/transport/internet/browser_dialer/manager.go @@ -0,0 +1,318 @@ +package browser_dialer + +import ( + "bytes" + "context" + stderrors "errors" + "net" + "net/http" + "net/url" + pathlib "path" + "strings" + "time" + + "github.com/gorilla/websocket" + "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/common/platform" + "github.com/xtls/xray-core/common/serial" + "github.com/xtls/xray-core/common/uuid" +) + +var dialersByAddress = map[string]*dialerInstance{} +var serversByListenAddr = map[string]*dialerServer{} +var initialized bool +var pendingURLs map[string]struct{} + +const browserDialerSubprotocol = "browser-dialer" + +var upgrader = &websocket.Upgrader{ + ReadBufferSize: 0, + WriteBufferSize: 0, + HandshakeTimeout: time.Second * 4, + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +func CheckLegacyEnv() error { + envAddress := platform.NewEnvFlag(platform.BrowserDialerAddress).GetValue(func() string { return "" }) + if envAddress == "" { + return nil + } + return errors.PrintRemovedFeatureError("env "+platform.BrowserDialerAddress, "sockopt.dialerProxy with browser://host:port/uuid") +} + +func IsBrowserDialerProxy(raw string) bool { + _, _, ok := parseBrowserDialerAddress(raw) + return ok +} + +func BeginCollectingDialerProxyURLs() error { + if initialized { + return errors.New("browser dialer does not support dynamic add/remove; restart is required after changing configuration") + } + if err := CheckLegacyEnv(); err != nil { + return err + } + pendingURLs = map[string]struct{}{} + return nil +} + +func RegisterDialerProxyURL(raw string) error { + if !IsBrowserDialerProxy(raw) { + return nil + } + if pendingURLs == nil { + return errors.New("browser dialer url collection is not initialized") + } + pendingURLs[raw] = struct{}{} + return nil +} + +func ConfigureCollectedDialerProxyURLs() error { + if initialized { + return errors.New("browser dialer does not support dynamic add/remove; restart is required after changing configuration") + } + if err := CheckLegacyEnv(); err != nil { + return err + } + listenAddrByPort := make(map[string]string, len(pendingURLs)) + for browserDialerURL := range pendingURLs { + listenAddr, _, ok := parseBrowserDialerAddress(browserDialerURL) + if !ok { + return errors.New("invalid browser dialer url: ", browserDialerURL) + } + _, port, err := net.SplitHostPort(listenAddr) + if err != nil { + return errors.New("invalid browser dialer listen address: ", listenAddr) + } + if existingAddr, found := listenAddrByPort[port]; found && existingAddr != listenAddr { + return errors.New("browser dialer cannot use the same port with a different listen address: ", existingAddr, " and ", listenAddr) + } + listenAddrByPort[port] = listenAddr + } + for existingAddr := range serversByListenAddr { + _, existingPort, splitErr := net.SplitHostPort(existingAddr) + if splitErr != nil { + continue + } + if newAddr, found := listenAddrByPort[existingPort]; found && newAddr != existingAddr { + return errors.New("browser dialer cannot use the same port with a different listen address: ", existingAddr, " and ", newAddr) + } + } + for browserDialerURL := range pendingURLs { + if _, err := ensureDialerWithAddress(browserDialerURL); err != nil { + return errors.New("failed to initialize browser dialer listener for url ", browserDialerURL).Base(err) + } + } + return nil +} + +// StartCollectedDialerProxyURLs starts listeners for browser dialer URLs +// prepared by ConfigureCollectedDialerProxyURLs. +// Subsequent calls after successful initialization return nil. +func StartCollectedDialerProxyURLs() error { + if initialized { + return nil + } + for listenAddr, server := range serversByListenAddr { + if err := server.start(); err != nil { + return errors.New("failed to start browser dialer listener on ", listenAddr).Base(err) + } + } + initialized = true + return nil +} + +func StopCollectedDialerProxyURLs() error { + var stopErrs []string + for listenAddr, server := range serversByListenAddr { + if err := server.stop(); err != nil { + stopErrs = append(stopErrs, serial.Concat("failed to stop browser dialer listener on ", listenAddr, ": ", err)) + } + } + dialersByAddress = map[string]*dialerInstance{} + serversByListenAddr = map[string]*dialerServer{} + pendingURLs = nil + initialized = false + if len(stopErrs) > 0 { + return errors.New(strings.Join(stopErrs, "; ")) + } + return nil +} + +type dialerInstance struct { + conns chan *websocket.Conn + page []byte +} + +type dialerServer struct { + server *http.Server + pageRoutes map[string]*dialerInstance + started bool +} + +func parseBrowserDialerAddress(addr string) (string, string, bool) { + if addr == "" { + return "", "", false + } + + parsedAddr, err := url.Parse(addr) + if err != nil || !strings.EqualFold(parsedAddr.Scheme, "browser") || parsedAddr.Host == "" || parsedAddr.Path == "" || parsedAddr.RawQuery != "" || parsedAddr.Fragment != "" { + return "", "", false + } + listenAddr := parsedAddr.Host + if _, _, err := net.SplitHostPort(listenAddr); err != nil { + return "", "", false + } + path := strings.TrimSuffix(parsedAddr.Path, "/") + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + cleanPath := pathlib.Clean(path) + if cleanPath == "." || cleanPath == "/" || cleanPath != path { + return "", "", false + } + if strings.Count(cleanPath, "/") != 1 { + return "", "", false + } + id := strings.TrimPrefix(cleanPath, "/") + id = strings.ToLower(id) + parsedUUID, err := uuid.ParseString(id) + if err != nil || parsedUUID.String() != id { + return "", "", false + } + return listenAddr, "/" + id, true +} + +func newDialerServer(listenAddr string) (*dialerServer, error) { + dialer := &dialerServer{ + pageRoutes: make(map[string]*dialerInstance), + } + dialer.server = &http.Server{ + Addr: listenAddr, + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + pageDialer := dialer.pageRoutes[r.URL.Path] + + if pageDialer != nil && websocket.IsWebSocketUpgrade(r) { + ok := false + for _, protocol := range websocket.Subprotocols(r) { + if protocol == browserDialerSubprotocol { + ok = true + break + } + } + if !ok { + closeConnection(w) + return + } + if conn, err := upgrader.Upgrade(w, r, http.Header{"Sec-WebSocket-Protocol": []string{browserDialerSubprotocol}}); err == nil { + pageDialer.conns <- conn + } else { + errors.LogError(context.Background(), "Browser dialer http upgrade unexpected error: ", err) + } + return + } + + if pageDialer != nil { + w.Header().Set("Access-Control-Allow-Origin", "*") + if _, err := w.Write(pageDialer.page); err != nil { + errors.LogError(context.Background(), "Browser dialer http page write unexpected error: ", err) + } + return + } + + closeConnection(w) + }), + } + return dialer, nil +} + +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() { + if err := d.server.Serve(listener); err != nil && !stderrors.Is(err, http.ErrServerClosed) { + errors.LogError(context.Background(), "Browser dialer http server unexpected error on ", d.server.Addr, ": ", err) + } + }() + return nil +} + +func (d *dialerServer) stop() error { + if !d.started { + return nil + } + d.started = false + return d.server.Close() +} + +func closeConnection(w http.ResponseWriter) { + hijacker, ok := w.(http.Hijacker) + if !ok { + return + } + conn, _, err := hijacker.Hijack() + if err != nil { + return + } + conn.Close() +} + +func getDialerByAddress(addr string) (*dialerInstance, error) { + listenAddr, path, ok := parseBrowserDialerAddress(addr) + if !ok { + return nil, errors.New("invalid browser dialer url: ", addr) + } + key := listenAddr + path + if dialer, found := dialersByAddress[key]; found { + return dialer, nil + } + return nil, errors.New("browser dialer is not configured for url: ", addr) +} + +func ensureDialerWithAddress(addr string) (*dialerInstance, error) { + listenAddr, path, ok := parseBrowserDialerAddress(addr) + if !ok { + return nil, errors.New("invalid browser dialer url: ", addr) + } + _, port, err := net.SplitHostPort(listenAddr) + if err != nil { + return nil, errors.New("invalid browser dialer listen address: ", listenAddr) + } + + key := listenAddr + path + if dialer, found := dialersByAddress[key]; found { + return dialer, nil + } + + server, found := serversByListenAddr[listenAddr] + if !found { + for existingAddr := range serversByListenAddr { + _, existingPort, splitErr := net.SplitHostPort(existingAddr) + if splitErr == nil && existingPort == port { + return nil, errors.New("browser dialer cannot use the same port with a different listen address: ", existingAddr, " and ", listenAddr) + } + } + newServer, serverErr := newDialerServer(listenAddr) + if serverErr != nil { + return nil, serverErr + } + server = newServer + serversByListenAddr[listenAddr] = server + } + + dialer := &dialerInstance{ + conns: make(chan *websocket.Conn, 256), + page: bytes.ReplaceAll(webpage, []byte("dialerPath"), []byte(strings.TrimPrefix(path, "/"))), + } + dialersByAddress[key] = dialer + server.pageRoutes[path] = dialer + return dialer, nil +} diff --git a/transport/internet/dialer.go b/transport/internet/dialer.go index 9342f26f5ff7..1a4fe468e255 100644 --- a/transport/internet/dialer.go +++ b/transport/internet/dialer.go @@ -14,6 +14,7 @@ import ( "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/transport" + "github.com/xtls/xray-core/transport/internet/browser_dialer" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/pipe" ) @@ -269,6 +270,9 @@ func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig } if len(sockopt.DialerProxy) > 0 { + if browser_dialer.IsBrowserDialerProxy(sockopt.DialerProxy) { + return nil, errors.New("dialerProxy ", sockopt.DialerProxy, " only supports WebSocket or splithttp").AtError() + } if obm == nil { return nil, errors.New("there is no outbound manager for dialerProxy").AtError() } diff --git a/transport/internet/splithttp/browser_client.go b/transport/internet/splithttp/browser_client.go index a70447f2d48e..7334d8755a71 100644 --- a/transport/internet/splithttp/browser_client.go +++ b/transport/internet/splithttp/browser_client.go @@ -15,6 +15,7 @@ import ( // BrowserDialerClient implements splithttp.DialerClient in terms of browser dialer type BrowserDialerClient struct { transportConfig *Config + browserDialer string } func (c *BrowserDialerClient) IsClosed() bool { @@ -33,7 +34,7 @@ func (c *BrowserDialerClient) OpenStream(ctx context.Context, url string, sessio c.transportConfig.FillStreamRequest(request, sessionId, "") - conn, err := browser_dialer.DialGet(request.URL.String(), request.Header, request.Cookies()) + conn, err := browser_dialer.DialGetWithAddress(c.browserDialer, request.URL.String(), request.Header, request.Cookies()) dummyAddr := &net.IPAddr{} if err != nil { return nil, dummyAddr, dummyAddr, err @@ -62,7 +63,7 @@ func (c *BrowserDialerClient) PostPacket(ctx context.Context, url string, sessio } } - err = browser_dialer.DialPacket(method, request.URL.String(), request.Header, request.Cookies(), bytes) + err = browser_dialer.DialPacketWithAddress(c.browserDialer, method, request.URL.String(), request.Header, request.Cookies(), bytes) if err != nil { return err } diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index 6f4ec1d83fa2..9b5562983cd1 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -40,6 +40,20 @@ type dialerConf struct { *internet.MemoryStreamConfig } +type errorDialerClient struct { + err error +} + +func (c *errorDialerClient) IsClosed() bool { return true } + +func (c *errorDialerClient) OpenStream(context.Context, string, string, io.Reader, bool) (io.ReadCloser, net.Addr, net.Addr, error) { + return nil, nil, nil, c.err +} + +func (c *errorDialerClient) PostPacket(context.Context, string, string, string, buf.MultiBuffer) error { + return c.err +} + var ( globalDialerMap map[dialerConf]*XmuxManager globalDialerAccess sync.Mutex @@ -47,9 +61,24 @@ var ( func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (DialerClient, *XmuxClient) { realityConfig := reality.ConfigFromStreamSettings(streamSettings) + browserDialer := "" + if streamSettings.SocketSettings != nil { + if browser_dialer.IsBrowserDialerProxy(streamSettings.SocketSettings.DialerProxy) { + browserDialer = streamSettings.SocketSettings.DialerProxy + } + } - if browser_dialer.HasBrowserDialer() && realityConfig == nil { - return &BrowserDialerClient{transportConfig: streamSettings.ProtocolSettings.(*Config)}, nil + if browserDialer != "" && realityConfig == nil { + transportConfig := streamSettings.ProtocolSettings.(*Config) + if transportConfig.Mode != "auto" && transportConfig.Mode != "packet-up" { + return &errorDialerClient{ + err: errors.New("browserDialer with splithttp only supports modes \"auto\" or \"packet-up\", got: \"", transportConfig.Mode, "\""), + }, nil + } + return &BrowserDialerClient{ + transportConfig: transportConfig, + browserDialer: browserDialer, + }, nil } globalDialerAccess.Lock() diff --git a/transport/internet/websocket/dialer.go b/transport/internet/websocket/dialer.go index e5354908d77c..6641ed50bbe3 100644 --- a/transport/internet/websocket/dialer.go +++ b/transport/internet/websocket/dialer.go @@ -117,8 +117,14 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in } uri := protocol + "://" + host + wsSettings.GetNormalizedPath() - if browser_dialer.HasBrowserDialer() { - conn, err := browser_dialer.DialWS(uri, ed) + browserDialer := "" + if streamSettings.SocketSettings != nil { + if browser_dialer.IsBrowserDialerProxy(streamSettings.SocketSettings.DialerProxy) { + browserDialer = streamSettings.SocketSettings.DialerProxy + } + } + if browserDialer != "" { + conn, err := browser_dialer.DialWSWithAddress(browserDialer, uri, ed) if err != nil { return nil, err }