Files
phishingclub/backend/vendor/github.com/enetx/surf/middleware_client.go
T
Ronni Skansing 07c8adaf76 update vendor deps
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
2025-11-06 23:31:08 +01:00

364 lines
11 KiB
Go

package surf
import (
"context"
"crypto/tls"
"fmt"
"maps"
"math/rand"
"net"
"net/url"
"time"
"github.com/enetx/g"
"github.com/enetx/http"
"github.com/enetx/http/cookiejar"
"github.com/enetx/http2"
"golang.org/x/net/publicsuffix"
)
// defaultDialerMW initializes the default network dialer for the surf client.
// Sets up timeout and keep-alive configuration for TCP connections.
func defaultDialerMW(client *Client) error {
client.dialer = &net.Dialer{Timeout: _dialerTimeout, KeepAlive: _TCPKeepAlive}
return nil
}
// defaultTLSConfigMW initializes the default TLS configuration for the surf client.
// Configures TLS settings with insecure skip verify enabled by default for flexibility.
func defaultTLSConfigMW(client *Client) error {
client.tlsConfig = &tls.Config{InsecureSkipVerify: true}
return nil
}
// defaultTransportMW initializes the default HTTP transport for the surf client.
// Configures connection pooling, timeouts, and enables HTTP/2 support by default.
func defaultTransportMW(client *Client) error {
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.DialContext = client.dialer.DialContext
transport.TLSClientConfig = client.tlsConfig
transport.MaxIdleConns = _maxIdleConns
transport.MaxConnsPerHost = _maxConnsPerHost
transport.MaxIdleConnsPerHost = _maxIdleConnsPerHost
transport.IdleConnTimeout = _idleConnTimeout
transport.ForceAttemptHTTP2 = true
client.transport = transport
return nil
}
// defaultClientMW initializes the default HTTP client for the surf client.
// Sets up the HTTP client with the configured transport and timeout settings.
func defaultClientMW(client *Client) error {
client.cli = &http.Client{Transport: client.transport, Timeout: _clientTimeout}
return nil
}
// boundaryMW sets a custom boundary function for multipart form data.
// The boundary function is called to generate unique boundaries for multipart requests.
func boundaryMW(client *Client, boundary func() g.String) error {
client.boundary = boundary
return nil
}
// forseHTTP1MW configures the client to use HTTP/1.1 forcefully.
// Disables HTTP/2 and forces the client to use only HTTP/1.1 protocol.
func forseHTTP1MW(client *Client) error {
transport := client.GetTransport().(*http.Transport)
transport.Protocols = new(http.Protocols)
transport.Protocols.SetHTTP1(true)
return nil
}
// sessionMW configures the client's cookie jar for session management.
// It initializes a new cookie jar and sets up the TLS configuration
// to manage client sessions efficiently.
func sessionMW(client *Client) error {
client.GetClient().Jar, _ = cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
client.GetTLSConfig().ClientSessionCache = tls.NewLRUClientSessionCache(0)
return nil
}
// disableKeepAliveMW disables the keep-alive setting for the client's transport.
func disableKeepAliveMW(client *Client) error {
client.GetTransport().(*http.Transport).DisableKeepAlives = true
return nil
}
// disableCompressionMW disables compression for the client's transport.
func disableCompressionMW(client *Client) error {
client.GetTransport().(*http.Transport).DisableCompression = true
return nil
}
// interfaceAddrMW configures the client's local network interface address for outbound connections.
// This allows binding the client to a specific network interface or IP address for dialing.
// Useful for systems with multiple network interfaces or for controlling which IP address to use.
func interfaceAddrMW(client *Client, address g.String) error {
if address != "" {
ip, err := net.ResolveTCPAddr("tcp", address.Std()+":0")
if err != nil {
return err
}
client.GetDialer().LocalAddr = ip
}
return nil
}
// timeoutMW configures the client's overall request timeout.
// This sets the maximum duration for entire HTTP requests including connection,
// request transmission, and response reading.
func timeoutMW(client *Client, timeout time.Duration) error {
client.GetClient().Timeout = timeout
return nil
}
// redirectPolicyMW configures the client's HTTP redirect handling behavior.
// Sets up redirect policies including maximum redirect count, host-only redirects,
// header forwarding on redirects, and custom redirect functions.
func redirectPolicyMW(client *Client) error {
builder := client.builder
maxRedirects := _maxRedirects
if builder != nil {
// Use custom redirect function if provided
if builder.checkRedirect != nil {
client.GetClient().CheckRedirect = builder.checkRedirect
return nil
}
// Override default max redirects if specified
if builder.maxRedirects != 0 {
maxRedirects = builder.maxRedirects
}
}
// Set up default redirect policy with configured behavior
client.GetClient().CheckRedirect = func(req *http.Request, via []*http.Request) error {
// Stop redirecting if maximum redirect count is exceeded
if len(via) >= maxRedirects {
return http.ErrUseLastResponse
}
if builder != nil {
// Only follow redirects within the same host if configured
if builder.followOnlyHostRedirects {
newHost := req.URL.Host
oldHost := via[0].Host
if oldHost == "" {
oldHost = via[0].URL.Host
}
if newHost != oldHost {
return http.ErrUseLastResponse
}
}
// Forward headers from original request to redirect if configured
if builder.forwardHeadersOnRedirect {
maps.Copy(req.Header, via[0].Header)
}
}
return nil
}
return nil
}
// dnsMW configures a custom DNS server for the client.
// Sets up the client to use the specified DNS server address for hostname resolution
// instead of the system's default DNS configuration.
func dnsMW(client *Client, dns g.String) error {
if dns.Empty() {
return nil
}
client.GetDialer().Resolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, _, _ string) (net.Conn, error) {
var dialer net.Dialer
return dialer.DialContext(ctx, "udp", dns.Std())
},
}
return nil
}
// dnsTLSMW configures DNS over TLS (DoT) for the client.
// Replaces the default DNS resolver with a secure DNS-over-TLS resolver
// to encrypt DNS queries and protect against DNS manipulation.
func dnsTLSMW(client *Client, resolver *net.Resolver) error {
client.GetDialer().Resolver = resolver
return nil
}
// unixSocketMW configures the client to connect via Unix domain sockets.
// Replaces the standard TCP connection with Unix socket communication,
// useful for connecting to local services that expose Unix socket interfaces.
func unixSocketMW(client *Client, address g.String) error {
if address.Empty() {
return nil
}
client.GetTransport().(*http.Transport).DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "unix", address.Std())
}
return nil
}
// proxyMW configures HTTP proxy settings for the client transport.
// Supports both static proxy configurations (string URLs) and dynamic proxy providers
// (functions that return proxy URLs for rotation). Handles various proxy types including
// HTTP, HTTPS, and SOCKS proxies. Skips configuration for JA3 and HTTP/3 transports
// which handle proxies differently.
func proxyMW(client *Client, proxys any) error {
// Skip proxy configuration for JA3 transport (handled separately)
if client.builder.ja {
return nil
}
// Skip if HTTP/3 transport is being used (handled separately)
if _, ok := client.GetTransport().(*uquicTransport); ok {
return nil
}
transport, ok := client.GetTransport().(*http.Transport)
if !ok {
return fmt.Errorf("transport is not *http.Transport")
}
// Clear proxy if nil provided
if proxys == nil {
transport.Proxy = nil
return nil
}
// Helper function to set static proxy
setProxy := func(proxy string) {
if proxy == "" {
return
}
if proxyURL, err := url.Parse(proxy); err == nil {
transport.Proxy = http.ProxyURL(proxyURL)
} else {
transport.Proxy = func(*http.Request) (*url.URL, error) {
return nil, fmt.Errorf("invalid proxy URL %q: %w", proxy, err)
}
}
}
// Handle static proxy configurations
switch v := proxys.(type) {
case string:
setProxy(v)
return nil
case g.String:
setProxy(v.Std())
return nil
}
// Handle dynamic proxy configurations - evaluate proxy per request
transport.Proxy = func(*http.Request) (*url.URL, error) {
var proxy string
switch v := proxys.(type) {
case func() g.String:
proxy = v().Std()
case []string:
if len(v) > 0 {
proxy = v[rand.Intn(len(v))]
}
case g.Slice[string]:
proxy = v.Random()
case g.Slice[g.String]:
proxy = v.Random().Std()
default:
return nil, fmt.Errorf("unsupported proxy type: %T", proxys)
}
if proxy == "" {
return nil, nil
}
return url.Parse(proxy)
}
return nil
}
// h2cMW configures HTTP/2 Cleartext (H2C) support for the client.
// H2C allows HTTP/2 communication over plain text connections without TLS.
// This is useful for internal communication or development scenarios where TLS is not required.
// Skips configuration if HTTP/3 transport is being used as they are incompatible.
func h2cMW(client *Client) error {
// H2C is incompatible with HTTP/3 transport - skip if HTTP/3 is being used
if _, ok := client.transport.(*uquicTransport); ok {
return nil
}
t2 := new(http2.Transport)
// Configure H2C specific settings
t2.AllowHTTP = true
t2.DisableCompression = client.GetTransport().(*http.Transport).DisableCompression
t2.IdleConnTimeout = client.transport.(*http.Transport).IdleConnTimeout
// Override TLS dial to use plain text connections
t2.DialTLSContext = func(_ context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
return net.Dial(network, addr)
}
// Apply HTTP/2 settings if configured
if client.builder.http2settings != nil {
h := client.builder.http2settings
// Helper function to append non-zero settings
appendSetting := func(id http2.SettingID, val uint32) {
if val != 0 || (id == http2.SettingEnablePush && h.usePush) {
t2.Settings = append(t2.Settings, http2.Setting{ID: id, Val: val})
}
}
// Apply all configured HTTP/2 settings
settings := [...]struct {
id http2.SettingID
val uint32
}{
{http2.SettingHeaderTableSize, h.headerTableSize},
{http2.SettingEnablePush, h.enablePush},
{http2.SettingMaxConcurrentStreams, h.maxConcurrentStreams},
{http2.SettingInitialWindowSize, h.initialWindowSize},
{http2.SettingMaxFrameSize, h.maxFrameSize},
{http2.SettingMaxHeaderListSize, h.maxHeaderListSize},
}
for _, s := range settings {
appendSetting(s.id, s.val)
}
// Apply flow control settings if configured
if h.connectionFlow != 0 {
t2.ConnectionFlow = h.connectionFlow
}
// Apply priority settings if configured
if !h.priorityParam.IsZero() {
t2.PriorityParam = h.priorityParam
}
if h.priorityFrames != nil {
t2.PriorityFrames = h.priorityFrames
}
}
client.cli.Transport = t2
return nil
}