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:
parent
8ed868b0f5
commit
af5bd0f65e
@ -20,13 +20,15 @@ type healthCheckSchema struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type proxyProviderSchema struct {
|
type proxyProviderSchema struct {
|
||||||
Type string `provider:"type"`
|
Type string `provider:"type"`
|
||||||
Path string `provider:"path"`
|
Path string `provider:"path"`
|
||||||
URL string `provider:"url,omitempty"`
|
URL string `provider:"url,omitempty"`
|
||||||
Interval int `provider:"interval,omitempty"`
|
Interval int `provider:"interval,omitempty"`
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,8 +36,9 @@ 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}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user