Feature: add custom request header to proxy provider

`header` value is type of string array
header:
  Accept:
    - 'application/vnd.github.v3.raw'
  Authorization:
    - ' token xxxxxxxxxxx'
  User-Agent:
    - 'Clash/v1.10.6'

`prefix-name` add a prefix to proxy name
prefix-name: 'XXX-'
This commit is contained in:
yaling888 2022-06-03 05:00:13 +08:00
parent 8ed868b0f5
commit af5bd0f65e
5 changed files with 62 additions and 23 deletions

View File

@ -27,6 +27,8 @@ type proxyProviderSchema struct {
Filter string `provider:"filter,omitempty"` Filter string `provider:"filter,omitempty"`
HealthCheck healthCheckSchema `provider:"health-check,omitempty"` HealthCheck healthCheckSchema `provider:"health-check,omitempty"`
ForceCertVerify bool `provider:"force-cert-verify,omitempty"` ForceCertVerify bool `provider:"force-cert-verify,omitempty"`
PrefixName string `provider:"prefix-name,omitempty"`
Header map[string][]string `provider:"header,omitempty"`
} }
func ParseProxyProvider(name string, mapping map[string]any, forceCertVerify bool) (types.ProxyProvider, error) { func ParseProxyProvider(name string, mapping map[string]any, forceCertVerify bool) (types.ProxyProvider, error) {
@ -59,12 +61,12 @@ func ParseProxyProvider(name string, mapping map[string]any, forceCertVerify boo
case "file": case "file":
vehicle = NewFileVehicle(path) vehicle = NewFileVehicle(path)
case "http": case "http":
vehicle = NewHTTPVehicle(schema.URL, path) vehicle = NewHTTPVehicle(schema.URL, path, schema.Header)
default: default:
return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type) return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type)
} }
interval := time.Duration(uint(schema.Interval)) * time.Second interval := time.Duration(uint(schema.Interval)) * time.Second
filter := schema.Filter filter := schema.Filter
return NewProxySetProvider(name, interval, filter, vehicle, hc, schema.ForceCertVerify) return NewProxySetProvider(name, interval, filter, vehicle, hc, schema.ForceCertVerify, schema.PrefixName)
} }

View File

