Skip to content

V2Ray Transport Factory ​

Source: transport/v2ray/transport.go, transport/v2ray/quic.go, transport/v2ray/grpc.go, transport/v2ray/grpc_lite.go

Factory Pattern ​

The V2Ray transport factory uses generic type aliases and a type-switch dispatcher to create server and client transports. This is the single entry point for all V2Ray transport creation.

Generic Constructor Types ​

go
type (
    ServerConstructor[O any] func(
        ctx context.Context,
        logger logger.ContextLogger,
        options O,
        tlsConfig tls.ServerConfig,
        handler adapter.V2RayServerTransportHandler,
    ) (adapter.V2RayServerTransport, error)

    ClientConstructor[O any] func(
        ctx context.Context,
        dialer N.Dialer,
        serverAddr M.Socksaddr,
        options O,
        tlsConfig tls.Config,
    ) (adapter.V2RayClientTransport, error)
)

These generic types parameterize the options struct O, allowing each transport to define its own configuration type while sharing the same constructor signature.

Server Transport Dispatch ​

go
func NewServerTransport(ctx context.Context, logger logger.ContextLogger,
    options option.V2RayTransportOptions, tlsConfig tls.ServerConfig,
    handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) {
    if options.Type == "" {
        return nil, nil
    }
    switch options.Type {
    case C.V2RayTransportTypeHTTP:
        return v2rayhttp.NewServer(ctx, logger, options.HTTPOptions, tlsConfig, handler)
    case C.V2RayTransportTypeWebsocket:
        return v2raywebsocket.NewServer(ctx, logger, options.WebsocketOptions, tlsConfig, handler)
    case C.V2RayTransportTypeQUIC:
        if tlsConfig == nil {
            return nil, C.ErrTLSRequired
        }
        return NewQUICServer(ctx, logger, options.QUICOptions, tlsConfig, handler)
    case C.V2RayTransportTypeGRPC:
        return NewGRPCServer(ctx, logger, options.GRPCOptions, tlsConfig, handler)
    case C.V2RayTransportTypeHTTPUpgrade:
        return v2rayhttpupgrade.NewServer(ctx, logger, options.HTTPUpgradeOptions, tlsConfig, handler)
    default:
        return nil, E.New("unknown transport type: " + options.Type)
    }
}

Key behaviors:

  • Empty type returns nil, nil (no transport configured)
  • QUIC requires TLS -- returns C.ErrTLSRequired if tlsConfig is nil
  • HTTP, WebSocket, and HTTP Upgrade are directly imported and called
  • gRPC and QUIC are dispatched through intermediate functions that handle build-tag switching

Client Transport Dispatch ​

NewClientTransport follows the same pattern. The client variant receives an N.Dialer and M.Socksaddr instead of a handler:

go
func NewClientTransport(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr,
    options option.V2RayTransportOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error)

Note the TLS config is tls.Config (client interface) vs tls.ServerConfig (server interface).

QUIC Registration Pattern ​

QUIC transport requires the with_quic build tag. Since the core v2ray package cannot directly import v2rayquic (which may not be compiled), it uses a registration pattern:

go
// quic.go
var (
    quicServerConstructor ServerConstructor[option.V2RayQUICOptions]
    quicClientConstructor ClientConstructor[option.V2RayQUICOptions]
)

func RegisterQUICConstructor(
    server ServerConstructor[option.V2RayQUICOptions],
    client ClientConstructor[option.V2RayQUICOptions],
) {
    quicServerConstructor = server
    quicClientConstructor = client
}

func NewQUICServer(...) (adapter.V2RayServerTransport, error) {
    if quicServerConstructor == nil {
        return nil, os.ErrInvalid
    }
    return quicServerConstructor(ctx, logger, options, tlsConfig, handler)
}

The v2rayquic package registers itself via init():

go
// v2rayquic/init.go
//go:build with_quic

func init() {
    v2ray.RegisterQUICConstructor(NewServer, NewClient)
}

If compiled without with_quic, the constructors remain nil, and NewQUICServer/NewQUICClient return os.ErrInvalid.

gRPC Build Tag Switching ​

gRPC has two implementations controlled by build tags:

With with_grpc (grpc.go):

go
//go:build with_grpc

func NewGRPCServer(...) (adapter.V2RayServerTransport, error) {
    if options.ForceLite {
        return v2raygrpclite.NewServer(ctx, logger, options, tlsConfig, handler)
    }
    return v2raygrpc.NewServer(ctx, logger, options, tlsConfig, handler)
}

When the full gRPC library is available, the user can still force the lite implementation via options.ForceLite.

Without with_grpc (grpc_lite.go):

go
//go:build !with_grpc

func NewGRPCServer(...) (adapter.V2RayServerTransport, error) {
    return v2raygrpclite.NewServer(ctx, logger, options, tlsConfig, handler)
}

Without the build tag, the lite implementation is always used regardless of ForceLite.

Configuration ​

json
{
  "transport": {
    "type": "ws",
    "path": "/path",
    "headers": {
      "Host": "example.com"
    },
    "max_early_data": 2048,
    "early_data_header_name": "Sec-WebSocket-Protocol"
  }
}

The V2RayTransportOptions struct contains a Type string and sub-option structs for each transport type (HTTPOptions, WebsocketOptions, QUICOptions, GRPCOptions, HTTPUpgradeOptions). Only the sub-options matching the selected type are used.