Spaces:
Sleeping
Sleeping
File size: 4,138 Bytes
da590a7 f5fca4e 9628c68 da590a7 9628c68 f5fca4e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | package utils
import (
"context"
"net"
"net/http"
"net/url"
"os"
"strings"
"time"
)
// NewHTTPClientWithGoogleDNS creates an HTTP client that forces DNS resolution via 8.8.8.8
func NewHTTPClientWithGoogleDNS() *http.Client {
dialer := &net.Dialer{
Resolver: &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{
Timeout: 10 * time.Second,
}
return d.DialContext(ctx, "udp", "8.8.8.8:53")
},
},
}
transport := &http.Transport{
DialContext: dialer.DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
return &http.Client{
Transport: transport,
}
}
// NewIPv4OnlyHTTPClient creates an HTTP client that forces IPv4 only (disables IPv6)
func NewIPv4OnlyHTTPClient() *http.Client {
dialer := &net.Dialer{
Resolver: &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
// Force IPv4 by dialing 8.8.8.8:53 (Google DNS)
d := net.Dialer{
Timeout: 10 * time.Second,
}
return d.DialContext(ctx, "udp", "8.8.8.8:53")
},
},
}
transport := &http.Transport{
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
// Force IPv4 by replacing any IPv6 addresses with IPv4 lookup
// If address contains colon (IPv6), resolve hostname separately and pick IPv4
if strings.Contains(address, ":") {
// Extract host from "host:port" or "[ipv6]:port"
host, port, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
// Resolve host to IPv4 addresses only
addrs, err := net.DefaultResolver.LookupIPAddr(ctx, host)
if err != nil {
return nil, err
}
// Find first IPv4 address
for _, addr := range addrs {
if addr.IP.To4() != nil {
return dialer.DialContext(ctx, "tcp", net.JoinHostPort(addr.IP.String(), port))
}
}
return nil, &net.DNSError{Err: "no IPv4 address found for " + host}
}
// Already IPv4 or hostname, use default dialer
return dialer.DialContext(ctx, "tcp", address)
},
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
return &http.Client{
Transport: transport,
}
}
// NewProxiedHTTPClient creates an HTTP client that uses a proxy if configured
// It respects standard proxy environment variables and adds proxy support for HTTPS
func NewProxiedHTTPClient() *http.Client {
// Check for proxy configuration
proxyURL := os.Getenv("TELEGRAM_PROXY_URL")
if proxyURL == "" {
proxyURL = os.Getenv("HTTPS_PROXY")
if proxyURL == "" {
proxyURL = os.Getenv("HTTP_PROXY")
}
}
transport := &http.Transport{
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
// If proxy is configured, set it up
if proxyURL != "" {
u, err := url.Parse(proxyURL)
if err != nil {
// If proxy URL is invalid, log and continue without proxy
// In production, we'd use a logger but this is a low-level utils function
// So we'll just proceed without proxy
} else {
transport.Proxy = http.ProxyURL(u)
}
}
return &http.Client{
Transport: transport,
}
}
// IsProxyOrNetworkError checks if an error indicates proxy or network policy block
func IsProxyOrNetworkError(err error) bool {
if err == nil {
return false
}
errStr := err.Error()
// Common network/block indicators
indicators := []string{
"operation not permitted",
"permission denied",
"network is unreachable",
"connection refused",
"timeout",
"no route to host",
"i/o timeout",
"dial tcp",
"connect:",
"proxy",
}
for _, indicator := range indicators {
if strings.Contains(strings.ToLower(errStr), indicator) {
return true
}
}
return false
}
|