Skip to content

Commit

Permalink
Naming
Browse files Browse the repository at this point in the history
  • Loading branch information
fortuna committed Dec 26, 2024
1 parent 52dcd84 commit 5b80741
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 57 deletions.
2 changes: 1 addition & 1 deletion client/go/outline/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func NewTransport(transportConfig string) *NewTransportResult {
}
}

transportPair, err := config.NewDefaultTransportProvider().NewInstance(context.Background(), transportYAML)
transportPair, err := config.NewDefaultTransportProvider().Parse(context.Background(), transportYAML)
if err != nil {
return &NewTransportResult{
Error: &platerrors.PlatformError{
Expand Down
4 changes: 2 additions & 2 deletions client/go/outline/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ password: SECRET`,
name: "Explicit endpoint",
input: `
endpoint:
$type: dial
$parser: dial
address: example.com:4321
cipher: chacha20-ietf-poly1305
secret: SECRET`,
Expand All @@ -70,7 +70,7 @@ secret: SECRET`,
name: "Multi-hop",
input: `
endpoint:
$type: dial
$parser: dial
address: exit.example.com:4321
dialer: ss://[email protected]:4321/
cipher: chacha20-ietf-poly1305
Expand Down
42 changes: 22 additions & 20 deletions client/go/outline/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ import (
"gopkg.in/yaml.v3"
)

const ConfigTypeKey = "$type"
const ConfigParserKey = "$parser"

type ConfigNode any
type ConfigFunction func(ctx context.Context, input any) (any, error)

type ParseFunc[ObjectType any] func(ctx context.Context, input any) (ObjectType, error)
type ParseFunc[OutputType any] func(ctx context.Context, input ConfigNode) (OutputType, error)

func ParseConfigYAML(configText string) (ConfigNode, error) {
var node any
Expand Down Expand Up @@ -55,68 +54,71 @@ func mapToAny(in map[string]any, out any) error {
return decoder.Decode(out)
}

type TypeProvider[T any] struct {
// TypeParser creates objects of the given type T from an input config.
// You can register type-specific sub-parsers that get called when marked in the config.
// The default value is not valid. Use [NewTypeParser] instead.
type TypeParser[T any] struct {
fallbackHandler ParseFunc[T]
parsers map[string]func(context.Context, map[string]any) (T, error)
subparsers map[string]func(context.Context, map[string]any) (T, error)
}

var _ ParseFunc[any] = (*TypeProvider[any])(nil).NewInstance
var _ ParseFunc[any] = (*TypeParser[any])(nil).Parse

func NewTypeProvider[T any](fallbackHandler func(context.Context, any) (T, error)) *TypeProvider[T] {
return &TypeProvider[T]{
func NewTypeParser[T any](fallbackHandler func(context.Context, ConfigNode) (T, error)) *TypeParser[T] {
return &TypeParser[T]{
fallbackHandler: fallbackHandler,
parsers: make(map[string]func(context.Context, map[string]any) (T, error)),
subparsers: make(map[string]func(context.Context, map[string]any) (T, error)),
}
}

func (p *TypeProvider[T]) NewInstance(ctx context.Context, input any) (T, error) {
func (p *TypeParser[T]) Parse(ctx context.Context, config ConfigNode) (T, error) {
var zero T

// Iterate while the input is a function call.
for {
inMap, ok := input.(map[string]any)
inMap, ok := config.(map[string]any)
if !ok {
break
}
parserNameAny, ok := inMap[ConfigTypeKey]
parserNameAny, ok := inMap[ConfigParserKey]
if !ok {
break
}
parserName, ok := parserNameAny.(string)
if !ok {
return zero, fmt.Errorf("parser name must be a string, found \"%T\"", parserNameAny)
}
parser, ok := p.parsers[parserName]
parser, ok := p.subparsers[parserName]
if !ok {
return zero, fmt.Errorf("provider \"%v\" for type %T is not registered", parserName, zero)
}

// $type is embedded in the value: {$type: ..., ...}.
// $parser is embedded in the value: {$parser: ..., ...}.
// Need to copy value and remove the type directive.
inputCopy := make(map[string]any, len(inMap))
for k, v := range inMap {
if k == ConfigTypeKey {
if k == ConfigParserKey {
continue
}
inputCopy[k] = v
}

var err error
input, err = parser(ctx, inputCopy)
config, err = parser(ctx, inputCopy)
if err != nil {
return zero, fmt.Errorf("parser \"%v\" failed: %w", parserName, err)
}
}

typed, ok := input.(T)
typed, ok := config.(T)
if ok {
return typed, nil
}

// Input is an intermediate type. We need a fallback handler.
return p.fallbackHandler(ctx, input)
return p.fallbackHandler(ctx, config)
}

func (p *TypeProvider[T]) RegisterParser(name string, function func(context.Context, map[string]any) (T, error)) {
p.parsers[name] = function
func (p *TypeParser[T]) RegisterSubParser(name string, function func(context.Context, map[string]any) (T, error)) {
p.subparsers[name] = function
}
2 changes: 1 addition & 1 deletion client/go/outline/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ func Test_parseConfigFromJSON(t *testing.T) {
require.NoError(t, err)
got, err := parseShadowsocksConfig(node)
if err == nil {
_, err = newShadowsocksParams(node)
_, err = parseShadowsocksParams(node)
}
if tt.wantErr {
require.Error(t, err)
Expand Down
2 changes: 1 addition & 1 deletion client/go/outline/config/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type DialEndpointConfig struct {
Dialer any
}

func newDirectDialerEndpoint[ConnType any](ctx context.Context, config any, newDialer ParseFunc[*Dialer[ConnType]]) (*Endpoint[ConnType], error) {
func parseDirectDialerEndpoint[ConnType any](ctx context.Context, config any, newDialer ParseFunc[*Dialer[ConnType]]) (*Endpoint[ConnType], error) {
if config == nil {
return nil, errors.New("endpoint config cannot be nil")
}
Expand Down
48 changes: 24 additions & 24 deletions client/go/outline/config/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,62 +109,62 @@ func (t *TransportPair) ListenPacket(ctx context.Context) (net.PacketConn, error
// return c
// }

func NewDefaultTransportProvider() *TypeProvider[*TransportPair] {
var streamEndpoints *TypeProvider[*Endpoint[transport.StreamConn]]
var packetEndpoints *TypeProvider[*Endpoint[net.Conn]]
func NewDefaultTransportProvider() *TypeParser[*TransportPair] {
var streamEndpoints *TypeParser[*Endpoint[transport.StreamConn]]
var packetEndpoints *TypeParser[*Endpoint[net.Conn]]

streamDialers := NewTypeProvider(func(ctx context.Context, input any) (*Dialer[transport.StreamConn], error) {
streamDialers := NewTypeParser(func(ctx context.Context, input ConfigNode) (*Dialer[transport.StreamConn], error) {
if input == nil {
return &Dialer[transport.StreamConn]{ConnectionProviderInfo{ConnTypeDirect, ""}, (&transport.TCPDialer{}).DialStream}, nil
}
return newShadowsocksStreamDialer(ctx, input, streamEndpoints.NewInstance)
return parseShadowsocksStreamDialer(ctx, input, streamEndpoints.Parse)
})

packetDialers := NewTypeProvider(func(ctx context.Context, input any) (*Dialer[net.Conn], error) {
packetDialers := NewTypeParser(func(ctx context.Context, input ConfigNode) (*Dialer[net.Conn], error) {
if input == nil {
return &Dialer[net.Conn]{ConnectionProviderInfo{ConnTypeDirect, ""}, (&transport.UDPDialer{}).DialPacket}, nil
}
return newShadowsocksPacketDialer(ctx, input, packetEndpoints.NewInstance)
return parseShadowsocksPacketDialer(ctx, input, packetEndpoints.Parse)
})

streamEndpoints = NewTypeProvider(func(ctx context.Context, input any) (*Endpoint[transport.StreamConn], error) {
return newDirectDialerEndpoint(ctx, input, streamDialers.NewInstance)
streamEndpoints = NewTypeParser(func(ctx context.Context, input ConfigNode) (*Endpoint[transport.StreamConn], error) {
return parseDirectDialerEndpoint(ctx, input, streamDialers.Parse)
})
streamEndpoints.RegisterParser("dial", func(ctx context.Context, input map[string]any) (*Endpoint[transport.StreamConn], error) {
return newDirectDialerEndpoint(ctx, input, streamDialers.NewInstance)
streamEndpoints.RegisterSubParser("dial", func(ctx context.Context, input map[string]any) (*Endpoint[transport.StreamConn], error) {
return parseDirectDialerEndpoint(ctx, input, streamDialers.Parse)
})

packetEndpoints = NewTypeProvider(func(ctx context.Context, input any) (*Endpoint[net.Conn], error) {
return newDirectDialerEndpoint(ctx, input, packetDialers.NewInstance)
packetEndpoints = NewTypeParser(func(ctx context.Context, input ConfigNode) (*Endpoint[net.Conn], error) {
return parseDirectDialerEndpoint(ctx, input, packetDialers.Parse)
})
packetEndpoints.RegisterParser("dial", func(ctx context.Context, input map[string]any) (*Endpoint[net.Conn], error) {
return newDirectDialerEndpoint(ctx, input, packetDialers.NewInstance)
packetEndpoints.RegisterSubParser("dial", func(ctx context.Context, input map[string]any) (*Endpoint[net.Conn], error) {
return parseDirectDialerEndpoint(ctx, input, packetDialers.Parse)
})

transports := NewTypeProvider(func(ctx context.Context, input any) (*TransportPair, error) {
return newShadowsocksTransport(ctx, input, streamEndpoints.NewInstance, packetEndpoints.NewInstance)
transports := NewTypeParser(func(ctx context.Context, input ConfigNode) (*TransportPair, error) {
return newShadowsocksTransport(ctx, input, streamEndpoints.Parse, packetEndpoints.Parse)
})

// Shadowsocks support.
streamDialers.RegisterParser("shadowsocks", func(ctx context.Context, input map[string]any) (*Dialer[transport.StreamConn], error) {
return newShadowsocksStreamDialer(ctx, input, streamEndpoints.NewInstance)
streamDialers.RegisterSubParser("shadowsocks", func(ctx context.Context, input map[string]any) (*Dialer[transport.StreamConn], error) {
return parseShadowsocksStreamDialer(ctx, input, streamEndpoints.Parse)
})
packetDialers.RegisterParser("shadowsocks", func(ctx context.Context, input map[string]any) (*Dialer[net.Conn], error) {
return newShadowsocksPacketDialer(ctx, input, packetEndpoints.NewInstance)
packetDialers.RegisterSubParser("shadowsocks", func(ctx context.Context, input map[string]any) (*Dialer[net.Conn], error) {
return parseShadowsocksPacketDialer(ctx, input, packetEndpoints.Parse)
})

// Websocket support.
streamEndpoints.RegisterParser("websocket", func(ctx context.Context, input map[string]any) (*Endpoint[transport.StreamConn], error) {
streamEndpoints.RegisterSubParser("websocket", func(ctx context.Context, input map[string]any) (*Endpoint[transport.StreamConn], error) {
// TODO
return nil, errors.ErrUnsupported
})
packetEndpoints.RegisterParser("websocket", func(ctx context.Context, input map[string]any) (*Endpoint[net.Conn], error) {
packetEndpoints.RegisterSubParser("websocket", func(ctx context.Context, input map[string]any) (*Endpoint[net.Conn], error) {
// TODO
return nil, errors.ErrUnsupported
})

// TODO: Introduce explit transport parser.
transports.RegisterParser("explicit", func(ctx context.Context, input map[string]any) (*TransportPair, error) {
transports.RegisterSubParser("explicit", func(ctx context.Context, input map[string]any) (*TransportPair, error) {
return nil, errors.ErrUnsupported
})

Expand Down
12 changes: 6 additions & 6 deletions client/go/outline/config/shadowsocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type LegacyShadowsocksConfig struct {
}

func newShadowsocksTransport(ctx context.Context, config ConfigNode, newSE ParseFunc[*Endpoint[transport.StreamConn]], newPE ParseFunc[*Endpoint[net.Conn]]) (*TransportPair, error) {
params, err := newShadowsocksParams(config)
params, err := parseShadowsocksParams(config)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -75,8 +75,8 @@ func newShadowsocksTransport(ctx context.Context, config ConfigNode, newSE Parse
}, nil
}

func newShadowsocksStreamDialer(ctx context.Context, config ConfigNode, newSE ParseFunc[*Endpoint[transport.StreamConn]]) (*Dialer[transport.StreamConn], error) {
params, err := newShadowsocksParams(config)
func parseShadowsocksStreamDialer(ctx context.Context, config ConfigNode, newSE ParseFunc[*Endpoint[transport.StreamConn]]) (*Dialer[transport.StreamConn], error) {
params, err := parseShadowsocksParams(config)
if err != nil {
return nil, err
}
Expand All @@ -96,7 +96,7 @@ func newShadowsocksStreamDialer(ctx context.Context, config ConfigNode, newSE Pa
return &Dialer[transport.StreamConn]{ConnectionProviderInfo{ConnTypeTunneled, se.FirstHop}, sd.DialStream}, nil
}

func newShadowsocksPacketDialer(ctx context.Context, config ConfigNode, newPE ParseFunc[*Endpoint[net.Conn]]) (*Dialer[net.Conn], error) {
func parseShadowsocksPacketDialer(ctx context.Context, config ConfigNode, newPE ParseFunc[*Endpoint[net.Conn]]) (*Dialer[net.Conn], error) {
pl, err := newShadowsocksPacketListener(ctx, config, newPE)
if err != nil {
return nil, err
Expand All @@ -106,7 +106,7 @@ func newShadowsocksPacketDialer(ctx context.Context, config ConfigNode, newPE Pa
}

func newShadowsocksPacketListener(ctx context.Context, config ConfigNode, newPE ParseFunc[*Endpoint[net.Conn]]) (*PacketListener, error) {
params, err := newShadowsocksParams(config)
params, err := parseShadowsocksParams(config)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -168,7 +168,7 @@ func parseShadowsocksConfig(node ConfigNode) (*ShadowsocksConfig, error) {
}
}

func newShadowsocksParams(node ConfigNode) (*shadowsocksParams, error) {
func parseShadowsocksParams(node ConfigNode) (*shadowsocksParams, error) {
config, err := parseShadowsocksConfig(node)
if err != nil {
return nil, err
Expand Down
4 changes: 2 additions & 2 deletions client/go/outline/config/shadowsocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ func TestParseShadowsocksURLNoEncoding(t *testing.T) {

func TestParseShadowsocksURLInvalidCipherInfoFails(t *testing.T) {
configString := "ss://[email protected]:1234"
_, err := newShadowsocksParams(configString)
_, err := parseShadowsocksParams(configString)
require.Error(t, err)
}

func TestParseShadowsocksURLUnsupportedCypherFails(t *testing.T) {
configString := "ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwnTpLeTUyN2duU3FEVFB3R0JpQ1RxUnlT@example.com:1234"
_, err := newShadowsocksParams(configString)
_, err := parseShadowsocksParams(configString)
require.Error(t, err)
}

Expand Down

0 comments on commit 5b80741

Please sign in to comment.