From 6b5aefc27a6d67678992cdf66886e8abd6c4c106 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Tue, 30 Sep 2025 08:56:30 +0800 Subject: [PATCH] feat(proxy): add SOCKS5 support and improve proxy handling - Added SOCKS5 proxy support, including authentication. - Improved handling of proxy schemes and associated error logging. - Enhanced transport creation for HTTP, HTTPS, and SOCKS5 proxies with better configuration management. --- sdk/cliproxy/rtprovider.go | 44 ++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/sdk/cliproxy/rtprovider.go b/sdk/cliproxy/rtprovider.go index f8595cb8..dad4fc23 100644 --- a/sdk/cliproxy/rtprovider.go +++ b/sdk/cliproxy/rtprovider.go @@ -1,12 +1,16 @@ package cliproxy import ( + "context" + "net" "net/http" "net/url" "strings" "sync" coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" + log "github.com/sirupsen/logrus" + "golang.org/x/net/proxy" ) // defaultRoundTripperProvider returns a per-auth HTTP RoundTripper based on @@ -25,27 +29,49 @@ func (p *defaultRoundTripperProvider) RoundTripperFor(auth *coreauth.Auth) http. if auth == nil { return nil } - proxy := strings.TrimSpace(auth.ProxyURL) - if proxy == "" { + proxyStr := strings.TrimSpace(auth.ProxyURL) + if proxyStr == "" { return nil } p.mu.RLock() - rt := p.cache[proxy] + rt := p.cache[proxyStr] p.mu.RUnlock() if rt != nil { return rt } - // Build HTTP/HTTPS proxy transport; ignore SOCKS for simplicity here. - u, err := url.Parse(proxy) - if err != nil { + // Parse the proxy URL to determine the scheme. + proxyURL, errParse := url.Parse(proxyStr) + if errParse != nil { + log.Errorf("parse proxy URL failed: %v", errParse) return nil } - if u.Scheme != "http" && u.Scheme != "https" { + var transport *http.Transport + // Handle different proxy schemes. + if proxyURL.Scheme == "socks5" { + // Configure SOCKS5 proxy with optional authentication. + username := proxyURL.User.Username() + password, _ := proxyURL.User.Password() + proxyAuth := &proxy.Auth{User: username, Password: password} + dialer, errSOCKS5 := proxy.SOCKS5("tcp", proxyURL.Host, proxyAuth, proxy.Direct) + if errSOCKS5 != nil { + log.Errorf("create SOCKS5 dialer failed: %v", errSOCKS5) + return nil + } + // Set up a custom transport using the SOCKS5 dialer. + transport = &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + return dialer.Dial(network, addr) + }, + } + } else if proxyURL.Scheme == "http" || proxyURL.Scheme == "https" { + // Configure HTTP or HTTPS proxy. + transport = &http.Transport{Proxy: http.ProxyURL(proxyURL)} + } else { + log.Errorf("unsupported proxy scheme: %s", proxyURL.Scheme) return nil } - transport := &http.Transport{Proxy: http.ProxyURL(u)} p.mu.Lock() - p.cache[proxy] = transport + p.cache[proxyStr] = transport p.mu.Unlock() return transport }