tlsclient

package module
v1.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 14, 2026 License: MIT Imports: 29 Imported by: 0

README

TLS Client

Go Reference

A powerful Go HTTP client library that provides advanced TLS fingerprinting capabilities, allowing you to emulate various browsers and devices for web scraping, testing, and automation tasks.

Features

  • 🌐 Browser Emulation: Mimic TLS fingerprints of Chrome, Safari, OkHttp4, and HttpUrlConnection
  • 🚀 HTTP Protocol Support: HTTP/1.1, HTTP/2, and HTTP/3
  • 🗜️ Automatic Decompression: Built-in support for gzip, brotli, zstd, and deflate
  • 🪝 Hooks: Pre-request and post-request hooks for request/response manipulation
  • 🔄 Smart Redirects: Configurable redirect handling with custom policies
  • 📊 Bandwidth Tracking: Monitor network usage and performance
  • 🎯 Certificate Pinning: Enhanced security with certificate validation
  • 🌍 Proxy Support: HTTP, HTTPS, and SOCKS proxy support
  • High Performance: Optimized for speed and efficiency

Installation

go get github.com/nukilabs/tlsclient

Quick Start

package main

import (
    "fmt"
    "io"
    "log"
    "net/url"

    "github.com/nukilabs/http"
    "github.com/nukilabs/tlsclient"
    "github.com/nukilabs/tlsclient/profiles"
    "github.com/nukilabs/tlsclient/redirects"
)

