Files
sing-box-extended/option/provider.go
Arsolitt 1e826a201d feat(provider): add custom HTTP headers support for remote providers
Some subscription panels (e.g. Remnawave with incy/happ integration)
require device-identification headers to return the real server list
instead of a dummy config. Add a 'headers' field to the remote provider
options so users can attach arbitrary HTTP headers to subscription
fetch requests.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-07-01 21:58:01 +03:00

80 lines
2.6 KiB
Go

package option
import (
"context"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/common/json/badoption"
"github.com/sagernet/sing/service"
)
type ProviderOptionsRegistry interface {
CreateOptions(providerType string) (any, bool)
}
type _Provider struct {
Type string `json:"type"`
Tag string `json:"tag,omitempty"`
Options any `json:"-"`
}
type Provider _Provider
func (h *Provider) MarshalJSONContext(ctx context.Context) ([]byte, error) {
return badjson.MarshallObjectsContext(ctx, (*_Provider)(h), h.Options)
}
func (h *Provider) UnmarshalJSONContext(ctx context.Context, content []byte) error {
err := json.UnmarshalContext(ctx, content, (*_Provider)(h))
if err != nil {
return err
}
registry := service.FromContext[ProviderOptionsRegistry](ctx)
if registry == nil {
return E.New("missing provider options registry in context")
}
options, loaded := registry.CreateOptions(h.Type)
if !loaded {
return E.New("unknown provider type: ", h.Type)
}
err = badjson.UnmarshallExcludedContext(ctx, content, (*_Provider)(h), options)
if err != nil {
return err
}
h.Options = options
return nil
}
type ProviderLocalOptions struct {
Path string `json:"path"`
RemoveEmojis bool `json:"remove_emojis,omitempty"`
HealthCheck ProviderHealthCheckOptions `json:"health_check,omitempty"`
}
type ProviderRemoteOptions struct {
URL string `json:"url"`
UserAgent string `json:"user_agent,omitempty"`
Headers badoption.HTTPHeader `json:"headers,omitempty"`
DownloadDetour string `json:"download_detour,omitempty"`
UpdateInterval badoption.Duration `json:"update_interval,omitempty"`
Exclude *badoption.Regexp `json:"exclude,omitempty"`
Include *badoption.Regexp `json:"include,omitempty"`
RemoveEmojis bool `json:"remove_emojis,omitempty"`
HealthCheck ProviderHealthCheckOptions `json:"health_check,omitempty"`
}
type ProviderInlineOptions struct {
Outbounds []Outbound `json:"outbounds,omitempty"`
RemoveEmojis bool `json:"remove_emojis,omitempty"`
HealthCheck ProviderHealthCheckOptions `json:"health_check,omitempty"`
}
type ProviderHealthCheckOptions struct {
Enabled bool `json:"enabled,omitempty"`
URL string `json:"url,omitempty"`
Interval badoption.Duration `json:"interval,omitempty"`
Timeout badoption.Duration `json:"timeout,omitempty"`
}