mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-07-02 17:58:46 +00:00
refactor: split browser dialer manager logic to keep dialer.go minimal
Agent-Logs-Url: https://github.com/XTLS/Xray-core/sessions/2f611863-296d-48df-b3c4-e02384132848 Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
9d4dd2c32f
commit
286a702bfa
@@ -1,23 +1,13 @@
|
|||||||
package browser_dialer
|
package browser_dialer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
stderrors "errors"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
pathlib "path"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"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
|
//go:embed dialer.html
|
||||||
@@ -30,274 +20,10 @@ type task struct {
|
|||||||
StreamResponse bool `json:"streamResponse"`
|
StreamResponse bool `json:"streamResponse"`
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
type webSocketExtra struct {
|
type webSocketExtra struct {
|
||||||
Protocol string `json:"protocol,omitempty"`
|
Protocol string `json:"protocol,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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
|
|
||||||
}
|
|
||||||
|
|
||||||
func DialWSWithAddress(addr string, uri string, ed []byte) (*websocket.Conn, error) {
|
func DialWSWithAddress(addr string, uri string, ed []byte) (*websocket.Conn, error) {
|
||||||
task := task{
|
task := task{
|
||||||
Method: "WS",
|
Method: "WS",
|
||||||
|
|||||||
@@ -0,0 +1,282 @@
|
|||||||
|
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/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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user