Refactor: HTTP clients, unified HTTP2/QUIC options, Apple engines
This commit is contained in:
+1
-1
@@ -24,7 +24,7 @@ type ACMECertificateProviderOptions struct {
|
||||
ExternalAccount *ACMEExternalAccountOptions `json:"external_account,omitempty"`
|
||||
DNS01Challenge *ACMEProviderDNS01ChallengeOptions `json:"dns01_challenge,omitempty"`
|
||||
KeyType ACMEKeyType `json:"key_type,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
HTTPClient *HTTPClientOptions `json:"http_client,omitempty"`
|
||||
}
|
||||
|
||||
type _ACMEProviderDNS01ChallengeOptions struct {
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/sing/common/byteformats"
|
||||
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"
|
||||
)
|
||||
|
||||
type HTTP2Options struct {
|
||||
IdleTimeout badoption.Duration `json:"idle_timeout,omitempty"`
|
||||
KeepAlivePeriod badoption.Duration `json:"keep_alive_period,omitempty"`
|
||||
StreamReceiveWindow byteformats.MemoryBytes `json:"stream_receive_window,omitempty"`
|
||||
ConnectionReceiveWindow byteformats.MemoryBytes `json:"connection_receive_window,omitempty"`
|
||||
MaxConcurrentStreams int `json:"max_concurrent_streams,omitempty"`
|
||||
}
|
||||
|
||||
type QUICOptions struct {
|
||||
HTTP2Options
|
||||
InitialPacketSize int `json:"initial_packet_size,omitempty"`
|
||||
DisablePathMTUDiscovery bool `json:"disable_path_mtu_discovery,omitempty"`
|
||||
}
|
||||
|
||||
type _HTTPClientOptions struct {
|
||||
Tag string `json:"tag,omitempty"`
|
||||
Engine string `json:"engine,omitempty"`
|
||||
Version int `json:"version,omitempty"`
|
||||
DisableVersionFallback bool `json:"disable_version_fallback,omitempty"`
|
||||
Headers badoption.HTTPHeader `json:"headers,omitempty"`
|
||||
HTTP2Options HTTP2Options `json:"-"`
|
||||
HTTP3Options QUICOptions `json:"-"`
|
||||
DefaultOutbound bool `json:"-"`
|
||||
ResolveOnDetour bool `json:"-"`
|
||||
DirectResolver bool `json:"-"`
|
||||
OutboundTLSOptionsContainer
|
||||
DialerOptions
|
||||
}
|
||||
|
||||
type (
|
||||
HTTPClient _HTTPClientOptions
|
||||
HTTPClientOptions _HTTPClientOptions
|
||||
)
|
||||
|
||||
func (h HTTPClient) Options() HTTPClientOptions {
|
||||
options := HTTPClientOptions(h)
|
||||
options.Tag = ""
|
||||
return options
|
||||
}
|
||||
|
||||
func (o HTTPClientOptions) IsEmpty() bool {
|
||||
if o.Tag != "" {
|
||||
return false
|
||||
}
|
||||
o.DefaultOutbound = false
|
||||
o.ResolveOnDetour = false
|
||||
o.DirectResolver = false
|
||||
return reflect.ValueOf(_HTTPClientOptions(o)).IsZero()
|
||||
}
|
||||
|
||||
func (o HTTPClientOptions) MarshalJSON() ([]byte, error) {
|
||||
if o.Tag != "" {
|
||||
return json.Marshal(o.Tag)
|
||||
}
|
||||
return badjson.MarshallObjects(_HTTPClientOptions(o), httpClientVariant(_HTTPClientOptions(o)))
|
||||
}
|
||||
|
||||
func (o *HTTPClientOptions) UnmarshalJSON(content []byte) error {
|
||||
if len(content) > 0 && content[0] == '"' {
|
||||
*o = HTTPClientOptions{}
|
||||
return json.Unmarshal(content, &o.Tag)
|
||||
}
|
||||
var options _HTTPClientOptions
|
||||
err := json.Unmarshal(content, &options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = unmarshalHTTPClientVersionOptions(content, &options, &options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.Tag = ""
|
||||
*o = HTTPClientOptions(options)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h HTTPClient) MarshalJSON() ([]byte, error) {
|
||||
return badjson.MarshallObjects(_HTTPClientOptions(h), httpClientVariant(_HTTPClientOptions(h)))
|
||||
}
|
||||
|
||||
func (h *HTTPClient) UnmarshalJSON(content []byte) error {
|
||||
err := json.Unmarshal(content, (*_HTTPClientOptions)(h))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return unmarshalHTTPClientVersionOptions(content, (*_HTTPClientOptions)(h), (*_HTTPClientOptions)(h))
|
||||
}
|
||||
|
||||
func unmarshalHTTPClientVersionOptions(content []byte, baseStruct any, options *_HTTPClientOptions) error {
|
||||
switch options.Version {
|
||||
case 1:
|
||||
return json.UnmarshalDisallowUnknownFields(content, baseStruct)
|
||||
case 0, 2:
|
||||
options.Version = 2
|
||||
return badjson.UnmarshallExcluded(content, baseStruct, &options.HTTP2Options)
|
||||
case 3:
|
||||
return badjson.UnmarshallExcluded(content, baseStruct, &options.HTTP3Options)
|
||||
default:
|
||||
return E.New("unknown HTTP version: ", options.Version)
|
||||
}
|
||||
}
|
||||
|
||||
func httpClientVariant(options _HTTPClientOptions) any {
|
||||
switch options.Version {
|
||||
case 1:
|
||||
return nil
|
||||
case 0, 2:
|
||||
return options.HTTP2Options
|
||||
case 3:
|
||||
return options.HTTP3Options
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+32
-23
@@ -7,17 +7,22 @@ import (
|
||||
|
||||
type HysteriaInboundOptions struct {
|
||||
ListenOptions
|
||||
Up *byteformats.NetworkBytesCompat `json:"up,omitempty"`
|
||||
UpMbps int `json:"up_mbps,omitempty"`
|
||||
Down *byteformats.NetworkBytesCompat `json:"down,omitempty"`
|
||||
DownMbps int `json:"down_mbps,omitempty"`
|
||||
Obfs string `json:"obfs,omitempty"`
|
||||
Users []HysteriaUser `json:"users,omitempty"`
|
||||
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
||||
ReceiveWindowClient uint64 `json:"recv_window_client,omitempty"`
|
||||
MaxConnClient int `json:"max_conn_client,omitempty"`
|
||||
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
||||
Up *byteformats.NetworkBytesCompat `json:"up,omitempty"`
|
||||
UpMbps int `json:"up_mbps,omitempty"`
|
||||
Down *byteformats.NetworkBytesCompat `json:"down,omitempty"`
|
||||
DownMbps int `json:"down_mbps,omitempty"`
|
||||
Obfs string `json:"obfs,omitempty"`
|
||||
Users []HysteriaUser `json:"users,omitempty"`
|
||||
// Deprecated: use QUIC fields instead
|
||||
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
||||
// Deprecated: use QUIC fields instead
|
||||
ReceiveWindowClient uint64 `json:"recv_window_client,omitempty"`
|
||||
// Deprecated: use QUIC fields instead
|
||||
MaxConnClient int `json:"max_conn_client,omitempty"`
|
||||
// Deprecated: use QUIC fields instead
|
||||
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
||||
InboundTLSOptionsContainer
|
||||
QUICOptions
|
||||
}
|
||||
|
||||
type HysteriaUser struct {
|
||||
@@ -29,18 +34,22 @@ type HysteriaUser struct {
|
||||
type HysteriaOutboundOptions struct {
|
||||
DialerOptions
|
||||
ServerOptions
|
||||
ServerPorts badoption.Listable[string] `json:"server_ports,omitempty"`
|
||||
HopInterval badoption.Duration `json:"hop_interval,omitempty"`
|
||||
Up *byteformats.NetworkBytesCompat `json:"up,omitempty"`
|
||||
UpMbps int `json:"up_mbps,omitempty"`
|
||||
Down *byteformats.NetworkBytesCompat `json:"down,omitempty"`
|
||||
DownMbps int `json:"down_mbps,omitempty"`
|
||||
Obfs string `json:"obfs,omitempty"`
|
||||
Auth []byte `json:"auth,omitempty"`
|
||||
AuthString string `json:"auth_str,omitempty"`
|
||||
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
||||
ReceiveWindow uint64 `json:"recv_window,omitempty"`
|
||||
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
ServerPorts badoption.Listable[string] `json:"server_ports,omitempty"`
|
||||
HopInterval badoption.Duration `json:"hop_interval,omitempty"`
|
||||
Up *byteformats.NetworkBytesCompat `json:"up,omitempty"`
|
||||
UpMbps int `json:"up_mbps,omitempty"`
|
||||
Down *byteformats.NetworkBytesCompat `json:"down,omitempty"`
|
||||
DownMbps int `json:"down_mbps,omitempty"`
|
||||
Obfs string `json:"obfs,omitempty"`
|
||||
Auth []byte `json:"auth,omitempty"`
|
||||
AuthString string `json:"auth_str,omitempty"`
|
||||
// Deprecated: use QUIC fields instead
|
||||
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
||||
// Deprecated: use QUIC fields instead
|
||||
ReceiveWindow uint64 `json:"recv_window,omitempty"`
|
||||
// Deprecated: use QUIC fields instead
|
||||
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
OutboundTLSOptionsContainer
|
||||
QUICOptions
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type Hysteria2InboundOptions struct {
|
||||
Users []Hysteria2User `json:"users,omitempty"`
|
||||
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
|
||||
InboundTLSOptionsContainer
|
||||
QUICOptions
|
||||
Masquerade *Hysteria2Masquerade `json:"masquerade,omitempty"`
|
||||
BBRProfile string `json:"bbr_profile,omitempty"`
|
||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||
@@ -122,6 +123,7 @@ type Hysteria2OutboundOptions struct {
|
||||
Password string `json:"password,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
OutboundTLSOptionsContainer
|
||||
QUICOptions
|
||||
BBRProfile string `json:"bbr_profile,omitempty"`
|
||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ type _Options struct {
|
||||
NTP *NTPOptions `json:"ntp,omitempty"`
|
||||
Certificate *CertificateOptions `json:"certificate,omitempty"`
|
||||
CertificateProviders []CertificateProvider `json:"certificate_providers,omitempty"`
|
||||
HTTPClients []HTTPClient `json:"http_clients,omitempty"`
|
||||
Endpoints []Endpoint `json:"endpoints,omitempty"`
|
||||
Inbounds []Inbound `json:"inbounds,omitempty"`
|
||||
Outbounds []Outbound `json:"outbounds,omitempty"`
|
||||
@@ -61,6 +62,10 @@ func checkOptions(options *Options) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = checkHTTPClients(options.HTTPClients)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -79,6 +84,20 @@ func checkCertificateProviders(providers []CertificateProvider) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkHTTPClients(clients []HTTPClient) error {
|
||||
seen := make(map[string]bool)
|
||||
for _, client := range clients {
|
||||
if client.Tag == "" {
|
||||
return E.New("missing http client tag")
|
||||
}
|
||||
if seen[client.Tag] {
|
||||
return E.New("duplicate http client tag: ", client.Tag)
|
||||
}
|
||||
seen[client.Tag] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkInbounds(inbounds []Inbound) error {
|
||||
seen := make(map[string]bool)
|
||||
for i, inbound := range inbounds {
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ type CloudflareOriginCACertificateProviderOptions struct {
|
||||
OriginCAKey string `json:"origin_ca_key,omitempty"`
|
||||
RequestType CloudflareOriginCARequestType `json:"request_type,omitempty"`
|
||||
RequestedValidity CloudflareOriginCARequestValidity `json:"requested_validity,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
HTTPClient *HTTPClientOptions `json:"http_client,omitempty"`
|
||||
}
|
||||
|
||||
type CloudflareOriginCARequestType string
|
||||
|
||||
@@ -20,6 +20,7 @@ type RouteOptions struct {
|
||||
DefaultNetworkType badoption.Listable[InterfaceType] `json:"default_network_type,omitempty"`
|
||||
DefaultFallbackNetworkType badoption.Listable[InterfaceType] `json:"default_fallback_network_type,omitempty"`
|
||||
DefaultFallbackDelay badoption.Duration `json:"default_fallback_delay,omitempty"`
|
||||
DefaultHTTPClient string `json:"default_http_client,omitempty"`
|
||||
}
|
||||
|
||||
type GeoIPOptions struct {
|
||||
|
||||
+3
-1
@@ -122,8 +122,10 @@ type LocalRuleSet struct {
|
||||
|
||||
type RemoteRuleSet struct {
|
||||
URL string `json:"url"`
|
||||
DownloadDetour string `json:"download_detour,omitempty"`
|
||||
HTTPClient *HTTPClientOptions `json:"http_client,omitempty"`
|
||||
UpdateInterval badoption.Duration `json:"update_interval,omitempty"`
|
||||
// Deprecated: use http_client instead
|
||||
DownloadDetour string `json:"download_detour,omitempty"`
|
||||
}
|
||||
|
||||
type _HeadlessRule struct {
|
||||
|
||||
+25
-8
@@ -3,18 +3,20 @@ package option
|
||||
import (
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
||||
type TailscaleEndpointOptions struct {
|
||||
// Deprecated: use control_http_client instead
|
||||
DialerOptions
|
||||
StateDirectory string `json:"state_directory,omitempty"`
|
||||
AuthKey string `json:"auth_key,omitempty"`
|
||||
ControlURL string `json:"control_url,omitempty"`
|
||||
ControlHTTPClient *HTTPClientOptions `json:"control_http_client,omitempty"`
|
||||
Ephemeral bool `json:"ephemeral,omitempty"`
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
AcceptRoutes bool `json:"accept_routes,omitempty"`
|
||||
@@ -53,9 +55,13 @@ type DERPServiceOptions struct {
|
||||
STUN *DERPSTUNListenOptions `json:"stun,omitempty"`
|
||||
}
|
||||
|
||||
type _DERPVerifyClientURLOptions struct {
|
||||
type _DERPVerifyClientURLBase struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
DialerOptions
|
||||
}
|
||||
|
||||
type _DERPVerifyClientURLOptions struct {
|
||||
_DERPVerifyClientURLBase
|
||||
HTTPClientOptions
|
||||
}
|
||||
|
||||
type DERPVerifyClientURLOptions _DERPVerifyClientURLOptions
|
||||
@@ -69,21 +75,32 @@ func (d DERPVerifyClientURLOptions) ServerIsDomain() bool {
|
||||
}
|
||||
|
||||
func (d DERPVerifyClientURLOptions) MarshalJSON() ([]byte, error) {
|
||||
if reflect.DeepEqual(d, _DERPVerifyClientURLOptions{}) {
|
||||
if d.URL != "" && d.HTTPClientOptions.IsEmpty() {
|
||||
return json.Marshal(d.URL)
|
||||
} else {
|
||||
return json.Marshal(_DERPVerifyClientURLOptions(d))
|
||||
}
|
||||
return badjson.MarshallObjects(d._DERPVerifyClientURLBase, HTTPClient(d.HTTPClientOptions))
|
||||
}
|
||||
|
||||
func (d *DERPVerifyClientURLOptions) UnmarshalJSON(bytes []byte) error {
|
||||
var stringValue string
|
||||
err := json.Unmarshal(bytes, &stringValue)
|
||||
if err == nil {
|
||||
d.URL = stringValue
|
||||
*d = DERPVerifyClientURLOptions{
|
||||
_DERPVerifyClientURLBase: _DERPVerifyClientURLBase{URL: stringValue},
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(bytes, (*_DERPVerifyClientURLOptions)(d))
|
||||
err = json.Unmarshal(bytes, &d._DERPVerifyClientURLBase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var client HTTPClient
|
||||
err = badjson.UnmarshallExcluded(bytes, &d._DERPVerifyClientURLBase, &client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.HTTPClientOptions = HTTPClientOptions(client)
|
||||
return nil
|
||||
}
|
||||
|
||||
type DERPMeshOptions struct {
|
||||
|
||||
@@ -28,6 +28,7 @@ type InboundTLSOptions struct {
|
||||
KeyPath string `json:"key_path,omitempty"`
|
||||
KernelTx bool `json:"kernel_tx,omitempty"`
|
||||
KernelRx bool `json:"kernel_rx,omitempty"`
|
||||
HandshakeTimeout badoption.Duration `json:"handshake_timeout,omitempty"`
|
||||
CertificateProvider *CertificateProviderOptions `json:"certificate_provider,omitempty"`
|
||||
|
||||
// Deprecated: use certificate_provider
|
||||
@@ -100,6 +101,7 @@ func (o *InboundTLSOptionsContainer) ReplaceInboundTLSOptions(options *InboundTL
|
||||
|
||||
type OutboundTLSOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Engine string `json:"engine,omitempty"`
|
||||
DisableSNI bool `json:"disable_sni,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
@@ -120,6 +122,7 @@ type OutboundTLSOptions struct {
|
||||
RecordFragment bool `json:"record_fragment,omitempty"`
|
||||
KernelTx bool `json:"kernel_tx,omitempty"`
|
||||
KernelRx bool `json:"kernel_rx,omitempty"`
|
||||
HandshakeTimeout badoption.Duration `json:"handshake_timeout,omitempty"`
|
||||
ECH *OutboundECHOptions `json:"ech,omitempty"`
|
||||
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
|
||||
Reality *OutboundRealityOptions `json:"reality,omitempty"`
|
||||
|
||||
@@ -10,6 +10,7 @@ type TUICInboundOptions struct {
|
||||
ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"`
|
||||
Heartbeat badoption.Duration `json:"heartbeat,omitempty"`
|
||||
InboundTLSOptionsContainer
|
||||
QUICOptions
|
||||
}
|
||||
|
||||
type TUICUser struct {
|
||||
@@ -30,4 +31,5 @@ type TUICOutboundOptions struct {
|
||||
Heartbeat badoption.Duration `json:"heartbeat,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
OutboundTLSOptionsContainer
|
||||
QUICOptions
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user