@ -97,7 +97,7 @@ func stopProxyProvider(pd *ProxySetProvider) {
_ = pd.fetcher.Destroy() _ = pd.fetcher.Destroy()
} }
func NewProxySetProvider(name string, interval time.Duration, filter string, vehicle types.Vehicle, hc *HealthCheck, forceCertVerify bool) (*ProxySetProvider, error) { func NewProxySetProvider(name string, interval time.Duration, filter string, vehicle types.Vehicle, hc *HealthCheck, forceCertVerify bool, prefixName string) (*ProxySetProvider, error) {
filterReg, err := regexp.Compile(filter) filterReg, err := regexp.Compile(filter)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid filter regex: %w", err) return nil, fmt.Errorf("invalid filter regex: %w", err)
@ -112,7 +112,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, veh
healthCheck: hc, healthCheck: hc,
} }
fetcher := newFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, filterReg, forceCertVerify), proxiesOnUpdate(pd)) fetcher := newFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, filterReg, forceCertVerify, prefixName), proxiesOnUpdate(pd))
pd.fetcher = fetcher pd.fetcher = fetcher
wrapper := &ProxySetProvider{pd} wrapper := &ProxySetProvider{pd}
@ -203,7 +203,7 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) {
} }
} }
func proxiesParseAndFilter(filter string, filterReg *regexp.Regexp, forceCertVerify bool) parser[[]C.Proxy] { func proxiesParseAndFilter(filter string, filterReg *regexp.Regexp, forceCertVerify bool, prefixName string) parser[[]C.Proxy] {
return func(buf []byte) ([]C.Proxy, error) { return func(buf []byte) ([]C.Proxy, error) {
schema := &ProxySchema{} schema := &ProxySchema{}
@ -224,6 +224,11 @@ func proxiesParseAndFilter(filter string, filterReg *regexp.Regexp, forceCertVer
if name, ok := mapping["name"]; ok && len(filter) > 0 && !filterReg.MatchString(name.(string)) { if name, ok := mapping["name"]; ok && len(filter) > 0 && !filterReg.MatchString(name.(string)) {
continue continue
} }
if prefixName != "" {
mapping["name"] = prefixName + mapping["name"].(string)
}
proxy, err := adapter.ParseProxy(mapping, forceCertVerify) proxy, err := adapter.ParseProxy(mapping, forceCertVerify)
if err != nil { if err != nil {
return nil, fmt.Errorf("proxy %d error: %w", idx, err) return nil, fmt.Errorf("proxy %d error: %w", idx, err)

View File

@ -9,7 +9,9 @@ import (
"os" "os"
"time" "time"
"github.com/Dreamacro/clash/common/convert"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
types "github.com/Dreamacro/clash/constant/provider" types "github.com/Dreamacro/clash/constant/provider"
) )
@ -36,6 +38,7 @@ func NewFileVehicle(path string) *FileVehicle {
type HTTPVehicle struct { type HTTPVehicle struct {
url string url string
path string path string
header http.Header
} }
func (h *HTTPVehicle) Type() types.VehicleType { func (h *HTTPVehicle) Type() types.VehicleType {
@ -60,11 +63,19 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
return nil, err return nil, err
} }
if h.header != nil {
req.Header = h.header
}
if user := uri.User; user != nil { if user := uri.User; user != nil {
password, _ := user.Password() password, _ := user.Password()
req.SetBasicAuth(user.Username(), password) req.SetBasicAuth(user.Username(), password)
} }
if req.UserAgent() == "" {
convert.SetUserAgent(req)
}
req = req.WithContext(ctx) req = req.WithContext(ctx)
transport := &http.Transport{ transport := &http.Transport{
@ -73,8 +84,13 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
IdleConnTimeout: 90 * time.Second, IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second, TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second, ExpectContinueTimeout: 1 * time.Second,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { DialContext: func(ctx context.Context, network, address string) (conn net.Conn, err error) {
return dialer.DialContext(ctx, network, address) conn, err = dialer.DialContext(ctx, network, address) // with direct
if err != nil {
// fallback to tun if tun enabled
conn, err = (&net.Dialer{Timeout: C.DefaultTCPTimeout}).Dial(network, address)
}
return
}, },
} }
@ -83,7 +99,9 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer func() {
_ = resp.Body.Close()
}()
buf, err := io.ReadAll(resp.Body) buf, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
@ -93,6 +111,6 @@ func (h *HTTPVehicle) Read() ([]byte, error) {
return buf, nil return buf, nil
} }
func NewHTTPVehicle(url string, path string) *HTTPVehicle { func NewHTTPVehicle(url string, path string, header http.Header) *HTTPVehicle {
return &HTTPVehicle{url, path} return &HTTPVehicle{url, path, header}
} }

View File

@ -6,13 +6,14 @@ import (
"net/http" "net/http"
"os" "os"
"github.com/Dreamacro/clash/common/convert"
"github.com/Dreamacro/clash/component/mmdb" "github.com/Dreamacro/clash/component/mmdb"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
) )
func downloadMMDB(path string) (err error) { func downloadMMDB(path string) (err error) {
resp, err := http.Get("https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb") resp, err := doGet("https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb")
if err != nil { if err != nil {
return return
} }
@ -51,7 +52,7 @@ func initMMDB() error {
} }
func downloadGeoSite(path string) (err error) { func downloadGeoSite(path string) (err error) {
resp, err := http.Get("https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat") resp, err := doGet("https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat")
if err != nil { if err != nil {
return return
} }
@ -110,3 +111,16 @@ func Init(dir string) error {
} }
return nil return nil
} }
func doGet(url string) (resp *http.Response, err error) {
var req *http.Request
req, err = http.NewRequest("GET", url, nil)
if err != nil {
return
}
convert.SetUserAgent(req)
resp, err = http.DefaultClient.Do(req)
return
}

View File

@ -49,14 +49,14 @@ func updateGeoDatabases(w http.ResponseWriter, r *http.Request) {
return return
} }
log.Warnln("[REST-API] update GEO databases successful, apply config...")
cfg, err := executor.ParseWithPath(constant.Path.Config()) cfg, err := executor.ParseWithPath(constant.Path.Config())
if err != nil { if err != nil {
log.Errorln("[REST-API] update GEO databases failed: %v", err) log.Errorln("[REST-API] update GEO databases failed: %v", err)
return return
} }
log.Warnln("[REST-API] update GEO databases successful, apply config...")
executor.ApplyConfig(cfg, false) executor.ApplyConfig(cfg, false)
}() }()