func main() {
    // Create a new client with Chrome profile
    client := tlsclient.New(profiles.Chrome138)
    client.SetRedirectFunc(redirects.Chrome) // Use Chrome's redirect policy
    client.SetProxy(&url.URL{
        Scheme: "http",
        Host:   "localhost:8888",
    })
    
    // Make a GET request
    req, err := client.NewRequest(http.MethodGet, "https://example.com", nil)
    if err != nil {
        log.Fatal(err)
    }

    // Add chrome-like headers
    req.Header = http.Header{
		"sec-ch-ua":                 {"\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Google Chrome\";v=\"138\""},
		"sec-ch-ua-mobile":          {"?0"},
		"sec-ch-ua-platform":        {"\"Windows\""},
		"upgrade-insecure-requests": {"1"},
		"user-agent":                {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"},
		"accept":                    {"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"},
		"sec-fetch-site":            {"none"},
		"sec-fetch-mode":            {"navigate"},
		"sec-fetch-user":            {"?1"},
		"sec-fetch-dest":            {"document"},
		"accept-encoding":           {"gzip, deflate, br, zstd"},
		"accept-language":           {"de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7"},
		"priority":                  {"u=0, i"},
		http.HeaderOrderKey:         {"sec-ch-ua", "sec-ch-ua-mobile", "sec-ch-ua-platform", "upgrade-insecure-requests", "user-agent", "accept", "sec-fetch-site", "sec-fetch-mode", "sec-fetch-user", "sec-fetch-dest", "accept-encoding", "accept-language", "priority"},
	}

    // Perform the request
    res, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    
    // Read and print the response
    body, err := io.ReadAll(res.Body)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println(string(body))
}

Available Profiles

Chrome Browsers
// Chrome versions 120-138
client := tlsclient.New(profiles.Chrome138)  // Latest Chrome
client := tlsclient.New(profiles.Chrome120)  // Older Chrome version
Safari Browsers
// Safari 17
client := tlsclient.New(profiles.Safari17)
Android HttpUrlConnection
// Android API levels 21-35
client := tlsclient.New(profiles.HttpUrlConnectionAndroid(33))  // Android 13
client := tlsclient.New(profiles.HttpUrlConnectionAndroid(21))  // Android 5.0
Android OkHttp4
// Android API levels 21-35
client := tlsclient.New(profiles.Okhttp4Android(33))  // Android 13
client := tlsclient.New(profiles.Okhttp4Android(29))  // Android 10

Configuration Options

Basic Options
client := tlsclient.New(profiles.Chrome138,
    tlsclient.WithTimeout(30*time.Second),  // Set request timeout
    tlsclient.WithNoCookieJar(),            // Disable cookie jar
    tlsclient.WithNoAutoDecompress(),       // Disable automatic decompression
    tlsclient.WithNoFollowRedirects(),      // Disable automatic redirects
)
Advanced Transport Options
client := tlsclient.New(profiles.Chrome138,
    tlsclient.WithTransportOptions(tlsclient.TransportOptions{
        ServerNameOverride: "example.com",
        InsecureSkipVerify: true,
        DisableKeepAlives:  false,
        IdleConnTimeout:    90 * time.Second,
        DisableIPV4:        false,
        DisableIPV6:        false,
    }),
)
Certificate Pinning
client := tlsclient.New(profiles.Chrome138,
    tlsclient.WithAutoPinning(),
)

Redirect Handling

Disable Redirects
client.SetFollowRedirects(false)
Custom Redirect Policy
client.SetRedirectFunc(func(req *http.Request, via []*http.Request) error {
    // Maximum of 5 redirects
    if len(via) >= 5 {
        return errors.New("too many redirects")
    }
    
    // Don't follow redirects to different hosts
    if req.URL.Host != via[0].URL.Host {
        return errors.New("cross-host redirect not allowed")
    }
    
    return nil
})
Built-in Redirect Policies
client.SetRedirectFunc(redirects.Chrome) // Use Chrome's redirect policy

Proxy Support

// HTTP Proxy
client.SetProxy(&url.URL{
    Scheme: "http",
    Host:   "proxy.example.com:8080",
})

// SOCKS5 Proxy
client.SetProxy(&url.URL{
    Scheme: "socks5",
    Host:   "proxy.example.com:1080",
})

// Authenticated Proxy
client.SetProxy(&url.URL{
    Scheme: "http",
    Host:   "proxy.example.com:8080",
    User:   url.UserPassword("username", "password"),
})

Hooks

Pre-Request Hooks

Pre-hooks run before each request and can modify the request before it is sent.

func AddAuthHeader(client *tlsclient.Client, req *http.Request) (*http.Request, error) {
    req.Header.Set("Authorization", "Bearer my-token")
    return req, nil
}

client.AddPreHooks(AddAuthHeader)
Post-Request Hooks

Post-hooks run after each request and can inspect or modify the response.

func GotBlockedHook(client *tlsclient.Client, req *http.Request, res *http.Response) (*http.Response, error) {
    if res.StatusCode == http.StatusTooManyRequests {
        // Switch to a different proxy if blocked
        client.SetProxy(&url.URL{
            Scheme: "http",
            Host:   "localhost:8889",
        })
    }
    return res, nil
}

client.AddPostHooks(GotBlockedHook)
Managing Hooks
// Set hooks (replaces any existing hooks)
client.SetPreHooks(hook1, hook2)
client.SetPostHooks(hook1, hook2)

// Add hooks (appends to existing hooks)
client.AddPreHooks(hook3)
client.AddPostHooks(hook3)

// Delete all hooks
client.DeletePreHooks()
client.DeletePostHooks()

Best Practices

Connection Reuse
// Reuse the same client for multiple requests
client := tlsclient.New(profiles.Chrome138)

// Make multiple requests with the same client
for _, url := range urls {
    res, err := client.Get(url)
    if err != nil {
        continue
    }
    // Process response...
    res.Body.Close()
}
Resource Management
res, err := client.Get("https://example.com")
if err != nil {
    return err
}
defer res.Body.Close() // Always close response body

// Read response body
body, err := io.ReadAll(res.Body)
if err != nil {
    return err
}

Contributing

We welcome contributions from the community! This TLS Client is constantly evolving, and we'd love your help in making it even better. Here are some ways you can contribute:

🌐 TLS Profiles

We're always looking to expand our browser and device emulation capabilities. If you have access to a browser, device, or HTTP client that isn't currently supported, we'd love to add it!

How to contribute a new TLS profile:

  1. Capture the TLS fingerprint using tools like:

  2. Create the profile by adding it to the appropriate file in /profiles/:

    // profiles/newbrowser.go
    var NewBrowser = ClientProfile{
        ClientHelloSpec: func() *tls.ClientHelloSpec {
            return &tls.ClientHelloSpec{
                CipherSuites: []uint16{
                    // Your cipher suites here
                },
                Extensions: []tls.TLSExtension{
                    // Your extensions here
                },
                // ... other TLS parameters
            }
        },
        Settings: []http2.Setting{
            // HTTP/2 settings
        },
        // ... other profile parameters
    }
    
  3. Add tests in /tests/ to verify the profile works correctly

  4. Update documentation to include the new profile in the README

Browsers/Clients we'd love to see:

  • Firefox (all versions)
  • Edge (Chromium-based)
  • Opera
  • Brave
  • Mobile browsers (iOS Safari, Chrome Mobile, Samsung Internet)
  • HTTP clients (curl, wget, Python requests, etc.)
🔄 Redirect Functions

Different browsers handle redirects in unique ways. We're building a comprehensive library of redirect behaviors!

How to contribute redirect functions:

  1. Study browser behavior by testing how your target browser handles:

    • Different redirect status codes (301, 302, 303, 307, 308)
    • Cross-origin redirects
    • Protocol changes (HTTP to HTTPS)
    • Maximum redirect limits
    • Header preservation during redirects
  2. Implement the function in /redirects/:

    // redirects/newbrowser.go
    func NewBrowser(req *http.Request, via []*http.Request) error {
        // Implement browser-specific redirect logic
        if len(via) >= 20 { // Browser's max redirect limit
            return errors.New("too many redirects")
        }
    
        // Add any browser-specific redirect handling
        return nil
    }
    
  3. Add comprehensive tests to verify the redirect behavior matches the real browser

  4. Document the behavior with comments explaining the browser's specific quirks

We appreciate all contributions, no matter how small! 🙏

Acknowledgments


© 2025 nukilabs

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrCertificatePinningFailed = errors.New("tlsclient: certificate pinning failed")

Functions

func DecompressBody

func DecompressBody(res *http.Response)

Types

type Client

type Client struct {
	http.Client

	AutoDecompress bool
	// contains filtered or unexported fields
}

func New

func New(profile profiles.ClientProfile, options ...Option) *Client

func (*Client) AddPostHooks added in v1.7.1

func (c *Client) AddPostHooks(hooks ...PostHook)

func (*Client) AddPreHooks added in v1.7.1

func (c *Client) AddPreHooks(hooks ...PreHook)

func (*Client) Clone added in v1.6.3

func (c *Client) Clone() *Client

func (*Client) CloseIdleConnections

func (c *Client) CloseIdleConnections()

func (*Client) DeletePostHooks added in v1.7.1

func (c *Client) DeletePostHooks()

func (*Client) DeletePreHooks added in v1.7.1

func (c *Client) DeletePreHooks()

func (*Client) Do

func (c *Client) Do(req *http.Request) (*http.Response, error)

func (*Client) Get

func (c *Client) Get(url string) (*http.Response, error)

func (*Client) GetCookieJar

func (c *Client) GetCookieJar() http.CookieJar

func (*Client) GetCookies

func (c *Client) GetCookies(u *url.URL) []*http.Cookie

func (*Client) GetProxy

func (c *Client) GetProxy() any

GetProxy returns the current proxy value. The type depends on what was passed to SetProxy: *url.URL for proxy URLs, net.IP for single direct IPs, or [2]net.IP for dual-stack.

func (*Client) Head

func (c *Client) Head(url string) (*http.Response, error)

func (*Client) Post

func (c *Client) Post(url, contentType string, body io.Reader) (*http.Response, error)

func (*Client) PostForm

func (c *Client) PostForm(url string, data url.Values) (*http.Response, error)

func (*Client) ResetInHook

func (c *Client) ResetInHook()

func (*Client) SetCookieJar

func (c *Client) SetCookieJar(jar http.CookieJar)

func (*Client) SetCookies

func (c *Client) SetCookies(u *url.URL, cookies []*http.Cookie)

func (*Client) SetFollowRedirects

func (c *Client) SetFollowRedirects(follow bool)

func (*Client) SetPostHooks added in v1.7.1

func (c *Client) SetPostHooks(hooks ...PostHook)

func (*Client) SetPreHooks added in v1.7.1

func (c *Client) SetPreHooks(hooks ...PreHook)

func (*Client) SetProxy

func (c *Client) SetProxy(v any) error

SetProxy configures the proxy/dialer for the client. Accepted types:

  • *url.URL: proxy URL (http, https, socks5) or bare IP (no scheme) for direct binding
  • net.IP: direct connection bound to a single local IP
  • [2]net.IP: dual-stack direct connection bound to IPv4 ([0]) and IPv6 ([1])
  • nil: direct connection with no local address binding

func (*Client) SetRedirectFunc

func (c *Client) SetRedirectFunc(f func(req *http.Request, via []*http.Request) error)

type Option

type Option func(*Client)

func WithAutoPinning

func WithAutoPinning() Option

func WithNoAutoDecompress

func WithNoAutoDecompress() Option

func WithNoCookieJar

func WithNoCookieJar() Option

func WithNoFollowRedirects

func WithNoFollowRedirects() Option

func WithPinner added in v1.6.3

func WithPinner(pinner *Pinner) Option

func WithQUICConfig added in v1.5.2

func WithQUICConfig(quicConf *quic.Config) Option

func WithTLSConfig added in v1.5.2

func WithTLSConfig(tlsConf *tls.Config) Option

func WithTimeout

func WithTimeout(timeout time.Duration) Option

func WithTracker

func WithTracker(tracker bandwidth.Tracker) Option

func WithTransportOptions

func WithTransportOptions(opts TransportOptions) Option

type Pinner

type Pinner struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

func NewPinner

func NewPinner(auto bool) *Pinner

func (*Pinner) AddPins

func (p *Pinner) AddPins(hostname string, pins []string)

func (*Pinner) AutoPin

func (p *Pinner) AutoPin(addr string)

func (*Pinner) Fingerprint

func (p *Pinner) Fingerprint(cert *x509.Certificate) string

func (*Pinner) Pin

func (p *Pinner) Pin(certs []*x509.Certificate, addr string) error

type PostHook added in v1.7.1

type PostHook func(*Client, *http.Request, *http.Response) (*http.Response, error)

type PreHook added in v1.7.1

type PreHook func(*Client, *http.Request) (*http.Request, error)

type ProxyError

type ProxyError struct {
	Message string
}

func (ProxyError) Error

func (e ProxyError) Error() string

type RoundTripper

type RoundTripper struct {
	sync.Mutex
	// contains filtered or unexported fields
}

func NewRoundTripper

func NewRoundTripper(profile profiles.ClientProfile, dialer proxy.ContextDialer, pinner *Pinner, tracker bandwidth.Tracker, tlsConf *tls.Config, quicConf *quic.Config, opts *TransportOptions) *RoundTripper

func (*RoundTripper) CloseIdleConnections added in v1.5.2

func (rt *RoundTripper) CloseIdleConnections()

func (*RoundTripper) RoundTrip

func (rt *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error)

type TransportOptions

type TransportOptions struct {
	DisableKeepAlives bool
	IdleConnTimeout   time.Duration
	DisableIPV4       bool
	DisableIPV6       bool
	DisableHTTP3      bool
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL