mirror of
https://github.com/v2fly/v2ray-core.git
synced 2025-01-04 16:37:12 -05:00
Merge branch 'master' of github.com:v2ray/v2ray-core into domainsocket
This commit is contained in:
commit
c542c043f3
@ -1,7 +1,7 @@
|
|||||||
sudo: required
|
sudo: required
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.9.4
|
- "1.10.1"
|
||||||
go_import_path: v2ray.com/core
|
go_import_path: v2ray.com/core
|
||||||
git:
|
git:
|
||||||
depth: 5
|
depth: 5
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"v2ray.com/core/common/signal"
|
"v2ray.com/core/common/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Commander is a V2Ray feature that provides gRPC methods to external clients.
|
||||||
type Commander struct {
|
type Commander struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
server *grpc.Server
|
server *grpc.Server
|
||||||
@ -21,22 +22,26 @@ type Commander struct {
|
|||||||
ohm core.OutboundHandlerManager
|
ohm core.OutboundHandlerManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewCommander creates a new Commander based on the given config.
|
||||||
func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
|
func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
|
||||||
v := core.FromContext(ctx)
|
v := core.MustFromContext(ctx)
|
||||||
if v == nil {
|
|
||||||
return nil, newError("V is not in context.")
|
|
||||||
}
|
|
||||||
c := &Commander{
|
c := &Commander{
|
||||||
config: *config,
|
config: *config,
|
||||||
ohm: v.OutboundHandlerManager(),
|
ohm: v.OutboundHandlerManager(),
|
||||||
v: v,
|
v: v,
|
||||||
}
|
}
|
||||||
if err := v.RegisterFeature((*core.Commander)(nil), c); err != nil {
|
if err := v.RegisterFeature((*Commander)(nil), c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type implements common.HasType.
|
||||||
|
func (c *Commander) Type() interface{} {
|
||||||
|
return (*Commander)(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start implements common.Runnable.
|
||||||
func (c *Commander) Start() error {
|
func (c *Commander) Start() error {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
c.server = grpc.NewServer()
|
c.server = grpc.NewServer()
|
||||||
@ -69,13 +74,14 @@ func (c *Commander) Start() error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
c.ohm.RemoveHandler(context.Background(), c.config.Tag)
|
c.ohm.RemoveHandler(context.Background(), c.config.Tag)
|
||||||
c.ohm.AddHandler(context.Background(), &CommanderOutbound{
|
c.ohm.AddHandler(context.Background(), &Outbound{
|
||||||
tag: c.config.Tag,
|
tag: c.config.Tag,
|
||||||
listener: listener,
|
listener: listener,
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close implements common.Closable.
|
||||||
func (c *Commander) Close() error {
|
func (c *Commander) Close() error {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"v2ray.com/core/common"
|
||||||
"v2ray.com/core/common/signal"
|
"v2ray.com/core/common/signal"
|
||||||
"v2ray.com/core/transport/ray"
|
"v2ray.com/core/transport/ray"
|
||||||
)
|
)
|
||||||
@ -24,17 +25,19 @@ func (l *OutboundListener) add(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accept implements net.Listener.
|
||||||
func (l *OutboundListener) Accept() (net.Conn, error) {
|
func (l *OutboundListener) Accept() (net.Conn, error) {
|
||||||
select {
|
select {
|
||||||
case <-l.done.C():
|
case <-l.done.C():
|
||||||
return nil, newError("listern closed")
|
return nil, newError("listen closed")
|
||||||
case c := <-l.buffer:
|
case c := <-l.buffer:
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close implement net.Listener.
|
||||||
func (l *OutboundListener) Close() error {
|
func (l *OutboundListener) Close() error {
|
||||||
l.done.Close()
|
common.Must(l.done.Close())
|
||||||
L:
|
L:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -47,6 +50,7 @@ L:
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Addr implements net.Listener.
|
||||||
func (l *OutboundListener) Addr() net.Addr {
|
func (l *OutboundListener) Addr() net.Addr {
|
||||||
return &net.TCPAddr{
|
return &net.TCPAddr{
|
||||||
IP: net.IP{0, 0, 0, 0},
|
IP: net.IP{0, 0, 0, 0},
|
||||||
@ -54,14 +58,16 @@ func (l *OutboundListener) Addr() net.Addr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommanderOutbound struct {
|
// Outbound is a core.OutboundHandler that handles gRPC connections.
|
||||||
|
type Outbound struct {
|
||||||
tag string
|
tag string
|
||||||
listener *OutboundListener
|
listener *OutboundListener
|
||||||
access sync.RWMutex
|
access sync.RWMutex
|
||||||
closed bool
|
closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *CommanderOutbound) Dispatch(ctx context.Context, r ray.OutboundRay) {
|
// Dispatch implements core.OutboundHandler.
|
||||||
|
func (co *Outbound) Dispatch(ctx context.Context, r ray.OutboundRay) {
|
||||||
co.access.RLock()
|
co.access.RLock()
|
||||||
|
|
||||||
if co.closed {
|
if co.closed {
|
||||||
@ -76,26 +82,26 @@ func (co *CommanderOutbound) Dispatch(ctx context.Context, r ray.OutboundRay) {
|
|||||||
co.listener.add(c)
|
co.listener.add(c)
|
||||||
co.access.RUnlock()
|
co.access.RUnlock()
|
||||||
<-closeSignal.Wait()
|
<-closeSignal.Wait()
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *CommanderOutbound) Tag() string {
|
// Tag implements core.OutboundHandler.
|
||||||
|
func (co *Outbound) Tag() string {
|
||||||
return co.tag
|
return co.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *CommanderOutbound) Start() error {
|
// Start implements common.Runnable.
|
||||||
|
func (co *Outbound) Start() error {
|
||||||
co.access.Lock()
|
co.access.Lock()
|
||||||
co.closed = false
|
co.closed = false
|
||||||
co.access.Unlock()
|
co.access.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *CommanderOutbound) Close() error {
|
// Close implements common.Closable.
|
||||||
|
func (co *Outbound) Close() error {
|
||||||
co.access.Lock()
|
co.access.Lock()
|
||||||
co.closed = true
|
defer co.access.Unlock()
|
||||||
co.listener.Close()
|
|
||||||
co.access.Unlock()
|
|
||||||
|
|
||||||
return nil
|
co.closed = true
|
||||||
|
return co.listener.Close()
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Service is a Commander service.
|
||||||
type Service interface {
|
type Service interface {
|
||||||
|
// Register registers the service itself to a gRPC server.
|
||||||
Register(*grpc.Server)
|
Register(*grpc.Server)
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"v2ray.com/core/common"
|
"v2ray.com/core/common"
|
||||||
"v2ray.com/core/common/buf"
|
"v2ray.com/core/common/buf"
|
||||||
"v2ray.com/core/common/net"
|
"v2ray.com/core/common/net"
|
||||||
|
"v2ray.com/core/common/protocol"
|
||||||
"v2ray.com/core/proxy"
|
"v2ray.com/core/proxy"
|
||||||
"v2ray.com/core/transport/ray"
|
"v2ray.com/core/transport/ray"
|
||||||
)
|
)
|
||||||
@ -23,18 +24,18 @@ var (
|
|||||||
type DefaultDispatcher struct {
|
type DefaultDispatcher struct {
|
||||||
ohm core.OutboundHandlerManager
|
ohm core.OutboundHandlerManager
|
||||||
router core.Router
|
router core.Router
|
||||||
|
policy core.PolicyManager
|
||||||
|
stats core.StatManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultDispatcher create a new DefaultDispatcher.
|
// NewDefaultDispatcher create a new DefaultDispatcher.
|
||||||
func NewDefaultDispatcher(ctx context.Context, config *Config) (*DefaultDispatcher, error) {
|
func NewDefaultDispatcher(ctx context.Context, config *Config) (*DefaultDispatcher, error) {
|
||||||
v := core.FromContext(ctx)
|
v := core.MustFromContext(ctx)
|
||||||
if v == nil {
|
|
||||||
return nil, newError("V is not in context.")
|
|
||||||
}
|
|
||||||
|
|
||||||
d := &DefaultDispatcher{
|
d := &DefaultDispatcher{
|
||||||
ohm: v.OutboundHandlerManager(),
|
ohm: v.OutboundHandlerManager(),
|
||||||
router: v.Router(),
|
router: v.Router(),
|
||||||
|
policy: v.PolicyManager(),
|
||||||
|
stats: v.Stats(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := v.RegisterFeature((*core.Dispatcher)(nil), d); err != nil {
|
if err := v.RegisterFeature((*core.Dispatcher)(nil), d); err != nil {
|
||||||
@ -43,14 +44,48 @@ func NewDefaultDispatcher(ctx context.Context, config *Config) (*DefaultDispatch
|
|||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start implements app.Application.
|
// Start implements common.Runnable.
|
||||||
func (*DefaultDispatcher) Start() error {
|
func (*DefaultDispatcher) Start() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements app.Application.
|
// Close implements common.Closable.
|
||||||
func (*DefaultDispatcher) Close() error { return nil }
|
func (*DefaultDispatcher) Close() error { return nil }
|
||||||
|
|
||||||
|
func (d *DefaultDispatcher) getStatCounter(name string) core.StatCounter {
|
||||||
|
c := d.stats.GetCounter(name)
|
||||||
|
if c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
c, err := d.stats.RegisterCounter(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DefaultDispatcher) getRayOption(user *protocol.User) []ray.Option {
|
||||||
|
var rayOptions []ray.Option
|
||||||
|
|
||||||
|
if user != nil && len(user.Email) > 0 {
|
||||||
|
p := d.policy.ForLevel(user.Level)
|
||||||
|
if p.Stats.UserUplink {
|
||||||
|
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
|
||||||
|
if c := d.getStatCounter(name); c != nil {
|
||||||
|
rayOptions = append(rayOptions, ray.WithUplinkStatCounter(c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Stats.UserDownlink {
|
||||||
|
name := "user>>>" + user.Email + ">>>traffic>>>downlink"
|
||||||
|
if c := d.getStatCounter(name); c != nil {
|
||||||
|
rayOptions = append(rayOptions, ray.WithDownlinkStatCounter(c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rayOptions
|
||||||
|
}
|
||||||
|
|
||||||
// Dispatch implements core.Dispatcher.
|
// Dispatch implements core.Dispatcher.
|
||||||
func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (ray.InboundRay, error) {
|
func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (ray.InboundRay, error) {
|
||||||
if !destination.IsValid() {
|
if !destination.IsValid() {
|
||||||
@ -58,15 +93,18 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
|||||||
}
|
}
|
||||||
ctx = proxy.ContextWithTarget(ctx, destination)
|
ctx = proxy.ContextWithTarget(ctx, destination)
|
||||||
|
|
||||||
outbound := ray.NewRay(ctx)
|
user := protocol.UserFromContext(ctx)
|
||||||
sniferList := proxyman.ProtocoSniffersFromContext(ctx)
|
rayOptions := d.getRayOption(user)
|
||||||
if destination.Address.Family().IsDomain() || len(sniferList) == 0 {
|
|
||||||
|
outbound := ray.New(ctx, rayOptions...)
|
||||||
|
snifferList := proxyman.ProtocolSniffersFromContext(ctx)
|
||||||
|
if destination.Address.Family().IsDomain() || len(snifferList) == 0 {
|
||||||
go d.routedDispatch(ctx, outbound, destination)
|
go d.routedDispatch(ctx, outbound, destination)
|
||||||
} else {
|
} else {
|
||||||
go func() {
|
go func() {
|
||||||
domain, err := snifer(ctx, sniferList, outbound)
|
domain, err := sniffer(ctx, snifferList, outbound)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
newError("sniffed domain: ", domain).WriteToLog()
|
newError("sniffed domain: ", domain).WithContext(ctx).WriteToLog()
|
||||||
destination.Address = net.ParseAddress(domain)
|
destination.Address = net.ParseAddress(domain)
|
||||||
ctx = proxy.ContextWithTarget(ctx, destination)
|
ctx = proxy.ContextWithTarget(ctx, destination)
|
||||||
}
|
}
|
||||||
@ -76,11 +114,11 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
|||||||
return outbound, nil
|
return outbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func snifer(ctx context.Context, sniferList []proxyman.KnownProtocols, outbound ray.OutboundRay) (string, error) {
|
func sniffer(ctx context.Context, snifferList []proxyman.KnownProtocols, outbound ray.OutboundRay) (string, error) {
|
||||||
payload := buf.New()
|
payload := buf.New()
|
||||||
defer payload.Release()
|
defer payload.Release()
|
||||||
|
|
||||||
sniffer := NewSniffer(sniferList)
|
sniffer := NewSniffer(snifferList)
|
||||||
totalAttempt := 0
|
totalAttempt := 0
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -111,13 +149,13 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, outbound ray.Out
|
|||||||
if d.router != nil {
|
if d.router != nil {
|
||||||
if tag, err := d.router.PickRoute(ctx); err == nil {
|
if tag, err := d.router.PickRoute(ctx); err == nil {
|
||||||
if handler := d.ohm.GetHandler(tag); handler != nil {
|
if handler := d.ohm.GetHandler(tag); handler != nil {
|
||||||
newError("taking detour [", tag, "] for [", destination, "]").WriteToLog()
|
newError("taking detour [", tag, "] for [", destination, "]").WithContext(ctx).WriteToLog()
|
||||||
dispatcher = handler
|
dispatcher = handler
|
||||||
} else {
|
} else {
|
||||||
newError("nonexisting tag: ", tag).AtWarning().WriteToLog()
|
newError("non existing tag: ", tag).AtWarning().WithContext(ctx).WriteToLog()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newError("default route for ", destination).WriteToLog()
|
newError("default route for ", destination).WithContext(ctx).WriteToLog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dispatcher.Dispatch(ctx, outbound)
|
dispatcher.Dispatch(ctx, outbound)
|
||||||
|
@ -173,10 +173,10 @@ type Sniffer struct {
|
|||||||
err []error
|
err []error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSniffer(sniferList []proxyman.KnownProtocols) *Sniffer {
|
func NewSniffer(snifferList []proxyman.KnownProtocols) *Sniffer {
|
||||||
s := new(Sniffer)
|
s := new(Sniffer)
|
||||||
|
|
||||||
for _, protocol := range sniferList {
|
for _, protocol := range snifferList {
|
||||||
var f func([]byte) (string, error)
|
var f func([]byte) (string, error)
|
||||||
switch protocol {
|
switch protocol {
|
||||||
case proxyman.KnownProtocols_HTTP:
|
case proxyman.KnownProtocols_HTTP:
|
||||||
|
@ -7,17 +7,14 @@ import (
|
|||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"v2ray.com/core"
|
"v2ray.com/core"
|
||||||
|
"v2ray.com/core/common"
|
||||||
"v2ray.com/core/common/buf"
|
"v2ray.com/core/common/buf"
|
||||||
"v2ray.com/core/common/dice"
|
"v2ray.com/core/common/dice"
|
||||||
"v2ray.com/core/common/net"
|
"v2ray.com/core/common/net"
|
||||||
|
"v2ray.com/core/common/signal"
|
||||||
"v2ray.com/core/transport/internet/udp"
|
"v2ray.com/core/transport/internet/udp"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
CleanupInterval = time.Second * 120
|
|
||||||
CleanupThreshold = 512
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
multiQuestionDNS = map[net.Address]bool{
|
multiQuestionDNS = map[net.Address]bool{
|
||||||
net.IPAddress([]byte{8, 8, 8, 8}): true,
|
net.IPAddress([]byte{8, 8, 8, 8}): true,
|
||||||
@ -45,7 +42,7 @@ type UDPNameServer struct {
|
|||||||
address net.Destination
|
address net.Destination
|
||||||
requests map[uint16]*PendingRequest
|
requests map[uint16]*PendingRequest
|
||||||
udpServer *udp.Dispatcher
|
udpServer *udp.Dispatcher
|
||||||
nextCleanup time.Time
|
cleanup *signal.PeriodicTask
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUDPNameServer(address net.Destination, dispatcher core.Dispatcher) *UDPNameServer {
|
func NewUDPNameServer(address net.Destination, dispatcher core.Dispatcher) *UDPNameServer {
|
||||||
@ -54,36 +51,35 @@ func NewUDPNameServer(address net.Destination, dispatcher core.Dispatcher) *UDPN
|
|||||||
requests: make(map[uint16]*PendingRequest),
|
requests: make(map[uint16]*PendingRequest),
|
||||||
udpServer: udp.NewDispatcher(dispatcher),
|
udpServer: udp.NewDispatcher(dispatcher),
|
||||||
}
|
}
|
||||||
|
s.cleanup = &signal.PeriodicTask{
|
||||||
|
Interval: time.Minute,
|
||||||
|
Execute: s.Cleanup,
|
||||||
|
}
|
||||||
|
common.Must(s.cleanup.Start())
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UDPNameServer) Cleanup() {
|
func (s *UDPNameServer) Cleanup() error {
|
||||||
expiredRequests := make([]uint16, 0, 16)
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
s.Lock()
|
s.Lock()
|
||||||
for id, r := range s.requests {
|
for id, r := range s.requests {
|
||||||
if r.expire.Before(now) {
|
if r.expire.Before(now) {
|
||||||
expiredRequests = append(expiredRequests, id)
|
|
||||||
close(r.response)
|
close(r.response)
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, id := range expiredRequests {
|
|
||||||
delete(s.requests, id)
|
delete(s.requests, id)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UDPNameServer) AssignUnusedID(response chan<- *ARecord) uint16 {
|
func (s *UDPNameServer) AssignUnusedID(response chan<- *ARecord) uint16 {
|
||||||
var id uint16
|
var id uint16
|
||||||
s.Lock()
|
s.Lock()
|
||||||
if len(s.requests) > CleanupThreshold && s.nextCleanup.Before(time.Now()) {
|
|
||||||
s.nextCleanup = time.Now().Add(CleanupInterval)
|
|
||||||
go s.Cleanup()
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
id = dice.RollUint16()
|
id = dice.RollUint16()
|
||||||
if _, found := s.requests[id]; found {
|
if _, found := s.requests[id]; found {
|
||||||
|
time.Sleep(time.Millisecond * 500)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
newError("add pending request id ", id).AtDebug().WriteToLog()
|
newError("add pending request id ", id).AtDebug().WriteToLog()
|
||||||
@ -182,6 +178,9 @@ func (s *UDPNameServer) QueryA(domain string) <-chan *ARecord {
|
|||||||
b, err := msgToBuffer(msg)
|
b, err := msgToBuffer(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError("failed to build A query for domain ", domain).Base(err).WriteToLog()
|
newError("failed to build A query for domain ", domain).Base(err).WriteToLog()
|
||||||
|
s.Lock()
|
||||||
|
delete(s.requests, id)
|
||||||
|
s.Unlock()
|
||||||
close(response)
|
close(response)
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
@ -28,11 +28,6 @@ func (r *DomainRecord) Expired() bool {
|
|||||||
return r.Expire.Before(time.Now())
|
return r.Expire.Before(time.Now())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DomainRecord) Inactive() bool {
|
|
||||||
now := time.Now()
|
|
||||||
return r.Expire.Before(now) || r.LastAccess.Add(time.Minute*5).Before(now)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
hosts map[string]net.IP
|
hosts map[string]net.IP
|
||||||
@ -54,11 +49,7 @@ func New(ctx context.Context, config *Config) (*Server, error) {
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
v := core.FromContext(ctx)
|
v := core.MustFromContext(ctx)
|
||||||
if v == nil {
|
|
||||||
return nil, newError("V is not in context.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.RegisterFeature((*core.DNSClient)(nil), server); err != nil {
|
if err := v.RegisterFeature((*core.DNSClient)(nil), server); err != nil {
|
||||||
return nil, newError("unable to register DNSClient.").Base(err)
|
return nil, newError("unable to register DNSClient.").Base(err)
|
||||||
}
|
}
|
||||||
@ -89,7 +80,7 @@ func (s *Server) Start() error {
|
|||||||
return s.task.Start()
|
return s.task.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements common.Runnable.
|
// Close implements common.Closable.
|
||||||
func (s *Server) Close() error {
|
func (s *Server) Close() error {
|
||||||
return s.task.Close()
|
return s.task.Close()
|
||||||
}
|
}
|
||||||
|
48
app/log/command/command.go
Normal file
48
app/log/command/command.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg command -path App,Log,Command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
|
||||||
|
"v2ray.com/core"
|
||||||
|
"v2ray.com/core/app/log"
|
||||||
|
"v2ray.com/core/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoggerServer struct {
|
||||||
|
V *core.Instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LoggerServer) RestartLogger(ctx context.Context, request *RestartLoggerRequest) (*RestartLoggerResponse, error) {
|
||||||
|
logger := s.V.GetFeature((*log.Instance)(nil))
|
||||||
|
if logger == nil {
|
||||||
|
return nil, newError("unable to get logger instance")
|
||||||
|
}
|
||||||
|
if err := logger.Close(); err != nil {
|
||||||
|
return nil, newError("failed to close logger").Base(err)
|
||||||
|
}
|
||||||
|
if err := logger.Start(); err != nil {
|
||||||
|
return nil, newError("failed to start logger").Base(err)
|
||||||
|
}
|
||||||
|
return &RestartLoggerResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type service struct {
|
||||||
|
v *core.Instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) Register(server *grpc.Server) {
|
||||||
|
RegisterLoggerServiceServer(server, &LoggerServer{
|
||||||
|
V: s.v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
|
||||||
|
s := core.MustFromContext(ctx)
|
||||||
|
return &service{v: s}, nil
|
||||||
|
}))
|
||||||
|
}
|
38
app/log/command/command_test.go
Normal file
38
app/log/command/command_test.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package command_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"v2ray.com/core"
|
||||||
|
"v2ray.com/core/app/dispatcher"
|
||||||
|
"v2ray.com/core/app/log"
|
||||||
|
. "v2ray.com/core/app/log/command"
|
||||||
|
"v2ray.com/core/app/proxyman"
|
||||||
|
_ "v2ray.com/core/app/proxyman/inbound"
|
||||||
|
_ "v2ray.com/core/app/proxyman/outbound"
|
||||||
|
"v2ray.com/core/common/serial"
|
||||||
|
. "v2ray.com/ext/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoggerRestart(t *testing.T) {
|
||||||
|
assert := With(t)
|
||||||
|
|
||||||
|
v, err := core.New(&core.Config{
|
||||||
|
App: []*serial.TypedMessage{
|
||||||
|
serial.ToTypedMessage(&log.Config{}),
|
||||||
|
serial.ToTypedMessage(&dispatcher.Config{}),
|
||||||
|
serial.ToTypedMessage(&proxyman.InboundConfig{}),
|
||||||
|
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert(err, IsNil)
|
||||||
|
assert(v.Start(), IsNil)
|
||||||
|
|
||||||
|
server := &LoggerServer{
|
||||||
|
V: v,
|
||||||
|
}
|
||||||
|
_, err = server.RestartLogger(context.Background(), &RestartLoggerRequest{})
|
||||||
|
assert(err, IsNil)
|
||||||
|
}
|
144
app/log/command/config.pb.go
Normal file
144
app/log/command/config.pb.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Config) Reset() { *m = Config{} }
|
||||||
|
func (m *Config) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||||
|
|
||||||
|
type RestartLoggerRequest struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RestartLoggerRequest) Reset() { *m = RestartLoggerRequest{} }
|
||||||
|
func (m *RestartLoggerRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*RestartLoggerRequest) ProtoMessage() {}
|
||||||
|
func (*RestartLoggerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||||
|
|
||||||
|
type RestartLoggerResponse struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RestartLoggerResponse) Reset() { *m = RestartLoggerResponse{} }
|
||||||
|
func (m *RestartLoggerResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*RestartLoggerResponse) ProtoMessage() {}
|
||||||
|
func (*RestartLoggerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Config)(nil), "v2ray.core.app.log.command.Config")
|
||||||
|
proto.RegisterType((*RestartLoggerRequest)(nil), "v2ray.core.app.log.command.RestartLoggerRequest")
|
||||||
|
proto.RegisterType((*RestartLoggerResponse)(nil), "v2ray.core.app.log.command.RestartLoggerResponse")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ context.Context
|
||||||
|
var _ grpc.ClientConn
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
const _ = grpc.SupportPackageIsVersion4
|
||||||
|
|
||||||
|
// Client API for LoggerService service
|
||||||
|
|
||||||
|
type LoggerServiceClient interface {
|
||||||
|
RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type loggerServiceClient struct {
|
||||||
|
cc *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoggerServiceClient(cc *grpc.ClientConn) LoggerServiceClient {
|
||||||
|
return &loggerServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) {
|
||||||
|
out := new(RestartLoggerResponse)
|
||||||
|
err := grpc.Invoke(ctx, "/v2ray.core.app.log.command.LoggerService/RestartLogger", in, out, c.cc, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server API for LoggerService service
|
||||||
|
|
||||||
|
type LoggerServiceServer interface {
|
||||||
|
RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterLoggerServiceServer(s *grpc.Server, srv LoggerServiceServer) {
|
||||||
|
s.RegisterService(&_LoggerService_serviceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _LoggerService_RestartLogger_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(RestartLoggerRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(LoggerServiceServer).RestartLogger(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/v2ray.core.app.log.command.LoggerService/RestartLogger",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(LoggerServiceServer).RestartLogger(ctx, req.(*RestartLoggerRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _LoggerService_serviceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "v2ray.core.app.log.command.LoggerService",
|
||||||
|
HandlerType: (*LoggerServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "RestartLogger",
|
||||||
|
Handler: _LoggerService_RestartLogger_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "v2ray.com/core/app/log/command/config.proto",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("v2ray.com/core/app/log/command/config.proto", fileDescriptor0) }
|
||||||
|
|
||||||
|
var fileDescriptor0 = []byte{
|
||||||
|
// 210 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x2e, 0x33, 0x2a, 0x4a,
|
||||||
|
0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0xcf,
|
||||||
|
0xc9, 0x4f, 0xd7, 0x4f, 0xce, 0xcf, 0xcd, 0x4d, 0xcc, 0x4b, 0xd1, 0x4f, 0xce, 0xcf, 0x4b, 0xcb,
|
||||||
|
0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x82, 0x29, 0x2e, 0x4a, 0xd5, 0x4b, 0x2c,
|
||||||
|
0x28, 0xd0, 0xcb, 0xc9, 0x4f, 0xd7, 0x83, 0x2a, 0x54, 0xe2, 0xe0, 0x62, 0x73, 0x06, 0xab, 0x55,
|
||||||
|
0x12, 0xe3, 0x12, 0x09, 0x4a, 0x2d, 0x2e, 0x49, 0x2c, 0x2a, 0xf1, 0xc9, 0x4f, 0x4f, 0x4f, 0x2d,
|
||||||
|
0x0a, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x51, 0x12, 0xe7, 0x12, 0x45, 0x13, 0x2f, 0x2e, 0xc8,
|
||||||
|
0xcf, 0x2b, 0x4e, 0x35, 0x6a, 0x67, 0xe4, 0xe2, 0x85, 0x08, 0x05, 0xa7, 0x16, 0x95, 0x65, 0x26,
|
||||||
|
0xa7, 0x0a, 0x95, 0x71, 0xf1, 0xa2, 0x28, 0x15, 0x32, 0xd0, 0xc3, 0x6d, 0xb5, 0x1e, 0x36, 0xdb,
|
||||||
|
0xa4, 0x0c, 0x49, 0xd0, 0x01, 0x71, 0x87, 0x12, 0x83, 0x93, 0x07, 0x97, 0x5c, 0x72, 0x7e, 0x2e,
|
||||||
|
0x1e, 0x9d, 0x01, 0x8c, 0x51, 0xec, 0x50, 0xe6, 0x2a, 0x26, 0xa9, 0x30, 0xa3, 0xa0, 0xc4, 0x4a,
|
||||||
|
0x3d, 0x67, 0x90, 0x3a, 0xc7, 0x82, 0x02, 0x3d, 0x9f, 0xfc, 0x74, 0x3d, 0x67, 0x88, 0x64, 0x12,
|
||||||
|
0x1b, 0x38, 0xc4, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x37, 0xc7, 0xfc, 0xda, 0x60, 0x01,
|
||||||
|
0x00, 0x00,
|
||||||
|
}
|
18
app/log/command/config.proto
Normal file
18
app/log/command/config.proto
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package v2ray.core.app.log.command;
|
||||||
|
option csharp_namespace = "V2Ray.Core.App.Log.Command";
|
||||||
|
option go_package = "command";
|
||||||
|
option java_package = "com.v2ray.core.app.log.command";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {
|
||||||
|
}
|
||||||
|
|
||||||
|
message RestartLoggerRequest {}
|
||||||
|
|
||||||
|
message RestartLoggerResponse{}
|
||||||
|
|
||||||
|
service LoggerService {
|
||||||
|
rpc RestartLogger(RestartLoggerRequest) returns (RestartLoggerResponse) {}
|
||||||
|
}
|
7
app/log/command/errors.generated.go
Normal file
7
app/log/command/errors.generated.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import "v2ray.com/core/common/errors"
|
||||||
|
|
||||||
|
func newError(values ...interface{}) *errors.Error {
|
||||||
|
return errors.New(values...).Path("App", "Log", "Command")
|
||||||
|
}
|
@ -6,11 +6,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"v2ray.com/core"
|
||||||
"v2ray.com/core/common"
|
"v2ray.com/core/common"
|
||||||
"v2ray.com/core/common/log"
|
"v2ray.com/core/common/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Instance is an app.Application that handles logs.
|
// Instance is a log.Handler that handles logs.
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
config *Config
|
config *Config
|
||||||
@ -23,17 +24,15 @@ type Instance struct {
|
|||||||
func New(ctx context.Context, config *Config) (*Instance, error) {
|
func New(ctx context.Context, config *Config) (*Instance, error) {
|
||||||
g := &Instance{
|
g := &Instance{
|
||||||
config: config,
|
config: config,
|
||||||
active: true,
|
active: false,
|
||||||
}
|
|
||||||
|
|
||||||
if err := g.initAccessLogger(); err != nil {
|
|
||||||
return nil, newError("failed to initialize access logger").Base(err).AtWarning()
|
|
||||||
}
|
|
||||||
if err := g.initErrorLogger(); err != nil {
|
|
||||||
return nil, newError("failed to initialize error logger").Base(err).AtWarning()
|
|
||||||
}
|
}
|
||||||
log.RegisterHandler(g)
|
log.RegisterHandler(g)
|
||||||
|
|
||||||
|
v := core.FromContext(ctx)
|
||||||
|
if v != nil {
|
||||||
|
common.Must(v.RegisterFeature((*log.Handler)(nil), g))
|
||||||
|
}
|
||||||
|
|
||||||
return g, nil
|
return g, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,24 +66,48 @@ func (g *Instance) initErrorLogger() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start implements app.Application.Start().
|
// Type implements common.HasType.
|
||||||
func (g *Instance) Start() error {
|
func (*Instance) Type() interface{} {
|
||||||
|
return (*Instance)(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Instance) startInternal() error {
|
||||||
g.Lock()
|
g.Lock()
|
||||||
defer g.Unlock()
|
defer g.Unlock()
|
||||||
|
|
||||||
|
if g.active {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
g.active = true
|
g.active = true
|
||||||
|
|
||||||
|
if err := g.initAccessLogger(); err != nil {
|
||||||
|
return newError("failed to initialize access logger").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
if err := g.initErrorLogger(); err != nil {
|
||||||
|
return newError("failed to initialize error logger").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Instance) isActive() bool {
|
// Start implements common.Runnable.Start().
|
||||||
g.RLock()
|
func (g *Instance) Start() error {
|
||||||
defer g.RUnlock()
|
if err := g.startInternal(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return g.active
|
newError("Logger started").AtDebug().WriteToLog()
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle implements log.Handler.
|
// Handle implements log.Handler.
|
||||||
func (g *Instance) Handle(msg log.Message) {
|
func (g *Instance) Handle(msg log.Message) {
|
||||||
if !g.isActive() {
|
g.RLock()
|
||||||
|
defer g.RUnlock()
|
||||||
|
|
||||||
|
if !g.active {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,13 +125,25 @@ func (g *Instance) Handle(msg log.Message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implement app.Application.Close().
|
// Close implements common.Closable.Close().
|
||||||
func (g *Instance) Close() error {
|
func (g *Instance) Close() error {
|
||||||
|
newError("Logger closing").AtDebug().WriteToLog()
|
||||||
|
|
||||||
g.Lock()
|
g.Lock()
|
||||||
defer g.Unlock()
|
defer g.Unlock()
|
||||||
|
|
||||||
|
if !g.active {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
g.active = false
|
g.active = false
|
||||||
|
|
||||||
|
common.Close(g.accessLogger)
|
||||||
|
g.accessLogger = nil
|
||||||
|
|
||||||
|
common.Close(g.errorLogger)
|
||||||
|
g.errorLogger = nil
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,24 +14,45 @@ func (s *Second) Duration() time.Duration {
|
|||||||
return time.Second * time.Duration(s.Value)
|
return time.Second * time.Duration(s.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OverrideWith overrides current Policy with another one.
|
func defaultPolicy() *Policy {
|
||||||
func (p *Policy) OverrideWith(another *Policy) {
|
p := core.DefaultPolicy()
|
||||||
if another.Timeout != nil {
|
|
||||||
if another.Timeout.Handshake != nil {
|
return &Policy{
|
||||||
p.Timeout.Handshake = another.Timeout.Handshake
|
Timeout: &Policy_Timeout{
|
||||||
}
|
Handshake: &Second{Value: uint32(p.Timeouts.Handshake / time.Second)},
|
||||||
if another.Timeout.ConnectionIdle != nil {
|
ConnectionIdle: &Second{Value: uint32(p.Timeouts.ConnectionIdle / time.Second)},
|
||||||
p.Timeout.ConnectionIdle = another.Timeout.ConnectionIdle
|
UplinkOnly: &Second{Value: uint32(p.Timeouts.UplinkOnly / time.Second)},
|
||||||
}
|
DownlinkOnly: &Second{Value: uint32(p.Timeouts.DownlinkOnly / time.Second)},
|
||||||
if another.Timeout.UplinkOnly != nil {
|
},
|
||||||
p.Timeout.UplinkOnly = another.Timeout.UplinkOnly
|
|
||||||
}
|
|
||||||
if another.Timeout.DownlinkOnly != nil {
|
|
||||||
p.Timeout.DownlinkOnly = another.Timeout.DownlinkOnly
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Policy_Timeout) overrideWith(another *Policy_Timeout) {
|
||||||
|
if another.Handshake != nil {
|
||||||
|
p.Handshake = &Second{Value: another.Handshake.Value}
|
||||||
|
}
|
||||||
|
if another.ConnectionIdle != nil {
|
||||||
|
p.ConnectionIdle = &Second{Value: another.ConnectionIdle.Value}
|
||||||
|
}
|
||||||
|
if another.UplinkOnly != nil {
|
||||||
|
p.UplinkOnly = &Second{Value: another.UplinkOnly.Value}
|
||||||
|
}
|
||||||
|
if another.DownlinkOnly != nil {
|
||||||
|
p.DownlinkOnly = &Second{Value: another.DownlinkOnly.Value}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Policy) overrideWith(another *Policy) {
|
||||||
|
if another.Timeout != nil {
|
||||||
|
p.Timeout.overrideWith(another.Timeout)
|
||||||
|
}
|
||||||
|
if another.Stats != nil && p.Stats == nil {
|
||||||
|
p.Stats = new(Policy_Stats)
|
||||||
|
*p.Stats = *another.Stats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToCorePolicy converts this Policy to core.Policy.
|
||||||
func (p *Policy) ToCorePolicy() core.Policy {
|
func (p *Policy) ToCorePolicy() core.Policy {
|
||||||
var cp core.Policy
|
var cp core.Policy
|
||||||
if p.Timeout != nil {
|
if p.Timeout != nil {
|
||||||
@ -40,5 +61,9 @@ func (p *Policy) ToCorePolicy() core.Policy {
|
|||||||
cp.Timeouts.DownlinkOnly = p.Timeout.DownlinkOnly.Duration()
|
cp.Timeouts.DownlinkOnly = p.Timeout.DownlinkOnly.Duration()
|
||||||
cp.Timeouts.UplinkOnly = p.Timeout.UplinkOnly.Duration()
|
cp.Timeouts.UplinkOnly = p.Timeout.UplinkOnly.Duration()
|
||||||
}
|
}
|
||||||
|
if p.Stats != nil {
|
||||||
|
cp.Stats.UserUplink = p.Stats.UserUplink
|
||||||
|
cp.Stats.UserDownlink = p.Stats.UserDownlink
|
||||||
|
}
|
||||||
return cp
|
return cp
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ func (m *Second) GetValue() uint32 {
|
|||||||
|
|
||||||
type Policy struct {
|
type Policy struct {
|
||||||
Timeout *Policy_Timeout `protobuf:"bytes,1,opt,name=timeout" json:"timeout,omitempty"`
|
Timeout *Policy_Timeout `protobuf:"bytes,1,opt,name=timeout" json:"timeout,omitempty"`
|
||||||
|
Stats *Policy_Stats `protobuf:"bytes,2,opt,name=stats" json:"stats,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Policy) Reset() { *m = Policy{} }
|
func (m *Policy) Reset() { *m = Policy{} }
|
||||||
@ -47,6 +48,13 @@ func (m *Policy) GetTimeout() *Policy_Timeout {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Policy) GetStats() *Policy_Stats {
|
||||||
|
if m != nil {
|
||||||
|
return m.Stats
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Timeout is a message for timeout settings in various stages, in seconds.
|
// Timeout is a message for timeout settings in various stages, in seconds.
|
||||||
type Policy_Timeout struct {
|
type Policy_Timeout struct {
|
||||||
Handshake *Second `protobuf:"bytes,1,opt,name=handshake" json:"handshake,omitempty"`
|
Handshake *Second `protobuf:"bytes,1,opt,name=handshake" json:"handshake,omitempty"`
|
||||||
@ -88,6 +96,30 @@ func (m *Policy_Timeout) GetDownlinkOnly() *Second {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Policy_Stats struct {
|
||||||
|
UserUplink bool `protobuf:"varint,1,opt,name=user_uplink,json=userUplink" json:"user_uplink,omitempty"`
|
||||||
|
UserDownlink bool `protobuf:"varint,2,opt,name=user_downlink,json=userDownlink" json:"user_downlink,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Policy_Stats) Reset() { *m = Policy_Stats{} }
|
||||||
|
func (m *Policy_Stats) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Policy_Stats) ProtoMessage() {}
|
||||||
|
func (*Policy_Stats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 1} }
|
||||||
|
|
||||||
|
func (m *Policy_Stats) GetUserUplink() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.UserUplink
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Policy_Stats) GetUserDownlink() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.UserDownlink
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Level map[uint32]*Policy `protobuf:"bytes,1,rep,name=level" json:"level,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
Level map[uint32]*Policy `protobuf:"bytes,1,rep,name=level" json:"level,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||||
}
|
}
|
||||||
@ -108,33 +140,38 @@ func init() {
|
|||||||
proto.RegisterType((*Second)(nil), "v2ray.core.app.policy.Second")
|
proto.RegisterType((*Second)(nil), "v2ray.core.app.policy.Second")
|
||||||
proto.RegisterType((*Policy)(nil), "v2ray.core.app.policy.Policy")
|
proto.RegisterType((*Policy)(nil), "v2ray.core.app.policy.Policy")
|
||||||
proto.RegisterType((*Policy_Timeout)(nil), "v2ray.core.app.policy.Policy.Timeout")
|
proto.RegisterType((*Policy_Timeout)(nil), "v2ray.core.app.policy.Policy.Timeout")
|
||||||
|
proto.RegisterType((*Policy_Stats)(nil), "v2ray.core.app.policy.Policy.Stats")
|
||||||
proto.RegisterType((*Config)(nil), "v2ray.core.app.policy.Config")
|
proto.RegisterType((*Config)(nil), "v2ray.core.app.policy.Config")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("v2ray.com/core/app/policy/config.proto", fileDescriptor0) }
|
func init() { proto.RegisterFile("v2ray.com/core/app/policy/config.proto", fileDescriptor0) }
|
||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
var fileDescriptor0 = []byte{
|
||||||
// 349 bytes of a gzipped FileDescriptorProto
|
// 410 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xc1, 0x4a, 0xeb, 0x40,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x5d, 0xab, 0xd3, 0x30,
|
||||||
0x14, 0x86, 0x49, 0x7a, 0x9b, 0x72, 0x4f, 0x6f, 0xaf, 0x32, 0x58, 0x88, 0x05, 0xa5, 0x14, 0x94,
|
0x18, 0xc7, 0x69, 0x6b, 0x7b, 0x8e, 0x4f, 0xcf, 0x54, 0x82, 0x07, 0xea, 0x40, 0x3d, 0x6c, 0x28,
|
||||||
0xae, 0x26, 0x90, 0x6e, 0x44, 0xb1, 0x62, 0x45, 0x41, 0x10, 0x2c, 0x51, 0x14, 0xdc, 0x94, 0x71,
|
0xbb, 0x4a, 0xa1, 0xbb, 0xf1, 0x05, 0x27, 0xce, 0x17, 0x10, 0x14, 0x47, 0xe6, 0x0b, 0x78, 0x33,
|
||||||
0x32, 0xda, 0xd0, 0xe9, 0x9c, 0x21, 0xa6, 0x95, 0xbc, 0x86, 0x6f, 0xe0, 0xd6, 0x87, 0xf2, 0x59,
|
0x62, 0x1a, 0x5d, 0x59, 0x96, 0x84, 0xbe, 0x4c, 0xfa, 0x35, 0xfc, 0x06, 0xde, 0xfa, 0xc9, 0xfc,
|
||||||
0x24, 0x99, 0x84, 0x6c, 0x5a, 0xe9, 0x6e, 0x72, 0xf8, 0xfe, 0x8f, 0x43, 0xfe, 0x03, 0x87, 0x4b,
|
0x18, 0xd2, 0xa4, 0xa5, 0x37, 0xdb, 0xdc, 0x5d, 0xfa, 0xf0, 0xfb, 0xff, 0x78, 0x12, 0xfe, 0x85,
|
||||||
0x3f, 0x66, 0x29, 0xe5, 0x38, 0xf7, 0x38, 0xc6, 0xc2, 0x63, 0x5a, 0x7b, 0x1a, 0x65, 0xc4, 0x53,
|
0x87, 0xbb, 0x24, 0xa7, 0x35, 0x66, 0x6a, 0x1b, 0x33, 0x95, 0xf3, 0x98, 0x6a, 0x1d, 0x6b, 0x25,
|
||||||
0x8f, 0xa3, 0x7a, 0x89, 0x5e, 0xa9, 0x8e, 0x31, 0x41, 0xd2, 0x2e, 0xb9, 0x58, 0x50, 0xa6, 0x35,
|
0x32, 0x56, 0xc7, 0x4c, 0xc9, 0xef, 0xd9, 0x0f, 0xac, 0x73, 0x55, 0x2a, 0x74, 0xd9, 0x71, 0x39,
|
||||||
0x35, 0x4c, 0x6f, 0x1f, 0x9c, 0x3b, 0xc1, 0x51, 0x85, 0x64, 0x07, 0xea, 0x4b, 0x26, 0x17, 0xc2,
|
0xc7, 0x54, 0x6b, 0x6c, 0x99, 0xd1, 0x3d, 0x08, 0x96, 0x9c, 0x29, 0x99, 0xa2, 0xdb, 0xe0, 0xef,
|
||||||
0xb5, 0xba, 0x56, 0xbf, 0x15, 0x98, 0x8f, 0xde, 0xb7, 0x0d, 0xce, 0x38, 0x47, 0xc9, 0x19, 0x34,
|
0xa8, 0xa8, 0x78, 0xe4, 0x5c, 0x39, 0x93, 0x01, 0xb1, 0x1f, 0xa3, 0xbf, 0x1e, 0x04, 0x0b, 0x83,
|
||||||
0x92, 0x68, 0x2e, 0x70, 0x91, 0xe4, 0x48, 0xd3, 0x3f, 0xa0, 0x2b, 0x9d, 0xd4, 0xf0, 0xf4, 0xde,
|
0xa2, 0xe7, 0x70, 0x56, 0x66, 0x5b, 0xae, 0xaa, 0xd2, 0x20, 0x61, 0xf2, 0x00, 0xef, 0x75, 0x62,
|
||||||
0xc0, 0x41, 0x99, 0xea, 0x7c, 0xd8, 0xd0, 0x28, 0x86, 0xe4, 0x04, 0xfe, 0x4e, 0x99, 0x0a, 0xdf,
|
0xcb, 0xe3, 0x8f, 0x16, 0x26, 0x5d, 0x0a, 0x3d, 0x06, 0xbf, 0x28, 0x69, 0x59, 0x44, 0xae, 0x89,
|
||||||
0xa6, 0x6c, 0x26, 0x0a, 0xdd, 0xde, 0x1a, 0x9d, 0xd9, 0x2f, 0xa8, 0x78, 0x72, 0x05, 0x5b, 0x1c,
|
0x8f, 0x8f, 0xc7, 0x97, 0x0d, 0x4a, 0x6c, 0x62, 0xf8, 0xcb, 0x85, 0xb3, 0xd6, 0x87, 0x9e, 0xc2,
|
||||||
0x95, 0x12, 0x3c, 0x89, 0x50, 0x4d, 0xa2, 0x50, 0x0a, 0xd7, 0xde, 0x44, 0xf1, 0xbf, 0x4a, 0x5d,
|
0xf5, 0x35, 0x95, 0x69, 0xb1, 0xa6, 0x1b, 0xde, 0x6e, 0x72, 0xf7, 0x80, 0xca, 0x5e, 0x8d, 0xf4,
|
||||||
0x87, 0x52, 0x90, 0x21, 0x34, 0x17, 0x5a, 0x46, 0x6a, 0x36, 0x41, 0x25, 0x53, 0xb7, 0xb6, 0x89,
|
0x3c, 0x7a, 0x03, 0x37, 0x99, 0x92, 0x92, 0xb3, 0x32, 0x53, 0x72, 0x95, 0xa5, 0x82, 0xb7, 0xdb,
|
||||||
0x03, 0x4c, 0xe2, 0x56, 0xc9, 0x94, 0x8c, 0xa0, 0x15, 0xe2, 0xbb, 0xaa, 0x0c, 0x7f, 0x36, 0x31,
|
0xfc, 0x47, 0x71, 0xa3, 0x4f, 0xbd, 0x4d, 0x05, 0x47, 0x33, 0x08, 0x2b, 0x2d, 0x32, 0xb9, 0x59,
|
||||||
0xfc, 0x2b, 0x33, 0x99, 0xa3, 0xf7, 0x69, 0x81, 0x73, 0x91, 0x17, 0x45, 0x86, 0x50, 0x97, 0x62,
|
0x29, 0x29, 0xea, 0xc8, 0x3b, 0xc5, 0x01, 0x36, 0xf1, 0x41, 0x8a, 0x1a, 0xcd, 0x61, 0x90, 0xaa,
|
||||||
0x29, 0xa4, 0x6b, 0x75, 0x6b, 0xfd, 0xa6, 0xdf, 0x5f, 0xa3, 0x31, 0x34, 0xbd, 0xc9, 0xd0, 0x4b,
|
0x9f, 0xb2, 0x37, 0x5c, 0x3b, 0xc5, 0x70, 0xd1, 0x65, 0x1a, 0xc7, 0xf0, 0x3d, 0xf8, 0xe6, 0x91,
|
||||||
0x95, 0xc4, 0x69, 0x60, 0x62, 0x9d, 0x47, 0x80, 0x6a, 0x48, 0xb6, 0xa1, 0x36, 0x13, 0x69, 0xd1,
|
0xd0, 0x7d, 0x08, 0xab, 0x82, 0xe7, 0x2b, 0xeb, 0x37, 0x6f, 0x72, 0x4e, 0xa0, 0x19, 0x7d, 0x32,
|
||||||
0x66, 0xf6, 0x24, 0x83, 0xb2, 0xe1, 0xdf, 0x7f, 0x96, 0xa9, 0xaf, 0x38, 0x80, 0x63, 0xfb, 0xc8,
|
0x13, 0x34, 0x86, 0x81, 0x01, 0xba, 0xb8, 0xb9, 0xf3, 0x39, 0xb9, 0x68, 0x86, 0xaf, 0xda, 0xd9,
|
||||||
0x1a, 0x9d, 0xc2, 0x2e, 0xc7, 0xf9, 0x6a, 0x7c, 0x6c, 0x3d, 0x39, 0xe6, 0xf5, 0x65, 0xb7, 0x1f,
|
0xe8, 0xb7, 0x03, 0xc1, 0x4b, 0x53, 0x19, 0x34, 0x03, 0x5f, 0xf0, 0x1d, 0x17, 0x91, 0x73, 0xe5,
|
||||||
0xfc, 0x80, 0x65, 0x0b, 0xc6, 0x82, 0x9e, 0x6b, 0x5d, 0x98, 0x9e, 0x9d, 0xfc, 0x02, 0x07, 0x3f,
|
0x4d, 0xc2, 0x64, 0x72, 0x60, 0x2b, 0x4b, 0xe3, 0x77, 0x0d, 0xfa, 0x5a, 0x96, 0x79, 0x4d, 0x6c,
|
||||||
0x01, 0x00, 0x00, 0xff, 0xff, 0xcf, 0x25, 0x25, 0xc2, 0xab, 0x02, 0x00, 0x00,
|
0x6c, 0xf8, 0x05, 0xa0, 0x1f, 0xa2, 0x5b, 0xe0, 0x6d, 0x78, 0xdd, 0xf6, 0xaa, 0x39, 0xa2, 0x69,
|
||||||
|
0xd7, 0xb5, 0xe3, 0x6f, 0x6f, 0x9b, 0xd0, 0x56, 0xf1, 0x89, 0xfb, 0xc8, 0x99, 0x3f, 0x83, 0x3b,
|
||||||
|
0x4c, 0x6d, 0xf7, 0xe3, 0x0b, 0xe7, 0x6b, 0x60, 0x4f, 0x7f, 0xdc, 0xcb, 0xcf, 0x09, 0xa1, 0xcd,
|
||||||
|
0x82, 0x39, 0xc7, 0x2f, 0xb4, 0x6e, 0x4d, 0xdf, 0x02, 0xf3, 0x2f, 0x4c, 0xff, 0x05, 0x00, 0x00,
|
||||||
|
0xff, 0xff, 0x98, 0xa8, 0x2f, 0xbe, 0x35, 0x03, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,13 @@ message Policy {
|
|||||||
Second downlink_only = 4;
|
Second downlink_only = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message Stats {
|
||||||
|
bool user_uplink = 1;
|
||||||
|
bool user_downlink = 2;
|
||||||
|
}
|
||||||
|
|
||||||
Timeout timeout = 1;
|
Timeout timeout = 1;
|
||||||
|
Stats stats = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
|
@ -9,30 +9,28 @@ import (
|
|||||||
|
|
||||||
// Instance is an instance of Policy manager.
|
// Instance is an instance of Policy manager.
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
levels map[uint32]core.Policy
|
levels map[uint32]*Policy
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates new Policy manager instance.
|
// New creates new Policy manager instance.
|
||||||
func New(ctx context.Context, config *Config) (*Instance, error) {
|
func New(ctx context.Context, config *Config) (*Instance, error) {
|
||||||
m := &Instance{
|
m := &Instance{
|
||||||
levels: make(map[uint32]core.Policy),
|
levels: make(map[uint32]*Policy),
|
||||||
}
|
}
|
||||||
if len(config.Level) > 0 {
|
if len(config.Level) > 0 {
|
||||||
for lv, p := range config.Level {
|
for lv, p := range config.Level {
|
||||||
dp := core.DefaultPolicy()
|
pp := defaultPolicy()
|
||||||
dp.OverrideWith(p.ToCorePolicy())
|
pp.overrideWith(p)
|
||||||
m.levels[lv] = dp
|
m.levels[lv] = pp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v := core.FromContext(ctx)
|
v := core.FromContext(ctx)
|
||||||
if v == nil {
|
if v != nil {
|
||||||
return nil, newError("V is not in context.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.RegisterFeature((*core.PolicyManager)(nil), m); err != nil {
|
if err := v.RegisterFeature((*core.PolicyManager)(nil), m); err != nil {
|
||||||
return nil, newError("unable to register PolicyManager in core").Base(err).AtError()
|
return nil, newError("unable to register PolicyManager in core").Base(err).AtError()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
@ -40,17 +38,17 @@ func New(ctx context.Context, config *Config) (*Instance, error) {
|
|||||||
// ForLevel implements core.PolicyManager.
|
// ForLevel implements core.PolicyManager.
|
||||||
func (m *Instance) ForLevel(level uint32) core.Policy {
|
func (m *Instance) ForLevel(level uint32) core.Policy {
|
||||||
if p, ok := m.levels[level]; ok {
|
if p, ok := m.levels[level]; ok {
|
||||||
return p
|
return p.ToCorePolicy()
|
||||||
}
|
}
|
||||||
return core.DefaultPolicy()
|
return core.DefaultPolicy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start implements app.Application.Start().
|
// Start implements common.Runnable.Start().
|
||||||
func (m *Instance) Start() error {
|
func (m *Instance) Start() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements app.Application.Close().
|
// Close implements common.Closable.Close().
|
||||||
func (m *Instance) Close() error {
|
func (m *Instance) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
37
app/policy/manager_test.go
Normal file
37
app/policy/manager_test.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package policy_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"v2ray.com/core"
|
||||||
|
. "v2ray.com/core/app/policy"
|
||||||
|
. "v2ray.com/ext/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPolicy(t *testing.T) {
|
||||||
|
assert := With(t)
|
||||||
|
|
||||||
|
manager, err := New(context.Background(), &Config{
|
||||||
|
Level: map[uint32]*Policy{
|
||||||
|
0: {
|
||||||
|
Timeout: &Policy_Timeout{
|
||||||
|
Handshake: &Second{
|
||||||
|
Value: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert(err, IsNil)
|
||||||
|
|
||||||
|
pDefault := core.DefaultPolicy()
|
||||||
|
|
||||||
|
p0 := manager.ForLevel(0)
|
||||||
|
assert(p0.Timeouts.Handshake, Equals, 2*time.Second)
|
||||||
|
assert(p0.Timeouts.ConnectionIdle, Equals, pDefault.Timeouts.ConnectionIdle)
|
||||||
|
|
||||||
|
p1 := manager.ForLevel(1)
|
||||||
|
assert(p1.Timeouts.Handshake, Equals, pDefault.Timeouts.Handshake)
|
||||||
|
}
|
11
app/proxyman/command/command.go
Normal file → Executable file
11
app/proxyman/command/command.go
Normal file → Executable file
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
// InboundOperation is the interface for operations that applies to inbound handlers.
|
// InboundOperation is the interface for operations that applies to inbound handlers.
|
||||||
type InboundOperation interface {
|
type InboundOperation interface {
|
||||||
// ApplyInbound appliess this operation to the given inbound handler.
|
// ApplyInbound applies this operation to the given inbound handler.
|
||||||
ApplyInbound(context.Context, core.InboundHandler) error
|
ApplyInbound(context.Context, core.InboundHandler) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler core.Inbou
|
|||||||
}
|
}
|
||||||
um, ok := p.(proxy.UserManager)
|
um, ok := p.(proxy.UserManager)
|
||||||
if !ok {
|
if !ok {
|
||||||
return newError("proxy is not an UserManager")
|
return newError("proxy is not a UserManager")
|
||||||
}
|
}
|
||||||
return um.AddUser(ctx, op.User)
|
return um.AddUser(ctx, op.User)
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ func (op *RemoveUserOperation) ApplyInbound(ctx context.Context, handler core.In
|
|||||||
}
|
}
|
||||||
um, ok := p.(proxy.UserManager)
|
um, ok := p.(proxy.UserManager)
|
||||||
if !ok {
|
if !ok {
|
||||||
return newError("proxy is not an UserManager")
|
return newError("proxy is not a UserManager")
|
||||||
}
|
}
|
||||||
return um.RemoveUser(ctx, op.Email)
|
return um.RemoveUser(ctx, op.Email)
|
||||||
}
|
}
|
||||||
@ -139,10 +139,7 @@ func (s *service) Register(server *grpc.Server) {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
|
||||||
s := core.FromContext(ctx)
|
s := core.MustFromContext(ctx)
|
||||||
if s == nil {
|
|
||||||
return nil, newError("V is not in context.")
|
|
||||||
}
|
|
||||||
return &service{v: s}, nil
|
return &service{v: s}, nil
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,8 @@ import v2ray_core_common_serial "v2ray.com/core/common/serial"
|
|||||||
import v2ray_core "v2ray.com/core"
|
import v2ray_core "v2ray.com/core"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
context "golang.org/x/net/context"
|
"context"
|
||||||
|
|
||||||
grpc "google.golang.org/grpc"
|
grpc "google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,10 +29,7 @@ type DynamicInboundHandler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*DynamicInboundHandler, error) {
|
func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*DynamicInboundHandler, error) {
|
||||||
v := core.FromContext(ctx)
|
v := core.MustFromContext(ctx)
|
||||||
if v == nil {
|
|
||||||
return nil, newError("V is not in context.")
|
|
||||||
}
|
|
||||||
h := &DynamicInboundHandler{
|
h := &DynamicInboundHandler{
|
||||||
tag: tag,
|
tag: tag,
|
||||||
proxyConfig: proxyConfig,
|
proxyConfig: proxyConfig,
|
||||||
|
@ -24,10 +24,7 @@ func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error)
|
|||||||
m := &Manager{
|
m := &Manager{
|
||||||
taggedHandlers: make(map[string]core.InboundHandler),
|
taggedHandlers: make(map[string]core.InboundHandler),
|
||||||
}
|
}
|
||||||
v := core.FromContext(ctx)
|
v := core.MustFromContext(ctx)
|
||||||
if v == nil {
|
|
||||||
return nil, newError("V is not in context")
|
|
||||||
}
|
|
||||||
if err := v.RegisterFeature((*core.InboundHandlerManager)(nil), m); err != nil {
|
if err := v.RegisterFeature((*core.InboundHandlerManager)(nil), m); err != nil {
|
||||||
return nil, newError("unable to register InboundHandlerManager").Base(err)
|
return nil, newError("unable to register InboundHandlerManager").Base(err)
|
||||||
}
|
}
|
||||||
@ -53,7 +50,7 @@ func (m *Manager) AddHandler(ctx context.Context, handler core.InboundHandler) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHandler returns core.InboundHandlerManager.
|
// GetHandler implements core.InboundHandlerManager.
|
||||||
func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandler, error) {
|
func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandler, error) {
|
||||||
m.access.RLock()
|
m.access.RLock()
|
||||||
defer m.access.RUnlock()
|
defer m.access.RUnlock()
|
||||||
@ -65,6 +62,7 @@ func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandl
|
|||||||
return handler, nil
|
return handler, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveHandler implements core.InboundHandlerManager.
|
||||||
func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
|
func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
|
||||||
if len(tag) == 0 {
|
if len(tag) == 0 {
|
||||||
return core.ErrNoClue
|
return core.ErrNoClue
|
||||||
@ -74,7 +72,9 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
|
|||||||
defer m.access.Unlock()
|
defer m.access.Unlock()
|
||||||
|
|
||||||
if handler, found := m.taggedHandlers[tag]; found {
|
if handler, found := m.taggedHandlers[tag]; found {
|
||||||
handler.Close()
|
if err := handler.Close(); err != nil {
|
||||||
|
newError("failed to close handler ", tag).Base(err).AtWarning().WithContext(ctx).WriteToLog()
|
||||||
|
}
|
||||||
delete(m.taggedHandlers, tag)
|
delete(m.taggedHandlers, tag)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -82,6 +82,7 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
|
|||||||
return core.ErrNoClue
|
return core.ErrNoClue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start implements common.Runnable.
|
||||||
func (m *Manager) Start() error {
|
func (m *Manager) Start() error {
|
||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
defer m.access.Unlock()
|
defer m.access.Unlock()
|
||||||
@ -102,6 +103,7 @@ func (m *Manager) Start() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close implements common.Closable.
|
||||||
func (m *Manager) Close() error {
|
func (m *Manager) Close() error {
|
||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
defer m.access.Unlock()
|
defer m.access.Unlock()
|
||||||
@ -118,6 +120,7 @@ func (m *Manager) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewHandler creates a new core.InboundHandler based on the given config.
|
||||||
func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (core.InboundHandler, error) {
|
func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (core.InboundHandler, error) {
|
||||||
rawReceiverSettings, err := config.ReceiverSettings.GetInstance()
|
rawReceiverSettings, err := config.ReceiverSettings.GetInstance()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -7,6 +7,8 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"v2ray.com/core/common/session"
|
||||||
|
|
||||||
"v2ray.com/core"
|
"v2ray.com/core"
|
||||||
"v2ray.com/core/app/proxyman"
|
"v2ray.com/core/app/proxyman"
|
||||||
"v2ray.com/core/common"
|
"v2ray.com/core/common"
|
||||||
@ -41,10 +43,13 @@ type tcpWorker struct {
|
|||||||
|
|
||||||
func (w *tcpWorker) callback(conn internet.Connection) {
|
func (w *tcpWorker) callback(conn internet.Connection) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
sid := session.NewID()
|
||||||
|
ctx = session.ContextWithID(ctx, sid)
|
||||||
|
|
||||||
if w.recvOrigDest {
|
if w.recvOrigDest {
|
||||||
dest, err := tcp.GetOriginalDestination(conn)
|
dest, err := tcp.GetOriginalDestination(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError("failed to get original destination").Base(err).WriteToLog()
|
newError("failed to get original destination").WithContext(ctx).Base(err).WriteToLog()
|
||||||
}
|
}
|
||||||
if dest.IsValid() {
|
if dest.IsValid() {
|
||||||
ctx = proxy.ContextWithOriginalTarget(ctx, dest)
|
ctx = proxy.ContextWithOriginalTarget(ctx, dest)
|
||||||
@ -59,10 +64,12 @@ func (w *tcpWorker) callback(conn internet.Connection) {
|
|||||||
ctx = proxyman.ContextWithProtocolSniffers(ctx, w.sniffers)
|
ctx = proxyman.ContextWithProtocolSniffers(ctx, w.sniffers)
|
||||||
}
|
}
|
||||||
if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil {
|
if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil {
|
||||||
newError("connection ends").Base(err).WriteToLog()
|
newError("connection ends").Base(err).WithContext(ctx).WriteToLog()
|
||||||
}
|
}
|
||||||
cancel()
|
cancel()
|
||||||
conn.Close()
|
if err := conn.Close(); err != nil {
|
||||||
|
newError("failed to close connection").Base(err).WithContext(ctx).WriteToLog()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *tcpWorker) Proxy() proxy.Inbound {
|
func (w *tcpWorker) Proxy() proxy.Inbound {
|
||||||
@ -128,7 +135,7 @@ func (c *udpConn) Write(buf []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *udpConn) Close() error {
|
func (c *udpConn) Close() error {
|
||||||
common.Close(c.done)
|
common.Must(c.done.Close())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +211,9 @@ func (w *udpWorker) getConnection(id connID) (*udpConn, bool) {
|
|||||||
func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest net.Destination) {
|
func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest net.Destination) {
|
||||||
id := connID{
|
id := connID{
|
||||||
src: source,
|
src: source,
|
||||||
dest: originalDest,
|
}
|
||||||
|
if originalDest.IsValid() {
|
||||||
|
id.dest = originalDest
|
||||||
}
|
}
|
||||||
conn, existing := w.getConnection(id)
|
conn, existing := w.getConnection(id)
|
||||||
select {
|
select {
|
||||||
@ -218,6 +227,9 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
|
|||||||
if !existing {
|
if !existing {
|
||||||
go func() {
|
go func() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
sid := session.NewID()
|
||||||
|
ctx = session.ContextWithID(ctx, sid)
|
||||||
|
|
||||||
if originalDest.IsValid() {
|
if originalDest.IsValid() {
|
||||||
ctx = proxy.ContextWithOriginalTarget(ctx, originalDest)
|
ctx = proxy.ContextWithOriginalTarget(ctx, originalDest)
|
||||||
}
|
}
|
||||||
@ -244,10 +256,7 @@ func (w *udpWorker) removeConn(id connID) {
|
|||||||
func (w *udpWorker) Start() error {
|
func (w *udpWorker) Start() error {
|
||||||
w.activeConn = make(map[connID]*udpConn, 16)
|
w.activeConn = make(map[connID]*udpConn, 16)
|
||||||
w.done = signal.NewDone()
|
w.done = signal.NewDone()
|
||||||
h, err := udp.ListenUDP(w.address, w.port, udp.ListenOption{
|
h, err := udp.ListenUDP(w.address, w.port, w.callback, udp.HubReceiveOriginalDestination(w.recvOrigDest), udp.HubCapacity(256))
|
||||||
Callback: w.callback,
|
|
||||||
ReceiveOriginalDest: w.recvOrigDest,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -257,11 +266,18 @@ func (w *udpWorker) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *udpWorker) Close() error {
|
func (w *udpWorker) Close() error {
|
||||||
|
w.Lock()
|
||||||
|
defer w.Unlock()
|
||||||
|
|
||||||
if w.hub != nil {
|
if w.hub != nil {
|
||||||
w.hub.Close()
|
w.hub.Close()
|
||||||
w.done.Close()
|
|
||||||
common.Close(w.proxy)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if w.done != nil {
|
||||||
|
common.Must(w.done.Close())
|
||||||
|
}
|
||||||
|
|
||||||
|
common.Close(w.proxy)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ const (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
OptionData bitmask.Byte = 0x01
|
OptionData bitmask.Byte = 0x01
|
||||||
|
OptionError bitmask.Byte = 0x02
|
||||||
)
|
)
|
||||||
|
|
||||||
type TargetNetwork byte
|
type TargetNetwork byte
|
||||||
@ -28,6 +29,13 @@ const (
|
|||||||
TargetNetworkUDP TargetNetwork = 0x02
|
TargetNetworkUDP TargetNetwork = 0x02
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var addrParser = protocol.NewAddressParser(
|
||||||
|
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4),
|
||||||
|
protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain),
|
||||||
|
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6),
|
||||||
|
protocol.PortThenAddress(),
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Frame format
|
Frame format
|
||||||
2 bytes - length
|
2 bytes - length
|
||||||
@ -48,88 +56,55 @@ type FrameMetadata struct {
|
|||||||
SessionStatus SessionStatus
|
SessionStatus SessionStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FrameMetadata) AsSupplier() buf.Supplier {
|
func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||||
return func(b []byte) (int, error) {
|
lenBytes := b.Bytes()
|
||||||
lengthBytes := b
|
b.AppendBytes(0x00, 0x00)
|
||||||
b = serial.Uint16ToBytes(uint16(0), b[:0]) // place holder for length
|
|
||||||
|
|
||||||
b = serial.Uint16ToBytes(f.SessionID, b)
|
len0 := b.Len()
|
||||||
b = append(b, byte(f.SessionStatus), byte(f.Option))
|
if err := b.AppendSupplier(serial.WriteUint16(f.SessionID)); err != nil {
|
||||||
length := 4
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.AppendBytes(byte(f.SessionStatus), byte(f.Option))
|
||||||
|
|
||||||
if f.SessionStatus == SessionStatusNew {
|
if f.SessionStatus == SessionStatusNew {
|
||||||
switch f.Target.Network {
|
switch f.Target.Network {
|
||||||
case net.Network_TCP:
|
case net.Network_TCP:
|
||||||
b = append(b, byte(TargetNetworkTCP))
|
b.AppendBytes(byte(TargetNetworkTCP))
|
||||||
case net.Network_UDP:
|
case net.Network_UDP:
|
||||||
b = append(b, byte(TargetNetworkUDP))
|
b.AppendBytes(byte(TargetNetworkUDP))
|
||||||
}
|
}
|
||||||
length++
|
|
||||||
|
|
||||||
b = serial.Uint16ToBytes(f.Target.Port.Value(), b)
|
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
|
||||||
length += 2
|
return err
|
||||||
|
|
||||||
addr := f.Target.Address
|
|
||||||
switch addr.Family() {
|
|
||||||
case net.AddressFamilyIPv4:
|
|
||||||
b = append(b, byte(protocol.AddressTypeIPv4))
|
|
||||||
b = append(b, addr.IP()...)
|
|
||||||
length += 5
|
|
||||||
case net.AddressFamilyIPv6:
|
|
||||||
b = append(b, byte(protocol.AddressTypeIPv6))
|
|
||||||
b = append(b, addr.IP()...)
|
|
||||||
length += 17
|
|
||||||
case net.AddressFamilyDomain:
|
|
||||||
domain := addr.Domain()
|
|
||||||
if protocol.IsDomainTooLong(domain) {
|
|
||||||
return 0, newError("domain name too long: ", domain)
|
|
||||||
}
|
|
||||||
nDomain := len(domain)
|
|
||||||
b = append(b, byte(protocol.AddressTypeDomain), byte(nDomain))
|
|
||||||
b = append(b, domain...)
|
|
||||||
length += nDomain + 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serial.Uint16ToBytes(uint16(length), lengthBytes[:0])
|
len1 := b.Len()
|
||||||
return length + 2, nil
|
serial.Uint16ToBytes(uint16(len1-len0), lenBytes)
|
||||||
}
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadFrameFrom(b []byte) (*FrameMetadata, error) {
|
func ReadFrameFrom(b *buf.Buffer) (*FrameMetadata, error) {
|
||||||
if len(b) < 4 {
|
if b.Len() < 4 {
|
||||||
return nil, newError("insufficient buffer: ", len(b))
|
return nil, newError("insufficient buffer: ", b.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
f := &FrameMetadata{
|
f := &FrameMetadata{
|
||||||
SessionID: serial.BytesToUint16(b[:2]),
|
SessionID: serial.BytesToUint16(b.BytesTo(2)),
|
||||||
SessionStatus: SessionStatus(b[2]),
|
SessionStatus: SessionStatus(b.Byte(2)),
|
||||||
Option: bitmask.Byte(b[3]),
|
Option: bitmask.Byte(b.Byte(3)),
|
||||||
}
|
}
|
||||||
|
|
||||||
b = b[4:]
|
|
||||||
|
|
||||||
if f.SessionStatus == SessionStatusNew {
|
if f.SessionStatus == SessionStatusNew {
|
||||||
network := TargetNetwork(b[0])
|
network := TargetNetwork(b.Byte(4))
|
||||||
port := net.PortFromBytes(b[1:3])
|
b.SliceFrom(5)
|
||||||
addrType := protocol.AddressType(b[3])
|
|
||||||
b = b[4:]
|
|
||||||
|
|
||||||
var addr net.Address
|
addr, port, err := addrParser.ReadAddressPort(nil, b)
|
||||||
switch addrType {
|
if err != nil {
|
||||||
case protocol.AddressTypeIPv4:
|
return nil, newError("failed to parse address and port").Base(err)
|
||||||
addr = net.IPAddress(b[0:4])
|
|
||||||
b = b[4:]
|
|
||||||
case protocol.AddressTypeIPv6:
|
|
||||||
addr = net.IPAddress(b[0:16])
|
|
||||||
b = b[16:]
|
|
||||||
case protocol.AddressTypeDomain:
|
|
||||||
nDomain := int(b[0])
|
|
||||||
addr = net.DomainAddress(string(b[1 : 1+nDomain]))
|
|
||||||
b = b[nDomain+1:]
|
|
||||||
default:
|
|
||||||
return nil, newError("unknown address type: ", addrType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch network {
|
switch network {
|
||||||
case TargetNetworkTCP:
|
case TargetNetworkTCP:
|
||||||
f.Target = net.TCPDestination(addr, port)
|
f.Target = net.TCPDestination(addr, port)
|
||||||
|
@ -10,8 +10,10 @@ import (
|
|||||||
|
|
||||||
"v2ray.com/core"
|
"v2ray.com/core"
|
||||||
"v2ray.com/core/app/proxyman"
|
"v2ray.com/core/app/proxyman"
|
||||||
|
"v2ray.com/core/common"
|
||||||
"v2ray.com/core/common/buf"
|
"v2ray.com/core/common/buf"
|
||||||
"v2ray.com/core/common/errors"
|
"v2ray.com/core/common/errors"
|
||||||
|
"v2ray.com/core/common/log"
|
||||||
"v2ray.com/core/common/net"
|
"v2ray.com/core/common/net"
|
||||||
"v2ray.com/core/common/protocol"
|
"v2ray.com/core/common/protocol"
|
||||||
"v2ray.com/core/common/signal"
|
"v2ray.com/core/common/signal"
|
||||||
@ -87,7 +89,7 @@ var muxCoolPort = net.Port(9527)
|
|||||||
func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client, error) {
|
func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client, error) {
|
||||||
ctx := proxy.ContextWithTarget(context.Background(), net.TCPDestination(muxCoolAddress, muxCoolPort))
|
ctx := proxy.ContextWithTarget(context.Background(), net.TCPDestination(muxCoolAddress, muxCoolPort))
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
pipe := ray.NewRay(ctx)
|
pipe := ray.New(ctx)
|
||||||
|
|
||||||
c := &Client{
|
c := &Client{
|
||||||
sessionManager: NewSessionManager(),
|
sessionManager: NewSessionManager(),
|
||||||
@ -131,7 +133,7 @@ func (m *Client) monitor() {
|
|||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
size := m.sessionManager.Size()
|
size := m.sessionManager.Size()
|
||||||
if size == 0 && m.sessionManager.CloseIfNoSession() {
|
if size == 0 && m.sessionManager.CloseIfNoSession() {
|
||||||
m.done.Close()
|
common.Must(m.done.Close())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,18 +148,15 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
|
|||||||
}
|
}
|
||||||
s.transferType = transferType
|
s.transferType = transferType
|
||||||
writer := NewWriter(s.ID, dest, output, transferType)
|
writer := NewWriter(s.ID, dest, output, transferType)
|
||||||
defer writer.Close()
|
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
newError("dispatching request to ", dest).WriteToLog()
|
newError("dispatching request to ", dest).WithContext(ctx).WriteToLog()
|
||||||
data, _ := s.input.ReadTimeout(time.Millisecond * 500)
|
|
||||||
if err := writer.WriteMultiBuffer(data); err != nil {
|
|
||||||
newError("failed to write first payload").Base(err).WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := buf.Copy(s.input, writer); err != nil {
|
if err := buf.Copy(s.input, writer); err != nil {
|
||||||
newError("failed to fetch all input").Base(err).WriteToLog()
|
newError("failed to fetch all input").Base(err).WithContext(ctx).WriteToLog()
|
||||||
|
writer.hasError = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writer.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Client) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) bool {
|
func (m *Client) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) bool {
|
||||||
@ -204,13 +203,21 @@ func (m *Client) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReade
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s, found := m.sessionManager.Get(meta.SessionID); found {
|
if s, found := m.sessionManager.Get(meta.SessionID); found {
|
||||||
return buf.Copy(s.NewReader(reader), s.output, buf.IgnoreWriterError())
|
if err := buf.Copy(s.NewReader(reader), s.output); err != nil {
|
||||||
|
drain(reader)
|
||||||
|
s.input.CloseError()
|
||||||
|
return s.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return drain(reader)
|
return drain(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Client) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
func (m *Client) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||||
if s, found := m.sessionManager.Get(meta.SessionID); found {
|
if s, found := m.sessionManager.Get(meta.SessionID); found {
|
||||||
|
if meta.Option.Has(OptionError) {
|
||||||
|
s.input.CloseError()
|
||||||
|
s.output.CloseError()
|
||||||
|
}
|
||||||
s.Close()
|
s.Close()
|
||||||
}
|
}
|
||||||
if meta.Option.Has(OptionData) {
|
if meta.Option.Has(OptionData) {
|
||||||
@ -261,7 +268,7 @@ type Server struct {
|
|||||||
// NewServer creates a new mux.Server.
|
// NewServer creates a new mux.Server.
|
||||||
func NewServer(ctx context.Context) *Server {
|
func NewServer(ctx context.Context) *Server {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
dispatcher: core.FromContext(ctx).Dispatcher(),
|
dispatcher: core.MustFromContext(ctx).Dispatcher(),
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@ -271,7 +278,7 @@ func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (ray.Inboun
|
|||||||
return s.dispatcher.Dispatch(ctx, dest)
|
return s.dispatcher.Dispatch(ctx, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
ray := ray.NewRay(ctx)
|
ray := ray.New(ctx)
|
||||||
worker := &ServerWorker{
|
worker := &ServerWorker{
|
||||||
dispatcher: s.dispatcher,
|
dispatcher: s.dispatcher,
|
||||||
outboundRay: ray,
|
outboundRay: ray,
|
||||||
@ -298,8 +305,10 @@ type ServerWorker struct {
|
|||||||
func handle(ctx context.Context, s *Session, output buf.Writer) {
|
func handle(ctx context.Context, s *Session, output buf.Writer) {
|
||||||
writer := NewResponseWriter(s.ID, output, s.transferType)
|
writer := NewResponseWriter(s.ID, output, s.transferType)
|
||||||
if err := buf.Copy(s.input, writer); err != nil {
|
if err := buf.Copy(s.input, writer); err != nil {
|
||||||
newError("session ", s.ID, " ends.").Base(err).WriteToLog()
|
newError("session ", s.ID, " ends.").Base(err).WithContext(ctx).WriteToLog()
|
||||||
|
writer.hasError = true
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Close()
|
writer.Close()
|
||||||
s.Close()
|
s.Close()
|
||||||
}
|
}
|
||||||
@ -312,7 +321,18 @@ func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.Bu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error {
|
func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||||
newError("received request for ", meta.Target).WriteToLog()
|
newError("received request for ", meta.Target).WithContext(ctx).WriteToLog()
|
||||||
|
{
|
||||||
|
msg := &log.AccessMessage{
|
||||||
|
To: meta.Target,
|
||||||
|
Status: log.AccessAccepted,
|
||||||
|
Reason: "",
|
||||||
|
}
|
||||||
|
if src, f := proxy.SourceFromContext(ctx); f {
|
||||||
|
msg.From = src
|
||||||
|
}
|
||||||
|
log.Record(msg)
|
||||||
|
}
|
||||||
inboundRay, err := w.dispatcher.Dispatch(ctx, meta.Target)
|
inboundRay, err := w.dispatcher.Dispatch(ctx, meta.Target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if meta.Option.Has(OptionData) {
|
if meta.Option.Has(OptionData) {
|
||||||
@ -343,13 +363,21 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if s, found := w.sessionManager.Get(meta.SessionID); found {
|
if s, found := w.sessionManager.Get(meta.SessionID); found {
|
||||||
return buf.Copy(s.NewReader(reader), s.output, buf.IgnoreWriterError())
|
if err := buf.Copy(s.NewReader(reader), s.output); err != nil {
|
||||||
|
drain(reader)
|
||||||
|
s.input.CloseError()
|
||||||
|
return s.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return drain(reader)
|
return drain(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||||
if s, found := w.sessionManager.Get(meta.SessionID); found {
|
if s, found := w.sessionManager.Get(meta.SessionID); found {
|
||||||
|
if meta.Option.Has(OptionError) {
|
||||||
|
s.input.CloseError()
|
||||||
|
s.output.CloseError()
|
||||||
|
}
|
||||||
s.Close()
|
s.Close()
|
||||||
}
|
}
|
||||||
if meta.Option.Has(OptionData) {
|
if meta.Option.Has(OptionData) {
|
||||||
@ -397,7 +425,7 @@ func (w *ServerWorker) run(ctx context.Context) {
|
|||||||
err := w.handleFrame(ctx, reader)
|
err := w.handleFrame(ctx, reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Cause(err) != io.EOF {
|
if errors.Cause(err) != io.EOF {
|
||||||
newError("unexpected EOF").Base(err).WriteToLog()
|
newError("unexpected EOF").Base(err).WithContext(ctx).WriteToLog()
|
||||||
input.CloseError()
|
input.CloseError()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -17,13 +17,13 @@ func ReadMetadata(reader io.Reader) (*FrameMetadata, error) {
|
|||||||
return nil, newError("invalid metalen ", metaLen).AtError()
|
return nil, newError("invalid metalen ", metaLen).AtError()
|
||||||
}
|
}
|
||||||
|
|
||||||
b := buf.New()
|
b := buf.NewSize(int32(metaLen))
|
||||||
defer b.Release()
|
defer b.Release()
|
||||||
|
|
||||||
if err := b.Reset(buf.ReadFullFrom(reader, int(metaLen))); err != nil {
|
if err := b.Reset(buf.ReadFullFrom(reader, int32(metaLen))); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return ReadFrameFrom(b.Bytes())
|
return ReadFrameFrom(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PacketReader is an io.Reader that reads whole chunk of Mux frames every time.
|
// PacketReader is an io.Reader that reads whole chunk of Mux frames every time.
|
||||||
@ -51,13 +51,8 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var b *buf.Buffer
|
b := buf.NewSize(int32(size))
|
||||||
if size <= buf.Size {
|
if err := b.Reset(buf.ReadFullFrom(r.reader, int32(size))); err != nil {
|
||||||
b = buf.New()
|
|
||||||
} else {
|
|
||||||
b = buf.NewLocal(int(size))
|
|
||||||
}
|
|
||||||
if err := b.AppendSupplier(buf.ReadFullFrom(r.reader, int(size))); err != nil {
|
|
||||||
b.Release()
|
b.Release()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -68,7 +63,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
// StreamReader reads Mux frame as a stream.
|
// StreamReader reads Mux frame as a stream.
|
||||||
type StreamReader struct {
|
type StreamReader struct {
|
||||||
reader *buf.BufferedReader
|
reader *buf.BufferedReader
|
||||||
leftOver int
|
leftOver int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStreamReader creates a new StreamReader.
|
// NewStreamReader creates a new StreamReader.
|
||||||
@ -91,7 +86,7 @@ func (r *StreamReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r.leftOver = int(size)
|
r.leftOver = int32(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
mb, err := r.reader.ReadAtMost(r.leftOver)
|
mb, err := r.reader.ReadAtMost(r.leftOver)
|
||||||
|
@ -13,6 +13,7 @@ type Writer struct {
|
|||||||
writer buf.Writer
|
writer buf.Writer
|
||||||
id uint16
|
id uint16
|
||||||
followup bool
|
followup bool
|
||||||
|
hasError bool
|
||||||
transferType protocol.TransferType
|
transferType protocol.TransferType
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ func (w *Writer) getNextFrameMeta() FrameMetadata {
|
|||||||
SessionID: w.id,
|
SessionID: w.id,
|
||||||
Target: w.dest,
|
Target: w.dest,
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.followup {
|
if w.followup {
|
||||||
meta.SessionStatus = SessionStatusKeep
|
meta.SessionStatus = SessionStatusKeep
|
||||||
} else {
|
} else {
|
||||||
@ -53,7 +55,7 @@ func (w *Writer) getNextFrameMeta() FrameMetadata {
|
|||||||
func (w *Writer) writeMetaOnly() error {
|
func (w *Writer) writeMetaOnly() error {
|
||||||
meta := w.getNextFrameMeta()
|
meta := w.getNextFrameMeta()
|
||||||
b := buf.New()
|
b := buf.New()
|
||||||
if err := b.Reset(meta.AsSupplier()); err != nil {
|
if err := meta.WriteTo(b); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(b))
|
return w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(b))
|
||||||
@ -64,14 +66,14 @@ func (w *Writer) writeData(mb buf.MultiBuffer) error {
|
|||||||
meta.Option.Set(OptionData)
|
meta.Option.Set(OptionData)
|
||||||
|
|
||||||
frame := buf.New()
|
frame := buf.New()
|
||||||
if err := frame.Reset(meta.AsSupplier()); err != nil {
|
if err := meta.WriteTo(frame); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := frame.AppendSupplier(serial.WriteUint16(uint16(mb.Len()))); err != nil {
|
if err := frame.AppendSupplier(serial.WriteUint16(uint16(mb.Len()))); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mb2 := buf.NewMultiBufferCap(len(mb) + 1)
|
mb2 := buf.NewMultiBufferCap(int32(len(mb)) + 1)
|
||||||
mb2.Append(frame)
|
mb2.Append(frame)
|
||||||
mb2.AppendMulti(mb)
|
mb2.AppendMulti(mb)
|
||||||
return w.writer.WriteMultiBuffer(mb2)
|
return w.writer.WriteMultiBuffer(mb2)
|
||||||
@ -105,9 +107,12 @@ func (w *Writer) Close() error {
|
|||||||
SessionID: w.id,
|
SessionID: w.id,
|
||||||
SessionStatus: SessionStatusEnd,
|
SessionStatus: SessionStatusEnd,
|
||||||
}
|
}
|
||||||
|
if w.hasError {
|
||||||
|
meta.Option.Set(OptionError)
|
||||||
|
}
|
||||||
|
|
||||||
frame := buf.New()
|
frame := buf.New()
|
||||||
common.Must(frame.Reset(meta.AsSupplier()))
|
common.Must(meta.WriteTo(frame))
|
||||||
|
|
||||||
w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(frame))
|
w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(frame))
|
||||||
return nil
|
return nil
|
||||||
|
@ -2,13 +2,11 @@ package outbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
|
||||||
|
|
||||||
"v2ray.com/core"
|
"v2ray.com/core"
|
||||||
"v2ray.com/core/app/proxyman"
|
"v2ray.com/core/app/proxyman"
|
||||||
"v2ray.com/core/app/proxyman/mux"
|
"v2ray.com/core/app/proxyman/mux"
|
||||||
"v2ray.com/core/common"
|
"v2ray.com/core/common"
|
||||||
"v2ray.com/core/common/errors"
|
|
||||||
"v2ray.com/core/common/net"
|
"v2ray.com/core/common/net"
|
||||||
"v2ray.com/core/proxy"
|
"v2ray.com/core/proxy"
|
||||||
"v2ray.com/core/transport/internet"
|
"v2ray.com/core/transport/internet"
|
||||||
@ -24,10 +22,7 @@ type Handler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (core.OutboundHandler, error) {
|
func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (core.OutboundHandler, error) {
|
||||||
v := core.FromContext(ctx)
|
v := core.MustFromContext(ctx)
|
||||||
if v == nil {
|
|
||||||
return nil, newError("V is not in context")
|
|
||||||
}
|
|
||||||
h := &Handler{
|
h := &Handler{
|
||||||
config: config,
|
config: config,
|
||||||
outboundManager: v.OutboundHandlerManager(),
|
outboundManager: v.OutboundHandlerManager(),
|
||||||
@ -85,14 +80,14 @@ func (h *Handler) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) {
|
|||||||
if h.mux != nil {
|
if h.mux != nil {
|
||||||
err := h.mux.Dispatch(ctx, outboundRay)
|
err := h.mux.Dispatch(ctx, outboundRay)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError("failed to process outbound traffic").Base(err).WriteToLog()
|
newError("failed to process outbound traffic").Base(err).WithContext(ctx).WriteToLog()
|
||||||
outboundRay.OutboundOutput().CloseError()
|
outboundRay.OutboundOutput().CloseError()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err := h.proxy.Process(ctx, outboundRay, h)
|
err := h.proxy.Process(ctx, outboundRay, h)
|
||||||
// Ensure outbound ray is properly closed.
|
// Ensure outbound ray is properly closed.
|
||||||
if err != nil && errors.Cause(err) != io.EOF {
|
if err != nil {
|
||||||
newError("failed to process outbound traffic").Base(err).WriteToLog()
|
newError("failed to process outbound traffic").Base(err).WithContext(ctx).WriteToLog()
|
||||||
outboundRay.OutboundOutput().CloseError()
|
outboundRay.OutboundOutput().CloseError()
|
||||||
} else {
|
} else {
|
||||||
outboundRay.OutboundOutput().Close()
|
outboundRay.OutboundOutput().Close()
|
||||||
@ -108,14 +103,14 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn
|
|||||||
tag := h.senderSettings.ProxySettings.Tag
|
tag := h.senderSettings.ProxySettings.Tag
|
||||||
handler := h.outboundManager.GetHandler(tag)
|
handler := h.outboundManager.GetHandler(tag)
|
||||||
if handler != nil {
|
if handler != nil {
|
||||||
newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog()
|
newError("proxying to ", tag, " for dest ", dest).AtDebug().WithContext(ctx).WriteToLog()
|
||||||
ctx = proxy.ContextWithTarget(ctx, dest)
|
ctx = proxy.ContextWithTarget(ctx, dest)
|
||||||
stream := ray.NewRay(ctx)
|
stream := ray.New(ctx)
|
||||||
go handler.Dispatch(ctx, stream)
|
go handler.Dispatch(ctx, stream)
|
||||||
return ray.NewConnection(stream.InboundOutput(), stream.InboundInput()), nil
|
return ray.NewConnection(stream.InboundOutput(), stream.InboundInput()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog()
|
newError("failed to get outbound handler with tag: ", tag).AtWarning().WithContext(ctx).WriteToLog()
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.senderSettings.Via != nil {
|
if h.senderSettings.Via != nil {
|
||||||
@ -140,7 +135,7 @@ func (h *Handler) Start() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements common.Runnable.
|
// Close implements common.Closable.
|
||||||
func (h *Handler) Close() error {
|
func (h *Handler) Close() error {
|
||||||
common.Close(h.mux)
|
common.Close(h.mux)
|
||||||
return nil
|
return nil
|
||||||
|
@ -25,10 +25,7 @@ func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error)
|
|||||||
m := &Manager{
|
m := &Manager{
|
||||||
taggedHandler: make(map[string]core.OutboundHandler),
|
taggedHandler: make(map[string]core.OutboundHandler),
|
||||||
}
|
}
|
||||||
v := core.FromContext(ctx)
|
v := core.MustFromContext(ctx)
|
||||||
if v == nil {
|
|
||||||
return nil, newError("V is not in context")
|
|
||||||
}
|
|
||||||
if err := v.RegisterFeature((*core.OutboundHandlerManager)(nil), m); err != nil {
|
if err := v.RegisterFeature((*core.OutboundHandlerManager)(nil), m); err != nil {
|
||||||
return nil, newError("unable to register OutboundHandlerManager").Base(err)
|
return nil, newError("unable to register OutboundHandlerManager").Base(err)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Package proxyman defines applications for manageing inbound and outbound proxies.
|
// Package proxyman defines applications for managing inbound and outbound proxies.
|
||||||
package proxyman
|
package proxyman
|
||||||
|
|
||||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg proxyman -path App,Proxyman
|
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg proxyman -path App,Proxyman
|
||||||
@ -17,7 +17,7 @@ func ContextWithProtocolSniffers(ctx context.Context, list []KnownProtocols) con
|
|||||||
return context.WithValue(ctx, protocolsKey, list)
|
return context.WithValue(ctx, protocolsKey, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProtocoSniffersFromContext(ctx context.Context) []KnownProtocols {
|
func ProtocolSniffersFromContext(ctx context.Context) []KnownProtocols {
|
||||||
if list, ok := ctx.Value(protocolsKey).([]KnownProtocols); ok {
|
if list, ok := ctx.Value(protocolsKey).([]KnownProtocols); ok {
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
@ -11,18 +11,16 @@ import (
|
|||||||
"v2ray.com/core/proxy"
|
"v2ray.com/core/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Router is an implementation of core.Router.
|
||||||
type Router struct {
|
type Router struct {
|
||||||
domainStrategy Config_DomainStrategy
|
domainStrategy Config_DomainStrategy
|
||||||
rules []Rule
|
rules []Rule
|
||||||
dns core.DNSClient
|
dns core.DNSClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewRouter creates a new Router based on the given config.
|
||||||
func NewRouter(ctx context.Context, config *Config) (*Router, error) {
|
func NewRouter(ctx context.Context, config *Config) (*Router, error) {
|
||||||
v := core.FromContext(ctx)
|
v := core.MustFromContext(ctx)
|
||||||
if v == nil {
|
|
||||||
return nil, newError("V is not in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
r := &Router{
|
r := &Router{
|
||||||
domainStrategy: config.DomainStrategy,
|
domainStrategy: config.DomainStrategy,
|
||||||
rules: make([]Rule, len(config.Rule)),
|
rules: make([]Rule, len(config.Rule)),
|
||||||
@ -72,6 +70,7 @@ func (r *ipResolver) Resolve() []net.Address {
|
|||||||
return r.ip
|
return r.ip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PickRoute implements core.Router.
|
||||||
func (r *Router) PickRoute(ctx context.Context) (string, error) {
|
func (r *Router) PickRoute(ctx context.Context) (string, error) {
|
||||||
resolver := &ipResolver{
|
resolver := &ipResolver{
|
||||||
dns: r.dns,
|
dns: r.dns,
|
||||||
@ -110,10 +109,12 @@ func (r *Router) PickRoute(ctx context.Context) (string, error) {
|
|||||||
return "", core.ErrNoClue
|
return "", core.ErrNoClue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start implements common.Runnable.
|
||||||
func (*Router) Start() error {
|
func (*Router) Start() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close implements common.Closable.
|
||||||
func (*Router) Close() error {
|
func (*Router) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
51
app/stats/command/command.go
Normal file
51
app/stats/command/command.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg command -path App,Stats,Command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
"v2ray.com/core"
|
||||||
|
"v2ray.com/core/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type statsServer struct {
|
||||||
|
stats core.StatManager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
|
||||||
|
c := s.stats.GetCounter(request.Name)
|
||||||
|
if c == nil {
|
||||||
|
return nil, newError(request.Name, " not found.")
|
||||||
|
}
|
||||||
|
var value int64
|
||||||
|
if request.Reset_ {
|
||||||
|
value = c.Set(0)
|
||||||
|
} else {
|
||||||
|
value = c.Value()
|
||||||
|
}
|
||||||
|
return &GetStatsResponse{
|
||||||
|
Stat: &Stat{
|
||||||
|
Name: request.Name,
|
||||||
|
Value: value,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type service struct {
|
||||||
|
v *core.Instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) Register(server *grpc.Server) {
|
||||||
|
RegisterStatsServiceServer(server, &statsServer{
|
||||||
|
stats: s.v.Stats(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
|
||||||
|
s := core.MustFromContext(ctx)
|
||||||
|
return &service{v: s}, nil
|
||||||
|
}))
|
||||||
|
}
|
198
app/stats/command/command.pb.go
Normal file
198
app/stats/command/command.pb.go
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type GetStatsRequest struct {
|
||||||
|
// Name of the stat counter.
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||||
|
// Whether or not to reset the counter to fetching its value.
|
||||||
|
Reset_ bool `protobuf:"varint,2,opt,name=reset" json:"reset,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *GetStatsRequest) Reset() { *m = GetStatsRequest{} }
|
||||||
|
func (m *GetStatsRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*GetStatsRequest) ProtoMessage() {}
|
||||||
|
func (*GetStatsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||||
|
|
||||||
|
func (m *GetStatsRequest) GetName() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *GetStatsRequest) GetReset_() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.Reset_
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stat struct {
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||||
|
Value int64 `protobuf:"varint,2,opt,name=value" json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Stat) Reset() { *m = Stat{} }
|
||||||
|
func (m *Stat) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Stat) ProtoMessage() {}
|
||||||
|
func (*Stat) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||||
|
|
||||||
|
func (m *Stat) GetName() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Stat) GetValue() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Value
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetStatsResponse struct {
|
||||||
|
Stat *Stat `protobuf:"bytes,1,opt,name=stat" json:"stat,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *GetStatsResponse) Reset() { *m = GetStatsResponse{} }
|
||||||
|
func (m *GetStatsResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*GetStatsResponse) ProtoMessage() {}
|
||||||
|
func (*GetStatsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||||
|
|
||||||
|
func (m *GetStatsResponse) GetStat() *Stat {
|
||||||
|
if m != nil {
|
||||||
|
return m.Stat
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Config) Reset() { *m = Config{} }
|
||||||
|
func (m *Config) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*GetStatsRequest)(nil), "v2ray.core.app.stats.command.GetStatsRequest")
|
||||||
|
proto.RegisterType((*Stat)(nil), "v2ray.core.app.stats.command.Stat")
|
||||||
|
proto.RegisterType((*GetStatsResponse)(nil), "v2ray.core.app.stats.command.GetStatsResponse")
|
||||||
|
proto.RegisterType((*Config)(nil), "v2ray.core.app.stats.command.Config")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ context.Context
|
||||||
|
var _ grpc.ClientConn
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
const _ = grpc.SupportPackageIsVersion4
|
||||||
|
|
||||||
|
// Client API for StatsService service
|
||||||
|
|
||||||
|
type StatsServiceClient interface {
|
||||||
|
GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type statsServiceClient struct {
|
||||||
|
cc *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStatsServiceClient(cc *grpc.ClientConn) StatsServiceClient {
|
||||||
|
return &statsServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) {
|
||||||
|
out := new(GetStatsResponse)
|
||||||
|
err := grpc.Invoke(ctx, "/v2ray.core.app.stats.command.StatsService/GetStats", in, out, c.cc, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server API for StatsService service
|
||||||
|
|
||||||
|
type StatsServiceServer interface {
|
||||||
|
GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterStatsServiceServer(s *grpc.Server, srv StatsServiceServer) {
|
||||||
|
s.RegisterService(&_StatsService_serviceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StatsService_GetStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetStatsRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StatsServiceServer).GetStats(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/v2ray.core.app.stats.command.StatsService/GetStats",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StatsServiceServer).GetStats(ctx, req.(*GetStatsRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _StatsService_serviceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "v2ray.core.app.stats.command.StatsService",
|
||||||
|
HandlerType: (*StatsServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "GetStats",
|
||||||
|
Handler: _StatsService_GetStats_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "v2ray.com/core/app/stats/command/command.proto",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("v2ray.com/core/app/stats/command/command.proto", fileDescriptor0) }
|
||||||
|
|
||||||
|
var fileDescriptor0 = []byte{
|
||||||
|
// 267 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x3f, 0x4b, 0x03, 0x31,
|
||||||
|
0x14, 0xc0, 0xbd, 0x5a, 0xeb, 0xf9, 0x14, 0x94, 0xe0, 0x50, 0xa4, 0xc3, 0x91, 0xa9, 0x8b, 0xef,
|
||||||
|
0xe4, 0x04, 0x17, 0x27, 0xbd, 0x41, 0x10, 0x07, 0x49, 0xc1, 0xc1, 0x2d, 0xc6, 0xa7, 0x14, 0xcd,
|
||||||
|
0x25, 0x26, 0xe9, 0x41, 0xf1, 0x1b, 0xf9, 0x29, 0x25, 0xb9, 0x1e, 0x82, 0xe0, 0xe1, 0x94, 0xf7,
|
||||||
|
0x92, 0xdf, 0xef, 0xfd, 0x21, 0x80, 0x6d, 0xe5, 0xe4, 0x1a, 0x95, 0xd1, 0xa5, 0x32, 0x8e, 0x4a,
|
||||||
|
0x69, 0x6d, 0xe9, 0x83, 0x0c, 0xbe, 0x54, 0x46, 0x6b, 0xd9, 0x3c, 0xf7, 0x27, 0x5a, 0x67, 0x82,
|
||||||
|
0x61, 0xb3, 0x9e, 0x77, 0x84, 0xd2, 0x5a, 0x4c, 0x2c, 0x6e, 0x18, 0x7e, 0x09, 0x87, 0x37, 0x14,
|
||||||
|
0x16, 0xf1, 0x4e, 0xd0, 0xc7, 0x8a, 0x7c, 0x60, 0x0c, 0xc6, 0x8d, 0xd4, 0x34, 0xcd, 0x8a, 0x6c,
|
||||||
|
0xbe, 0x27, 0x52, 0xcc, 0x8e, 0x61, 0xc7, 0x91, 0xa7, 0x30, 0x1d, 0x15, 0xd9, 0x3c, 0x17, 0x5d,
|
||||||
|
0xc2, 0xcf, 0x60, 0x1c, 0xcd, 0xbf, 0x8c, 0x56, 0xbe, 0xaf, 0x28, 0x19, 0xdb, 0xa2, 0x4b, 0xf8,
|
||||||
|
0x2d, 0x1c, 0xfd, 0xb4, 0xf3, 0xd6, 0x34, 0x9e, 0xd8, 0x05, 0x8c, 0xe3, 0x4c, 0xc9, 0xde, 0xaf,
|
||||||
|
0x38, 0x0e, 0xcd, 0x8b, 0x51, 0x15, 0x89, 0xe7, 0x39, 0x4c, 0x6a, 0xd3, 0xbc, 0x2c, 0x5f, 0xab,
|
||||||
|
0x4f, 0x38, 0x48, 0x25, 0x17, 0xe4, 0xda, 0xa5, 0x22, 0xf6, 0x06, 0x79, 0xdf, 0x85, 0x9d, 0x0e,
|
||||||
|
0xd7, 0xfb, 0xb5, 0xfc, 0x09, 0xfe, 0x17, 0xef, 0x86, 0xe7, 0x5b, 0xd7, 0x77, 0x50, 0x28, 0xa3,
|
||||||
|
0x07, 0xb5, 0xfb, 0xec, 0x71, 0x77, 0x13, 0x7e, 0x8d, 0x66, 0x0f, 0x95, 0x90, 0x6b, 0xac, 0x23,
|
||||||
|
0x79, 0x65, 0x6d, 0xda, 0xc8, 0x63, 0xdd, 0x3d, 0x3f, 0x4d, 0xd2, 0xa7, 0x9d, 0x7f, 0x07, 0x00,
|
||||||
|
0x00, 0xff, 0xff, 0x10, 0x3a, 0x8a, 0xf3, 0xe6, 0x01, 0x00, 0x00,
|
||||||
|
}
|
29
app/stats/command/command.proto
Normal file
29
app/stats/command/command.proto
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package v2ray.core.app.stats.command;
|
||||||
|
option csharp_namespace = "V2Ray.Core.App.Stats.Command";
|
||||||
|
option go_package = "command";
|
||||||
|
option java_package = "com.v2ray.core.app.stats.command";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message GetStatsRequest {
|
||||||
|
// Name of the stat counter.
|
||||||
|
string name = 1;
|
||||||
|
// Whether or not to reset the counter to fetching its value.
|
||||||
|
bool reset = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Stat {
|
||||||
|
string name = 1;
|
||||||
|
int64 value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetStatsResponse {
|
||||||
|
Stat stat = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service StatsService {
|
||||||
|
rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Config {}
|
7
app/stats/command/errors.generated.go
Normal file
7
app/stats/command/errors.generated.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import "v2ray.com/core/common/errors"
|
||||||
|
|
||||||
|
func newError(values ...interface{}) *errors.Error {
|
||||||
|
return errors.New(values...).Path("App", "Stats", "Command")
|
||||||
|
}
|
13
app/stats/config.go
Normal file
13
app/stats/config.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"v2ray.com/core/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
|
return NewManager(ctx, config.(*Config))
|
||||||
|
}))
|
||||||
|
}
|
42
app/stats/config.pb.go
Normal file
42
app/stats/config.pb.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Config) Reset() { *m = Config{} }
|
||||||
|
func (m *Config) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Config)(nil), "v2ray.core.app.stats.Config")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("v2ray.com/core/app/stats/config.proto", fileDescriptor0) }
|
||||||
|
|
||||||
|
var fileDescriptor0 = []byte{
|
||||||
|
// 123 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x33, 0x2a, 0x4a,
|
||||||
|
0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0x2f,
|
||||||
|
0x2e, 0x49, 0x2c, 0x29, 0xd6, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f,
|
||||||
|
0xc9, 0x17, 0x12, 0x81, 0x29, 0x2b, 0x4a, 0xd5, 0x4b, 0x2c, 0x28, 0xd0, 0x03, 0x2b, 0x51, 0xe2,
|
||||||
|
0xe0, 0x62, 0x73, 0x06, 0xab, 0x72, 0xb2, 0xe2, 0x92, 0x48, 0xce, 0xcf, 0xd5, 0xc3, 0xa6, 0x2a,
|
||||||
|
0x80, 0x31, 0x8a, 0x15, 0xcc, 0x58, 0xc5, 0x24, 0x12, 0x66, 0x14, 0x94, 0x58, 0xa9, 0xe7, 0x0c,
|
||||||
|
0x92, 0x77, 0x2c, 0x28, 0xd0, 0x0b, 0x06, 0x09, 0x27, 0xb1, 0x81, 0xad, 0x30, 0x06, 0x04, 0x00,
|
||||||
|
0x00, 0xff, 0xff, 0x88, 0x24, 0xc6, 0x41, 0x8b, 0x00, 0x00, 0x00,
|
||||||
|
}
|
11
app/stats/config.proto
Normal file
11
app/stats/config.proto
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package v2ray.core.app.stats;
|
||||||
|
option csharp_namespace = "V2Ray.Core.App.Stats";
|
||||||
|
option go_package = "stats";
|
||||||
|
option java_package = "com.v2ray.core.app.stats";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {
|
||||||
|
|
||||||
|
}
|
5
app/stats/errors.generated.go
Normal file
5
app/stats/errors.generated.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
import "v2ray.com/core/common/errors"
|
||||||
|
|
||||||
|
func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Stats") }
|
83
app/stats/stats.go
Normal file
83
app/stats/stats.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package stats
|
||||||
|
|
||||||
|
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg stats -path App,Stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"v2ray.com/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Counter is an implementation of core.StatCounter.
|
||||||
|
type Counter struct {
|
||||||
|
value int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements core.StatCounter.
|
||||||
|
func (c *Counter) Value() int64 {
|
||||||
|
return atomic.LoadInt64(&c.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set implements core.StatCounter.
|
||||||
|
func (c *Counter) Set(newValue int64) int64 {
|
||||||
|
return atomic.SwapInt64(&c.value, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add implements core.StatCounter.
|
||||||
|
func (c *Counter) Add(delta int64) int64 {
|
||||||
|
return atomic.AddInt64(&c.value, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manager is an implementation of core.StatManager.
|
||||||
|
type Manager struct {
|
||||||
|
access sync.RWMutex
|
||||||
|
counters map[string]*Counter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManager(ctx context.Context, config *Config) (*Manager, error) {
|
||||||
|
m := &Manager{
|
||||||
|
counters: make(map[string]*Counter),
|
||||||
|
}
|
||||||
|
|
||||||
|
v := core.FromContext(ctx)
|
||||||
|
if v != nil {
|
||||||
|
if err := v.RegisterFeature((*core.StatManager)(nil), m); err != nil {
|
||||||
|
return nil, newError("failed to register StatManager").Base(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) RegisterCounter(name string) (core.StatCounter, error) {
|
||||||
|
m.access.Lock()
|
||||||
|
defer m.access.Unlock()
|
||||||
|
|
||||||
|
if _, found := m.counters[name]; found {
|
||||||
|
return nil, newError("Counter ", name, " already registered.")
|
||||||
|
}
|
||||||
|
newError("create new counter ", name).AtDebug().WriteToLog()
|
||||||
|
c := new(Counter)
|
||||||
|
m.counters[name] = c
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) GetCounter(name string) core.StatCounter {
|
||||||
|
m.access.RLock()
|
||||||
|
defer m.access.RUnlock()
|
||||||
|
|
||||||
|
if c, found := m.counters[name]; found {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
32
app/stats/stats_test.go
Normal file
32
app/stats/stats_test.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package stats_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"v2ray.com/core"
|
||||||
|
. "v2ray.com/core/app/stats"
|
||||||
|
"v2ray.com/core/common"
|
||||||
|
. "v2ray.com/ext/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInternface(t *testing.T) {
|
||||||
|
assert := With(t)
|
||||||
|
|
||||||
|
assert((*Manager)(nil), Implements, (*core.StatManager)(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatsCounter(t *testing.T) {
|
||||||
|
assert := With(t)
|
||||||
|
|
||||||
|
raw, err := common.CreateObject(context.Background(), &Config{})
|
||||||
|
assert(err, IsNil)
|
||||||
|
|
||||||
|
m := raw.(core.StatManager)
|
||||||
|
c, err := m.RegisterCounter("test.counter")
|
||||||
|
assert(err, IsNil)
|
||||||
|
|
||||||
|
assert(c.Add(1), Equals, int64(1))
|
||||||
|
assert(c.Set(0), Equals, int64(1))
|
||||||
|
assert(c.Value(), Equals, int64(0))
|
||||||
|
}
|
59
clock.go
59
clock.go
@ -1,59 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Clock is a V2Ray feature that returns current time.
|
|
||||||
type Clock interface {
|
|
||||||
Feature
|
|
||||||
|
|
||||||
// Now returns current time.
|
|
||||||
Now() time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type syncClock struct {
|
|
||||||
sync.RWMutex
|
|
||||||
Clock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *syncClock) Now() time.Time {
|
|
||||||
c.RLock()
|
|
||||||
defer c.RUnlock()
|
|
||||||
|
|
||||||
if c.Clock == nil {
|
|
||||||
return time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Clock.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *syncClock) Start() error {
|
|
||||||
c.RLock()
|
|
||||||
defer c.RUnlock()
|
|
||||||
|
|
||||||
if c.Clock == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Clock.Start()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *syncClock) Close() error {
|
|
||||||
c.RLock()
|
|
||||||
defer c.RUnlock()
|
|
||||||
|
|
||||||
if c.Clock == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Clock.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *syncClock) Set(clock Clock) {
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
c.Clock = clock
|
|
||||||
}
|
|
44
commander.go
44
commander.go
@ -1,44 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Commander is a feature that accepts commands from external source.
|
|
||||||
type Commander interface {
|
|
||||||
Feature
|
|
||||||
}
|
|
||||||
|
|
||||||
type syncCommander struct {
|
|
||||||
sync.RWMutex
|
|
||||||
Commander
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *syncCommander) Start() error {
|
|
||||||
c.RLock()
|
|
||||||
defer c.RUnlock()
|
|
||||||
|
|
||||||
if c.Commander == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Commander.Start()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *syncCommander) Close() error {
|
|
||||||
c.RLock()
|
|
||||||
defer c.RUnlock()
|
|
||||||
|
|
||||||
if c.Commander == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Commander.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *syncCommander) Set(commander Commander) {
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
c.Commander = commander
|
|
||||||
}
|
|
@ -1,3 +1,4 @@
|
|||||||
package buf
|
// Package buf provides a light-weight memory allocation mechanism.
|
||||||
|
package buf // import "v2ray.com/core/common/buf"
|
||||||
|
|
||||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg buf -path Buf
|
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg buf -path Buf
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// Package buf provides a light-weight memory allocation mechanism.
|
|
||||||
package buf
|
package buf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -13,10 +12,9 @@ type Supplier func([]byte) (int, error)
|
|||||||
// quickly.
|
// quickly.
|
||||||
type Buffer struct {
|
type Buffer struct {
|
||||||
v []byte
|
v []byte
|
||||||
pool Pool
|
|
||||||
|
|
||||||
start int
|
start int32
|
||||||
end int
|
end int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release recycles the buffer into an internal buffer pool.
|
// Release recycles the buffer into an internal buffer pool.
|
||||||
@ -24,11 +22,8 @@ func (b *Buffer) Release() {
|
|||||||
if b == nil || b.v == nil {
|
if b == nil || b.v == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if b.pool != nil {
|
freeBytes(b.v)
|
||||||
b.pool.Free(b)
|
|
||||||
}
|
|
||||||
b.v = nil
|
b.v = nil
|
||||||
b.pool = nil
|
|
||||||
b.start = 0
|
b.start = 0
|
||||||
b.end = 0
|
b.end = 0
|
||||||
}
|
}
|
||||||
@ -48,24 +43,24 @@ func (b *Buffer) AppendBytes(bytes ...byte) int {
|
|||||||
// Append appends a byte array to the end of the buffer.
|
// Append appends a byte array to the end of the buffer.
|
||||||
func (b *Buffer) Append(data []byte) int {
|
func (b *Buffer) Append(data []byte) int {
|
||||||
nBytes := copy(b.v[b.end:], data)
|
nBytes := copy(b.v[b.end:], data)
|
||||||
b.end += nBytes
|
b.end += int32(nBytes)
|
||||||
return nBytes
|
return nBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendSupplier appends the content of a BytesWriter to the buffer.
|
// AppendSupplier appends the content of a BytesWriter to the buffer.
|
||||||
func (b *Buffer) AppendSupplier(writer Supplier) error {
|
func (b *Buffer) AppendSupplier(writer Supplier) error {
|
||||||
nBytes, err := writer(b.v[b.end:])
|
nBytes, err := writer(b.v[b.end:])
|
||||||
b.end += nBytes
|
b.end += int32(nBytes)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Byte returns the bytes at index.
|
// Byte returns the bytes at index.
|
||||||
func (b *Buffer) Byte(index int) byte {
|
func (b *Buffer) Byte(index int32) byte {
|
||||||
return b.v[b.start+index]
|
return b.v[b.start+index]
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetByte sets the byte value at index.
|
// SetByte sets the byte value at index.
|
||||||
func (b *Buffer) SetByte(index int, value byte) {
|
func (b *Buffer) SetByte(index int32, value byte) {
|
||||||
b.v[b.start+index] = value
|
b.v[b.start+index] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,12 +73,12 @@ func (b *Buffer) Bytes() []byte {
|
|||||||
func (b *Buffer) Reset(writer Supplier) error {
|
func (b *Buffer) Reset(writer Supplier) error {
|
||||||
nBytes, err := writer(b.v)
|
nBytes, err := writer(b.v)
|
||||||
b.start = 0
|
b.start = 0
|
||||||
b.end = nBytes
|
b.end = int32(nBytes)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BytesRange returns a slice of this buffer with given from and to bounary.
|
// BytesRange returns a slice of this buffer with given from and to boundary.
|
||||||
func (b *Buffer) BytesRange(from, to int) []byte {
|
func (b *Buffer) BytesRange(from, to int32) []byte {
|
||||||
if from < 0 {
|
if from < 0 {
|
||||||
from += b.Len()
|
from += b.Len()
|
||||||
}
|
}
|
||||||
@ -94,7 +89,7 @@ func (b *Buffer) BytesRange(from, to int) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BytesFrom returns a slice of this Buffer starting from the given position.
|
// BytesFrom returns a slice of this Buffer starting from the given position.
|
||||||
func (b *Buffer) BytesFrom(from int) []byte {
|
func (b *Buffer) BytesFrom(from int32) []byte {
|
||||||
if from < 0 {
|
if from < 0 {
|
||||||
from += b.Len()
|
from += b.Len()
|
||||||
}
|
}
|
||||||
@ -102,7 +97,7 @@ func (b *Buffer) BytesFrom(from int) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BytesTo returns a slice of this Buffer from start to the given position.
|
// BytesTo returns a slice of this Buffer from start to the given position.
|
||||||
func (b *Buffer) BytesTo(to int) []byte {
|
func (b *Buffer) BytesTo(to int32) []byte {
|
||||||
if to < 0 {
|
if to < 0 {
|
||||||
to += b.Len()
|
to += b.Len()
|
||||||
}
|
}
|
||||||
@ -110,7 +105,7 @@ func (b *Buffer) BytesTo(to int) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Slice cuts the buffer at the given position.
|
// Slice cuts the buffer at the given position.
|
||||||
func (b *Buffer) Slice(from, to int) {
|
func (b *Buffer) Slice(from, to int32) {
|
||||||
if from < 0 {
|
if from < 0 {
|
||||||
from += b.Len()
|
from += b.Len()
|
||||||
}
|
}
|
||||||
@ -125,7 +120,7 @@ func (b *Buffer) Slice(from, to int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SliceFrom cuts the buffer at the given position.
|
// SliceFrom cuts the buffer at the given position.
|
||||||
func (b *Buffer) SliceFrom(from int) {
|
func (b *Buffer) SliceFrom(from int32) {
|
||||||
if from < 0 {
|
if from < 0 {
|
||||||
from += b.Len()
|
from += b.Len()
|
||||||
}
|
}
|
||||||
@ -133,7 +128,7 @@ func (b *Buffer) SliceFrom(from int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Len returns the length of the buffer content.
|
// Len returns the length of the buffer content.
|
||||||
func (b *Buffer) Len() int {
|
func (b *Buffer) Len() int32 {
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -147,13 +142,13 @@ func (b *Buffer) IsEmpty() bool {
|
|||||||
|
|
||||||
// IsFull returns true if the buffer has no more room to grow.
|
// IsFull returns true if the buffer has no more room to grow.
|
||||||
func (b *Buffer) IsFull() bool {
|
func (b *Buffer) IsFull() bool {
|
||||||
return b.end == len(b.v)
|
return b.end == int32(len(b.v))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write implements Write method in io.Writer.
|
// Write implements Write method in io.Writer.
|
||||||
func (b *Buffer) Write(data []byte) (int, error) {
|
func (b *Buffer) Write(data []byte) (int, error) {
|
||||||
nBytes := copy(b.v[b.end:], data)
|
nBytes := copy(b.v[b.end:], data)
|
||||||
b.end += nBytes
|
b.end += int32(nBytes)
|
||||||
return nBytes, nil
|
return nBytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,10 +158,10 @@ func (b *Buffer) Read(data []byte) (int, error) {
|
|||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
nBytes := copy(data, b.v[b.start:b.end])
|
nBytes := copy(data, b.v[b.start:b.end])
|
||||||
if nBytes == b.Len() {
|
if int32(nBytes) == b.Len() {
|
||||||
b.Clear()
|
b.Clear()
|
||||||
} else {
|
} else {
|
||||||
b.start += nBytes
|
b.start += int32(nBytes)
|
||||||
}
|
}
|
||||||
return nBytes, nil
|
return nBytes, nil
|
||||||
}
|
}
|
||||||
@ -176,15 +171,16 @@ func (b *Buffer) String() string {
|
|||||||
return string(b.Bytes())
|
return string(b.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a Buffer with 0 length and 8K capacity.
|
// New creates a Buffer with 0 length and 2K capacity.
|
||||||
func New() *Buffer {
|
func New() *Buffer {
|
||||||
return mediumPool.Allocate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLocal creates and returns a buffer with 0 length and given capacity on current thread.
|
|
||||||
func NewLocal(size int) *Buffer {
|
|
||||||
return &Buffer{
|
return &Buffer{
|
||||||
v: make([]byte, size),
|
v: pool[0].Get().([]byte),
|
||||||
pool: nil,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSize creates and returns a buffer with 0 length and at least the given capacity. Capacity must be positive.
|
||||||
|
func NewSize(capacity int32) *Buffer {
|
||||||
|
return &Buffer{
|
||||||
|
v: newBytes(capacity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,49 +4,60 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pool provides functionality to generate and recycle buffers on demand.
|
|
||||||
type Pool interface {
|
|
||||||
// Allocate either returns a unused buffer from the pool, or generates a new one from system.
|
|
||||||
Allocate() *Buffer
|
|
||||||
// Free recycles the given buffer.
|
|
||||||
Free(*Buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyncPool is a buffer pool based on sync.Pool
|
|
||||||
type SyncPool struct {
|
|
||||||
allocator *sync.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSyncPool creates a SyncPool with given buffer size.
|
|
||||||
func NewSyncPool(bufferSize uint32) *SyncPool {
|
|
||||||
pool := &SyncPool{
|
|
||||||
allocator: &sync.Pool{
|
|
||||||
New: func() interface{} { return make([]byte, bufferSize) },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return pool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate implements Pool.Allocate().
|
|
||||||
func (p *SyncPool) Allocate() *Buffer {
|
|
||||||
return &Buffer{
|
|
||||||
v: p.allocator.Get().([]byte),
|
|
||||||
pool: p,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free implements Pool.Free().
|
|
||||||
func (p *SyncPool) Free(buffer *Buffer) {
|
|
||||||
if buffer.v != nil {
|
|
||||||
p.allocator.Put(buffer.v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Size of a regular buffer.
|
// Size of a regular buffer.
|
||||||
Size = 2 * 1024
|
Size = 2 * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func createAllocFunc(size int32) func() interface{} {
|
||||||
mediumPool Pool = NewSyncPool(Size)
|
return func() interface{} {
|
||||||
|
return make([]byte, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following parameters controls the size of buffer pools.
|
||||||
|
// There are numPools pools. Starting from 2k size, the size of each pool is sizeMulti of the previous one.
|
||||||
|
// Package buf is guaranteed to not use buffers larger than the largest pool.
|
||||||
|
// Other packets may use larger buffers.
|
||||||
|
const (
|
||||||
|
numPools = 5
|
||||||
|
sizeMulti = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pool [numPools]sync.Pool
|
||||||
|
poolSize [numPools]int32
|
||||||
|
largeSize int32
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
size := int32(Size)
|
||||||
|
for i := 0; i < numPools; i++ {
|
||||||
|
pool[i] = sync.Pool{
|
||||||
|
New: createAllocFunc(size),
|
||||||
|
}
|
||||||
|
poolSize[i] = size
|
||||||
|
largeSize = size
|
||||||
|
size *= sizeMulti
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBytes(size int32) []byte {
|
||||||
|
for idx, ps := range poolSize {
|
||||||
|
if size <= ps {
|
||||||
|
return pool[idx].Get().([]byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return make([]byte, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func freeBytes(b []byte) {
|
||||||
|
size := int32(cap(b))
|
||||||
|
b = b[0:cap(b)]
|
||||||
|
for i := numPools - 1; i >= 0; i-- {
|
||||||
|
if size >= poolSize[i] {
|
||||||
|
pool[i].Put(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package buf_test
|
package buf_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "v2ray.com/core/common/buf"
|
. "v2ray.com/core/common/buf"
|
||||||
@ -17,10 +16,10 @@ func TestBufferClear(t *testing.T) {
|
|||||||
|
|
||||||
payload := "Bytes"
|
payload := "Bytes"
|
||||||
buffer.Append([]byte(payload))
|
buffer.Append([]byte(payload))
|
||||||
assert(buffer.Len(), Equals, len(payload))
|
assert(buffer.Len(), Equals, int32(len(payload)))
|
||||||
|
|
||||||
buffer.Clear()
|
buffer.Clear()
|
||||||
assert(buffer.Len(), Equals, 0)
|
assert(buffer.Len(), Equals, int32(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBufferIsEmpty(t *testing.T) {
|
func TestBufferIsEmpty(t *testing.T) {
|
||||||
@ -42,32 +41,6 @@ func TestBufferString(t *testing.T) {
|
|||||||
assert(buffer.String(), Equals, "Test String")
|
assert(buffer.String(), Equals, "Test String")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBufferWrite(t *testing.T) {
|
|
||||||
assert := With(t)
|
|
||||||
|
|
||||||
buffer := NewLocal(8)
|
|
||||||
nBytes, err := buffer.Write([]byte("abcd"))
|
|
||||||
assert(err, IsNil)
|
|
||||||
assert(nBytes, Equals, 4)
|
|
||||||
nBytes, err = buffer.Write([]byte("abcde"))
|
|
||||||
assert(err, IsNil)
|
|
||||||
assert(nBytes, Equals, 4)
|
|
||||||
assert(buffer.String(), Equals, "abcdabcd")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSyncPool(t *testing.T) {
|
|
||||||
assert := With(t)
|
|
||||||
|
|
||||||
p := NewSyncPool(32)
|
|
||||||
b := p.Allocate()
|
|
||||||
assert(b.Len(), Equals, 0)
|
|
||||||
|
|
||||||
assert(b.AppendSupplier(ReadFrom(rand.Reader)), IsNil)
|
|
||||||
assert(b.Len(), Equals, 32)
|
|
||||||
|
|
||||||
b.Release()
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkNewBuffer(b *testing.B) {
|
func BenchmarkNewBuffer(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
buffer := New()
|
buffer := New()
|
||||||
@ -77,7 +50,7 @@ func BenchmarkNewBuffer(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkNewLocalBuffer(b *testing.B) {
|
func BenchmarkNewLocalBuffer(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
buffer := NewLocal(Size)
|
buffer := NewSize(Size)
|
||||||
buffer.Release()
|
buffer.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ func (h *copyHandler) writeTo(writer Writer, mb MultiBuffer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SizeCounter is for counting bytes copied by Copy().
|
||||||
type SizeCounter struct {
|
type SizeCounter struct {
|
||||||
Size int64
|
Size int64
|
||||||
}
|
}
|
||||||
@ -91,7 +92,9 @@ func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
|
|||||||
buffer.Release()
|
buffer.Release()
|
||||||
return werr
|
return werr
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ func ReadFrom(reader io.Reader) Supplier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadFullFrom creates a Supplier to read full buffer from a given io.Reader.
|
// ReadFullFrom creates a Supplier to read full buffer from a given io.Reader.
|
||||||
func ReadFullFrom(reader io.Reader, size int) Supplier {
|
func ReadFullFrom(reader io.Reader, size int32) Supplier {
|
||||||
return func(b []byte) (int, error) {
|
return func(b []byte) (int, error) {
|
||||||
return io.ReadFull(reader, b[:size])
|
return io.ReadFull(reader, b[:size])
|
||||||
}
|
}
|
||||||
@ -67,6 +67,7 @@ func NewWriter(writer io.Writer) Writer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSequentialWriter returns a Writer that write Buffers in a MultiBuffer sequentially.
|
||||||
func NewSequentialWriter(writer io.Writer) Writer {
|
func NewSequentialWriter(writer io.Writer) Writer {
|
||||||
return &seqWriter{
|
return &seqWriter{
|
||||||
writer: writer,
|
writer: writer,
|
||||||
|
@ -30,6 +30,26 @@ func ReadAllToMultiBuffer(reader io.Reader) (MultiBuffer, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadSizeToMultiBuffer reads specific number of bytes from reader into a MultiBuffer.
|
||||||
|
func ReadSizeToMultiBuffer(reader io.Reader, size int32) (MultiBuffer, error) {
|
||||||
|
mb := NewMultiBufferCap(32)
|
||||||
|
|
||||||
|
for size > 0 {
|
||||||
|
bSize := size
|
||||||
|
if bSize > Size {
|
||||||
|
bSize = Size
|
||||||
|
}
|
||||||
|
b := NewSize(bSize)
|
||||||
|
if err := b.Reset(ReadFullFrom(reader, bSize)); err != nil {
|
||||||
|
mb.Release()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
size -= bSize
|
||||||
|
mb.Append(b)
|
||||||
|
}
|
||||||
|
return mb, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ReadAllToBytes reads all content from the reader into a byte array, until EOF.
|
// ReadAllToBytes reads all content from the reader into a byte array, until EOF.
|
||||||
func ReadAllToBytes(reader io.Reader) ([]byte, error) {
|
func ReadAllToBytes(reader io.Reader) ([]byte, error) {
|
||||||
mb, err := ReadAllToMultiBuffer(reader)
|
mb, err := ReadAllToMultiBuffer(reader)
|
||||||
@ -46,7 +66,7 @@ func ReadAllToBytes(reader io.Reader) ([]byte, error) {
|
|||||||
type MultiBuffer []*Buffer
|
type MultiBuffer []*Buffer
|
||||||
|
|
||||||
// NewMultiBufferCap creates a new MultiBuffer instance.
|
// NewMultiBufferCap creates a new MultiBuffer instance.
|
||||||
func NewMultiBufferCap(capacity int) MultiBuffer {
|
func NewMultiBufferCap(capacity int32) MultiBuffer {
|
||||||
return MultiBuffer(make([]*Buffer, 0, capacity))
|
return MultiBuffer(make([]*Buffer, 0, capacity))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +77,9 @@ func NewMultiBufferValue(b ...*Buffer) MultiBuffer {
|
|||||||
|
|
||||||
// Append appends buffer to the end of this MultiBuffer
|
// Append appends buffer to the end of this MultiBuffer
|
||||||
func (mb *MultiBuffer) Append(buf *Buffer) {
|
func (mb *MultiBuffer) Append(buf *Buffer) {
|
||||||
|
if buf != nil {
|
||||||
*mb = append(*mb, buf)
|
*mb = append(*mb, buf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendMulti appends a MultiBuffer to the end of this one.
|
// AppendMulti appends a MultiBuffer to the end of this one.
|
||||||
@ -71,7 +93,7 @@ func (mb MultiBuffer) Copy(b []byte) int {
|
|||||||
for _, bb := range mb {
|
for _, bb := range mb {
|
||||||
nBytes := copy(b[total:], bb.Bytes())
|
nBytes := copy(b[total:], bb.Bytes())
|
||||||
total += nBytes
|
total += nBytes
|
||||||
if nBytes < bb.Len() {
|
if int32(nBytes) < bb.Len() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,8 +137,8 @@ func (mb *MultiBuffer) Write(b []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Len returns the total number of bytes in the MultiBuffer.
|
// Len returns the total number of bytes in the MultiBuffer.
|
||||||
func (mb MultiBuffer) Len() int {
|
func (mb MultiBuffer) Len() int32 {
|
||||||
size := 0
|
size := int32(0)
|
||||||
for _, b := range mb {
|
for _, b := range mb {
|
||||||
size += b.Len()
|
size += b.Len()
|
||||||
}
|
}
|
||||||
@ -152,9 +174,9 @@ func (mb MultiBuffer) ToNetBuffers() net.Buffers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SliceBySize splits the beginning of this MultiBuffer into another one, for at most size bytes.
|
// SliceBySize splits the beginning of this MultiBuffer into another one, for at most size bytes.
|
||||||
func (mb *MultiBuffer) SliceBySize(size int) MultiBuffer {
|
func (mb *MultiBuffer) SliceBySize(size int32) MultiBuffer {
|
||||||
slice := NewMultiBufferCap(10)
|
slice := NewMultiBufferCap(10)
|
||||||
sliceSize := 0
|
sliceSize := int32(0)
|
||||||
endIndex := len(*mb)
|
endIndex := len(*mb)
|
||||||
for i, b := range *mb {
|
for i, b := range *mb {
|
||||||
if b.Len()+sliceSize > size {
|
if b.Len()+sliceSize > size {
|
||||||
@ -167,7 +189,7 @@ func (mb *MultiBuffer) SliceBySize(size int) MultiBuffer {
|
|||||||
}
|
}
|
||||||
*mb = (*mb)[endIndex:]
|
*mb = (*mb)[endIndex:]
|
||||||
if endIndex == 0 && len(*mb) > 0 {
|
if endIndex == 0 && len(*mb) > 0 {
|
||||||
b := New()
|
b := NewSize(size)
|
||||||
common.Must(b.Reset(ReadFullFrom((*mb)[0], size)))
|
common.Must(b.Reset(ReadFullFrom((*mb)[0], size)))
|
||||||
return NewMultiBufferValue(b)
|
return NewMultiBufferValue(b)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package buf_test
|
package buf_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"v2ray.com/core/common"
|
||||||
. "v2ray.com/core/common/buf"
|
. "v2ray.com/core/common/buf"
|
||||||
. "v2ray.com/ext/assert"
|
. "v2ray.com/ext/assert"
|
||||||
)
|
)
|
||||||
@ -31,5 +33,18 @@ func TestMultiBufferAppend(t *testing.T) {
|
|||||||
b := New()
|
b := New()
|
||||||
b.AppendBytes('a', 'b')
|
b.AppendBytes('a', 'b')
|
||||||
mb.Append(b)
|
mb.Append(b)
|
||||||
assert(mb.Len(), Equals, 2)
|
assert(mb.Len(), Equals, int32(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiBufferSliceBySizeLarge(t *testing.T) {
|
||||||
|
assert := With(t)
|
||||||
|
|
||||||
|
lb := NewSize(8 * 1024)
|
||||||
|
common.Must(lb.Reset(ReadFrom(rand.Reader)))
|
||||||
|
|
||||||
|
var mb MultiBuffer
|
||||||
|
mb.Append(lb)
|
||||||
|
|
||||||
|
mb2 := mb.SliceBySize(4 * 1024)
|
||||||
|
assert(mb2.Len(), Equals, int32(4*1024))
|
||||||
}
|
}
|
||||||
|
@ -12,26 +12,35 @@ type BytesToBufferReader struct {
|
|||||||
buffer []byte
|
buffer []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBytesToBufferReader returns a new BytesToBufferReader.
|
||||||
func NewBytesToBufferReader(reader io.Reader) Reader {
|
func NewBytesToBufferReader(reader io.Reader) Reader {
|
||||||
return &BytesToBufferReader{
|
return &BytesToBufferReader{
|
||||||
Reader: reader,
|
Reader: reader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mediumSize = 8 * 1024
|
|
||||||
const largeSize = 64 * 1024
|
|
||||||
|
|
||||||
func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) {
|
func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) {
|
||||||
b := New()
|
b := New()
|
||||||
|
for i := 0; i < 64; i++ {
|
||||||
err := b.Reset(ReadFrom(r.Reader))
|
err := b.Reset(ReadFrom(r.Reader))
|
||||||
if b.IsFull() {
|
if b.IsFull() {
|
||||||
r.buffer = make([]byte, mediumSize)
|
r.buffer = newBytes(Size + 1)
|
||||||
}
|
}
|
||||||
if !b.IsEmpty() {
|
if !b.IsEmpty() {
|
||||||
return NewMultiBufferValue(b), nil
|
return NewMultiBufferValue(b), nil
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
b.Release()
|
b.Release()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, newError("Reader returns too many empty payloads.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BytesToBufferReader) freeBuffer() {
|
||||||
|
freeBytes(r.buffer)
|
||||||
|
r.buffer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadMultiBuffer implements Reader.
|
// ReadMultiBuffer implements Reader.
|
||||||
@ -42,29 +51,35 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) {
|
|||||||
|
|
||||||
nBytes, err := r.Reader.Read(r.buffer)
|
nBytes, err := r.Reader.Read(r.buffer)
|
||||||
if nBytes > 0 {
|
if nBytes > 0 {
|
||||||
mb := NewMultiBufferCap(nBytes/Size + 1)
|
mb := NewMultiBufferCap(int32(nBytes/Size) + 1)
|
||||||
mb.Write(r.buffer[:nBytes])
|
mb.Write(r.buffer[:nBytes])
|
||||||
if nBytes == len(r.buffer) && len(r.buffer) == mediumSize {
|
if nBytes == len(r.buffer) && nBytes < int(largeSize) {
|
||||||
r.buffer = make([]byte, largeSize)
|
freeBytes(r.buffer)
|
||||||
|
r.buffer = newBytes(int32(nBytes) + 1)
|
||||||
|
} else if nBytes < Size {
|
||||||
|
r.freeBuffer()
|
||||||
}
|
}
|
||||||
return mb, nil
|
return mb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.freeBuffer()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read() returns empty payload and nil err. We don't expect this to happen, but just in case.
|
||||||
|
return r.readSmall()
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// BufferedReader is a Reader that keeps its internal buffer.
|
||||||
_ Reader = (*BufferedReader)(nil)
|
|
||||||
_ io.Reader = (*BufferedReader)(nil)
|
|
||||||
_ io.ByteReader = (*BufferedReader)(nil)
|
|
||||||
_ io.WriterTo = (*BufferedReader)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type BufferedReader struct {
|
type BufferedReader struct {
|
||||||
stream Reader
|
stream Reader
|
||||||
leftOver MultiBuffer
|
leftOver MultiBuffer
|
||||||
buffered bool
|
buffered bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBufferedReader returns a new BufferedReader.
|
||||||
func NewBufferedReader(reader Reader) *BufferedReader {
|
func NewBufferedReader(reader Reader) *BufferedReader {
|
||||||
return &BufferedReader{
|
return &BufferedReader{
|
||||||
stream: reader,
|
stream: reader,
|
||||||
@ -72,20 +87,29 @@ func NewBufferedReader(reader Reader) *BufferedReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetBuffered sets whether to keep the interal buffer.
|
||||||
func (r *BufferedReader) SetBuffered(f bool) {
|
func (r *BufferedReader) SetBuffered(f bool) {
|
||||||
r.buffered = f
|
r.buffered = f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsBuffered returns true if internal buffer is used.
|
||||||
func (r *BufferedReader) IsBuffered() bool {
|
func (r *BufferedReader) IsBuffered() bool {
|
||||||
return r.buffered
|
return r.buffered
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BufferedBytes returns the number of bytes that is cached in this reader.
|
||||||
|
func (r *BufferedReader) BufferedBytes() int32 {
|
||||||
|
return r.leftOver.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadByte implements io.ByteReader.
|
||||||
func (r *BufferedReader) ReadByte() (byte, error) {
|
func (r *BufferedReader) ReadByte() (byte, error) {
|
||||||
var b [1]byte
|
var b [1]byte
|
||||||
_, err := r.Read(b[:])
|
_, err := r.Read(b[:])
|
||||||
return b[0], err
|
return b[0], err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read implements io.Reader. It reads from internal buffer first (if available) and then reads from the underlying reader.
|
||||||
func (r *BufferedReader) Read(b []byte) (int, error) {
|
func (r *BufferedReader) Read(b []byte) (int, error) {
|
||||||
if r.leftOver != nil {
|
if r.leftOver != nil {
|
||||||
nBytes, _ := r.leftOver.Read(b)
|
nBytes, _ := r.leftOver.Read(b)
|
||||||
@ -113,6 +137,7 @@ func (r *BufferedReader) Read(b []byte) (int, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadMultiBuffer implements Reader.
|
||||||
func (r *BufferedReader) ReadMultiBuffer() (MultiBuffer, error) {
|
func (r *BufferedReader) ReadMultiBuffer() (MultiBuffer, error) {
|
||||||
if r.leftOver != nil {
|
if r.leftOver != nil {
|
||||||
mb := r.leftOver
|
mb := r.leftOver
|
||||||
@ -124,7 +149,7 @@ func (r *BufferedReader) ReadMultiBuffer() (MultiBuffer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadAtMost returns a MultiBuffer with at most size.
|
// ReadAtMost returns a MultiBuffer with at most size.
|
||||||
func (r *BufferedReader) ReadAtMost(size int) (MultiBuffer, error) {
|
func (r *BufferedReader) ReadAtMost(size int32) (MultiBuffer, error) {
|
||||||
if r.leftOver == nil {
|
if r.leftOver == nil {
|
||||||
mb, err := r.stream.ReadMultiBuffer()
|
mb, err := r.stream.ReadMultiBuffer()
|
||||||
if mb.IsEmpty() && err != nil {
|
if mb.IsEmpty() && err != nil {
|
||||||
@ -148,6 +173,7 @@ func (r *BufferedReader) writeToInternal(writer io.Writer) (int64, error) {
|
|||||||
if err := mbWriter.WriteMultiBuffer(r.leftOver); err != nil {
|
if err := mbWriter.WriteMultiBuffer(r.leftOver); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
r.leftOver = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -164,6 +190,7 @@ func (r *BufferedReader) writeToInternal(writer io.Writer) (int64, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteTo implements io.WriterTo.
|
||||||
func (r *BufferedReader) WriteTo(writer io.Writer) (int64, error) {
|
func (r *BufferedReader) WriteTo(writer io.Writer) (int64, error) {
|
||||||
nBytes, err := r.writeToInternal(writer)
|
nBytes, err := r.writeToInternal(writer)
|
||||||
if errors.Cause(err) == io.EOF {
|
if errors.Cause(err) == io.EOF {
|
||||||
|
@ -17,15 +17,23 @@ func TestAdaptiveReader(t *testing.T) {
|
|||||||
reader := NewReader(bytes.NewReader(make([]byte, 1024*1024)))
|
reader := NewReader(bytes.NewReader(make([]byte, 1024*1024)))
|
||||||
b, err := reader.ReadMultiBuffer()
|
b, err := reader.ReadMultiBuffer()
|
||||||
assert(err, IsNil)
|
assert(err, IsNil)
|
||||||
assert(b.Len(), Equals, 2*1024)
|
assert(b.Len(), Equals, int32(2*1024))
|
||||||
|
|
||||||
b, err = reader.ReadMultiBuffer()
|
b, err = reader.ReadMultiBuffer()
|
||||||
assert(err, IsNil)
|
assert(err, IsNil)
|
||||||
assert(b.Len(), Equals, 8*1024)
|
assert(b.Len(), Equals, int32(8*1024))
|
||||||
|
|
||||||
b, err = reader.ReadMultiBuffer()
|
b, err = reader.ReadMultiBuffer()
|
||||||
assert(err, IsNil)
|
assert(err, IsNil)
|
||||||
assert(b.Len(), Equals, 64*1024)
|
assert(b.Len(), Equals, int32(32*1024))
|
||||||
|
|
||||||
|
b, err = reader.ReadMultiBuffer()
|
||||||
|
assert(err, IsNil)
|
||||||
|
assert(b.Len(), Equals, int32(128*1024))
|
||||||
|
|
||||||
|
b, err = reader.ReadMultiBuffer()
|
||||||
|
assert(err, IsNil)
|
||||||
|
assert(b.Len(), Equals, int32(512*1024))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBytesReaderWriteTo(t *testing.T) {
|
func TestBytesReaderWriteTo(t *testing.T) {
|
||||||
|
@ -11,6 +11,7 @@ type BufferToBytesWriter struct {
|
|||||||
io.Writer
|
io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBufferToBytesWriter returns a new BufferToBytesWriter.
|
||||||
func NewBufferToBytesWriter(writer io.Writer) *BufferToBytesWriter {
|
func NewBufferToBytesWriter(writer io.Writer) *BufferToBytesWriter {
|
||||||
return &BufferToBytesWriter{
|
return &BufferToBytesWriter{
|
||||||
Writer: writer,
|
Writer: writer,
|
||||||
@ -22,7 +23,7 @@ func (w *BufferToBytesWriter) WriteMultiBuffer(mb MultiBuffer) error {
|
|||||||
defer mb.Release()
|
defer mb.Release()
|
||||||
|
|
||||||
bs := mb.ToNetBuffers()
|
bs := mb.ToNetBuffers()
|
||||||
_, err := bs.WriteTo(w)
|
_, err := bs.WriteTo(w.Writer)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ func NewBufferedWriter(writer Writer) *BufferedWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteByte implements io.ByteWriter.
|
||||||
func (w *BufferedWriter) WriteByte(c byte) error {
|
func (w *BufferedWriter) WriteByte(c byte) error {
|
||||||
_, err := w.Write([]byte{c})
|
_, err := w.Write([]byte{c})
|
||||||
return err
|
return err
|
||||||
@ -121,6 +123,7 @@ func (w *BufferedWriter) Flush() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetBuffered sets whether the internal buffer is used. If set to false, Flush() will be called to clear the buffer.
|
||||||
func (w *BufferedWriter) SetBuffered(f bool) error {
|
func (w *BufferedWriter) SetBuffered(f bool) error {
|
||||||
w.buffered = f
|
w.buffered = f
|
||||||
if !f {
|
if !f {
|
||||||
|
@ -47,7 +47,7 @@ func TestBytesWriterReadFrom(t *testing.T) {
|
|||||||
|
|
||||||
mb, err := cache.ReadMultiBuffer()
|
mb, err := cache.ReadMultiBuffer()
|
||||||
assert(err, IsNil)
|
assert(err, IsNil)
|
||||||
assert(mb.Len(), Equals, size)
|
assert(mb.Len(), Equals, int32(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDiscardBytes(t *testing.T) {
|
func TestDiscardBytes(t *testing.T) {
|
||||||
|
@ -11,9 +11,8 @@ func Must(err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must2 panics if the second parameter is not nil.
|
// Must2 panics if the second parameter is not nil, otherwise returns the first parameter.
|
||||||
func Must2(v interface{}, err error) {
|
func Must2(v interface{}, err error) interface{} {
|
||||||
if err != nil {
|
Must(err)
|
||||||
panic(err)
|
return v
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,7 @@ type AuthenticationReader struct {
|
|||||||
reader *buf.BufferedReader
|
reader *buf.BufferedReader
|
||||||
sizeParser ChunkSizeDecoder
|
sizeParser ChunkSizeDecoder
|
||||||
transferType protocol.TransferType
|
transferType protocol.TransferType
|
||||||
|
size int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthenticationReader(auth Authenticator, sizeParser ChunkSizeDecoder, reader io.Reader, transferType protocol.TransferType) *AuthenticationReader {
|
func NewAuthenticationReader(auth Authenticator, sizeParser ChunkSizeDecoder, reader io.Reader, transferType protocol.TransferType) *AuthenticationReader {
|
||||||
@ -101,35 +102,48 @@ func NewAuthenticationReader(auth Authenticator, sizeParser ChunkSizeDecoder, re
|
|||||||
reader: buf.NewBufferedReader(buf.NewReader(reader)),
|
reader: buf.NewBufferedReader(buf.NewReader(reader)),
|
||||||
sizeParser: sizeParser,
|
sizeParser: sizeParser,
|
||||||
transferType: transferType,
|
transferType: transferType,
|
||||||
|
size: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *AuthenticationReader) readSize() (int, error) {
|
func (r *AuthenticationReader) readSize() (int32, error) {
|
||||||
|
if r.size != -1 {
|
||||||
|
s := r.size
|
||||||
|
r.size = -1
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
sizeBytes := make([]byte, r.sizeParser.SizeBytes())
|
sizeBytes := make([]byte, r.sizeParser.SizeBytes())
|
||||||
_, err := io.ReadFull(r.reader, sizeBytes)
|
_, err := io.ReadFull(r.reader, sizeBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
size, err := r.sizeParser.Decode(sizeBytes)
|
size, err := r.sizeParser.Decode(sizeBytes)
|
||||||
return int(size), err
|
return int32(size), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
var errSoft = newError("waiting for more data")
|
||||||
|
|
||||||
|
func (r *AuthenticationReader) readInternal(soft bool) (*buf.Buffer, error) {
|
||||||
|
if soft && r.reader.BufferedBytes() < r.sizeParser.SizeBytes() {
|
||||||
|
return nil, errSoft
|
||||||
|
}
|
||||||
|
|
||||||
size, err := r.readSize()
|
size, err := r.readSize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if size == r.auth.Overhead() {
|
if size == -2 || size == int32(r.auth.Overhead()) {
|
||||||
|
r.size = -2
|
||||||
return nil, io.EOF
|
return nil, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
var b *buf.Buffer
|
if soft && size > r.reader.BufferedBytes() {
|
||||||
if size <= buf.Size {
|
r.size = size
|
||||||
b = buf.New()
|
return nil, errSoft
|
||||||
} else {
|
|
||||||
b = buf.NewLocal(size)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b := buf.NewSize(size)
|
||||||
if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil {
|
if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil {
|
||||||
b.Release()
|
b.Release()
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -140,8 +154,33 @@ func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
b.Release()
|
b.Release()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
b.Slice(0, len(rb))
|
b.Slice(0, int32(len(rb)))
|
||||||
return buf.NewMultiBufferValue(b), nil
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||||
|
b, err := r.readInternal(false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mb := buf.NewMultiBufferCap(32)
|
||||||
|
mb.Append(b)
|
||||||
|
|
||||||
|
for {
|
||||||
|
b, err := r.readInternal(true)
|
||||||
|
if err == errSoft || err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
mb.Release()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mb.Append(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthenticationWriter struct {
|
type AuthenticationWriter struct {
|
||||||
@ -161,12 +200,12 @@ func NewAuthenticationWriter(auth Authenticator, sizeParser ChunkSizeEncoder, wr
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) {
|
func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) {
|
||||||
encryptedSize := b.Len() + w.auth.Overhead()
|
encryptedSize := int(b.Len()) + w.auth.Overhead()
|
||||||
|
|
||||||
eb := buf.New()
|
eb := buf.New()
|
||||||
common.Must(eb.Reset(func(bb []byte) (int, error) {
|
common.Must(eb.Reset(func(bb []byte) (int, error) {
|
||||||
w.sizeParser.Encode(uint16(encryptedSize), bb[:0])
|
w.sizeParser.Encode(uint16(encryptedSize), bb[:0])
|
||||||
return w.sizeParser.SizeBytes(), nil
|
return int(w.sizeParser.SizeBytes()), nil
|
||||||
}))
|
}))
|
||||||
if err := eb.AppendSupplier(func(bb []byte) (int, error) {
|
if err := eb.AppendSupplier(func(bb []byte) (int, error) {
|
||||||
_, err := w.auth.Seal(bb[:0], b.Bytes())
|
_, err := w.auth.Seal(bb[:0], b.Bytes())
|
||||||
@ -182,8 +221,8 @@ func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) {
|
|||||||
func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
|
func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
|
||||||
defer mb.Release()
|
defer mb.Release()
|
||||||
|
|
||||||
payloadSize := buf.Size - w.auth.Overhead() - w.sizeParser.SizeBytes()
|
payloadSize := buf.Size - int32(w.auth.Overhead()) - w.sizeParser.SizeBytes()
|
||||||
mb2Write := buf.NewMultiBufferCap(len(mb) + 10)
|
mb2Write := buf.NewMultiBufferCap(int32(len(mb) + 10))
|
||||||
|
|
||||||
for {
|
for {
|
||||||
b := buf.New()
|
b := buf.New()
|
||||||
@ -209,12 +248,20 @@ func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
|
|||||||
func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error {
|
func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error {
|
||||||
defer mb.Release()
|
defer mb.Release()
|
||||||
|
|
||||||
mb2Write := buf.NewMultiBufferCap(len(mb) * 2)
|
if mb.IsEmpty() {
|
||||||
|
b := buf.New()
|
||||||
|
defer b.Release()
|
||||||
|
|
||||||
for {
|
eb, _ := w.seal(b)
|
||||||
|
return w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(eb))
|
||||||
|
}
|
||||||
|
|
||||||
|
mb2Write := buf.NewMultiBufferCap(int32(len(mb)) + 1)
|
||||||
|
|
||||||
|
for !mb.IsEmpty() {
|
||||||
b := mb.SplitFirst()
|
b := mb.SplitFirst()
|
||||||
if b == nil {
|
if b == nil {
|
||||||
b = buf.New()
|
continue
|
||||||
}
|
}
|
||||||
eb, err := w.seal(b)
|
eb, err := w.seal(b)
|
||||||
b.Release()
|
b.Release()
|
||||||
@ -223,9 +270,6 @@ func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mb2Write.Append(eb)
|
mb2Write.Append(eb)
|
||||||
if mb.IsEmpty() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return w.writer.WriteMultiBuffer(mb2Write)
|
return w.writer.WriteMultiBuffer(mb2Write)
|
||||||
|
@ -24,13 +24,15 @@ func TestAuthenticationReaderWriter(t *testing.T) {
|
|||||||
aead, err := cipher.NewGCM(block)
|
aead, err := cipher.NewGCM(block)
|
||||||
assert(err, IsNil)
|
assert(err, IsNil)
|
||||||
|
|
||||||
rawPayload := make([]byte, 8192*10)
|
const payloadSize = 1024 * 80
|
||||||
|
rawPayload := make([]byte, payloadSize)
|
||||||
rand.Read(rawPayload)
|
rand.Read(rawPayload)
|
||||||
|
|
||||||
payload := buf.NewLocal(8192 * 10)
|
payload := buf.NewSize(payloadSize)
|
||||||
payload.Append(rawPayload)
|
payload.Append(rawPayload)
|
||||||
|
assert(payload.Len(), Equals, int32(payloadSize))
|
||||||
|
|
||||||
cache := buf.NewLocal(160 * 1024)
|
cache := buf.NewSize(160 * 1024)
|
||||||
iv := make([]byte, 12)
|
iv := make([]byte, 12)
|
||||||
rand.Read(iv)
|
rand.Read(iv)
|
||||||
|
|
||||||
@ -43,9 +45,8 @@ func TestAuthenticationReaderWriter(t *testing.T) {
|
|||||||
}, PlainChunkSizeParser{}, cache, protocol.TransferTypeStream)
|
}, PlainChunkSizeParser{}, cache, protocol.TransferTypeStream)
|
||||||
|
|
||||||
assert(writer.WriteMultiBuffer(buf.NewMultiBufferValue(payload)), IsNil)
|
assert(writer.WriteMultiBuffer(buf.NewMultiBufferValue(payload)), IsNil)
|
||||||
assert(cache.Len(), Equals, 82658)
|
assert(cache.Len(), Equals, int32(82658))
|
||||||
assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil)
|
assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil)
|
||||||
assert(err, IsNil)
|
|
||||||
|
|
||||||
reader := NewAuthenticationReader(&AEADAuthenticator{
|
reader := NewAuthenticationReader(&AEADAuthenticator{
|
||||||
AEAD: aead,
|
AEAD: aead,
|
||||||
@ -57,14 +58,16 @@ func TestAuthenticationReaderWriter(t *testing.T) {
|
|||||||
|
|
||||||
var mb buf.MultiBuffer
|
var mb buf.MultiBuffer
|
||||||
|
|
||||||
for mb.Len() < len(rawPayload) {
|
for mb.Len() < payloadSize {
|
||||||
mb2, err := reader.ReadMultiBuffer()
|
mb2, err := reader.ReadMultiBuffer()
|
||||||
assert(err, IsNil)
|
assert(err, IsNil)
|
||||||
|
|
||||||
mb.AppendMulti(mb2)
|
mb.AppendMulti(mb2)
|
||||||
}
|
}
|
||||||
|
|
||||||
mbContent := make([]byte, 8192*10)
|
assert(mb.Len(), Equals, int32(payloadSize))
|
||||||
|
|
||||||
|
mbContent := make([]byte, payloadSize)
|
||||||
mb.Read(mbContent)
|
mb.Read(mbContent)
|
||||||
assert(mbContent, Equals, rawPayload)
|
assert(mbContent, Equals, rawPayload)
|
||||||
|
|
||||||
@ -83,7 +86,7 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) {
|
|||||||
aead, err := cipher.NewGCM(block)
|
aead, err := cipher.NewGCM(block)
|
||||||
assert(err, IsNil)
|
assert(err, IsNil)
|
||||||
|
|
||||||
cache := buf.NewLocal(1024)
|
cache := buf.NewSize(1024)
|
||||||
iv := make([]byte, 12)
|
iv := make([]byte, 12)
|
||||||
rand.Read(iv)
|
rand.Read(iv)
|
||||||
|
|
||||||
@ -105,7 +108,7 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) {
|
|||||||
payload.Append(pb2)
|
payload.Append(pb2)
|
||||||
|
|
||||||
assert(writer.WriteMultiBuffer(payload), IsNil)
|
assert(writer.WriteMultiBuffer(payload), IsNil)
|
||||||
assert(cache.Len(), GreaterThan, 0)
|
assert(cache.Len(), GreaterThan, int32(0))
|
||||||
assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil)
|
assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil)
|
||||||
assert(err, IsNil)
|
assert(err, IsNil)
|
||||||
|
|
||||||
@ -122,12 +125,10 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) {
|
|||||||
|
|
||||||
b1 := mb.SplitFirst()
|
b1 := mb.SplitFirst()
|
||||||
assert(b1.String(), Equals, "abcd")
|
assert(b1.String(), Equals, "abcd")
|
||||||
assert(mb.IsEmpty(), IsTrue)
|
|
||||||
|
|
||||||
mb, err = reader.ReadMultiBuffer()
|
|
||||||
assert(err, IsNil)
|
|
||||||
b2 := mb.SplitFirst()
|
b2 := mb.SplitFirst()
|
||||||
assert(b2.String(), Equals, "efgh")
|
assert(b2.String(), Equals, "efgh")
|
||||||
|
|
||||||
assert(mb.IsEmpty(), IsTrue)
|
assert(mb.IsEmpty(), IsTrue)
|
||||||
|
|
||||||
_, err = reader.ReadMultiBuffer()
|
_, err = reader.ReadMultiBuffer()
|
||||||
|
20
common/crypto/chunk.go
Normal file → Executable file
20
common/crypto/chunk.go
Normal file → Executable file
@ -8,21 +8,21 @@ import (
|
|||||||
"v2ray.com/core/common/serial"
|
"v2ray.com/core/common/serial"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChunkSizeDecoder is an utility class to decode size value from bytes.
|
// ChunkSizeDecoder is a utility class to decode size value from bytes.
|
||||||
type ChunkSizeDecoder interface {
|
type ChunkSizeDecoder interface {
|
||||||
SizeBytes() int
|
SizeBytes() int32
|
||||||
Decode([]byte) (uint16, error)
|
Decode([]byte) (uint16, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChunkSizeEncoder is an utility class to encode size value into bytes.
|
// ChunkSizeEncoder is a utility class to encode size value into bytes.
|
||||||
type ChunkSizeEncoder interface {
|
type ChunkSizeEncoder interface {
|
||||||
SizeBytes() int
|
SizeBytes() int32
|
||||||
Encode(uint16, []byte) []byte
|
Encode(uint16, []byte) []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlainChunkSizeParser struct{}
|
type PlainChunkSizeParser struct{}
|
||||||
|
|
||||||
func (PlainChunkSizeParser) SizeBytes() int {
|
func (PlainChunkSizeParser) SizeBytes() int32 {
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,8 +38,8 @@ type AEADChunkSizeParser struct {
|
|||||||
Auth *AEADAuthenticator
|
Auth *AEADAuthenticator
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *AEADChunkSizeParser) SizeBytes() int {
|
func (p *AEADChunkSizeParser) SizeBytes() int32 {
|
||||||
return 2 + p.Auth.Overhead()
|
return 2 + int32(p.Auth.Overhead())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *AEADChunkSizeParser) Encode(size uint16, b []byte) []byte {
|
func (p *AEADChunkSizeParser) Encode(size uint16, b []byte) []byte {
|
||||||
@ -62,7 +62,7 @@ type ChunkStreamReader struct {
|
|||||||
reader *buf.BufferedReader
|
reader *buf.BufferedReader
|
||||||
|
|
||||||
buffer []byte
|
buffer []byte
|
||||||
leftOverSize int
|
leftOverSize int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChunkStreamReader(sizeDecoder ChunkSizeDecoder, reader io.Reader) *ChunkStreamReader {
|
func NewChunkStreamReader(sizeDecoder ChunkSizeDecoder, reader io.Reader) *ChunkStreamReader {
|
||||||
@ -90,7 +90,7 @@ func (r *ChunkStreamReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
if nextSize == 0 {
|
if nextSize == 0 {
|
||||||
return nil, io.EOF
|
return nil, io.EOF
|
||||||
}
|
}
|
||||||
size = int(nextSize)
|
size = int32(nextSize)
|
||||||
}
|
}
|
||||||
r.leftOverSize = size
|
r.leftOverSize = size
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ func (w *ChunkStreamWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
b := buf.New()
|
b := buf.New()
|
||||||
common.Must(b.Reset(func(buffer []byte) (int, error) {
|
common.Must(b.Reset(func(buffer []byte) (int, error) {
|
||||||
w.sizeEncoder.Encode(uint16(slice.Len()), buffer[:0])
|
w.sizeEncoder.Encode(uint16(slice.Len()), buffer[:0])
|
||||||
return w.sizeEncoder.SizeBytes(), nil
|
return int(w.sizeEncoder.SizeBytes()), nil
|
||||||
}))
|
}))
|
||||||
mb2Write.Append(b)
|
mb2Write.Append(b)
|
||||||
mb2Write.AppendMulti(slice)
|
mb2Write.AppendMulti(slice)
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
func TestChunkStreamIO(t *testing.T) {
|
func TestChunkStreamIO(t *testing.T) {
|
||||||
assert := With(t)
|
assert := With(t)
|
||||||
|
|
||||||
cache := buf.NewLocal(8192)
|
cache := buf.NewSize(8192)
|
||||||
|
|
||||||
writer := NewChunkStreamWriter(PlainChunkSizeParser{}, cache)
|
writer := NewChunkStreamWriter(PlainChunkSizeParser{}, cache)
|
||||||
reader := NewChunkStreamReader(PlainChunkSizeParser{}, cache)
|
reader := NewChunkStreamReader(PlainChunkSizeParser{}, cache)
|
||||||
@ -27,16 +27,16 @@ func TestChunkStreamIO(t *testing.T) {
|
|||||||
|
|
||||||
assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil)
|
assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil)
|
||||||
|
|
||||||
assert(cache.Len(), Equals, 13)
|
assert(cache.Len(), Equals, int32(13))
|
||||||
|
|
||||||
mb, err := reader.ReadMultiBuffer()
|
mb, err := reader.ReadMultiBuffer()
|
||||||
assert(err, IsNil)
|
assert(err, IsNil)
|
||||||
assert(mb.Len(), Equals, 4)
|
assert(mb.Len(), Equals, int32(4))
|
||||||
assert(mb[0].Bytes(), Equals, []byte("abcd"))
|
assert(mb[0].Bytes(), Equals, []byte("abcd"))
|
||||||
|
|
||||||
mb, err = reader.ReadMultiBuffer()
|
mb, err = reader.ReadMultiBuffer()
|
||||||
assert(err, IsNil)
|
assert(err, IsNil)
|
||||||
assert(mb.Len(), Equals, 3)
|
assert(mb.Len(), Equals, int32(3))
|
||||||
assert(mb[0].Bytes(), Equals, []byte("efg"))
|
assert(mb[0].Bytes(), Equals, []byte("efg"))
|
||||||
|
|
||||||
_, err = reader.ReadMultiBuffer()
|
_, err = reader.ReadMultiBuffer()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Package crypto provides common crypto libraries for V2Ray.
|
// Package crypto provides common crypto libraries for V2Ray.
|
||||||
package crypto
|
package crypto // import "v2ray.com/core/common/crypto"
|
||||||
|
|
||||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg crypto -path Crypto
|
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg crypto -path Crypto
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Package dice contains common functions to generate random number.
|
// Package dice contains common functions to generate random number.
|
||||||
// It also initialize math/rand with the time in seconds at launch time.
|
// It also initialize math/rand with the time in seconds at launch time.
|
||||||
package dice
|
package dice // import "v2ray.com/core/common/dice"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
32
common/dice/dice_test.go
Normal file
32
common/dice/dice_test.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package dice_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "v2ray.com/core/common/dice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkRoll1(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Roll(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRoll20(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Roll(20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkIntn1(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rand.Intn(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkIntn20(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rand.Intn(20)
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,13 @@
|
|||||||
// Package errors is a drop-in replacement for Golang lib 'errors'.
|
// Package errors is a drop-in replacement for Golang lib 'errors'.
|
||||||
package errors
|
package errors // import "v2ray.com/core/common/errors"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"v2ray.com/core/common/log"
|
"v2ray.com/core/common/log"
|
||||||
"v2ray.com/core/common/serial"
|
"v2ray.com/core/common/serial"
|
||||||
|
"v2ray.com/core/common/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
type hasInnerError interface {
|
type hasInnerError interface {
|
||||||
@ -17,12 +19,17 @@ type hasSeverity interface {
|
|||||||
Severity() log.Severity
|
Severity() log.Severity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type hasContext interface {
|
||||||
|
Context() context.Context
|
||||||
|
}
|
||||||
|
|
||||||
// Error is an error object with underlying error.
|
// Error is an error object with underlying error.
|
||||||
type Error struct {
|
type Error struct {
|
||||||
message []interface{}
|
message []interface{}
|
||||||
inner error
|
inner error
|
||||||
severity log.Severity
|
severity log.Severity
|
||||||
path []string
|
path []string
|
||||||
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error implements error.Error().
|
// Error implements error.Error().
|
||||||
@ -50,6 +57,27 @@ func (v *Error) Base(err error) *Error {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Error) WithContext(ctx context.Context) *Error {
|
||||||
|
v.ctx = ctx
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Error) Context() context.Context {
|
||||||
|
if v.ctx != nil {
|
||||||
|
return v.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.inner == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if c, ok := v.inner.(hasContext); ok {
|
||||||
|
return c.Context()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Error) atSeverity(s log.Severity) *Error {
|
func (v *Error) atSeverity(s log.Severity) *Error {
|
||||||
v.severity = s
|
v.severity = s
|
||||||
return v
|
return v
|
||||||
@ -103,9 +131,21 @@ func (v *Error) String() string {
|
|||||||
|
|
||||||
// WriteToLog writes current error into log.
|
// WriteToLog writes current error into log.
|
||||||
func (v *Error) WriteToLog() {
|
func (v *Error) WriteToLog() {
|
||||||
|
ctx := v.Context()
|
||||||
|
var sid session.ID
|
||||||
|
if ctx != nil {
|
||||||
|
sid = session.IDFromContext(ctx)
|
||||||
|
}
|
||||||
|
var c interface{} = v
|
||||||
|
if sid > 0 {
|
||||||
|
c = sessionLog{
|
||||||
|
id: sid,
|
||||||
|
content: v,
|
||||||
|
}
|
||||||
|
}
|
||||||
log.Record(&log.GeneralMessage{
|
log.Record(&log.GeneralMessage{
|
||||||
Severity: GetSeverity(v),
|
Severity: GetSeverity(v),
|
||||||
Content: v,
|
Content: c,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,3 +179,12 @@ func GetSeverity(err error) log.Severity {
|
|||||||
}
|
}
|
||||||
return log.Severity_Info
|
return log.Severity_Info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sessionLog struct {
|
||||||
|
id session.ID
|
||||||
|
content interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sessionLog) String() string {
|
||||||
|
return serial.Concat("[", s.id, "] ", s.content)
|
||||||
|
}
|
||||||
|
@ -21,3 +21,22 @@ type Runnable interface {
|
|||||||
|
|
||||||
Closable
|
Closable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasType is the interface for objects that knows its type.
|
||||||
|
type HasType interface {
|
||||||
|
// Type returns the type of the object.
|
||||||
|
Type() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChainedClosable []Closable
|
||||||
|
|
||||||
|
func NewChainedClosable(c ...Closable) ChainedClosable {
|
||||||
|
return ChainedClosable(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc ChainedClosable) Close() error {
|
||||||
|
for _, c := range cc {
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package log
|
package log // import "v2ray.com/core/common/log"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
@ -24,7 +24,7 @@ type GeneralMessage struct {
|
|||||||
|
|
||||||
// String implements Message.
|
// String implements Message.
|
||||||
func (m *GeneralMessage) String() string {
|
func (m *GeneralMessage) String() string {
|
||||||
return serial.Concat("[", m.Severity, "]: ", m.Content)
|
return serial.Concat("[", m.Severity, "] ", m.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record writes a message into log stream.
|
// Record writes a message into log stream.
|
||||||
|
@ -28,5 +28,5 @@ func TestLogRecord(t *testing.T) {
|
|||||||
Content: net.ParseAddress(ip),
|
Content: net.ParseAddress(ip),
|
||||||
})
|
})
|
||||||
|
|
||||||
assert(logger.value, Equals, "[Error]: "+ip)
|
assert(logger.value, Equals, "[Error] "+ip)
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ type generalLogger struct {
|
|||||||
creator WriterCreator
|
creator WriterCreator
|
||||||
buffer chan Message
|
buffer chan Message
|
||||||
access *signal.Semaphore
|
access *signal.Semaphore
|
||||||
|
done *signal.Done
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLogger returns a generic log handler that can handle all type of messages.
|
// NewLogger returns a generic log handler that can handle all type of messages.
|
||||||
@ -31,6 +32,7 @@ func NewLogger(logWriterCreator WriterCreator) Handler {
|
|||||||
creator: logWriterCreator,
|
creator: logWriterCreator,
|
||||||
buffer: make(chan Message, 16),
|
buffer: make(chan Message, 16),
|
||||||
access: signal.NewSemaphore(1),
|
access: signal.NewSemaphore(1),
|
||||||
|
done: signal.NewDone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +51,8 @@ func (l *generalLogger) run() {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case <-l.done.C():
|
||||||
|
return
|
||||||
case msg := <-l.buffer:
|
case msg := <-l.buffer:
|
||||||
logger.Write(msg.String() + platform.LineSeparator())
|
logger.Write(msg.String() + platform.LineSeparator())
|
||||||
dataWritten = true
|
dataWritten = true
|
||||||
@ -74,6 +78,10 @@ func (l *generalLogger) Handle(msg Message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *generalLogger) Close() error {
|
||||||
|
return l.done.Close()
|
||||||
|
}
|
||||||
|
|
||||||
type consoleLogWriter struct {
|
type consoleLogWriter struct {
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
40
common/log/logger_test.go
Normal file
40
common/log/logger_test.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package log_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"v2ray.com/core/common"
|
||||||
|
"v2ray.com/core/common/buf"
|
||||||
|
. "v2ray.com/core/common/log"
|
||||||
|
. "v2ray.com/ext/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFileLogger(t *testing.T) {
|
||||||
|
assert := With(t)
|
||||||
|
|
||||||
|
f, err := ioutil.TempFile("", "vtest")
|
||||||
|
assert(err, IsNil)
|
||||||
|
path := f.Name()
|
||||||
|
common.Must(f.Close())
|
||||||
|
|
||||||
|
creator, err := CreateFileLogWriter(path)
|
||||||
|
assert(err, IsNil)
|
||||||
|
|
||||||
|
handler := NewLogger(creator)
|
||||||
|
handler.Handle(&GeneralMessage{Content: "Test Log"})
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
common.Must(common.Close(handler))
|
||||||
|
|
||||||
|
f, err = os.Open(path)
|
||||||
|
assert(err, IsNil)
|
||||||
|
|
||||||
|
b, err := buf.ReadAllToBytes(f)
|
||||||
|
assert(err, IsNil)
|
||||||
|
assert(string(b), HasSubstring, "Test Log")
|
||||||
|
|
||||||
|
common.Must(f.Close())
|
||||||
|
}
|
@ -22,7 +22,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// AddressFamily is the type of address.
|
// AddressFamily is the type of address.
|
||||||
type AddressFamily int
|
type AddressFamily byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// AddressFamilyIPv4 represents address as IPv4
|
// AddressFamilyIPv4 represents address as IPv4
|
||||||
|
@ -6,9 +6,9 @@ import (
|
|||||||
|
|
||||||
// Destination represents a network destination including address and protocol (tcp / udp).
|
// Destination represents a network destination including address and protocol (tcp / udp).
|
||||||
type Destination struct {
|
type Destination struct {
|
||||||
Network Network
|
|
||||||
Port Port
|
|
||||||
Address Address
|
Address Address
|
||||||
|
Port Port
|
||||||
|
Network Network
|
||||||
}
|
}
|
||||||
|
|
||||||
// DestinationFromAddr generates a Destination from a net address.
|
// DestinationFromAddr generates a Destination from a net address.
|
||||||
@ -56,7 +56,7 @@ func (d Destination) IsValid() bool {
|
|||||||
return d.Network != Network_Unknown
|
return d.Network != Network_Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsDestination converts current Enpoint into Destination.
|
// AsDestination converts current Endpoint into Destination.
|
||||||
func (p *Endpoint) AsDestination() Destination {
|
func (p *Endpoint) AsDestination() Destination {
|
||||||
return Destination{
|
return Destination{
|
||||||
Network: p.Network,
|
Network: p.Network,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Package net is a drop-in replacement to Golang's net package, with some more functionalities.
|
// Package net is a drop-in replacement to Golang's net package, with some more functionalities.
|
||||||
package net
|
package net // import "v2ray.com/core/common/net"
|
||||||
|
|
||||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg net -path Net
|
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg net -path Net
|
||||||
|
@ -46,6 +46,15 @@ func (n Network) URLPrefix() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HasNetwork(list []Network, network Network) bool {
|
||||||
|
for _, value := range list {
|
||||||
|
if string(value) == string(network) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// HasNetwork returns true if the given network is in v NetworkList.
|
// HasNetwork returns true if the given network is in v NetworkList.
|
||||||
func (l NetworkList) HasNetwork(network Network) bool {
|
func (l NetworkList) HasNetwork(network Network) bool {
|
||||||
for _, value := range l.Network {
|
for _, value := range l.Network {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package platform
|
package platform // import "v2ray.com/core/common/platform"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
@ -53,7 +53,7 @@ func getExecutableDir() string {
|
|||||||
return filepath.Dir(exec)
|
return filepath.Dir(exec)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExecuableSubDir(dir string) func() string {
|
func getExecutableSubDir(dir string) func() string {
|
||||||
return func() string {
|
return func() string {
|
||||||
return filepath.Join(getExecutableDir(), dir)
|
return filepath.Join(getExecutableDir(), dir)
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ func GetAssetLocation(file string) string {
|
|||||||
|
|
||||||
func GetPluginDirectory() string {
|
func GetPluginDirectory() string {
|
||||||
const name = "v2ray.location.plugin"
|
const name = "v2ray.location.plugin"
|
||||||
pluginDir := EnvFlag{Name: name, AltName: NormalizeEnvName(name)}.GetValue(getExecuableSubDir("plugins"))
|
pluginDir := EnvFlag{Name: name, AltName: NormalizeEnvName(name)}.GetValue(getExecutableSubDir("plugins"))
|
||||||
return pluginDir
|
return pluginDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package predicate
|
package predicate // import "v2ray.com/core/common/predicate"
|
||||||
|
|
||||||
type Predicate func() bool
|
type Predicate func() bool
|
||||||
|
|
||||||
|
2
common/protocol/account.go
Normal file → Executable file
2
common/protocol/account.go
Normal file → Executable file
@ -1,6 +1,6 @@
|
|||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
// Account is an user identity used for authentication.
|
// Account is a user identity used for authentication.
|
||||||
type Account interface {
|
type Account interface {
|
||||||
Equals(Account) bool
|
Equals(Account) bool
|
||||||
}
|
}
|
||||||
|
204
common/protocol/address.go
Normal file
204
common/protocol/address.go
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"v2ray.com/core/common/buf"
|
||||||
|
"v2ray.com/core/common/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddressOption func(*AddressParser)
|
||||||
|
|
||||||
|
func PortThenAddress() AddressOption {
|
||||||
|
return func(p *AddressParser) {
|
||||||
|
p.portFirst = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddressFamilyByte(b byte, f net.AddressFamily) AddressOption {
|
||||||
|
return func(p *AddressParser) {
|
||||||
|
p.addrTypeMap[b] = f
|
||||||
|
p.addrByteMap[f] = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddressTypeParser func(byte) byte
|
||||||
|
|
||||||
|
func WithAddressTypeParser(atp AddressTypeParser) AddressOption {
|
||||||
|
return func(p *AddressParser) {
|
||||||
|
p.typeParser = atp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddressParser struct {
|
||||||
|
addrTypeMap map[byte]net.AddressFamily
|
||||||
|
addrByteMap map[net.AddressFamily]byte
|
||||||
|
portFirst bool
|
||||||
|
typeParser AddressTypeParser
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAddressParser(options ...AddressOption) *AddressParser {
|
||||||
|
p := &AddressParser{
|
||||||
|
addrTypeMap: make(map[byte]net.AddressFamily, 8),
|
||||||
|
addrByteMap: make(map[net.AddressFamily]byte, 8),
|
||||||
|
}
|
||||||
|
for _, opt := range options {
|
||||||
|
opt(p)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AddressParser) readPort(b *buf.Buffer, reader io.Reader) (net.Port, error) {
|
||||||
|
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return net.PortFromBytes(b.BytesFrom(-2)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func maybeIPPrefix(b byte) bool {
|
||||||
|
return b == '[' || (b >= '0' && b <= '9')
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidDomain(d string) bool {
|
||||||
|
for _, c := range d {
|
||||||
|
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '.' || c == '_') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Address, error) {
|
||||||
|
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addrType := b.Byte(b.Len() - 1)
|
||||||
|
if p.typeParser != nil {
|
||||||
|
addrType = p.typeParser(addrType)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrFamily, valid := p.addrTypeMap[addrType]
|
||||||
|
if !valid {
|
||||||
|
return nil, newError("unknown address type: ", addrType)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch addrFamily {
|
||||||
|
case net.AddressFamilyIPv4:
|
||||||
|
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return net.IPAddress(b.BytesFrom(-4)), nil
|
||||||
|
case net.AddressFamilyIPv6:
|
||||||
|
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return net.IPAddress(b.BytesFrom(-16)), nil
|
||||||
|
case net.AddressFamilyDomain:
|
||||||
|
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
domainLength := int32(b.Byte(b.Len() - 1))
|
||||||
|
if err := b.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
domain := string(b.BytesFrom(-domainLength))
|
||||||
|
if maybeIPPrefix(domain[0]) {
|
||||||
|
addr := net.ParseAddress(domain)
|
||||||
|
if addr.Family().IsIPv4() || addrFamily.IsIPv6() {
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isValidDomain(domain) {
|
||||||
|
return nil, newError("invalid domain name: ", domain)
|
||||||
|
}
|
||||||
|
return net.DomainAddress(domain), nil
|
||||||
|
default:
|
||||||
|
panic("impossible case")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AddressParser) ReadAddressPort(buffer *buf.Buffer, input io.Reader) (net.Address, net.Port, error) {
|
||||||
|
if buffer == nil {
|
||||||
|
buffer = buf.New()
|
||||||
|
defer buffer.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.portFirst {
|
||||||
|
port, err := p.readPort(buffer, input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
addr, err := p.readAddress(buffer, input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
return addr, port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err := p.readAddress(buffer, input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := p.readPort(buffer, input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr, port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AddressParser) writePort(writer io.Writer, port net.Port) error {
|
||||||
|
_, err := writer.Write(port.Bytes(nil))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AddressParser) writeAddress(writer io.Writer, address net.Address) error {
|
||||||
|
tb, valid := p.addrByteMap[address.Family()]
|
||||||
|
if !valid {
|
||||||
|
return newError("unknown address family", address.Family())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch address.Family() {
|
||||||
|
case net.AddressFamilyIPv4, net.AddressFamilyIPv6:
|
||||||
|
if _, err := writer.Write([]byte{tb}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := writer.Write(address.IP()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case net.AddressFamilyDomain:
|
||||||
|
domain := address.Domain()
|
||||||
|
if isDomainTooLong(domain) {
|
||||||
|
return newError("Super long domain is not supported: ", domain)
|
||||||
|
}
|
||||||
|
if _, err := writer.Write([]byte{tb, byte(len(domain))}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := writer.Write([]byte(domain)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AddressParser) WriteAddressPort(writer io.Writer, addr net.Address, port net.Port) error {
|
||||||
|
if p.portFirst {
|
||||||
|
if err := p.writePort(writer, port); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.writeAddress(writer, addr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.writeAddress(writer, addr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.writePort(writer, port); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
118
common/protocol/address_test.go
Normal file
118
common/protocol/address_test.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package protocol_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"v2ray.com/core/common/buf"
|
||||||
|
"v2ray.com/core/common/net"
|
||||||
|
. "v2ray.com/core/common/protocol"
|
||||||
|
. "v2ray.com/ext/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddressReading(t *testing.T) {
|
||||||
|
assert := With(t)
|
||||||
|
|
||||||
|
data := []struct {
|
||||||
|
Options []AddressOption
|
||||||
|
Input []byte
|
||||||
|
Address net.Address
|
||||||
|
Port net.Port
|
||||||
|
Error bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Options: []AddressOption{},
|
||||||
|
Input: []byte{},
|
||||||
|
Error: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Options: []AddressOption{},
|
||||||
|
Input: []byte{0, 0, 0, 0, 0},
|
||||||
|
Error: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)},
|
||||||
|
Input: []byte{1, 0, 0, 0, 0, 0, 53},
|
||||||
|
Address: net.IPAddress([]byte{0, 0, 0, 0}),
|
||||||
|
Port: net.Port(53),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)},
|
||||||
|
Input: []byte{1, 0, 0, 0, 0},
|
||||||
|
Error: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Options: []AddressOption{AddressFamilyByte(0x04, net.AddressFamilyIPv6)},
|
||||||
|
Input: []byte{4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 80},
|
||||||
|
Address: net.IPAddress([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}),
|
||||||
|
Port: net.Port(80),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)},
|
||||||
|
Input: []byte{3, 9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 80},
|
||||||
|
Address: net.DomainAddress("v2ray.com"),
|
||||||
|
Port: net.Port(80),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)},
|
||||||
|
Input: []byte{3, 9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0},
|
||||||
|
Error: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)},
|
||||||
|
Input: []byte{3, 7, 56, 46, 56, 46, 56, 46, 56, 0, 80},
|
||||||
|
Address: net.ParseAddress("8.8.8.8"),
|
||||||
|
Port: net.Port(80),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)},
|
||||||
|
Input: []byte{3, 7, 10, 46, 56, 46, 56, 46, 56, 0, 80},
|
||||||
|
Error: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range data {
|
||||||
|
b := buf.New()
|
||||||
|
parser := NewAddressParser(tc.Options...)
|
||||||
|
addr, port, err := parser.ReadAddressPort(b, bytes.NewReader(tc.Input))
|
||||||
|
b.Release()
|
||||||
|
if tc.Error {
|
||||||
|
assert(err, IsNotNil)
|
||||||
|
} else {
|
||||||
|
assert(addr, Equals, tc.Address)
|
||||||
|
assert(port, Equals, tc.Port)
|
||||||
|
assert(err, IsNil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddressWriting(t *testing.T) {
|
||||||
|
assert := With(t)
|
||||||
|
|
||||||
|
data := []struct {
|
||||||
|
Options []AddressOption
|
||||||
|
Address net.Address
|
||||||
|
Port net.Port
|
||||||
|
Bytes []byte
|
||||||
|
Error bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)},
|
||||||
|
Address: net.LocalHostIP,
|
||||||
|
Port: net.Port(80),
|
||||||
|
Bytes: []byte{1, 127, 0, 0, 1, 0, 80},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range data {
|
||||||
|
parser := NewAddressParser(tc.Options...)
|
||||||
|
|
||||||
|
b := buf.New()
|
||||||
|
err := parser.WriteAddressPort(b, tc.Address, tc.Port)
|
||||||
|
if tc.Error {
|
||||||
|
assert(err, IsNotNil)
|
||||||
|
} else {
|
||||||
|
assert(b.Bytes(), Equals, tc.Bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
common/protocol/context.go
Normal file → Executable file
4
common/protocol/context.go
Normal file → Executable file
@ -10,12 +10,12 @@ const (
|
|||||||
userKey key = iota
|
userKey key = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContextWithUser returns a context combined with an User.
|
// ContextWithUser returns a context combined with a User.
|
||||||
func ContextWithUser(ctx context.Context, user *User) context.Context {
|
func ContextWithUser(ctx context.Context, user *User) context.Context {
|
||||||
return context.WithValue(ctx, userKey, user)
|
return context.WithValue(ctx, userKey, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserFromContext extracts an User from the given context, if any.
|
// UserFromContext extracts a User from the given context, if any.
|
||||||
func UserFromContext(ctx context.Context) *User {
|
func UserFromContext(ctx context.Context) *User {
|
||||||
v := ctx.Value(userKey)
|
v := ctx.Value(userKey)
|
||||||
if v == nil {
|
if v == nil {
|
||||||
|
@ -38,24 +38,11 @@ const (
|
|||||||
RequestOptionChunkMasking bitmask.Byte = 0x04
|
RequestOptionChunkMasking bitmask.Byte = 0x04
|
||||||
)
|
)
|
||||||
|
|
||||||
type Security byte
|
|
||||||
|
|
||||||
func (s Security) Is(t SecurityType) bool {
|
|
||||||
return s == Security(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NormSecurity(s Security) Security {
|
|
||||||
if s.Is(SecurityType_UNKNOWN) {
|
|
||||||
return Security(SecurityType_LEGACY)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
type RequestHeader struct {
|
type RequestHeader struct {
|
||||||
Version byte
|
Version byte
|
||||||
Command RequestCommand
|
Command RequestCommand
|
||||||
Option bitmask.Byte
|
Option bitmask.Byte
|
||||||
Security Security
|
Security SecurityType
|
||||||
Port net.Port
|
Port net.Port
|
||||||
Address net.Address
|
Address net.Address
|
||||||
User *User
|
User *User
|
||||||
@ -88,16 +75,16 @@ type CommandSwitchAccount struct {
|
|||||||
ValidMin byte
|
ValidMin byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *SecurityConfig) AsSecurity() Security {
|
func (sc *SecurityConfig) GetSecurityType() SecurityType {
|
||||||
if sc == nil || sc.Type == SecurityType_AUTO {
|
if sc == nil || sc.Type == SecurityType_AUTO {
|
||||||
if runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x" {
|
if runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x" {
|
||||||
return Security(SecurityType_AES128_GCM)
|
return SecurityType_AES128_GCM
|
||||||
}
|
}
|
||||||
return Security(SecurityType_CHACHA20_POLY1305)
|
return SecurityType_CHACHA20_POLY1305
|
||||||
}
|
}
|
||||||
return NormSecurity(Security(sc.Type))
|
return sc.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsDomainTooLong(domain string) bool {
|
func isDomainTooLong(domain string) bool {
|
||||||
return len(domain) > 256
|
return len(domain) > 256
|
||||||
}
|
}
|
||||||
|
2
common/protocol/id.go
Normal file → Executable file
2
common/protocol/id.go
Normal file → Executable file
@ -19,7 +19,7 @@ func DefaultIDHash(key []byte) hash.Hash {
|
|||||||
return hmac.New(md5.New, key)
|
return hmac.New(md5.New, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The ID of en entity, in the form of an UUID.
|
// The ID of en entity, in the form of a UUID.
|
||||||
type ID struct {
|
type ID struct {
|
||||||
uuid uuid.UUID
|
uuid uuid.UUID
|
||||||
cmdKey [IDBytesLen]byte
|
cmdKey [IDBytesLen]byte
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
type TransferType int
|
type TransferType byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TransferTypeStream TransferType = 0
|
TransferTypeStream TransferType = 0
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
package protocol
|
package protocol // import "v2ray.com/core/common/protocol"
|
||||||
|
|
||||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg protocol -path Protocol
|
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg protocol -path Protocol
|
||||||
|
@ -110,10 +110,10 @@ func (s *ServerSpec) PickUser() *User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ServerSpec) IsValid() bool {
|
func (s *ServerSpec) IsValid() bool {
|
||||||
return v.valid.IsValid()
|
return s.valid.IsValid()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ServerSpec) Invalidate() {
|
func (s *ServerSpec) Invalidate() {
|
||||||
v.valid.Invalidate()
|
s.valid.Invalidate()
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package retry
|
package retry // import "v2ray.com/core/common/retry"
|
||||||
|
|
||||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg retry -path Retry
|
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg retry -path Retry
|
||||||
|
|
||||||
|
4
common/serial/bytes.go
Normal file → Executable file
4
common/serial/bytes.go
Normal file → Executable file
@ -7,12 +7,12 @@ func ByteToHexString(value byte) string {
|
|||||||
return hex.EncodeToString([]byte{value})
|
return hex.EncodeToString([]byte{value})
|
||||||
}
|
}
|
||||||
|
|
||||||
// BytesToUint16 deserializes a byte array to an uint16 in big endian order. The byte array must have at least 2 elements.
|
// BytesToUint16 deserializes a byte array to a uint16 in big endian order. The byte array must have at least 2 elements.
|
||||||
func BytesToUint16(value []byte) uint16 {
|
func BytesToUint16(value []byte) uint16 {
|
||||||
return uint16(value[0])<<8 | uint16(value[1])
|
return uint16(value[0])<<8 | uint16(value[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// BytesToUint32 deserializes a byte array to an uint32 in big endian order. The byte array must have at least 4 elements.
|
// BytesToUint32 deserializes a byte array to a uint32 in big endian order. The byte array must have at least 4 elements.
|
||||||
func BytesToUint32(value []byte) uint32 {
|
func BytesToUint32(value []byte) uint32 {
|
||||||
return uint32(value[0])<<24 |
|
return uint32(value[0])<<24 |
|
||||||
uint32(value[1])<<16 |
|
uint32(value[1])<<16 |
|
||||||
|
2
common/serial/numbers.go
Normal file → Executable file
2
common/serial/numbers.go
Normal file → Executable file
@ -3,7 +3,7 @@ package serial
|
|||||||
import "strconv"
|
import "strconv"
|
||||||
import "io"
|
import "io"
|
||||||
|
|
||||||
// Uint16ToBytes serializes an uint16 into bytes in big endian order.
|
// Uint16ToBytes serializes a uint16 into bytes in big endian order.
|
||||||
func Uint16ToBytes(value uint16, b []byte) []byte {
|
func Uint16ToBytes(value uint16, b []byte) []byte {
|
||||||
return append(b, byte(value>>8), byte(value))
|
return append(b, byte(value>>8), byte(value))
|
||||||
}
|
}
|
||||||
|
28
common/serial/string_test.go
Normal file
28
common/serial/string_test.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package serial_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "v2ray.com/core/common/serial"
|
||||||
|
. "v2ray.com/ext/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestToString(t *testing.T) {
|
||||||
|
assert := With(t)
|
||||||
|
|
||||||
|
s := "a"
|
||||||
|
data := []struct {
|
||||||
|
Value interface{}
|
||||||
|
String string
|
||||||
|
}{
|
||||||
|
{Value: s, String: s},
|
||||||
|
{Value: &s, String: s},
|
||||||
|
{Value: errors.New("t"), String: "t"},
|
||||||
|
{Value: []byte{'b', 'c'}, String: "[62,63]"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range data {
|
||||||
|
assert(ToString(c.Value), Equals, c.String)
|
||||||
|
}
|
||||||
|
}
|
40
common/session/session.go
Normal file
40
common/session/session.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Package session provides functions for sessions of incoming requests.
|
||||||
|
package session // import "v2ray.com/core/common/session"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ID of a session.
|
||||||
|
type ID uint32
|
||||||
|
|
||||||
|
// NewID generates a new ID. The generated ID is high likely to be unique, but not cryptographically secure.
|
||||||
|
// The generated ID will never be 0.
|
||||||
|
func NewID() ID {
|
||||||
|
for {
|
||||||
|
id := ID(rand.Uint32())
|
||||||
|
if id != 0 {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sessionKey int
|
||||||
|
|
||||||
|
const (
|
||||||
|
idSessionKey sessionKey = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContextWithID returns a new context with the given ID.
|
||||||
|
func ContextWithID(ctx context.Context, id ID) context.Context {
|
||||||
|
return context.WithValue(ctx, idSessionKey, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDFromContext returns ID in this context, or 0 if not contained.
|
||||||
|
func IDFromContext(ctx context.Context) ID {
|
||||||
|
if id, ok := ctx.Value(idSessionKey).(ID); ok {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
4
common/signal/done.go
Normal file → Executable file
4
common/signal/done.go
Normal file → Executable file
@ -4,7 +4,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Done is an utility for notifications of something being done.
|
// Done is a utility for notifications of something being done.
|
||||||
type Done struct {
|
type Done struct {
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
c chan struct{}
|
c chan struct{}
|
||||||
@ -38,7 +38,7 @@ func (d *Done) Wait() {
|
|||||||
<-d.c
|
<-d.c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close marks this Done 'done'. This method may be called mutliple times. All calls after first call will have no effect on its status.
|
// Close marks this Done 'done'. This method may be called multiple times. All calls after first call will have no effect on its status.
|
||||||
func (d *Done) Close() error {
|
func (d *Done) Close() error {
|
||||||
d.access.Lock()
|
d.access.Lock()
|
||||||
defer d.access.Unlock()
|
defer d.access.Unlock()
|
||||||
|
@ -12,7 +12,7 @@ func executeAndFulfill(f func() error, done chan<- error) {
|
|||||||
close(done)
|
close(done)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecuteAsync executes a function asychrously and return its result.
|
// ExecuteAsync executes a function asynchronously and return its result.
|
||||||
func ExecuteAsync(f func() error) <-chan error {
|
func ExecuteAsync(f func() error) <-chan error {
|
||||||
done := make(chan error, 1)
|
done := make(chan error, 1)
|
||||||
go executeAndFulfill(f, done)
|
go executeAndFulfill(f, done)
|
||||||
|
2
common/signal/notifier.go
Normal file → Executable file
2
common/signal/notifier.go
Normal file → Executable file
@ -1,6 +1,6 @@
|
|||||||
package signal
|
package signal
|
||||||
|
|
||||||
// Notifier is an utility for notifying changes. The change producer may notify changes multiple time, and the consumer may get notified asychronously.
|
// Notifier is a utility for notifying changes. The change producer may notify changes multiple time, and the consumer may get notified asynchronously.
|
||||||
type Notifier struct {
|
type Notifier struct {
|
||||||
c chan struct{}
|
c chan struct{}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ func (t *PeriodicTask) Start() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements common.Runnable.
|
// Close implements common.Closable.
|
||||||
func (t *PeriodicTask) Close() error {
|
func (t *PeriodicTask) Close() error {
|
||||||
t.access.Lock()
|
t.access.Lock()
|
||||||
defer t.access.Unlock()
|
defer t.access.Unlock()
|
||||||
|
14
common/uuid/uuid.go
Normal file → Executable file
14
common/uuid/uuid.go
Normal file → Executable file
@ -1,4 +1,4 @@
|
|||||||
package uuid
|
package uuid // import "v2ray.com/core/common/uuid"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -49,26 +49,26 @@ func (u *UUID) Equals(another *UUID) bool {
|
|||||||
// Next generates a deterministic random UUID based on this UUID.
|
// Next generates a deterministic random UUID based on this UUID.
|
||||||
func (u *UUID) Next() UUID {
|
func (u *UUID) Next() UUID {
|
||||||
md5hash := md5.New()
|
md5hash := md5.New()
|
||||||
md5hash.Write(u.Bytes())
|
common.Must2(md5hash.Write(u.Bytes()))
|
||||||
md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81"))
|
common.Must2(md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81")))
|
||||||
var newid UUID
|
var newid UUID
|
||||||
for {
|
for {
|
||||||
md5hash.Sum(newid[:0])
|
md5hash.Sum(newid[:0])
|
||||||
if !newid.Equals(u) {
|
if !newid.Equals(u) {
|
||||||
return newid
|
return newid
|
||||||
}
|
}
|
||||||
md5hash.Write([]byte("533eff8a-4113-4b10-b5ce-0f5d76b98cd2"))
|
common.Must2(md5hash.Write([]byte("533eff8a-4113-4b10-b5ce-0f5d76b98cd2")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates an UUID with random value.
|
// New creates a UUID with random value.
|
||||||
func New() UUID {
|
func New() UUID {
|
||||||
var uuid UUID
|
var uuid UUID
|
||||||
common.Must2(rand.Read(uuid.Bytes()))
|
common.Must2(rand.Read(uuid.Bytes()))
|
||||||
return uuid
|
return uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseBytes converts an UUID in byte form to object.
|
// ParseBytes converts a UUID in byte form to object.
|
||||||
func ParseBytes(b []byte) (UUID, error) {
|
func ParseBytes(b []byte) (UUID, error) {
|
||||||
var uuid UUID
|
var uuid UUID
|
||||||
if len(b) != 16 {
|
if len(b) != 16 {
|
||||||
@ -78,7 +78,7 @@ func ParseBytes(b []byte) (UUID, error) {
|
|||||||
return uuid, nil
|
return uuid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseString converts an UUID in string form to object.
|
// ParseString converts a UUID in string form to object.
|
||||||
func ParseString(str string) (UUID, error) {
|
func ParseString(str string) (UUID, error) {
|
||||||
var uuid UUID
|
var uuid UUID
|
||||||
|
|
||||||
|
88
config.go
Executable file
88
config.go
Executable file
@ -0,0 +1,88 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"v2ray.com/core/common"
|
||||||
|
"v2ray.com/core/common/buf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfigFormat is a configurable format of V2Ray config file.
|
||||||
|
type ConfigFormat struct {
|
||||||
|
Name string
|
||||||
|
Extension []string
|
||||||
|
Loader ConfigLoader
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigLoader is a utility to load V2Ray config from external source.
|
||||||
|
type ConfigLoader func(input io.Reader) (*Config, error)
|
||||||
|
|
||||||
|
var (
|
||||||
|
configLoaderByName = make(map[string]*ConfigFormat)
|
||||||
|
configLoaderByExt = make(map[string]*ConfigFormat)
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterConfigLoader add a new ConfigLoader.
|
||||||
|
func RegisterConfigLoader(format *ConfigFormat) error {
|
||||||
|
name := strings.ToLower(format.Name)
|
||||||
|
if _, found := configLoaderByName[name]; found {
|
||||||
|
return newError(format.Name, " already registered.")
|
||||||
|
}
|
||||||
|
configLoaderByName[name] = format
|
||||||
|
|
||||||
|
for _, ext := range format.Extension {
|
||||||
|
lext := strings.ToLower(ext)
|
||||||
|
if f, found := configLoaderByExt[lext]; found {
|
||||||
|
return newError(ext, " already registered to ", f.Name)
|
||||||
|
}
|
||||||
|
configLoaderByExt[lext] = format
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExtension(filename string) string {
|
||||||
|
idx := strings.LastIndexByte(filename, '.')
|
||||||
|
if idx == -1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return filename[idx+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfig loads config with given format from given source.
|
||||||
|
func LoadConfig(formatName string, filename string, input io.Reader) (*Config, error) {
|
||||||
|
ext := getExtension(filename)
|
||||||
|
if len(ext) > 0 {
|
||||||
|
if f, found := configLoaderByExt[ext]; found {
|
||||||
|
return f.Loader(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f, found := configLoaderByName[formatName]; found {
|
||||||
|
return f.Loader(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, newError("Unable to load config in ", formatName).AtWarning()
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadProtobufConfig(input io.Reader) (*Config, error) {
|
||||||
|
config := new(Config)
|
||||||
|
data, err := buf.ReadAllToBytes(input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := proto.Unmarshal(data, config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(RegisterConfigLoader(&ConfigFormat{
|
||||||
|
Name: "Protobuf",
|
||||||
|
Extension: []string{"pb"},
|
||||||
|
Loader: loadProtobufConfig,
|
||||||
|
}))
|
||||||
|
}
|
91
config.pb.go
91
config.pb.go
@ -17,35 +17,13 @@ var _ = math.Inf
|
|||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
// Configuration serialization format.
|
// Config is the master config of V2Ray. V2Ray takes this config as input and functions accordingly.
|
||||||
type ConfigFormat int32
|
|
||||||
|
|
||||||
const (
|
|
||||||
ConfigFormat_Protobuf ConfigFormat = 0
|
|
||||||
ConfigFormat_JSON ConfigFormat = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
var ConfigFormat_name = map[int32]string{
|
|
||||||
0: "Protobuf",
|
|
||||||
1: "JSON",
|
|
||||||
}
|
|
||||||
var ConfigFormat_value = map[string]int32{
|
|
||||||
"Protobuf": 0,
|
|
||||||
"JSON": 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x ConfigFormat) String() string {
|
|
||||||
return proto.EnumName(ConfigFormat_name, int32(x))
|
|
||||||
}
|
|
||||||
func (ConfigFormat) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
|
||||||
|
|
||||||
// Master config of V2Ray. V2Ray takes this config as input and functions accordingly.
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// Inbound handler configurations. Must have at least one item.
|
// Inbound handler configurations. Must have at least one item.
|
||||||
Inbound []*InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbound" json:"inbound,omitempty"`
|
Inbound []*InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbound" json:"inbound,omitempty"`
|
||||||
// Outbound handler configurations. Must have at least one item. The first item is used as default for routing.
|
// Outbound handler configurations. Must have at least one item. The first item is used as default for routing.
|
||||||
Outbound []*OutboundHandlerConfig `protobuf:"bytes,2,rep,name=outbound" json:"outbound,omitempty"`
|
Outbound []*OutboundHandlerConfig `protobuf:"bytes,2,rep,name=outbound" json:"outbound,omitempty"`
|
||||||
// App configuration. Must be one in the app directory.
|
// App is for configurations of all features in V2Ray. A feature must implement the Feature interface, and its config type must be registered through common.RegisterConfig.
|
||||||
App []*v2ray_core_common_serial.TypedMessage `protobuf:"bytes,4,rep,name=app" json:"app,omitempty"`
|
App []*v2ray_core_common_serial.TypedMessage `protobuf:"bytes,4,rep,name=app" json:"app,omitempty"`
|
||||||
// Transport settings.
|
// Transport settings.
|
||||||
Transport *v2ray_core_transport.Config `protobuf:"bytes,5,opt,name=transport" json:"transport,omitempty"`
|
Transport *v2ray_core_transport.Config `protobuf:"bytes,5,opt,name=transport" json:"transport,omitempty"`
|
||||||
@ -94,10 +72,11 @@ func (m *Config) GetExtension() []*v2ray_core_common_serial.TypedMessage {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InboundHandlerConfig is the configuration for inbound handler.
|
||||||
type InboundHandlerConfig struct {
|
type InboundHandlerConfig struct {
|
||||||
// Tag of the inbound handler.
|
// Tag of the inbound handler. The tag must be unique among all inbound handlers
|
||||||
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
|
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
|
||||||
// Settings for how this inbound proxy is handled. Must be ReceiverConfig above.
|
// Settings for how this inbound proxy is handled.
|
||||||
ReceiverSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings" json:"receiver_settings,omitempty"`
|
ReceiverSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings" json:"receiver_settings,omitempty"`
|
||||||
// Settings for inbound proxy. Must be one of the inbound proxies.
|
// Settings for inbound proxy. Must be one of the inbound proxies.
|
||||||
ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"`
|
ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"`
|
||||||
@ -129,10 +108,11 @@ func (m *InboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.Type
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OutboundHandlerConfig is the configuration for outbound handler.
|
||||||
type OutboundHandlerConfig struct {
|
type OutboundHandlerConfig struct {
|
||||||
// Tag of this outbound handler.
|
// Tag of this outbound handler.
|
||||||
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
|
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
|
||||||
// Settings for how to dial connection for this outbound handler. Must be SenderConfig above.
|
// Settings for how to dial connection for this outbound handler.
|
||||||
SenderSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=sender_settings,json=senderSettings" json:"sender_settings,omitempty"`
|
SenderSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=sender_settings,json=senderSettings" json:"sender_settings,omitempty"`
|
||||||
// Settings for this outbound proxy. Must be one of the outbound proxies.
|
// Settings for this outbound proxy. Must be one of the outbound proxies.
|
||||||
ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"`
|
ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"`
|
||||||
@ -186,39 +166,36 @@ func init() {
|
|||||||
proto.RegisterType((*Config)(nil), "v2ray.core.Config")
|
proto.RegisterType((*Config)(nil), "v2ray.core.Config")
|
||||||
proto.RegisterType((*InboundHandlerConfig)(nil), "v2ray.core.InboundHandlerConfig")
|
proto.RegisterType((*InboundHandlerConfig)(nil), "v2ray.core.InboundHandlerConfig")
|
||||||
proto.RegisterType((*OutboundHandlerConfig)(nil), "v2ray.core.OutboundHandlerConfig")
|
proto.RegisterType((*OutboundHandlerConfig)(nil), "v2ray.core.OutboundHandlerConfig")
|
||||||
proto.RegisterEnum("v2ray.core.ConfigFormat", ConfigFormat_name, ConfigFormat_value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("v2ray.com/core/config.proto", fileDescriptor0) }
|
func init() { proto.RegisterFile("v2ray.com/core/config.proto", fileDescriptor0) }
|
||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
var fileDescriptor0 = []byte{
|
||||||
// 436 bytes of a gzipped FileDescriptorProto
|
// 405 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x93, 0x41, 0x6f, 0xd3, 0x30,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x93, 0x41, 0x6f, 0x94, 0x40,
|
||||||
0x14, 0xc7, 0x71, 0x13, 0xba, 0xf6, 0x6d, 0x94, 0x60, 0x01, 0xb2, 0x06, 0x87, 0x50, 0x69, 0x53,
|
0x1c, 0xc5, 0x33, 0x0b, 0x6e, 0xcb, 0xbf, 0x71, 0x5d, 0x27, 0x6a, 0x26, 0xd5, 0x03, 0x6e, 0x52,
|
||||||
0xc5, 0xc1, 0x45, 0xe5, 0x82, 0x26, 0x71, 0x61, 0x08, 0xc1, 0xa4, 0xd1, 0x29, 0x45, 0x1c, 0xb8,
|
0xc3, 0x69, 0x30, 0x78, 0x31, 0x4d, 0xbc, 0x58, 0x0f, 0x6a, 0xd2, 0xd4, 0x50, 0xe3, 0xc1, 0x4b,
|
||||||
0x4c, 0x6e, 0xfa, 0x56, 0x45, 0x5a, 0xec, 0xc8, 0x76, 0xa7, 0xe6, 0x2b, 0xf1, 0x3d, 0xb8, 0xf1,
|
0x33, 0x85, 0xbf, 0x84, 0xa4, 0xcc, 0x90, 0x99, 0x69, 0xb3, 0x7c, 0x25, 0xbf, 0x87, 0x37, 0xbf,
|
||||||
0x8d, 0xb8, 0xa0, 0xc4, 0x49, 0x93, 0x41, 0x0f, 0x14, 0x69, 0xa7, 0xc4, 0x79, 0xfe, 0xfd, 0xdf,
|
0x91, 0x17, 0x03, 0x03, 0x0b, 0xd5, 0x3d, 0xb8, 0x26, 0x3d, 0xc1, 0xf0, 0xe6, 0xf7, 0xde, 0xff,
|
||||||
0xfb, 0x25, 0x31, 0x3c, 0xbb, 0x99, 0x68, 0x91, 0xf3, 0x58, 0xa5, 0xe3, 0x58, 0x69, 0x1c, 0xc7,
|
0x01, 0x03, 0x4f, 0x6f, 0x12, 0x2d, 0x1a, 0x9e, 0xa9, 0x2a, 0xce, 0x94, 0xc6, 0x38, 0x53, 0xf2,
|
||||||
0x4a, 0x5e, 0x25, 0x4b, 0x9e, 0x69, 0x65, 0x15, 0x85, 0xba, 0xa8, 0xf1, 0xf0, 0xd5, 0x5f, 0x1b,
|
0x5b, 0x59, 0xf0, 0x5a, 0x2b, 0xab, 0x28, 0x0c, 0xa2, 0xc6, 0xc3, 0x97, 0x7f, 0x6d, 0xac, 0x2a,
|
||||||
0xd3, 0x54, 0xc9, 0xb1, 0x41, 0x9d, 0x88, 0xeb, 0xb1, 0xcd, 0x33, 0x5c, 0x5c, 0xa6, 0x68, 0x8c,
|
0x25, 0x63, 0x83, 0xba, 0x14, 0x57, 0xb1, 0x6d, 0x6a, 0xcc, 0x2f, 0x2a, 0x34, 0x46, 0x14, 0xe8,
|
||||||
0x58, 0xa2, 0xa3, 0x0f, 0x8f, 0xfe, 0x20, 0xac, 0x16, 0xd2, 0x64, 0x4a, 0xdb, 0x5b, 0x4d, 0x86,
|
0xe8, 0xc3, 0xa3, 0x3f, 0x08, 0xab, 0x85, 0x34, 0xb5, 0xd2, 0xf6, 0x56, 0xc8, 0xea, 0xc7, 0x0c,
|
||||||
0x3f, 0x3a, 0xd0, 0x3d, 0x2d, 0x1f, 0xd0, 0x13, 0xd8, 0x4b, 0xe4, 0x5c, 0xad, 0xe4, 0x82, 0x91,
|
0xe6, 0x27, 0xdd, 0x03, 0x7a, 0x0c, 0x7b, 0xa5, 0xbc, 0x54, 0xd7, 0x32, 0x67, 0x24, 0xf4, 0xa2,
|
||||||
0xd0, 0x1b, 0xed, 0x4f, 0x42, 0xde, 0x4c, 0xc0, 0x3f, 0xb9, 0xd2, 0x47, 0x21, 0x17, 0xd7, 0xa8,
|
0x83, 0x24, 0xe4, 0xe3, 0x04, 0xfc, 0x83, 0x93, 0xde, 0x0b, 0x99, 0x5f, 0xa1, 0x76, 0x48, 0x3a,
|
||||||
0x1d, 0x12, 0xd5, 0x00, 0x7d, 0x0b, 0x3d, 0xb5, 0xb2, 0x0e, 0xee, 0x94, 0xf0, 0x8b, 0x36, 0x3c,
|
0x00, 0xf4, 0x0d, 0xec, 0xab, 0x6b, 0xeb, 0xe0, 0x59, 0x07, 0x3f, 0x9f, 0xc2, 0x67, 0xbd, 0x76,
|
||||||
0xad, 0x6a, 0xb7, 0xe9, 0x0d, 0x42, 0xdf, 0x80, 0x27, 0xb2, 0x8c, 0xf9, 0x25, 0x79, 0xdc, 0x26,
|
0x9b, 0xde, 0x20, 0xf4, 0x35, 0x78, 0xa2, 0xae, 0x99, 0xdf, 0x91, 0x2f, 0xa6, 0xa4, 0x2b, 0xca,
|
||||||
0x9d, 0x28, 0x77, 0xa2, 0xfc, 0x4b, 0x21, 0x7a, 0xee, 0x3c, 0xa3, 0x02, 0xa1, 0x27, 0xd0, 0xdf,
|
0x5d, 0x51, 0xfe, 0xb9, 0x2d, 0x7a, 0xea, 0x7a, 0xa6, 0x2d, 0x42, 0x8f, 0x21, 0xd8, 0x34, 0x63,
|
||||||
0x98, 0xb1, 0xfb, 0x21, 0x19, 0xed, 0x4f, 0x9e, 0xb7, 0xf9, 0x4d, 0x91, 0x57, 0x4d, 0x9b, 0xed,
|
0xf7, 0x42, 0x12, 0x1d, 0x24, 0xcf, 0xa6, 0xfc, 0x46, 0xe4, 0x7d, 0xe8, 0xb8, 0x9d, 0xbe, 0x83,
|
||||||
0xf4, 0x3d, 0xf4, 0x71, 0x6d, 0x51, 0x9a, 0x44, 0x49, 0xd6, 0xdd, 0xa9, 0x77, 0x03, 0x9e, 0xf9,
|
0x00, 0xd7, 0x16, 0xa5, 0x29, 0x95, 0x64, 0xf3, 0x9d, 0xb2, 0x47, 0xf0, 0xa3, 0xbf, 0xef, 0x2d,
|
||||||
0x3d, 0x2f, 0xf0, 0x87, 0x3f, 0x09, 0x3c, 0xde, 0xf6, 0x8a, 0x68, 0x00, 0x9e, 0x15, 0x4b, 0x46,
|
0xfd, 0xd5, 0x4f, 0x02, 0x8f, 0xb6, 0xbd, 0x22, 0xba, 0x04, 0xcf, 0x8a, 0x82, 0x91, 0x90, 0x44,
|
||||||
0x42, 0x32, 0xea, 0x47, 0xc5, 0x2d, 0x9d, 0xc1, 0x23, 0x8d, 0x31, 0x26, 0x37, 0xa8, 0x2f, 0x0d,
|
0x41, 0xda, 0xde, 0xd2, 0x73, 0x78, 0xa8, 0x31, 0xc3, 0xf2, 0x06, 0xf5, 0x85, 0x41, 0x6b, 0x4b,
|
||||||
0x5a, 0x9b, 0xc8, 0xa5, 0x61, 0x9d, 0x72, 0xf4, 0x7f, 0x6d, 0x1f, 0xd4, 0x01, 0xb3, 0x8a, 0xa7,
|
0x59, 0x18, 0x36, 0xeb, 0x46, 0xff, 0xd7, 0xf8, 0xe5, 0x60, 0x70, 0xde, 0xf3, 0xf4, 0x14, 0x16,
|
||||||
0xe7, 0x30, 0xc8, 0xb4, 0x5a, 0xe7, 0x4d, 0xa2, 0xb7, 0x53, 0xe2, 0x83, 0x92, 0xae, 0xe3, 0x86,
|
0xb5, 0x56, 0xeb, 0x66, 0x74, 0xf4, 0x76, 0x72, 0xbc, 0xdf, 0xd1, 0x83, 0xdd, 0xea, 0x17, 0x81,
|
||||||
0xbf, 0x08, 0x3c, 0xd9, 0xfa, 0xd1, 0xb6, 0xf8, 0x4c, 0xe1, 0xa1, 0x41, 0xb9, 0xf8, 0x7f, 0x9b,
|
0xc7, 0x5b, 0x3f, 0xda, 0x96, 0x3e, 0x67, 0xf0, 0xc0, 0xa0, 0xcc, 0xff, 0xbf, 0xcd, 0xc2, 0xe1,
|
||||||
0x81, 0xc3, 0xef, 0xc8, 0x85, 0x3e, 0x85, 0x2e, 0xae, 0xb3, 0x44, 0x23, 0xf3, 0x43, 0x32, 0xf2,
|
0x77, 0xd4, 0x85, 0x3e, 0x81, 0x39, 0xae, 0xeb, 0x52, 0x23, 0xf3, 0x43, 0x12, 0x79, 0x69, 0xbf,
|
||||||
0xa2, 0x6a, 0x45, 0x19, 0xec, 0x15, 0x21, 0x28, 0xdd, 0x8f, 0xd3, 0x8f, 0xea, 0xe5, 0xcb, 0x63,
|
0xa2, 0x0c, 0xf6, 0x5a, 0x13, 0x94, 0xee, 0xc7, 0x09, 0xd2, 0x61, 0xf9, 0xf6, 0x08, 0x16, 0x99,
|
||||||
0x38, 0x70, 0xb6, 0x1f, 0x94, 0x4e, 0x85, 0xa5, 0x07, 0xd0, 0xbb, 0x28, 0x4e, 0xcb, 0x7c, 0x75,
|
0xaa, 0x26, 0x69, 0x9f, 0xc8, 0x57, 0xbf, 0xbd, 0x7e, 0x9f, 0xc1, 0x97, 0x24, 0x15, 0x0d, 0x3f,
|
||||||
0x15, 0xdc, 0xa3, 0x3d, 0xf0, 0xcf, 0x66, 0xd3, 0xcf, 0x01, 0x79, 0x77, 0x04, 0x83, 0x58, 0xa5,
|
0x51, 0x1a, 0x2f, 0xe7, 0xdd, 0x11, 0x7a, 0xf5, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x15, 0xee,
|
||||||
0xad, 0xa9, 0x2e, 0xc8, 0x37, 0xbf, 0xb8, 0x7e, 0xef, 0xc0, 0xd7, 0x49, 0x24, 0x72, 0x7e, 0xaa,
|
0x31, 0xc6, 0x03, 0x00, 0x00,
|
||||||
0x34, 0xce, 0xbb, 0xe5, 0x51, 0x7b, 0xfd, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08, 0x59, 0x1b,
|
|
||||||
0xee, 0x03, 0x00, 0x00,
|
|
||||||
}
|
}
|
||||||
|
18
config.proto
18
config.proto
@ -9,13 +9,7 @@ option java_multiple_files = true;
|
|||||||
import "v2ray.com/core/common/serial/typed_message.proto";
|
import "v2ray.com/core/common/serial/typed_message.proto";
|
||||||
import "v2ray.com/core/transport/config.proto";
|
import "v2ray.com/core/transport/config.proto";
|
||||||
|
|
||||||
// Configuration serialization format.
|
// Config is the master config of V2Ray. V2Ray takes this config as input and functions accordingly.
|
||||||
enum ConfigFormat {
|
|
||||||
Protobuf = 0;
|
|
||||||
JSON = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Master config of V2Ray. V2Ray takes this config as input and functions accordingly.
|
|
||||||
message Config {
|
message Config {
|
||||||
// Inbound handler configurations. Must have at least one item.
|
// Inbound handler configurations. Must have at least one item.
|
||||||
repeated InboundHandlerConfig inbound = 1;
|
repeated InboundHandlerConfig inbound = 1;
|
||||||
@ -25,7 +19,7 @@ message Config {
|
|||||||
|
|
||||||
reserved 3;
|
reserved 3;
|
||||||
|
|
||||||
// App configuration. Must be one in the app directory.
|
// App is for configurations of all features in V2Ray. A feature must implement the Feature interface, and its config type must be registered through common.RegisterConfig.
|
||||||
repeated v2ray.core.common.serial.TypedMessage app = 4;
|
repeated v2ray.core.common.serial.TypedMessage app = 4;
|
||||||
|
|
||||||
// Transport settings.
|
// Transport settings.
|
||||||
@ -36,19 +30,21 @@ message Config {
|
|||||||
repeated v2ray.core.common.serial.TypedMessage extension = 6;
|
repeated v2ray.core.common.serial.TypedMessage extension = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InboundHandlerConfig is the configuration for inbound handler.
|
||||||
message InboundHandlerConfig {
|
message InboundHandlerConfig {
|
||||||
// Tag of the inbound handler.
|
// Tag of the inbound handler. The tag must be unique among all inbound handlers
|
||||||
string tag = 1;
|
string tag = 1;
|
||||||
// Settings for how this inbound proxy is handled. Must be ReceiverConfig above.
|
// Settings for how this inbound proxy is handled.
|
||||||
v2ray.core.common.serial.TypedMessage receiver_settings = 2;
|
v2ray.core.common.serial.TypedMessage receiver_settings = 2;
|
||||||
// Settings for inbound proxy. Must be one of the inbound proxies.
|
// Settings for inbound proxy. Must be one of the inbound proxies.
|
||||||
v2ray.core.common.serial.TypedMessage proxy_settings = 3;
|
v2ray.core.common.serial.TypedMessage proxy_settings = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OutboundHandlerConfig is the configuration for outbound handler.
|
||||||
message OutboundHandlerConfig {
|
message OutboundHandlerConfig {
|
||||||
// Tag of this outbound handler.
|
// Tag of this outbound handler.
|
||||||
string tag = 1;
|
string tag = 1;
|
||||||
// Settings for how to dial connection for this outbound handler. Must be SenderConfig above.
|
// Settings for how to dial connection for this outbound handler.
|
||||||
v2ray.core.common.serial.TypedMessage sender_settings = 2;
|
v2ray.core.common.serial.TypedMessage sender_settings = 2;
|
||||||
// Settings for this outbound proxy. Must be one of the outbound proxies.
|
// Settings for this outbound proxy. Must be one of the outbound proxies.
|
||||||
v2ray.core.common.serial.TypedMessage proxy_settings = 3;
|
v2ray.core.common.serial.TypedMessage proxy_settings = 3;
|
||||||
|
11
context.go
11
context.go
@ -8,10 +8,19 @@ type key int
|
|||||||
|
|
||||||
const v2rayKey key = 1
|
const v2rayKey key = 1
|
||||||
|
|
||||||
// FromContext returns a Instance from the given context, or nil if the context doesn't contain one.
|
// FromContext returns an Instance from the given context, or nil if the context doesn't contain one.
|
||||||
func FromContext(ctx context.Context) *Instance {
|
func FromContext(ctx context.Context) *Instance {
|
||||||
if s, ok := ctx.Value(v2rayKey).(*Instance); ok {
|
if s, ok := ctx.Value(v2rayKey).(*Instance); ok {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MustFromContext returns an Instance from the given context, or panics if not present.
|
||||||
|
func MustFromContext(ctx context.Context) *Instance {
|
||||||
|
v := FromContext(ctx)
|
||||||
|
if v == nil {
|
||||||
|
panic("V is not in context.")
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
18
core.go
Normal file → Executable file
18
core.go
Normal file → Executable file
@ -12,16 +12,14 @@ package core
|
|||||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg core -path Core
|
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg core -path Core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"v2ray.com/core/common/serial"
|
||||||
|
|
||||||
"v2ray.com/core/common/platform"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "3.9"
|
version = "3.16"
|
||||||
build = "Custom"
|
build = "Custom"
|
||||||
codename = "die Commanderin"
|
codename = "die Commanderin"
|
||||||
intro = "An unified platform for anti-censorship."
|
intro = "A unified platform for anti-censorship."
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version returns V2Ray's version as a string, in the form of "x.y.z" where x, y and z are numbers.
|
// Version returns V2Ray's version as a string, in the form of "x.y.z" where x, y and z are numbers.
|
||||||
@ -30,10 +28,12 @@ func Version() string {
|
|||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintVersion prints current version into console.
|
// VersionStatement returns a list of strings representing the full version info.
|
||||||
func PrintVersion() {
|
func VersionStatement() []string {
|
||||||
fmt.Printf("V2Ray %s (%s) %s%s", Version(), codename, build, platform.LineSeparator())
|
return []string{
|
||||||
fmt.Printf("%s%s", intro, platform.LineSeparator())
|
serial.Concat("V2Ray ", Version(), " (", codename, ") ", build),
|
||||||
|
intro,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user