Documentation
¶
Index ¶
- Variables
- func CheckLineEndings(raw []byte) string
- func Compress(data []byte, encoding string) ([]byte, error)
- func Decompress(data []byte, encoding string) ([]byte, bool)
- func NormalizeEncoding(encoding string) (string, bool)
- func ParseRequestLine(line []byte) (method, path, query, version string, err error)
- func PathWithoutQuery(p string) string
- type CertManager
- type H2RequestData
- type H2ResponseData
- type Header
- type Headers
- type HistoryEntry
- func (e *HistoryEntry) FormatRequest(buf *bytes.Buffer) []byte
- func (e *HistoryEntry) FormatResponse(buf *bytes.Buffer) []byte
- func (e *HistoryEntry) GetHost() string
- func (e *HistoryEntry) GetMethod() string
- func (e *HistoryEntry) GetPath() string
- func (e *HistoryEntry) GetRequestHeader(name string) string
- func (e *HistoryEntry) GetResponseHeader(name string) string
- func (e *HistoryEntry) GetStatusCode() int
- type HistoryMeta
- type HistoryStore
- func (h *HistoryStore) Close()
- func (h *HistoryStore) Count() int
- func (h *HistoryStore) Get(offset uint32) (*HistoryEntry, bool)
- func (h *HistoryStore) GetMeta(offset uint32) (*HistoryMeta, bool)
- func (h *HistoryStore) List(count int, startOffset uint32) []*HistoryEntry
- func (h *HistoryStore) ListMeta(count int, startOffset uint32) []HistoryMeta
- func (h *HistoryStore) Store(entry *HistoryEntry) uint32
- func (h *HistoryStore) Update(entry *HistoryEntry)
- type JSONModifier
- type Modifications
- type ProxyServer
- func (s *ProxyServer) Addr() string
- func (s *ProxyServer) CertManager() *CertManager
- func (s *ProxyServer) History() *HistoryStore
- func (s *ProxyServer) Serve() error
- func (s *ProxyServer) SetRuleApplier(applier RuleApplier)
- func (s *ProxyServer) Shutdown(ctx context.Context) error
- func (s *ProxyServer) WaitReady(ctx context.Context) error
- type RawHTTP1Request
- type RawHTTP1Response
- func (r *RawHTTP1Response) GetHeader(name string) string
- func (r *RawHTTP1Response) RemoveHeader(name string)
- func (r *RawHTTP1Response) SerializeHeaders(buf *bytes.Buffer) []byte
- func (r *RawHTTP1Response) SerializeRaw(buf *bytes.Buffer, preserveChunked bool) []byte
- func (r *RawHTTP1Response) SetHeader(name, value string)
- type RuleApplier
- type SendOptions
- type SendResult
- type Sender
- type Target
- type TimeoutConfig
- type WSFrame
- type WireFormat
Constants ¶
This section is empty.
Variables ¶
Functions ¶
func CheckLineEndings ¶
CheckLineEndings detects line ending issues in HTTP headers. Returns a description of the issue, or empty string if OK.
func Compress ¶
Compress compresses data with the specified encoding. Returns (compressed data, error). Unknown encodings return the original data unchanged.
Handles the same normalization as Decompress for consistency.
func Decompress ¶
Decompress decompresses data based on Content-Encoding. Returns (decompressed data, wasCompressed). If wasCompressed is true but returned data is nil, decompression failed. Unknown encodings return (original data, false).
Handles: - Case variations: "GZIP", "Gzip" normalized to "gzip" - Whitespace: " gzip " trimmed - x-gzip alias: treated as gzip - deflate: tries raw DEFLATE first, then zlib-wrapped - Multiple encodings (e.g., "gzip, br"): skipped (can't partially decode)
func NormalizeEncoding ¶
NormalizeEncoding normalizes a Content-Encoding header value. Returns the normalized encoding and whether it's a single supported encoding. Multiple encodings (e.g., "gzip, br") return ("", false) since we can't partially decode.
func ParseRequestLine ¶
ParseRequestLine extracts method, path, query, version from request line. Tolerant: accepts malformed lines if method and path are extractable.
func PathWithoutQuery ¶
PathWithoutQuery returns the path portion before any query string.
Types ¶
type CertManager ¶
type CertManager struct {
// contains filtered or unexported fields
}
CertManager handles CA certificate loading/generation and on-demand certificate generation for HTTPS MITM interception.
func (*CertManager) CACert ¶
func (m *CertManager) CACert() *x509.Certificate
CACert returns the CA certificate for clients to trust.
func (*CertManager) Close ¶
func (m *CertManager) Close() error
Close releases resources held by the cert cache.
func (*CertManager) GetCertificate ¶
func (m *CertManager) GetCertificate(hostname string) (*tls.Certificate, error)
GetCertificate returns a certificate for the hostname. Generates and caches if not already cached.
type H2RequestData ¶
type H2RequestData struct {
// Pseudo-headers
Method string `json:"method" msgpack:"m"` // from :method
Scheme string `json:"scheme" msgpack:"s"` // from :scheme
Authority string `json:"authority" msgpack:"a"` // from :authority
Path string `json:"path" msgpack:"p"` // from :path
// Regular headers (not pseudo-headers)
Headers Headers `json:"headers" msgpack:"h"`
// Body is the request body
Body []byte `json:"body,omitempty" msgpack:"b,omitempty"`
}
H2RequestData represents an HTTP/2 request for history storage.
func (*H2RequestData) GetHeader ¶
func (r *H2RequestData) GetHeader(name string) string
GetHeader returns the first header value with the given name (case-insensitive).
func (*H2RequestData) SetHeader ¶
func (r *H2RequestData) SetHeader(name, value string)
SetHeader sets or replaces the first header with the given name (case-insensitive).
type H2ResponseData ¶
type H2ResponseData struct {
// StatusCode from :status pseudo-header
StatusCode int `json:"status_code" msgpack:"sc"`
// Regular headers (not pseudo-headers)
Headers Headers `json:"headers" msgpack:"h"`
// Body is the response body
Body []byte `json:"body,omitempty" msgpack:"b,omitempty"`
}
H2ResponseData represents an HTTP/2 response for history storage.
func (*H2ResponseData) GetHeader ¶
func (r *H2ResponseData) GetHeader(name string) string
GetHeader returns the first header value with the given name (case-insensitive).
func (*H2ResponseData) SetHeader ¶
func (r *H2ResponseData) SetHeader(name, value string)
SetHeader sets or replaces the first header with the given name (case-insensitive).
type Header ¶
type Header struct {
// Name preserves original casing and whitespace anomalies
// (e.g., "Content-Type", "content-type", or "Header " with trailing space)
Name string `json:"name" msgpack:"n"`
// Value is the header value with leading/trailing whitespace trimmed
Value string `json:"value" msgpack:"v"`
// RawLine contains the original wire bytes for this header (excluding line ending).
// Used by SerializeRaw() to preserve exact wire format including obs-fold.
// nil when header was programmatically created or Wire format not tracked.
RawLine []byte `json:"raw_line,omitempty" msgpack:"rl,omitempty"`
}
Header represents a single HTTP header preserving original formatting.
type Headers ¶
type Headers []Header
Headers is a slice of Header with helper methods for case-insensitive access. JSON serializes as an array, same as []Header.
func (*Headers) Get ¶
Get returns the first header value with the given name (case-insensitive). Returns empty string if not found.
type HistoryEntry ¶
type HistoryEntry struct {
// Offset is the monotonic history index
Offset uint32 `json:"offset" msgpack:"o"`
// Protocol identifies the HTTP version: "http/1.1", "h2", or "websocket"
Protocol string `json:"protocol" msgpack:"pr"`
// HTTP/1.1 request/response (nil for HTTP/2)
Request *RawHTTP1Request `json:"request,omitempty" msgpack:"rq,omitempty"`
Response *RawHTTP1Response `json:"response,omitempty" msgpack:"rs,omitempty"`
// HTTP/2 request/response (nil for HTTP/1.1)
H2Request *H2RequestData `json:"h2_request,omitempty" msgpack:"h2q,omitempty"`
H2Response *H2ResponseData `json:"h2_response,omitempty" msgpack:"h2r,omitempty"`
H2StreamID uint32 `json:"h2_stream_id,omitempty" msgpack:"h2s,omitempty"` // for debugging/correlation
// WSFrames contains WebSocket frames for Protocol="websocket" entries.
// The handshake is stored in Request/Response; frames are appended here.
WSFrames []WSFrame `json:"ws_frames,omitempty" msgpack:"ws,omitempty"`
// Timing metadata
Timestamp time.Time `json:"timestamp" msgpack:"ts"`
Duration time.Duration `json:"duration" msgpack:"d"`
}
HistoryEntry represents a stored request/response pair. Embeds the parsed types directly for memory efficiency. The SerializeRaw() methods on Request/Response reconstruct wire bytes on demand.
func (*HistoryEntry) FormatRequest ¶
func (e *HistoryEntry) FormatRequest(buf *bytes.Buffer) []byte
FormatRequest returns the request in wire-compatible format. For HTTP/1.1, uses SerializeRaw to preserve anomalies like bare-LF. For HTTP/2, builds a similar text format from pseudo-headers and headers.
func (*HistoryEntry) FormatResponse ¶
func (e *HistoryEntry) FormatResponse(buf *bytes.Buffer) []byte
FormatResponse returns the response in wire-compatible format. For HTTP/1.1, uses SerializeRaw to preserve anomalies like bare-LF. For HTTP/2, builds a similar text format from pseudo-headers and headers.
func (*HistoryEntry) GetHost ¶
func (e *HistoryEntry) GetHost() string
GetHost returns the host for any protocol.
func (*HistoryEntry) GetMethod ¶
func (e *HistoryEntry) GetMethod() string
GetMethod returns the request method for any protocol.
func (*HistoryEntry) GetPath ¶
func (e *HistoryEntry) GetPath() string
GetPath returns the request path for any protocol.
func (*HistoryEntry) GetRequestHeader ¶
func (e *HistoryEntry) GetRequestHeader(name string) string
GetRequestHeader returns a request header value (case-insensitive).
func (*HistoryEntry) GetResponseHeader ¶
func (e *HistoryEntry) GetResponseHeader(name string) string
GetResponseHeader returns a response header value (case-insensitive).
func (*HistoryEntry) GetStatusCode ¶
func (e *HistoryEntry) GetStatusCode() int
GetStatusCode returns the response status code for any protocol.
type HistoryMeta ¶
type HistoryMeta struct {
Offset uint32 `msgpack:"o"`
Protocol string `msgpack:"pr"`
Method string `msgpack:"m"`
Host string `msgpack:"h"`
Path string `msgpack:"p"` // includes query string
Status int `msgpack:"s"`
ContentType string `msgpack:"ct"`
RespLen int `msgpack:"rl"`
H2StreamID uint32 `msgpack:"h2,omitempty"`
Timestamp time.Time `msgpack:"ts"`
Duration time.Duration `msgpack:"d"`
}
HistoryMeta holds lightweight metadata extracted at store time. Used by summary/list paths to avoid deserializing full request/response bodies.
type HistoryStore ¶
type HistoryStore struct {
// contains filtered or unexported fields
}
HistoryStore provides typed access to proxy history backed by store.Storage.
func (*HistoryStore) Count ¶
func (h *HistoryStore) Count() int
Count returns total number of entries.
func (*HistoryStore) Get ¶
func (h *HistoryStore) Get(offset uint32) (*HistoryEntry, bool)
Get retrieves an entry by offset.
func (*HistoryStore) GetMeta ¶
func (h *HistoryStore) GetMeta(offset uint32) (*HistoryMeta, bool)
GetMeta retrieves lightweight metadata for an entry by offset.
func (*HistoryStore) List ¶
func (h *HistoryStore) List(count int, startOffset uint32) []*HistoryEntry
List returns entries starting from startOffset, up to count. Returns entries in offset order.
func (*HistoryStore) ListMeta ¶
func (h *HistoryStore) ListMeta(count int, startOffset uint32) []HistoryMeta
ListMeta returns metadata for entries starting from startOffset, up to count. Only deserializes lightweight metadata, skipping full request/response bodies.
func (*HistoryStore) Store ¶
func (h *HistoryStore) Store(entry *HistoryEntry) uint32
Store adds an entry and assigns the next offset. Returns the assigned offset.
func (*HistoryStore) Update ¶
func (h *HistoryStore) Update(entry *HistoryEntry)
Update persists changes to an existing entry. The entry must have been previously stored (Offset must be valid).
type JSONModifier ¶
JSONModifier modifies JSON body with set/remove operations. Provided by service layer to avoid circular imports.
type Modifications ¶
type Modifications struct {
Method string // Override HTTP method
SetHeaders map[string]string // Add or replace headers
RemoveHeaders []string // Remove headers by name
Body []byte // Replace entire body (mutually exclusive with JSON mods)
SetJSON map[string]any // Modify JSON fields
RemoveJSON []string // Remove JSON fields
SetParams map[string]string // Set query parameters
RemoveParams []string // Remove query parameters
}
Modifications specifies changes to apply to a request.
type ProxyServer ¶
type ProxyServer struct {
// contains filtered or unexported fields
}
ProxyServer is an HTTP proxy server that captures request/response pairs.
func NewProxyServer ¶
func NewProxyServer(port int, configDir string, maxBodyBytes int, historyStorage store.Storage, timeouts TimeoutConfig) (*ProxyServer, error)
NewProxyServer creates a new proxy server with HTTPS MITM support. configDir is the directory for CA certificates (e.g., ~/.sectool). maxBodyBytes limits request and response body sizes stored in history. historyStorage is the storage backend for proxy history entries.
func (*ProxyServer) Addr ¶
func (s *ProxyServer) Addr() string
Addr returns the proxy listener address (e.g., "127.0.0.1:12345").
func (*ProxyServer) CertManager ¶
func (s *ProxyServer) CertManager() *CertManager
CertManager returns the certificate manager for external access.
func (*ProxyServer) History ¶
func (s *ProxyServer) History() *HistoryStore
History returns the history store for external access.
func (*ProxyServer) Serve ¶
func (s *ProxyServer) Serve() error
Serve starts accepting connections. Blocks until shutdown.
func (*ProxyServer) SetRuleApplier ¶
func (s *ProxyServer) SetRuleApplier(applier RuleApplier)
SetRuleApplier sets the rule applier for all handlers. Call after construction but before Serve().
type RawHTTP1Request ¶
type RawHTTP1Request struct {
// Request line components
Method string `json:"method" msgpack:"m"` // "GET", "POST", etc.
Path string `json:"path" msgpack:"p"` // path without query string, e.g., "/path"
Query string `json:"query,omitempty" msgpack:"q,omitempty"` // query string without leading ?, e.g., "foo=bar"
Version string `json:"version" msgpack:"v"` // "HTTP/1.1" or "HTTP/1.0"
// Headers preserves order and original name casing/whitespace
Headers Headers `json:"headers" msgpack:"h"`
// Body is the request body (decoded if chunked, raw otherwise)
// For chunked encoding, this contains the reassembled body without chunk framing
Body []byte `json:"body,omitempty" msgpack:"b,omitempty"`
// Trailers for chunked encoding (raw bytes, rare but must preserve)
// TODO - FUTURE - Parse trailers into []Header if trailer rules are needed
Trailers []byte `json:"trailers,omitempty" msgpack:"t,omitempty"`
// Protocol metadata for replay fidelity
Protocol string `json:"protocol" msgpack:"pr"` // "http/1.1" - stored for history/replay
// Wire contains metadata about the original wire encoding.
// Used by SerializeRaw() to preserve exact wire format.
Wire *WireFormat `json:"wire,omitempty" msgpack:"w,omitempty"`
}
RawHTTP1Request represents a parsed HTTP/1.1 request with wire-level fidelity. The SerializeRaw() method reconstructs wire bytes from the stored components.
func (*RawHTTP1Request) GetHeader ¶
func (r *RawHTTP1Request) GetHeader(name string) string
GetHeader returns the first header value with the given name (case-insensitive).
func (*RawHTTP1Request) RemoveHeader ¶
func (r *RawHTTP1Request) RemoveHeader(name string)
RemoveHeader removes all headers with the given name (case-insensitive).
func (*RawHTTP1Request) SerializeRaw ¶
func (r *RawHTTP1Request) SerializeRaw(buf *bytes.Buffer, preserveChunked bool) []byte
SerializeRaw reconstructs wire bytes preserving original formatting when available. If preserveChunked is true and Wire.WasChunked is true, emits chunked encoding. Uses Header.RawLine when available to preserve exact original bytes including obs-fold. Uses bare LF line endings when Wire.UsedBareLF is true. Falls back to standard formatting when Wire is nil or RawLine is not available.
func (*RawHTTP1Request) SetHeader ¶
func (r *RawHTTP1Request) SetHeader(name, value string)
SetHeader sets or replaces the first header with the given name (case-insensitive).
type RawHTTP1Response ¶
type RawHTTP1Response struct {
// Status line components
Version string `json:"version" msgpack:"v"` // "HTTP/1.1" or "HTTP/1.0"
StatusCode int `json:"status_code" msgpack:"sc"` // 200, 404, etc.
StatusText string `json:"status_text,omitempty" msgpack:"st,omitempty"` // "OK", "Not Found", etc.
// Headers preserves order and original name casing
Headers Headers `json:"headers" msgpack:"h"`
// Body is the response body (decoded if chunked, raw otherwise)
Body []byte `json:"body,omitempty" msgpack:"b,omitempty"`
// Trailers for chunked encoding (raw bytes)
// TODO - FUTURE - Parse trailers into []Header if trailer rules are needed
Trailers []byte `json:"trailers,omitempty" msgpack:"t,omitempty"`
// Wire contains metadata about the original wire encoding.
// Used by SerializeRaw() to preserve exact wire format.
Wire *WireFormat `json:"wire,omitempty" msgpack:"w,omitempty"`
}
RawHTTP1Response represents a parsed HTTP/1.1 response with wire-level fidelity.
func (*RawHTTP1Response) GetHeader ¶
func (r *RawHTTP1Response) GetHeader(name string) string
GetHeader returns the first header value with the given name (case-insensitive).
func (*RawHTTP1Response) RemoveHeader ¶
func (r *RawHTTP1Response) RemoveHeader(name string)
RemoveHeader removes all headers with the given name (case-insensitive).
func (*RawHTTP1Response) SerializeHeaders ¶
func (r *RawHTTP1Response) SerializeHeaders(buf *bytes.Buffer) []byte
SerializeHeaders reconstructs the status line and headers only (no body). Useful for SendRequestResult where headers and body are returned separately.
func (*RawHTTP1Response) SerializeRaw ¶
func (r *RawHTTP1Response) SerializeRaw(buf *bytes.Buffer, preserveChunked bool) []byte
SerializeRaw reconstructs wire bytes preserving original formatting when available. If preserveChunked is true and Wire.WasChunked is true, emits chunked encoding. Uses Header.RawLine when available to preserve exact original bytes including obs-fold. Uses bare LF line endings when Wire.UsedBareLF is true. Falls back to standard formatting when Wire is nil or RawLine is not available.
func (*RawHTTP1Response) SetHeader ¶
func (r *RawHTTP1Response) SetHeader(name, value string)
SetHeader sets or replaces the first header with the given name (case-insensitive).
type RuleApplier ¶
type RuleApplier interface {
// ApplyRequestRules applies request header and body rules.
// Returns the modified request (may be same instance if no changes).
ApplyRequestRules(req *RawHTTP1Request) *RawHTTP1Request
// ApplyResponseRules applies response header and body rules.
// Handles decompression/recompression for body rules.
ApplyResponseRules(resp *RawHTTP1Response) *RawHTTP1Response
// ApplyRequestBodyOnlyRules applies only body rules to a request body.
// Used by HTTP/2 where headers are sent separately before body.
// Requires headers for Content-Encoding detection (compression-aware).
// Does not apply header rules.
// Returns error if recompression fails (caller should reset stream).
ApplyRequestBodyOnlyRules(body []byte, headers Headers) ([]byte, error)
// ApplyResponseBodyOnlyRules applies only body rules to a response body.
// Used by HTTP/2 where headers are sent separately before body.
// Requires headers for Content-Encoding detection (compression-aware).
// Does not apply header rules.
ApplyResponseBodyOnlyRules(body []byte, headers Headers) []byte
// ApplyWSRules applies WebSocket rules to frame payload.
// direction is "ws:to-server" or "ws:to-client".
ApplyWSRules(payload []byte, direction string) []byte
// HasBodyRules returns true if there are body rules for request or response.
// Used by HTTP/2 handler to decide whether to buffer full bodies.
// isRequest=true checks for request_body rules, false checks for response_body rules.
HasBodyRules(isRequest bool) bool
}
RuleApplier applies match/replace rules to requests and responses. Implemented by the service layer (NativeProxyBackend). Rules are applied in the order they were added (list order).
type SendOptions ¶
type SendOptions struct {
RawRequest []byte // Raw HTTP request bytes
Target Target // Where to send
Modifications *Modifications // Optional changes
Force bool // Bypass validation
// Protocol specifies the original request's protocol.
// Values: "http/1.1", "h2", or "" (defaults to http/1.1)
// When "h2", the sender will negotiate HTTP/2 with the server.
Protocol string
}
SendOptions configures request sending.
type SendResult ¶
type SendResult struct {
Response *RawHTTP1Response
Duration time.Duration
}
type Sender ¶
type Sender struct {
// JSONModifier is called to apply JSON modifications to request body.
// If nil, SetJSON/RemoveJSON modifications are ignored.
JSONModifier JSONModifier
// Timeouts holds configurable timeout values for dial, read, and write.
// Zero values mean no timeout.
Timeouts TimeoutConfig
}
Sender sends HTTP requests with wire-level fidelity.
func (*Sender) Send ¶
func (s *Sender) Send(ctx context.Context, opts SendOptions) (*SendResult, error)
Send sends a request and returns the response.
func (*Sender) SendWithRedirects ¶
func (s *Sender) SendWithRedirects(ctx context.Context, opts SendOptions) (*SendResult, error)
SendWithRedirects sends a request and follows redirects.
type TimeoutConfig ¶
type TimeoutConfig struct {
DialTimeout time.Duration
ReadTimeout time.Duration
WriteTimeout time.Duration
}
TimeoutConfig holds configurable timeout values for proxy operations.
type WSFrame ¶
type WSFrame struct {
// Direction is "to-server" or "to-client"
Direction string `json:"direction" msgpack:"dr"`
// Opcode is the WebSocket opcode (1=text, 2=binary, 8=close, 9=ping, 10=pong)
Opcode byte `json:"opcode" msgpack:"op"`
// Payload is the frame payload (unmasked)
Payload []byte `json:"payload,omitempty" msgpack:"pl,omitempty"`
// Timestamp when the frame was captured
Timestamp time.Time `json:"timestamp" msgpack:"ts"`
}
WSFrame represents a single WebSocket frame stored in history.
type WireFormat ¶
type WireFormat struct {
// WasChunked indicates the body was received with chunked transfer encoding.
// When true, SerializeRaw can optionally re-emit chunked encoding.
WasChunked bool `json:"was_chunked,omitempty" msgpack:"wc,omitempty"`
// UsedBareLF indicates the message used bare LF (\n) instead of CRLF (\r\n).
// When true, SerializeRaw uses bare LF for line endings.
UsedBareLF bool `json:"used_bare_lf,omitempty" msgpack:"lf,omitempty"`
}
WireFormat stores metadata about the original wire encoding.