| package executor |
|
|
| import ( |
| "context" |
| "fmt" |
| "net" |
| "net/http" |
| "net/url" |
| "strings" |
| "time" |
|
|
| "github.com/router-for-me/CLIProxyAPI/v6/internal/config" |
| cliproxyauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" |
| log "github.com/sirupsen/logrus" |
| "golang.org/x/net/proxy" |
| ) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func newProxyAwareHTTPClient(ctx context.Context, cfg *config.Config, auth *cliproxyauth.Auth, timeout time.Duration) *http.Client { |
| httpClient := &http.Client{} |
| if timeout > 0 { |
| httpClient.Timeout = timeout |
| } |
|
|
| |
| var proxyURL string |
| if auth != nil { |
| proxyURL = strings.TrimSpace(auth.ProxyURL) |
| } |
|
|
| |
| if proxyURL == "" && cfg != nil { |
| proxyURL = strings.TrimSpace(cfg.ProxyURL) |
| } |
|
|
| |
| if proxyURL != "" { |
| transport := buildProxyTransport(proxyURL) |
| if transport != nil { |
| httpClient.Transport = transport |
| return httpClient |
| } |
| |
| log.Debugf("failed to setup proxy from URL: %s, falling back to context transport", proxyURL) |
| } |
|
|
| |
| if rt, ok := ctx.Value("cliproxy.roundtripper").(http.RoundTripper); ok && rt != nil { |
| httpClient.Transport = rt |
| } |
|
|
| return httpClient |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| func buildProxyTransport(proxyURL string) *http.Transport { |
| if proxyURL == "" { |
| return nil |
| } |
|
|
| parsedURL, errParse := url.Parse(proxyURL) |
| if errParse != nil { |
| log.Errorf("parse proxy URL failed: %v", errParse) |
| return nil |
| } |
|
|
| var transport *http.Transport |
|
|
| |
| if parsedURL.Scheme == "socks5" { |
| |
| var proxyAuth *proxy.Auth |
| if parsedURL.User != nil { |
| username := parsedURL.User.Username() |
| password, _ := parsedURL.User.Password() |
| proxyAuth = &proxy.Auth{User: username, Password: password} |
| } |
| dialer, errSOCKS5 := proxy.SOCKS5("tcp", parsedURL.Host, proxyAuth, proxy.Direct) |
| if errSOCKS5 != nil { |
| log.Errorf("create SOCKS5 dialer failed: %v", errSOCKS5) |
| return nil |
| } |
| |
| transport = &http.Transport{ |
| DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { |
| return dialer.Dial(network, addr) |
| }, |
| } |
| } else if parsedURL.Scheme == "http" || parsedURL.Scheme == "https" { |
| |
| transport = &http.Transport{Proxy: http.ProxyURL(parsedURL)} |
| } else { |
| log.Errorf("unsupported proxy scheme: %s", parsedURL.Scheme) |
| return nil |
| } |
|
|
| return transport |
| } |
|
|
| |
| type RequestPreparer interface { |
| PrepareRequest(req *http.Request, auth *cliproxyauth.Auth) error |
| } |
|
|
| |
| |
| func ExecuteHttpRequest(ctx context.Context, cfg *config.Config, auth *cliproxyauth.Auth, req *http.Request, preparer RequestPreparer) (*http.Response, error) { |
| if req == nil { |
| return nil, fmt.Errorf("request is nil") |
| } |
| if ctx == nil { |
| ctx = req.Context() |
| } |
| httpReq := req.WithContext(ctx) |
| if preparer != nil { |
| if err := preparer.PrepareRequest(httpReq, auth); err != nil { |
| return nil, err |
| } |
| } |
| httpClient := newProxyAwareHTTPClient(ctx, cfg, auth, 0) |
| return httpClient.Do(httpReq) |
| } |
|
|