Documentation
¶
Overview ¶
Package echo provides a simple proxy server implementation in Go, inspired by Whistle.
Features ¶
- HTTP Proxy: Supports standard HTTP proxying.
- HTTPS/TCP Tunneling: Supports CONNECT method for HTTPS and generic TCP tunneling.
- WebSocket Support: Supports WebSocket upgrades (hijacking) and tunneling.
- Plugin System: Flexible plugin system to modify requests and responses.
Quick Start ¶
To start the proxy server, provide a Root CA certificate and private key:
certFile, _ := os.ReadFile("certs/rootCA.crt")
keyFile, _ := os.ReadFile("certs/rootCA.key")
e, err := echo.NewEcho(certFile, keyFile)
if err != nil {
log.Fatal(err)
}
server := &http.Server{
Addr: ":8888",
Handler: e,
}
server.ListenAndServe()
Plugins ¶
Add plugins to intercept and modify requests/responses:
e.AddPlugin(&echo.Plugin{
Match: "example.com",
OnRequest: func(ctx *echo.Context) {
ctx.SetRequestHeader("X-Custom-Header", "value")
},
OnResponse: func(ctx *echo.Context) {
body, _ := ctx.GetResponseBody()
ctx.SetResponseBody(strings.ReplaceAll(body, "old", "new"))
},
})
Forwarding ¶
Use TargetConfig to forward requests to a different server:
e.AddPlugin(&echo.Plugin{
Match: "example.com",
Target: &echo.TargetConfig{Protocol: "http", Host: "localhost", Port: 3000},
})
Mock Response ¶
Use MockResponse to return a static response:
e.AddPlugin(&echo.Plugin{
Match: "example.com/api",
MockResponse: &echo.MockResponse{
StatusCode: 200,
Headers: map[string]string{"Content-Type": "application/json"},
Body: `{"status":"ok"}`,
},
})
Index ¶
- Variables
- func CopyHeader(dst, src http.Header)
- func DecompressBody(res *http.Response) (io.ReadCloser, error)
- func DelHopHeaders(header http.Header)
- func IsMatch(hostname, pattern string) bool
- func IsWebSocketRequest(r *http.Request) bool
- func SetLogEnabled(enabled bool)
- type ConnectHandler
- type Context
- func (c *Context) DelRequestHeader(key string)
- func (c *Context) DelResponseHeader(key string)
- func (c *Context) GetMockResponse() *MockResponse
- func (c *Context) GetRequestHeader(key string) string
- func (c *Context) GetResponseBody() (string, error)
- func (c *Context) GetResponseHeader(key string) string
- func (c *Context) Mock(status int, headers map[string]string, body interface{})
- func (c *Context) SetRequestHeader(key, value string)
- func (c *Context) SetResponseBody(body string)
- func (c *Context) SetResponseHeader(key, value string)
- type Echo
- type HTTPHandler
- type MitmServer
- type MockResponse
- type Options
- type Plugin
- type PluginLoader
- func (l *PluginLoader) AddPlugin(plugin *Plugin)
- func (l *PluginLoader) GetPlugins() []*Plugin
- func (l *PluginLoader) Load(plugins []*Plugin) error
- func (l *PluginLoader) MatchPlugin(hostname string) *Plugin
- func (l *PluginLoader) MatchPluginForRequest(r *http.Request) *Plugin
- func (l *PluginLoader) MatchPlugins(hostname string) []*Plugin
- func (l *PluginLoader) MatchPluginsForRequest(r *http.Request) []*Plugin
- type TCPRelay
- type TargetConfig
- type WebSocketHandler
Constants ¶
This section is empty.
Variables ¶
var BypassDomains = []string{
"*.openai.com",
"*.chatgpt.com",
"chat.openai.com",
"api.openai.com",
"auth0.openai.com",
"*.apple.com",
"*.icloud.com",
"*.mzstatic.com",
"*.apple-cloudkit.com",
"*.cdn-apple.com",
"*.itunes.com",
"*.appleimg.com",
"*.google.com",
"*.googleapis.com",
"*.gstatic.com",
"*.googleusercontent.com",
"*.googlevideo.com",
"*.youtube.com",
"*.ytimg.com",
"*.ggpht.com",
"*.android.com",
"*.microsoft.com",
"*.microsoftonline.com",
"*.live.com",
"*.office.com",
"*.office365.com",
"*.windows.com",
"*.windowsupdate.com",
"*.azure.com",
"*.bing.com",
"*.msn.com",
"*.amazon.com",
"*.amazonaws.com",
"*.cloudfront.net",
"*.paypal.com",
"*.stripe.com",
"*.visa.com",
"*.mastercard.com",
"*.americanexpress.com",
"*.facebook.com",
"*.instagram.com",
"*.whatsapp.com",
"*.twitter.com",
"*.x.com",
"*.okta.com",
"*.auth0.com",
"*.duo.com",
"*.dropbox.com",
"*.slack.com",
"*.zoom.us",
"*.netflix.com",
"*.spotify.com",
}
BypassDomains contains domains that should bypass MITM interception. These services typically use certificate pinning or have strict security requirements.
Functions ¶
func CopyHeader ¶
CopyHeader copies headers from source to destination
func DecompressBody ¶
func DecompressBody(res *http.Response) (io.ReadCloser, error)
DecompressBody returns a reader that decompresses the response body if needed
func DelHopHeaders ¶
DelHopHeaders removes hop-by-hop headers
func IsMatch ¶
IsMatch checks if a hostname matches a pattern Supports: - Exact match: "example.com" - Wildcard: "*.example.com" - Substring: "example" (matches "example.com", "test.example.com", etc.)
func IsWebSocketRequest ¶
IsWebSocketRequest checks if the request is a WebSocket upgrade
func SetLogEnabled ¶
func SetLogEnabled(enabled bool)
Types ¶
type ConnectHandler ¶
type ConnectHandler struct {
CertManager *cert.Manager
PluginLoader *PluginLoader
HTTPHandler *HTTPHandler // Shared HTTP handler
InterceptOnlyMatched bool // Only intercept if plugin matches
UpstreamProxy string // Upstream proxy URL
// contains filtered or unexported fields
}
ConnectHandler handles CONNECT requests and MITM
func (*ConnectHandler) HandleTunnel ¶
func (h *ConnectHandler) HandleTunnel(w http.ResponseWriter, r *http.Request)
HandleTunnel handles the CONNECT request
type Context ¶
type Context struct {
Req *http.Request
Res *http.Response // Nil in OnRequest
// contains filtered or unexported fields
}
Context provides access to the request and response for plugins
func (*Context) DelRequestHeader ¶
DelRequestHeader deletes a header from the request
func (*Context) DelResponseHeader ¶
DelResponseHeader deletes a header from the response
func (*Context) GetMockResponse ¶
func (c *Context) GetMockResponse() *MockResponse
GetMockResponse returns the set mock response
func (*Context) GetRequestHeader ¶
GetRequestHeader gets a header from the request
func (*Context) GetResponseBody ¶
GetResponseBody reads and returns the response body as a string It automatically decompresses the body if needed and updates the response to be uncompressed for subsequent reads.
func (*Context) GetResponseHeader ¶
GetResponseHeader gets a header from the response
func (*Context) SetRequestHeader ¶
SetRequestHeader sets a header on the request
func (*Context) SetResponseBody ¶
SetResponseBody sets the response body
func (*Context) SetResponseHeader ¶
SetResponseHeader sets a header on the response
type Echo ¶
type Echo struct {
// contains filtered or unexported fields
}
func NewEchoWithOptions ¶ added in v0.7.0
NewEchoWithOptions creates a new Echo instance with custom options
func (*Echo) ListenTCP ¶ added in v0.10.0
ListenTCP starts a TCP relay on listenAddr that accepts raw TCP connections and forwards them to echoAddr using HTTP proxy protocol. The relay infers the target destination from TLS SNI or HTTP Host header.
func (*Echo) ShutdownTCP ¶ added in v0.10.0
func (e *Echo) ShutdownTCP()
ShutdownTCP stops the TCP relay if one is running.
func (*Echo) ShutdownTUN ¶ added in v0.10.0
func (e *Echo) ShutdownTUN()
ShutdownTUN stops the TUN forwarder if one is running.
type HTTPHandler ¶
type HTTPHandler struct {
PluginLoader *PluginLoader
Transport *http.Transport
FallbackTransport *http.Transport // 直连,用于上游代理不可用时 fallback
UpstreamProxy string
}
HTTPHandler handles standard HTTP proxy requests
func NewHTTPHandler ¶
func NewHTTPHandler(loader *PluginLoader) *HTTPHandler
NewHTTPHandler creates a new HTTP handler with a custom transport
func NewHTTPHandlerWithUpstream ¶ added in v0.9.0
func NewHTTPHandlerWithUpstream(loader *PluginLoader, upstreamProxy string) *HTTPHandler
NewHTTPHandlerWithUpstream creates a new HTTP handler with upstream proxy support
func (*HTTPHandler) HandleRequest ¶
func (h *HTTPHandler) HandleRequest(w http.ResponseWriter, r *http.Request)
HandleRequest processes the HTTP request
type MitmServer ¶
type MockResponse ¶
type MockResponse struct {
StatusCode int
Headers map[string]string
Body interface{} // string or []byte
}
MockResponse defines a static response to return
type Options ¶ added in v0.7.0
type Options struct {
// EnableBuiltinBypass enables built-in bypass rules for common services
// that use certificate pinning (Apple, Google, ChatGPT, etc.)
EnableBuiltinBypass bool
// InterceptOnlyMatched if true, only intercept requests that match a plugin.
// By default (false), all HTTPS traffic on port 443 is intercepted.
// When enabled, unmatched requests are tunneled directly without MITM.
InterceptOnlyMatched bool
// UpstreamProxy specifies an upstream proxy to forward requests to.
// Format: "http://proxy:port" or "socks5://proxy:port"
// When set, echo will forward all outbound requests through this proxy
// instead of connecting directly to targets.
UpstreamProxy string
// Tun enables TUN-level traffic forwarding (process-based routing).
// When true, TunConfig is required.
Tun bool
// TunConfig is the TUN configuration. Only used when Tun is true.
// Can be loaded from a file via tun.LoadConfig() or built programmatically.
TunConfig *tun.TunConfig
// TunDefaultInterface overrides TunConfig.Route.DefaultInterface.
// Use this as an initialization-level fallback when automatic default
// interface detection fails on multi-adapter Windows machines.
TunDefaultInterface string
}
Options configures Echo behavior
type Plugin ¶
type Plugin struct {
Match string
Target *TargetConfig
MockResponse *MockResponse
Bypass bool // If true, skip MITM and tunnel directly
Disabled bool // If true, the plugin is ignored by matchers
// Hooks
OnRequest func(ctx *Context)
OnResponse func(ctx *Context)
}
Plugin represents a forwarding rule configuration
type PluginLoader ¶
type PluginLoader struct {
// contains filtered or unexported fields
}
PluginLoader handles loading and managing plugins
func NewPluginLoader ¶
func NewPluginLoader(plugins []*Plugin) (*PluginLoader, error)
NewPluginLoader creates a new plugin loader
func (*PluginLoader) AddPlugin ¶
func (l *PluginLoader) AddPlugin(plugin *Plugin)
func (*PluginLoader) GetPlugins ¶
func (l *PluginLoader) GetPlugins() []*Plugin
GetPlugins returns all loaded plugins
func (*PluginLoader) Load ¶
func (l *PluginLoader) Load(plugins []*Plugin) error
Load loads plugins from the hardcoded registry
func (*PluginLoader) MatchPlugin ¶
func (l *PluginLoader) MatchPlugin(hostname string) *Plugin
MatchPlugin finds the first plugin that matches the given hostname
func (*PluginLoader) MatchPluginForRequest ¶
func (l *PluginLoader) MatchPluginForRequest(r *http.Request) *Plugin
func (*PluginLoader) MatchPlugins ¶
func (l *PluginLoader) MatchPlugins(hostname string) []*Plugin
MatchPlugins returns all plugins that match the given hostname, in order
func (*PluginLoader) MatchPluginsForRequest ¶
func (l *PluginLoader) MatchPluginsForRequest(r *http.Request) []*Plugin
MatchPluginsForRequest returns all plugins that match the given request URL/host, in order
type TCPRelay ¶ added in v0.10.0
type TCPRelay struct {
// contains filtered or unexported fields
}
TCPRelay accepts raw TCP connections on a listen port, infers the target destination from TLS SNI or HTTP Host header, and forwards traffic to an upstream Echo HTTP proxy using standard HTTP proxy protocol.
Chrome → ProxyBridge (raw TCP) → TCPRelay :9900 → (HTTP proxy) → Echo :8899
func NewTCPRelay ¶ added in v0.10.0
NewTCPRelay creates a new TCP relay.
type TargetConfig ¶
TargetConfig defines where to forward requests
func (*TargetConfig) GetDefaultPort ¶
func (t *TargetConfig) GetDefaultPort() int
GetDefaultPort returns the default port for the protocol
func (*TargetConfig) GetHostPort ¶
func (t *TargetConfig) GetHostPort() string
GetHostPort returns the host:port combination
func (*TargetConfig) GetTargetURL ¶
func (t *TargetConfig) GetTargetURL(path string) string
GetTargetURL returns the full target URL for forwarding
type WebSocketHandler ¶
type WebSocketHandler struct {
PluginLoader *PluginLoader
}
WebSocketHandler handles WebSocket upgrades
func (*WebSocketHandler) HandleUpgrade ¶
func (h *WebSocketHandler) HandleUpgrade(w http.ResponseWriter, r *http.Request, isSecure bool)
HandleUpgrade handles the WebSocket upgrade request