capture

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package capture is the exchange format between a client that connects and the report describing what it sent.

One shape serves both sides on purpose. The echo server answers every client — a real browser or this library — with the same JSON, so comparing the two is comparing two values of one type rather than reconciling two formats. The alternative, a browser-shaped struct and a client-shaped struct, is where "these fields look equivalent" quietly becomes "these fields are equivalent".

The authoritative field is RawClientHello: the bytes as they arrived. Every list below is derived from it and exists to be read by a human. A comparison re-parses the raw bytes instead of trusting the rendering, so a bug in the rendering can never make two different handshakes look identical.

Index

Constants

View Source
const (
	SourceBrowser  = "browser"
	SourceTLSFetch = "tlsforge"
)

Source labels which side produced a capture.

Variables

This section is empty.

Functions

This section is empty.

Types

type Brand

type Brand struct {
	Brand   string `json:"brand"`
	Version string `json:"version"`
}

Brand is one entry of navigator.userAgentData.brands.

type Capture

type Capture struct {
	Source     string `json:"source,omitempty"`
	Profile    string `json:"profile,omitempty"`
	Negotiated string `json:"alpn_negotiated,omitempty"`

	// RawClientHello is the ClientHello exactly as it arrived, record layer
	// included. Everything in TLS below is derived from it.
	RawClientHello []byte `json:"raw_client_hello"`

	TLS       TLS        `json:"tls"`
	HTTP2     *HTTP2     `json:"http2,omitempty"`
	HTTP1     *HTTP1     `json:"http1,omitempty"`
	Navigator *Navigator `json:"navigator,omitempty"`
}

Capture is one client's complete self-description.

func (*Capture) Hello

func (c *Capture) Hello() (*fingerprint.ClientHello, error)

Hello re-parses the authoritative bytes.

Comparison goes through here rather than through the rendered lists so that a mistake in the rendering cannot make two different handshakes compare equal.

func (*Capture) UserAgent

func (c *Capture) UserAgent() string

UserAgent returns the user-agent the request carried, which is the one that has to agree with the TLS handshake.

type HTTP1

type HTTP1 struct {
	Method string `json:"method"`
	Path   string `json:"path"`
	Proto  string `json:"proto"`
	// HeaderOrder stays lower-case so an HTTP/1.1 set can still be compared
	// directly with HTTP/2, where HPACK requires lower-case names. HeaderNames
	// is the same sequence as it appeared on the HTTP/1.1 wire.
	HeaderOrder []string      `json:"header_order"`
	HeaderNames []string      `json:"header_names,omitempty"`
	Headers     []HeaderField `json:"headers"`
}

HTTP1 is what an HTTP/1.1 client sent.

func (*HTTP1) Fingerprint

func (h *HTTP1) Fingerprint() *fingerprint.HTTP1

Fingerprint converts the rendered HTTP/1.1 view into its comparable form.

type HTTP2

type HTTP2 struct {
	Akamai            string        `json:"akamai_fingerprint"`
	Settings          []Setting     `json:"settings"`
	WindowUpdate      uint32        `json:"window_update"`
	Priorities        []Priority    `json:"priorities,omitempty"`
	HeaderPriority    *Priority     `json:"header_priority,omitempty"`
	PseudoHeaderOrder []string      `json:"pseudo_header_order"`
	HeaderOrder       []string      `json:"header_order"`
	Headers           []HeaderField `json:"headers"`
}

HTTP2 is the readable rendering of the connection preamble and first request.

func FromHTTP2

func FromHTTP2(h *fingerprint.HTTP2) *HTTP2

FromHTTP2 renders observed HTTP/2 traffic for humans.

func (*HTTP2) Fingerprint

func (h *HTTP2) Fingerprint() *fingerprint.HTTP2

Fingerprint converts the rendered HTTP/2 view back into the comparable form.

type HeaderField

type HeaderField struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

HeaderField is one header in wire order.

type Navigator struct {
	UserAgent           string          `json:"user_agent"`
	Languages           []string        `json:"languages,omitempty"`
	Platform            string          `json:"platform,omitempty"`
	Mobile              bool            `json:"mobile"`
	Brands              []Brand         `json:"brands,omitempty"`
	FullVersionList     []Brand         `json:"full_version_list,omitempty"`
	Architecture        string          `json:"architecture,omitempty"`
	Bitness             string          `json:"bitness,omitempty"`
	Model               string          `json:"model,omitempty"`
	PlatformVersion     string          `json:"platform_version,omitempty"`
	UAFullVersion       string          `json:"ua_full_version,omitempty"`
	WOW64               bool            `json:"wow64,omitempty"`
	FormFactors         []string        `json:"form_factors,omitempty"`
	DeviceMemory        float64         `json:"device_memory,omitempty"`
	HardwareConcurrency int             `json:"hardware_concurrency,omitempty"`
	Extra               json.RawMessage `json:"extra,omitempty"`
}

Navigator is what the browser says about itself in JavaScript.

It is collected because the HTTP layer has to agree with it: a request whose user-agent claims Chrome 151 while sec-ch-ua-platform-version claims a macOS that shipped with Chrome 120 is a contradiction no real browser produces. The high-entropy hints are stored verbatim rather than re-derived for the same reason — inventing one would be inventing a machine.

type Priority

type Priority struct {
	StreamID  uint32 `json:"stream_id"`
	Exclusive bool   `json:"exclusive"`
	DependsOn uint32 `json:"depends_on"`
	Weight    uint8  `json:"weight"`
}

Priority is a standalone PRIORITY frame.

type Setting

type Setting struct {
	ID    uint16 `json:"id"`
	Name  string `json:"name"`
	Value uint32 `json:"value"`
}

Setting is one HTTP/2 setting, named.

type TLS

type TLS struct {
	JA3     string `json:"ja3"`
	JA3Hash string `json:"ja3_hash"`
	JA4     string `json:"ja4"`
	JA4Raw  string `json:"ja4_r"`

	LegacyVersion       Value    `json:"legacy_version"`
	ServerName          string   `json:"server_name,omitempty"`
	CipherSuites        []Value  `json:"cipher_suites"`
	Extensions          []Value  `json:"extensions"`
	SupportedVersions   []Value  `json:"supported_versions,omitempty"`
	SupportedGroups     []Value  `json:"supported_groups,omitempty"`
	SignatureAlgorithms []Value  `json:"signature_algorithms,omitempty"`
	KeyShareGroups      []Value  `json:"key_share_groups,omitempty"`
	CertCompression     []Value  `json:"compress_certificate,omitempty"`
	ECPointFormats      []uint8  `json:"ec_point_formats,omitempty"`
	PSKKeyExchangeModes []uint8  `json:"psk_key_exchange_modes,omitempty"`
	ALPN                []string `json:"alpn,omitempty"`
	ApplicationSettings []string `json:"application_settings,omitempty"`

	// Resumed reports a pre_shared_key extension, which changes the extension
	// count and therefore the JA4. A resumed hello and a cold one from the SAME
	// browser have different JA4s; comparing across that line reports a
	// difference that is not one.
	Resumed bool `json:"session_resumed"`
}

TLS is the readable rendering of a ClientHello.

func FromClientHello

func FromClientHello(hello *fingerprint.ClientHello) TLS

FromClientHello renders a parsed hello for humans.

type Value

type Value struct {
	Code uint16 `json:"code"`
	Name string `json:"name"`
}

Value is a wire code together with its human name, so the JSON is readable without a registry lookup and still exact.

Jump to

Keyboard shortcuts

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