mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-09-01 16:01:55 +03:00
Add documentation for network
This commit is contained in:
@@ -27,9 +27,33 @@ const (
|
|||||||
DefaultPreferIP = "prefer-ipv6"
|
DefaultPreferIP = "prefer-ipv6"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Network defines a knowledge how to work with a network. It may sound
|
||||||
|
// fun but it encapsulates all the knowledge how to properly establish
|
||||||
|
// connections to remote hosts and configure HTTP clients.
|
||||||
|
//
|
||||||
|
// For example, if you want to use SOCKS5 proxy, you probably want to
|
||||||
|
// have all traffic routed to this proxy: telegram connections, http
|
||||||
|
// requests and so on. This knowledge is encapsulated into instances of
|
||||||
|
// such interface.
|
||||||
|
//
|
||||||
|
// mtglib uses Network for:
|
||||||
|
//
|
||||||
|
// 1. Dialing to Telegram
|
||||||
|
//
|
||||||
|
// 2. Dialing to front domain
|
||||||
|
//
|
||||||
|
// 3. Doing HTTP requests (for example, for FireHOL ipblocklist).
|
||||||
type Network interface {
|
type Network interface {
|
||||||
|
// Dial establishes context-free TCP connections.
|
||||||
Dial(network, address string) (net.Conn, error)
|
Dial(network, address string) (net.Conn, error)
|
||||||
|
|
||||||
|
// DialContext dials using a context. This is a preferrable
|
||||||
|
// way of establishing TCP connections.
|
||||||
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||||
|
|
||||||
|
// MakeHTTPClient build an HTTP client with given dial function. If
|
||||||
|
// nothing is provided, then DialContext of this interface is going
|
||||||
|
// to be used.
|
||||||
MakeHTTPClient(func(ctx context.Context, network, address string) (net.Conn, error)) *http.Client
|
MakeHTTPClient(func(ctx context.Context, network, address string) (net.Conn, error)) *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,11 @@ func (d *defaultDialer) DialContext(ctx context.Context, network, address string
|
|||||||
return tcpConn, nil
|
return tcpConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDefaultDialer build a new dialer which dials bypassing proxies
|
||||||
|
// etc.
|
||||||
|
//
|
||||||
|
// The most default one you can imagine. But it has tunes TCP
|
||||||
|
// connections and setups SO_REUSEPORT.
|
||||||
func NewDefaultDialer(timeout time.Duration, bufferSize int) (Dialer, error) {
|
func NewDefaultDialer(timeout time.Duration, bufferSize int) (Dialer, error) {
|
||||||
switch {
|
switch {
|
||||||
case timeout < 0:
|
case timeout < 0:
|
||||||
|
|||||||
+58
-5
@@ -1,3 +1,20 @@
|
|||||||
|
// Network contains a default implementation of the network.
|
||||||
|
//
|
||||||
|
// Please see mtglib.Network interface to get some basic idea behind
|
||||||
|
// this abstraction.
|
||||||
|
//
|
||||||
|
// Some notable feature of this implementation:
|
||||||
|
//
|
||||||
|
// 1. It detaches dialer from a network. Dialer is something which
|
||||||
|
// implements a real dialer and network completes it with more higher
|
||||||
|
// level details.
|
||||||
|
//
|
||||||
|
// 2. It uses only TCP connections. Even for DNS it uses DNS-Over-HTTPS
|
||||||
|
//
|
||||||
|
// 3. It has some simple implementation of DNS cache which is good
|
||||||
|
// enough for our purpose.
|
||||||
|
//
|
||||||
|
// 4. It sets uses SO_REUSEPORT port if applicable.
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -8,23 +25,59 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultTimeout = 10 * time.Second
|
// DefaultTimeout is a default timeout for establishing TCP
|
||||||
DefaultHTTPTimeout = 10 * time.Second
|
// connection.
|
||||||
DefaultBufferSize = 16 * 1024 // 16 kib
|
DefaultTimeout = 10 * time.Second
|
||||||
|
|
||||||
|
// DefaultHTTPTimeout defines a default timeout for making HTTP
|
||||||
|
// request.
|
||||||
|
DefaultHTTPTimeout = 10 * time.Second
|
||||||
|
|
||||||
|
// DefaultBufferSize defines a TCP buffer size. Both read and write, so
|
||||||
|
// for real size, please multiply this number by 2.
|
||||||
|
DefaultBufferSize = 16 * 1024 // 16 kib
|
||||||
|
|
||||||
|
// ProxyDialerOpenThreshold is used for load balancing SOCKS5 dialer
|
||||||
|
// only.
|
||||||
|
//
|
||||||
|
// This dialer uses circuit breaker with of 3 stages: OPEN,
|
||||||
|
// HALF_OPEN and CLOSED. If state is CLOSED, all requests go in
|
||||||
|
// a normal mode. If you get more that ProxyDialerOpenThreshold
|
||||||
|
// errors, circuit breaker goes into OPEN mode.
|
||||||
|
//
|
||||||
|
// When circuit breaker is in OPEN mode, it forbids all request to
|
||||||
|
// a given proxy. But after ProxyDialerHalfOpenTimeout it gives a
|
||||||
|
// second chance and opens an access for a SINGLE request. If this
|
||||||
|
// request success, then circuit breaker closes, otherwise opens
|
||||||
|
// again.
|
||||||
|
//
|
||||||
|
// When circuit breaker is closed, it clears an error states each
|
||||||
|
// ProxyDialerResetFailuresTimeout.
|
||||||
ProxyDialerOpenThreshold = 5
|
ProxyDialerOpenThreshold = 5
|
||||||
ProxyDialerHalfOpenTimeout = time.Minute
|
ProxyDialerHalfOpenTimeout = time.Minute
|
||||||
ProxyDialerResetFailuresTimeout = 10 * time.Second
|
ProxyDialerResetFailuresTimeout = 10 * time.Second
|
||||||
|
|
||||||
|
// DefaultDOHHostname defines a default IP address for DOH host.
|
||||||
|
// Since mtg is simple, please pass IP address here. We do not
|
||||||
|
// have bootstrap servers here embedded.
|
||||||
DefaultDOHHostname = "9.9.9.9"
|
DefaultDOHHostname = "9.9.9.9"
|
||||||
DNSTimeout = 5 * time.Second
|
|
||||||
|
// DNSTimeout defines a timeout for DNS queries.
|
||||||
|
DNSTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrCircuitBreakerOpened = errors.New("circuit breaker is opened")
|
// ErrCircuitBreakerOpened is returned when proxy is being accessed
|
||||||
|
// but circuit breaker is opened.
|
||||||
|
ErrCircuitBreakerOpened = errors.New("circuit breaker is opened")
|
||||||
|
|
||||||
|
// ErrCannotDialWithAllProxies is returned when load balancing
|
||||||
|
// client is trying to access proxies but all of them are failed.
|
||||||
ErrCannotDialWithAllProxies = errors.New("cannot dial with all proxies")
|
ErrCannotDialWithAllProxies = errors.New("cannot dial with all proxies")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Dialer defines an interface which is required to bootstrap a network
|
||||||
|
// instance from.
|
||||||
type Dialer interface {
|
type Dialer interface {
|
||||||
Dial(network, address string) (net.Conn, error)
|
Dial(network, address string) (net.Conn, error)
|
||||||
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||||
|
|||||||
@@ -32,6 +32,16 @@ func (l loadBalancedSocks5Dialer) DialContext(ctx context.Context, network, addr
|
|||||||
return nil, ErrCannotDialWithAllProxies
|
return nil, ErrCannotDialWithAllProxies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewLoadBalancedSocks5Dialer builds a new load balancing SOCKS5
|
||||||
|
// dialer.
|
||||||
|
//
|
||||||
|
// The main difference from one which is made by NewSocks5Dialer is that
|
||||||
|
// we actually have a list of these proxies. When dial is requested,
|
||||||
|
// any proxy is picked and used. If proxy fails for some reason, we try
|
||||||
|
// another one.
|
||||||
|
//
|
||||||
|
// So, it is mostly useful if you have some routes with proxies which
|
||||||
|
// are not always online or having buggy network.
|
||||||
func NewLoadBalancedSocks5Dialer(baseDialer Dialer, proxyURLs []*url.URL) (Dialer, error) {
|
func NewLoadBalancedSocks5Dialer(baseDialer Dialer, proxyURLs []*url.URL) (Dialer, error) {
|
||||||
dialers := make([]Dialer, 0, len(proxyURLs))
|
dialers := make([]Dialer, 0, len(proxyURLs))
|
||||||
|
|
||||||
|
|||||||
@@ -115,6 +115,10 @@ func (n *network) dnsResolve(protocol, address string) ([]string, error) {
|
|||||||
return ips, nil
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewNetwork assembles an mtglib.Network compatible structure
|
||||||
|
// based on a dialer and given params.
|
||||||
|
//
|
||||||
|
// It brings simple DNS cache and DNS-Over-HTTPS when necessary.
|
||||||
func NewNetwork(dialer Dialer,
|
func NewNetwork(dialer Dialer,
|
||||||
userAgent, dohHostname string,
|
userAgent, dohHostname string,
|
||||||
httpTimeout time.Duration) (mtglib.Network, error) {
|
httpTimeout time.Duration) (mtglib.Network, error) {
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import (
|
|||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewSocks5Dialer build a new dialer from a given one (so, in theory
|
||||||
|
// you can chain here). Proxy parameters are passed with URI in a form of:
|
||||||
|
//
|
||||||
|
// socks5://[user:[password]]@host:port
|
||||||
func NewSocks5Dialer(baseDialer Dialer, proxyURL *url.URL) (Dialer, error) {
|
func NewSocks5Dialer(baseDialer Dialer, proxyURL *url.URL) (Dialer, error) {
|
||||||
rv, err := proxy.FromURL(proxyURL, baseDialer)
|
rv, err := proxy.FromURL(proxyURL, baseDialer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user