159 lines
4.8 KiB
Go
159 lines
4.8 KiB
Go
package option
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/sagernet/sing-box/schema"
|
|
"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" enum:"go,apple"`
|
|
Version int `json:"version,omitempty" enum:"0,1,2,3"`
|
|
DisableVersionFallback bool `json:"disable_version_fallback,omitempty"`
|
|
Headers badoption.HTTPHeader `json:"headers,omitempty"`
|
|
HTTP2Options HTTP2Options `json:"-"`
|
|
HTTP3Options QUICOptions `json:"-"`
|
|
DefaultOutbound bool `json:"-"`
|
|
DisableEmptyDirectCheck 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
|
|
}
|
|
}
|
|
|
|
func describeHTTPClientObject(builder schema.Builder) (*schema.Node, error) {
|
|
node := schema.StrictObject()
|
|
err := builder.FlattenStruct(node, reflect.TypeFor[HTTPClient]())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = builder.FlattenStruct(node, reflect.TypeFor[QUICOptions]())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return node, nil
|
|
}
|
|
|
|
func (h HTTPClient) DescribeSchema(builder schema.Builder) (*schema.Node, error) {
|
|
return builder.Define("HTTPClient", func() (*schema.Node, error) {
|
|
return describeHTTPClientObject(builder)
|
|
})
|
|
}
|
|
|
|
func (o HTTPClientOptions) DescribeSchema(builder schema.Builder) (*schema.Node, error) {
|
|
return builder.Define("HTTPClientReference", func() (*schema.Node, error) {
|
|
clientObject, err := describeHTTPClientObject(builder)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clientObject.Properties.Remove("tag")
|
|
return schema.AnyOf(schema.TagReferenceNode("http_client"), clientObject), nil
|
|
})
|
|
}
|