1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-06-03 06:30:42 +00:00

Merge branch 'master' of github.com:v2ray/v2ray-core into domainsocket

This commit is contained in:
Shelikhoo 2018-04-05 19:11:33 +08:00
commit c542c043f3
No known key found for this signature in database
GPG Key ID: C4D5E79D22B25316
216 changed files with 4951 additions and 1909 deletions

View File

@ -1,7 +1,7 @@
sudo: required
language: go
go:
- 1.9.4
- "1.10.1"
go_import_path: v2ray.com/core
git:
depth: 5

View File

@ -13,6 +13,7 @@ import (
"v2ray.com/core/common/signal"
)
// Commander is a V2Ray feature that provides gRPC methods to external clients.
type Commander struct {
sync.Mutex
server *grpc.Server
@ -21,22 +22,26 @@ type Commander struct {
ohm core.OutboundHandlerManager
}
// NewCommander creates a new Commander based on the given config.
func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
v := core.MustFromContext(ctx)
c := &Commander{
config: *config,
ohm: v.OutboundHandlerManager(),
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 c, nil
}
// Type implements common.HasType.
func (c *Commander) Type() interface{} {
return (*Commander)(nil)
}
// Start implements common.Runnable.
func (c *Commander) Start() error {
c.Lock()
c.server = grpc.NewServer()
@ -69,13 +74,14 @@ func (c *Commander) Start() error {
}()
c.ohm.RemoveHandler(context.Background(), c.config.Tag)
c.ohm.AddHandler(context.Background(), &CommanderOutbound{
c.ohm.AddHandler(context.Background(), &Outbound{
tag: c.config.Tag,
listener: listener,
})
return nil
}
// Close implements common.Closable.
func (c *Commander) Close() error {
c.Lock()
defer c.Unlock()

View File

@ -5,6 +5,7 @@ import (
"net"
"sync"
"v2ray.com/core/common"
"v2ray.com/core/common/signal"
"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) {
select {
case <-l.done.C():
return nil, newError("listern closed")
return nil, newError("listen closed")
case c := <-l.buffer:
return c, nil
}
}
// Close implement net.Listener.
func (l *OutboundListener) Close() error {
l.done.Close()
common.Must(l.done.Close())
L:
for {
select {
@ -47,6 +50,7 @@ L:
return nil
}
// Addr implements net.Listener.
func (l *OutboundListener) Addr() net.Addr {
return &net.TCPAddr{
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
listener *OutboundListener
access sync.RWMutex
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()
if co.closed {
@ -76,26 +82,26 @@ func (co *CommanderOutbound) Dispatch(ctx context.Context, r ray.OutboundRay) {
co.listener.add(c)
co.access.RUnlock()
<-closeSignal.Wait()
return
}
func (co *CommanderOutbound) Tag() string {
// Tag implements core.OutboundHandler.
func (co *Outbound) Tag() string {
return co.tag
}
func (co *CommanderOutbound) Start() error {
// Start implements common.Runnable.
func (co *Outbound) Start() error {
co.access.Lock()
co.closed = false
co.access.Unlock()
return nil
}
func (co *CommanderOutbound) Close() error {
// Close implements common.Closable.
func (co *Outbound) Close() error {
co.access.Lock()
co.closed = true
co.listener.Close()
co.access.Unlock()
defer co.access.Unlock()
return nil
co.closed = true
return co.listener.Close()
}

View File

@ -4,6 +4,8 @@ import (
"google.golang.org/grpc"
)
// Service is a Commander service.
type Service interface {
// Register registers the service itself to a gRPC server.
Register(*grpc.Server)
}

View File

@ -11,6 +11,7 @@ import (
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/proxy"
"v2ray.com/core/transport/ray"
)
@ -23,18 +24,18 @@ var (
type DefaultDispatcher struct {
ohm core.OutboundHandlerManager
router core.Router
policy core.PolicyManager
stats core.StatManager
}
// NewDefaultDispatcher create a new DefaultDispatcher.
func NewDefaultDispatcher(ctx context.Context, config *Config) (*DefaultDispatcher, error) {
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
v := core.MustFromContext(ctx)
d := &DefaultDispatcher{
ohm: v.OutboundHandlerManager(),
router: v.Router(),
policy: v.PolicyManager(),
stats: v.Stats(),
}
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
}
// Start implements app.Application.
// Start implements common.Runnable.
func (*DefaultDispatcher) Start() error {
return nil
}
// Close implements app.Application.
// Close implements common.Closable.
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.
func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (ray.InboundRay, error) {
if !destination.IsValid() {
@ -58,15 +93,18 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
}
ctx = proxy.ContextWithTarget(ctx, destination)
outbound := ray.NewRay(ctx)
sniferList := proxyman.ProtocoSniffersFromContext(ctx)
if destination.Address.Family().IsDomain() || len(sniferList) == 0 {
user := protocol.UserFromContext(ctx)
rayOptions := d.getRayOption(user)
outbound := ray.New(ctx, rayOptions...)
snifferList := proxyman.ProtocolSniffersFromContext(ctx)
if destination.Address.Family().IsDomain() || len(snifferList) == 0 {
go d.routedDispatch(ctx, outbound, destination)
} else {
go func() {
domain, err := snifer(ctx, sniferList, outbound)
domain, err := sniffer(ctx, snifferList, outbound)
if err == nil {
newError("sniffed domain: ", domain).WriteToLog()
newError("sniffed domain: ", domain).WithContext(ctx).WriteToLog()
destination.Address = net.ParseAddress(domain)
ctx = proxy.ContextWithTarget(ctx, destination)
}
@ -76,11 +114,11 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
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()
defer payload.Release()
sniffer := NewSniffer(sniferList)
sniffer := NewSniffer(snifferList)
totalAttempt := 0
for {
select {
@ -111,13 +149,13 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, outbound ray.Out
if d.router != nil {
if tag, err := d.router.PickRoute(ctx); err == 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
} else {
newError("nonexisting tag: ", tag).AtWarning().WriteToLog()
newError("non existing tag: ", tag).AtWarning().WithContext(ctx).WriteToLog()
}
} else {
newError("default route for ", destination).WriteToLog()
newError("default route for ", destination).WithContext(ctx).WriteToLog()
}
}
dispatcher.Dispatch(ctx, outbound)

View File

@ -173,10 +173,10 @@ type Sniffer struct {
err []error
}
func NewSniffer(sniferList []proxyman.KnownProtocols) *Sniffer {
func NewSniffer(snifferList []proxyman.KnownProtocols) *Sniffer {
s := new(Sniffer)
for _, protocol := range sniferList {
for _, protocol := range snifferList {
var f func([]byte) (string, error)
switch protocol {
case proxyman.KnownProtocols_HTTP:

View File

@ -7,17 +7,14 @@ import (
"github.com/miekg/dns"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/dice"
"v2ray.com/core/common/net"
"v2ray.com/core/common/signal"
"v2ray.com/core/transport/internet/udp"
)
const (
CleanupInterval = time.Second * 120
CleanupThreshold = 512
)
var (
multiQuestionDNS = map[net.Address]bool{
net.IPAddress([]byte{8, 8, 8, 8}): true,
@ -42,10 +39,10 @@ type PendingRequest struct {
type UDPNameServer struct {
sync.Mutex
address net.Destination
requests map[uint16]*PendingRequest
udpServer *udp.Dispatcher
nextCleanup time.Time
address net.Destination
requests map[uint16]*PendingRequest
udpServer *udp.Dispatcher
cleanup *signal.PeriodicTask
}
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),
udpServer: udp.NewDispatcher(dispatcher),
}
s.cleanup = &signal.PeriodicTask{
Interval: time.Minute,
Execute: s.Cleanup,
}
common.Must(s.cleanup.Start())
return s
}
func (s *UDPNameServer) Cleanup() {
expiredRequests := make([]uint16, 0, 16)
func (s *UDPNameServer) Cleanup() error {
now := time.Now()
s.Lock()
for id, r := range s.requests {
if r.expire.Before(now) {
expiredRequests = append(expiredRequests, id)
close(r.response)
delete(s.requests, id)
}
}
for _, id := range expiredRequests {
delete(s.requests, id)
}
s.Unlock()
return nil
}
func (s *UDPNameServer) AssignUnusedID(response chan<- *ARecord) uint16 {
var id uint16
s.Lock()
if len(s.requests) > CleanupThreshold && s.nextCleanup.Before(time.Now()) {
s.nextCleanup = time.Now().Add(CleanupInterval)
go s.Cleanup()
}
for {
id = dice.RollUint16()
if _, found := s.requests[id]; found {
time.Sleep(time.Millisecond * 500)
continue
}
newError("add pending request id ", id).AtDebug().WriteToLog()
@ -182,6 +178,9 @@ func (s *UDPNameServer) QueryA(domain string) <-chan *ARecord {
b, err := msgToBuffer(msg)
if err != nil {
newError("failed to build A query for domain ", domain).Base(err).WriteToLog()
s.Lock()
delete(s.requests, id)
s.Unlock()
close(response)
return response
}

View File

@ -28,11 +28,6 @@ func (r *DomainRecord) Expired() bool {
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 {
sync.Mutex
hosts map[string]net.IP
@ -54,11 +49,7 @@ func New(ctx context.Context, config *Config) (*Server, error) {
return nil
},
}
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
v := core.MustFromContext(ctx)
if err := v.RegisterFeature((*core.DNSClient)(nil), server); err != nil {
return nil, newError("unable to register DNSClient.").Base(err)
}
@ -89,7 +80,7 @@ func (s *Server) Start() error {
return s.task.Start()
}
// Close implements common.Runnable.
// Close implements common.Closable.
func (s *Server) Close() error {
return s.task.Close()
}

View 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
}))
}

View 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)
}

View 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,
}

View 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) {}
}

View 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")
}

View File

@ -6,11 +6,12 @@ import (
"context"
"sync"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/log"
)
// Instance is an app.Application that handles logs.
// Instance is a log.Handler that handles logs.
type Instance struct {
sync.RWMutex
config *Config
@ -23,17 +24,15 @@ type Instance struct {
func New(ctx context.Context, config *Config) (*Instance, error) {
g := &Instance{
config: config,
active: true,
}
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()
active: false,
}
log.RegisterHandler(g)
v := core.FromContext(ctx)
if v != nil {
common.Must(v.RegisterFeature((*log.Handler)(nil), g))
}
return g, nil
}
@ -67,24 +66,48 @@ func (g *Instance) initErrorLogger() error {
return nil
}
// Start implements app.Application.Start().
func (g *Instance) Start() error {
// Type implements common.HasType.
func (*Instance) Type() interface{} {
return (*Instance)(nil)
}
func (g *Instance) startInternal() error {
g.Lock()
defer g.Unlock()
if g.active {
return nil
}
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
}
func (g *Instance) isActive() bool {
g.RLock()
defer g.RUnlock()
// Start implements common.Runnable.Start().
func (g *Instance) Start() error {
if err := g.startInternal(); err != nil {
return err
}
return g.active
newError("Logger started").AtDebug().WriteToLog()
return nil
}
// Handle implements log.Handler.
func (g *Instance) Handle(msg log.Message) {
if !g.isActive() {
g.RLock()
defer g.RUnlock()
if !g.active {
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 {
newError("Logger closing").AtDebug().WriteToLog()
g.Lock()
defer g.Unlock()
if !g.active {
return nil
}
g.active = false
common.Close(g.accessLogger)
g.accessLogger = nil
common.Close(g.errorLogger)
g.errorLogger = nil
return nil
}

View File

@ -14,24 +14,45 @@ func (s *Second) Duration() time.Duration {
return time.Second * time.Duration(s.Value)
}
// OverrideWith overrides current Policy with another one.
func (p *Policy) OverrideWith(another *Policy) {
if another.Timeout != nil {
if another.Timeout.Handshake != nil {
p.Timeout.Handshake = another.Timeout.Handshake
}
if another.Timeout.ConnectionIdle != nil {
p.Timeout.ConnectionIdle = another.Timeout.ConnectionIdle
}
if another.Timeout.UplinkOnly != nil {
p.Timeout.UplinkOnly = another.Timeout.UplinkOnly
}
if another.Timeout.DownlinkOnly != nil {
p.Timeout.DownlinkOnly = another.Timeout.DownlinkOnly
}
func defaultPolicy() *Policy {
p := core.DefaultPolicy()
return &Policy{
Timeout: &Policy_Timeout{
Handshake: &Second{Value: uint32(p.Timeouts.Handshake / time.Second)},
ConnectionIdle: &Second{Value: uint32(p.Timeouts.ConnectionIdle / time.Second)},
UplinkOnly: &Second{Value: uint32(p.Timeouts.UplinkOnly / time.Second)},
DownlinkOnly: &Second{Value: uint32(p.Timeouts.DownlinkOnly / time.Second)},
},
}
}
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 {
var cp core.Policy
if p.Timeout != nil {
@ -40,5 +61,9 @@ func (p *Policy) ToCorePolicy() core.Policy {
cp.Timeouts.DownlinkOnly = p.Timeout.DownlinkOnly.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
}

View File

@ -33,6 +33,7 @@ func (m *Second) GetValue() uint32 {
type Policy struct {
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{} }
@ -47,6 +48,13 @@ func (m *Policy) GetTimeout() *Policy_Timeout {
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.
type Policy_Timeout struct {
Handshake *Second `protobuf:"bytes,1,opt,name=handshake" json:"handshake,omitempty"`
@ -88,6 +96,30 @@ func (m *Policy_Timeout) GetDownlinkOnly() *Second {
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 {
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((*Policy)(nil), "v2ray.core.app.policy.Policy")
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")
}
func init() { proto.RegisterFile("v2ray.com/core/app/policy/config.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 349 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xc1, 0x4a, 0xeb, 0x40,
0x14, 0x86, 0x49, 0x7a, 0x9b, 0x72, 0x4f, 0x6f, 0xaf, 0x32, 0x58, 0x88, 0x05, 0xa5, 0x14, 0x94,
0xae, 0x26, 0x90, 0x6e, 0x44, 0xb1, 0x62, 0x45, 0x41, 0x10, 0x2c, 0x51, 0x14, 0xdc, 0x94, 0x71,
0x32, 0xda, 0xd0, 0xe9, 0x9c, 0x21, 0xa6, 0x95, 0xbc, 0x86, 0x6f, 0xe0, 0xd6, 0x87, 0xf2, 0x59,
0x24, 0x99, 0x84, 0x6c, 0x5a, 0xe9, 0x6e, 0x72, 0xf8, 0xfe, 0x8f, 0x43, 0xfe, 0x03, 0x87, 0x4b,
0x3f, 0x66, 0x29, 0xe5, 0x38, 0xf7, 0x38, 0xc6, 0xc2, 0x63, 0x5a, 0x7b, 0x1a, 0x65, 0xc4, 0x53,
0x8f, 0xa3, 0x7a, 0x89, 0x5e, 0xa9, 0x8e, 0x31, 0x41, 0xd2, 0x2e, 0xb9, 0x58, 0x50, 0xa6, 0x35,
0x35, 0x4c, 0x6f, 0x1f, 0x9c, 0x3b, 0xc1, 0x51, 0x85, 0x64, 0x07, 0xea, 0x4b, 0x26, 0x17, 0xc2,
0xb5, 0xba, 0x56, 0xbf, 0x15, 0x98, 0x8f, 0xde, 0xb7, 0x0d, 0xce, 0x38, 0x47, 0xc9, 0x19, 0x34,
0x92, 0x68, 0x2e, 0x70, 0x91, 0xe4, 0x48, 0xd3, 0x3f, 0xa0, 0x2b, 0x9d, 0xd4, 0xf0, 0xf4, 0xde,
0xc0, 0x41, 0x99, 0xea, 0x7c, 0xd8, 0xd0, 0x28, 0x86, 0xe4, 0x04, 0xfe, 0x4e, 0x99, 0x0a, 0xdf,
0xa6, 0x6c, 0x26, 0x0a, 0xdd, 0xde, 0x1a, 0x9d, 0xd9, 0x2f, 0xa8, 0x78, 0x72, 0x05, 0x5b, 0x1c,
0x95, 0x12, 0x3c, 0x89, 0x50, 0x4d, 0xa2, 0x50, 0x0a, 0xd7, 0xde, 0x44, 0xf1, 0xbf, 0x4a, 0x5d,
0x87, 0x52, 0x90, 0x21, 0x34, 0x17, 0x5a, 0x46, 0x6a, 0x36, 0x41, 0x25, 0x53, 0xb7, 0xb6, 0x89,
0x03, 0x4c, 0xe2, 0x56, 0xc9, 0x94, 0x8c, 0xa0, 0x15, 0xe2, 0xbb, 0xaa, 0x0c, 0x7f, 0x36, 0x31,
0xfc, 0x2b, 0x33, 0x99, 0xa3, 0xf7, 0x69, 0x81, 0x73, 0x91, 0x17, 0x45, 0x86, 0x50, 0x97, 0x62,
0x29, 0xa4, 0x6b, 0x75, 0x6b, 0xfd, 0xa6, 0xdf, 0x5f, 0xa3, 0x31, 0x34, 0xbd, 0xc9, 0xd0, 0x4b,
0x95, 0xc4, 0x69, 0x60, 0x62, 0x9d, 0x47, 0x80, 0x6a, 0x48, 0xb6, 0xa1, 0x36, 0x13, 0x69, 0xd1,
0x66, 0xf6, 0x24, 0x83, 0xb2, 0xe1, 0xdf, 0x7f, 0x96, 0xa9, 0xaf, 0x38, 0x80, 0x63, 0xfb, 0xc8,
0x1a, 0x9d, 0xc2, 0x2e, 0xc7, 0xf9, 0x6a, 0x7c, 0x6c, 0x3d, 0x39, 0xe6, 0xf5, 0x65, 0xb7, 0x1f,
0xfc, 0x80, 0x65, 0x0b, 0xc6, 0x82, 0x9e, 0x6b, 0x5d, 0x98, 0x9e, 0x9d, 0xfc, 0x02, 0x07, 0x3f,
0x01, 0x00, 0x00, 0xff, 0xff, 0xcf, 0x25, 0x25, 0xc2, 0xab, 0x02, 0x00, 0x00,
// 410 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x5d, 0xab, 0xd3, 0x30,
0x18, 0xc7, 0x69, 0x6b, 0x7b, 0x8e, 0x4f, 0xcf, 0x54, 0x82, 0x07, 0xea, 0x40, 0x3d, 0x6c, 0x28,
0xbb, 0x4a, 0xa1, 0xbb, 0xf1, 0x05, 0x27, 0xce, 0x17, 0x10, 0x14, 0x47, 0xe6, 0x0b, 0x78, 0x33,
0x62, 0x1a, 0x5d, 0x59, 0x96, 0x84, 0xbe, 0x4c, 0xfa, 0x35, 0xfc, 0x06, 0xde, 0xfa, 0xc9, 0xfc,
0x18, 0xd2, 0xa4, 0xa5, 0x37, 0xdb, 0xdc, 0x5d, 0xfa, 0xf0, 0xfb, 0xff, 0x78, 0x12, 0xfe, 0x85,
0x87, 0xbb, 0x24, 0xa7, 0x35, 0x66, 0x6a, 0x1b, 0x33, 0x95, 0xf3, 0x98, 0x6a, 0x1d, 0x6b, 0x25,
0x32, 0x56, 0xc7, 0x4c, 0xc9, 0xef, 0xd9, 0x0f, 0xac, 0x73, 0x55, 0x2a, 0x74, 0xd9, 0x71, 0x39,
0xc7, 0x54, 0x6b, 0x6c, 0x99, 0xd1, 0x3d, 0x08, 0x96, 0x9c, 0x29, 0x99, 0xa2, 0xdb, 0xe0, 0xef,
0xa8, 0xa8, 0x78, 0xe4, 0x5c, 0x39, 0x93, 0x01, 0xb1, 0x1f, 0xa3, 0xbf, 0x1e, 0x04, 0x0b, 0x83,
0xa2, 0xe7, 0x70, 0x56, 0x66, 0x5b, 0xae, 0xaa, 0xd2, 0x20, 0x61, 0xf2, 0x00, 0xef, 0x75, 0x62,
0xcb, 0xe3, 0x8f, 0x16, 0x26, 0x5d, 0x0a, 0x3d, 0x06, 0xbf, 0x28, 0x69, 0x59, 0x44, 0xae, 0x89,
0x8f, 0x8f, 0xc7, 0x97, 0x0d, 0x4a, 0x6c, 0x62, 0xf8, 0xcb, 0x85, 0xb3, 0xd6, 0x87, 0x9e, 0xc2,
0xf5, 0x35, 0x95, 0x69, 0xb1, 0xa6, 0x1b, 0xde, 0x6e, 0x72, 0xf7, 0x80, 0xca, 0x5e, 0x8d, 0xf4,
0x3c, 0x7a, 0x03, 0x37, 0x99, 0x92, 0x92, 0xb3, 0x32, 0x53, 0x72, 0x95, 0xa5, 0x82, 0xb7, 0xdb,
0xfc, 0x47, 0x71, 0xa3, 0x4f, 0xbd, 0x4d, 0x05, 0x47, 0x33, 0x08, 0x2b, 0x2d, 0x32, 0xb9, 0x59,
0x29, 0x29, 0xea, 0xc8, 0x3b, 0xc5, 0x01, 0x36, 0xf1, 0x41, 0x8a, 0x1a, 0xcd, 0x61, 0x90, 0xaa,
0x9f, 0xb2, 0x37, 0x5c, 0x3b, 0xc5, 0x70, 0xd1, 0x65, 0x1a, 0xc7, 0xf0, 0x3d, 0xf8, 0xe6, 0x91,
0xd0, 0x7d, 0x08, 0xab, 0x82, 0xe7, 0x2b, 0xeb, 0x37, 0x6f, 0x72, 0x4e, 0xa0, 0x19, 0x7d, 0x32,
0x13, 0x34, 0x86, 0x81, 0x01, 0xba, 0xb8, 0xb9, 0xf3, 0x39, 0xb9, 0x68, 0x86, 0xaf, 0xda, 0xd9,
0xe8, 0xb7, 0x03, 0xc1, 0x4b, 0x53, 0x19, 0x34, 0x03, 0x5f, 0xf0, 0x1d, 0x17, 0x91, 0x73, 0xe5,
0x4d, 0xc2, 0x64, 0x72, 0x60, 0x2b, 0x4b, 0xe3, 0x77, 0x0d, 0xfa, 0x5a, 0x96, 0x79, 0x4d, 0x6c,
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,
}

View File

@ -19,7 +19,13 @@ message Policy {
Second downlink_only = 4;
}
message Stats {
bool user_uplink = 1;
bool user_downlink = 2;
}
Timeout timeout = 1;
Stats stats = 2;
}
message Config {

View File

@ -9,29 +9,27 @@ import (
// Instance is an instance of Policy manager.
type Instance struct {
levels map[uint32]core.Policy
levels map[uint32]*Policy
}
// New creates new Policy manager instance.
func New(ctx context.Context, config *Config) (*Instance, error) {
m := &Instance{
levels: make(map[uint32]core.Policy),
levels: make(map[uint32]*Policy),
}
if len(config.Level) > 0 {
for lv, p := range config.Level {
dp := core.DefaultPolicy()
dp.OverrideWith(p.ToCorePolicy())
m.levels[lv] = dp
pp := defaultPolicy()
pp.overrideWith(p)
m.levels[lv] = pp
}
}
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
if err := v.RegisterFeature((*core.PolicyManager)(nil), m); err != nil {
return nil, newError("unable to register PolicyManager in core").Base(err).AtError()
if v != nil {
if err := v.RegisterFeature((*core.PolicyManager)(nil), m); err != nil {
return nil, newError("unable to register PolicyManager in core").Base(err).AtError()
}
}
return m, nil
@ -40,17 +38,17 @@ func New(ctx context.Context, config *Config) (*Instance, error) {
// ForLevel implements core.PolicyManager.
func (m *Instance) ForLevel(level uint32) core.Policy {
if p, ok := m.levels[level]; ok {
return p
return p.ToCorePolicy()
}
return core.DefaultPolicy()
}
// Start implements app.Application.Start().
// Start implements common.Runnable.Start().
func (m *Instance) Start() error {
return nil
}
// Close implements app.Application.Close().
// Close implements common.Closable.Close().
func (m *Instance) Close() error {
return nil
}

View 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
View File

@ -11,7 +11,7 @@ import (
// InboundOperation is the interface for operations that applies to inbound handlers.
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
}
@ -37,7 +37,7 @@ func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler core.Inbou
}
um, ok := p.(proxy.UserManager)
if !ok {
return newError("proxy is not an UserManager")
return newError("proxy is not a UserManager")
}
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)
if !ok {
return newError("proxy is not an UserManager")
return newError("proxy is not a UserManager")
}
return um.RemoveUser(ctx, op.Email)
}
@ -139,10 +139,7 @@ func (s *service) Register(server *grpc.Server) {
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
s := core.FromContext(ctx)
if s == nil {
return nil, newError("V is not in context.")
}
s := core.MustFromContext(ctx)
return &service{v: s}, nil
}))
}

View File

@ -8,7 +8,8 @@ import v2ray_core_common_serial "v2ray.com/core/common/serial"
import v2ray_core "v2ray.com/core"
import (
context "golang.org/x/net/context"
"context"
grpc "google.golang.org/grpc"
)

View File

@ -29,10 +29,7 @@ type DynamicInboundHandler struct {
}
func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*DynamicInboundHandler, error) {
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
v := core.MustFromContext(ctx)
h := &DynamicInboundHandler{
tag: tag,
proxyConfig: proxyConfig,

View File

@ -24,10 +24,7 @@ func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error)
m := &Manager{
taggedHandlers: make(map[string]core.InboundHandler),
}
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context")
}
v := core.MustFromContext(ctx)
if err := v.RegisterFeature((*core.InboundHandlerManager)(nil), m); err != nil {
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
}
// GetHandler returns core.InboundHandlerManager.
// GetHandler implements core.InboundHandlerManager.
func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandler, error) {
m.access.RLock()
defer m.access.RUnlock()
@ -65,6 +62,7 @@ func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandl
return handler, nil
}
// RemoveHandler implements core.InboundHandlerManager.
func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
if len(tag) == 0 {
return core.ErrNoClue
@ -74,7 +72,9 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
defer m.access.Unlock()
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)
return nil
}
@ -82,6 +82,7 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
return core.ErrNoClue
}
// Start implements common.Runnable.
func (m *Manager) Start() error {
m.access.Lock()
defer m.access.Unlock()
@ -102,6 +103,7 @@ func (m *Manager) Start() error {
return nil
}
// Close implements common.Closable.
func (m *Manager) Close() error {
m.access.Lock()
defer m.access.Unlock()
@ -118,6 +120,7 @@ func (m *Manager) Close() error {
return nil
}
// NewHandler creates a new core.InboundHandler based on the given config.
func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (core.InboundHandler, error) {
rawReceiverSettings, err := config.ReceiverSettings.GetInstance()
if err != nil {

View File

@ -7,6 +7,8 @@ import (
"sync/atomic"
"time"
"v2ray.com/core/common/session"
"v2ray.com/core"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/common"
@ -41,10 +43,13 @@ type tcpWorker struct {
func (w *tcpWorker) callback(conn internet.Connection) {
ctx, cancel := context.WithCancel(context.Background())
sid := session.NewID()
ctx = session.ContextWithID(ctx, sid)
if w.recvOrigDest {
dest, err := tcp.GetOriginalDestination(conn)
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() {
ctx = proxy.ContextWithOriginalTarget(ctx, dest)
@ -59,10 +64,12 @@ func (w *tcpWorker) callback(conn internet.Connection) {
ctx = proxyman.ContextWithProtocolSniffers(ctx, w.sniffers)
}
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()
conn.Close()
if err := conn.Close(); err != nil {
newError("failed to close connection").Base(err).WithContext(ctx).WriteToLog()
}
}
func (w *tcpWorker) Proxy() proxy.Inbound {
@ -128,7 +135,7 @@ func (c *udpConn) Write(buf []byte) (int, error) {
}
func (c *udpConn) Close() error {
common.Close(c.done)
common.Must(c.done.Close())
return nil
}
@ -203,8 +210,10 @@ func (w *udpWorker) getConnection(id connID) (*udpConn, bool) {
func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest net.Destination) {
id := connID{
src: source,
dest: originalDest,
src: source,
}
if originalDest.IsValid() {
id.dest = originalDest
}
conn, existing := w.getConnection(id)
select {
@ -218,6 +227,9 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
if !existing {
go func() {
ctx := context.Background()
sid := session.NewID()
ctx = session.ContextWithID(ctx, sid)
if originalDest.IsValid() {
ctx = proxy.ContextWithOriginalTarget(ctx, originalDest)
}
@ -244,10 +256,7 @@ func (w *udpWorker) removeConn(id connID) {
func (w *udpWorker) Start() error {
w.activeConn = make(map[connID]*udpConn, 16)
w.done = signal.NewDone()
h, err := udp.ListenUDP(w.address, w.port, udp.ListenOption{
Callback: w.callback,
ReceiveOriginalDest: w.recvOrigDest,
})
h, err := udp.ListenUDP(w.address, w.port, w.callback, udp.HubReceiveOriginalDestination(w.recvOrigDest), udp.HubCapacity(256))
if err != nil {
return err
}
@ -257,11 +266,18 @@ func (w *udpWorker) Start() error {
}
func (w *udpWorker) Close() error {
w.Lock()
defer w.Unlock()
if w.hub != nil {
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
}

View File

@ -18,7 +18,8 @@ const (
)
const (
OptionData bitmask.Byte = 0x01
OptionData bitmask.Byte = 0x01
OptionError bitmask.Byte = 0x02
)
type TargetNetwork byte
@ -28,6 +29,13 @@ const (
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
2 bytes - length
@ -48,88 +56,55 @@ type FrameMetadata struct {
SessionStatus SessionStatus
}
func (f FrameMetadata) AsSupplier() buf.Supplier {
return func(b []byte) (int, error) {
lengthBytes := b
b = serial.Uint16ToBytes(uint16(0), b[:0]) // place holder for length
func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
lenBytes := b.Bytes()
b.AppendBytes(0x00, 0x00)
b = serial.Uint16ToBytes(f.SessionID, b)
b = append(b, byte(f.SessionStatus), byte(f.Option))
length := 4
len0 := b.Len()
if err := b.AppendSupplier(serial.WriteUint16(f.SessionID)); err != nil {
return err
}
if f.SessionStatus == SessionStatusNew {
switch f.Target.Network {
case net.Network_TCP:
b = append(b, byte(TargetNetworkTCP))
case net.Network_UDP:
b = append(b, byte(TargetNetworkUDP))
}
length++
b.AppendBytes(byte(f.SessionStatus), byte(f.Option))
b = serial.Uint16ToBytes(f.Target.Port.Value(), b)
length += 2
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
}
if f.SessionStatus == SessionStatusNew {
switch f.Target.Network {
case net.Network_TCP:
b.AppendBytes(byte(TargetNetworkTCP))
case net.Network_UDP:
b.AppendBytes(byte(TargetNetworkUDP))
}
serial.Uint16ToBytes(uint16(length), lengthBytes[:0])
return length + 2, nil
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
return err
}
}
len1 := b.Len()
serial.Uint16ToBytes(uint16(len1-len0), lenBytes)
return nil
}
func ReadFrameFrom(b []byte) (*FrameMetadata, error) {
if len(b) < 4 {
return nil, newError("insufficient buffer: ", len(b))
func ReadFrameFrom(b *buf.Buffer) (*FrameMetadata, error) {
if b.Len() < 4 {
return nil, newError("insufficient buffer: ", b.Len())
}
f := &FrameMetadata{
SessionID: serial.BytesToUint16(b[:2]),
SessionStatus: SessionStatus(b[2]),
Option: bitmask.Byte(b[3]),
SessionID: serial.BytesToUint16(b.BytesTo(2)),
SessionStatus: SessionStatus(b.Byte(2)),
Option: bitmask.Byte(b.Byte(3)),
}
b = b[4:]
if f.SessionStatus == SessionStatusNew {
network := TargetNetwork(b[0])
port := net.PortFromBytes(b[1:3])
addrType := protocol.AddressType(b[3])
b = b[4:]
network := TargetNetwork(b.Byte(4))
b.SliceFrom(5)
var addr net.Address
switch addrType {
case protocol.AddressTypeIPv4:
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)
addr, port, err := addrParser.ReadAddressPort(nil, b)
if err != nil {
return nil, newError("failed to parse address and port").Base(err)
}
switch network {
case TargetNetworkTCP:
f.Target = net.TCPDestination(addr, port)

View File

@ -10,8 +10,10 @@ import (
"v2ray.com/core"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/log"
"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"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) {
ctx := proxy.ContextWithTarget(context.Background(), net.TCPDestination(muxCoolAddress, muxCoolPort))
ctx, cancel := context.WithCancel(ctx)
pipe := ray.NewRay(ctx)
pipe := ray.New(ctx)
c := &Client{
sessionManager: NewSessionManager(),
@ -131,7 +133,7 @@ func (m *Client) monitor() {
case <-timer.C:
size := m.sessionManager.Size()
if size == 0 && m.sessionManager.CloseIfNoSession() {
m.done.Close()
common.Must(m.done.Close())
return
}
}
@ -146,18 +148,15 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
}
s.transferType = transferType
writer := NewWriter(s.ID, dest, output, transferType)
defer writer.Close()
defer s.Close()
newError("dispatching request to ", dest).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
}
newError("dispatching request to ", dest).WithContext(ctx).WriteToLog()
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 {
@ -204,13 +203,21 @@ func (m *Client) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReade
}
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)
}
func (m *Client) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
if s, found := m.sessionManager.Get(meta.SessionID); found {
if meta.Option.Has(OptionError) {
s.input.CloseError()
s.output.CloseError()
}
s.Close()
}
if meta.Option.Has(OptionData) {
@ -261,7 +268,7 @@ type Server struct {
// NewServer creates a new mux.Server.
func NewServer(ctx context.Context) *Server {
s := &Server{
dispatcher: core.FromContext(ctx).Dispatcher(),
dispatcher: core.MustFromContext(ctx).Dispatcher(),
}
return s
}
@ -271,7 +278,7 @@ func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (ray.Inboun
return s.dispatcher.Dispatch(ctx, dest)
}
ray := ray.NewRay(ctx)
ray := ray.New(ctx)
worker := &ServerWorker{
dispatcher: s.dispatcher,
outboundRay: ray,
@ -298,8 +305,10 @@ type ServerWorker struct {
func handle(ctx context.Context, s *Session, output buf.Writer) {
writer := NewResponseWriter(s.ID, output, s.transferType)
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()
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 {
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)
if err != nil {
if meta.Option.Has(OptionData) {
@ -343,13 +363,21 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
return nil
}
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)
}
func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
if s, found := w.sessionManager.Get(meta.SessionID); found {
if meta.Option.Has(OptionError) {
s.input.CloseError()
s.output.CloseError()
}
s.Close()
}
if meta.Option.Has(OptionData) {
@ -397,7 +425,7 @@ func (w *ServerWorker) run(ctx context.Context) {
err := w.handleFrame(ctx, reader)
if err != nil {
if errors.Cause(err) != io.EOF {
newError("unexpected EOF").Base(err).WriteToLog()
newError("unexpected EOF").Base(err).WithContext(ctx).WriteToLog()
input.CloseError()
}
return

View File

@ -17,13 +17,13 @@ func ReadMetadata(reader io.Reader) (*FrameMetadata, error) {
return nil, newError("invalid metalen ", metaLen).AtError()
}
b := buf.New()
b := buf.NewSize(int32(metaLen))
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 ReadFrameFrom(b.Bytes())
return ReadFrameFrom(b)
}
// 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
}
var b *buf.Buffer
if size <= buf.Size {
b = buf.New()
} else {
b = buf.NewLocal(int(size))
}
if err := b.AppendSupplier(buf.ReadFullFrom(r.reader, int(size))); err != nil {
b := buf.NewSize(int32(size))
if err := b.Reset(buf.ReadFullFrom(r.reader, int32(size))); err != nil {
b.Release()
return nil, err
}
@ -68,7 +63,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
// StreamReader reads Mux frame as a stream.
type StreamReader struct {
reader *buf.BufferedReader
leftOver int
leftOver int32
}
// NewStreamReader creates a new StreamReader.
@ -91,7 +86,7 @@ func (r *StreamReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
if err != nil {
return nil, err
}
r.leftOver = int(size)
r.leftOver = int32(size)
}
mb, err := r.reader.ReadAtMost(r.leftOver)

View File

@ -13,6 +13,7 @@ type Writer struct {
writer buf.Writer
id uint16
followup bool
hasError bool
transferType protocol.TransferType
}
@ -40,6 +41,7 @@ func (w *Writer) getNextFrameMeta() FrameMetadata {
SessionID: w.id,
Target: w.dest,
}
if w.followup {
meta.SessionStatus = SessionStatusKeep
} else {
@ -53,7 +55,7 @@ func (w *Writer) getNextFrameMeta() FrameMetadata {
func (w *Writer) writeMetaOnly() error {
meta := w.getNextFrameMeta()
b := buf.New()
if err := b.Reset(meta.AsSupplier()); err != nil {
if err := meta.WriteTo(b); err != nil {
return err
}
return w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(b))
@ -64,14 +66,14 @@ func (w *Writer) writeData(mb buf.MultiBuffer) error {
meta.Option.Set(OptionData)
frame := buf.New()
if err := frame.Reset(meta.AsSupplier()); err != nil {
if err := meta.WriteTo(frame); err != nil {
return err
}
if err := frame.AppendSupplier(serial.WriteUint16(uint16(mb.Len()))); err != nil {
return err
}
mb2 := buf.NewMultiBufferCap(len(mb) + 1)
mb2 := buf.NewMultiBufferCap(int32(len(mb)) + 1)
mb2.Append(frame)
mb2.AppendMulti(mb)
return w.writer.WriteMultiBuffer(mb2)
@ -105,9 +107,12 @@ func (w *Writer) Close() error {
SessionID: w.id,
SessionStatus: SessionStatusEnd,
}
if w.hasError {
meta.Option.Set(OptionError)
}
frame := buf.New()
common.Must(frame.Reset(meta.AsSupplier()))
common.Must(meta.WriteTo(frame))
w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(frame))
return nil

View File

@ -2,13 +2,11 @@ package outbound
import (
"context"
"io"
"v2ray.com/core"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/app/proxyman/mux"
"v2ray.com/core/common"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/net"
"v2ray.com/core/proxy"
"v2ray.com/core/transport/internet"
@ -24,10 +22,7 @@ type Handler struct {
}
func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (core.OutboundHandler, error) {
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context")
}
v := core.MustFromContext(ctx)
h := &Handler{
config: config,
outboundManager: v.OutboundHandlerManager(),
@ -85,14 +80,14 @@ func (h *Handler) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) {
if h.mux != nil {
err := h.mux.Dispatch(ctx, outboundRay)
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()
}
} else {
err := h.proxy.Process(ctx, outboundRay, h)
// Ensure outbound ray is properly closed.
if err != nil && errors.Cause(err) != io.EOF {
newError("failed to process outbound traffic").Base(err).WriteToLog()
if err != nil {
newError("failed to process outbound traffic").Base(err).WithContext(ctx).WriteToLog()
outboundRay.OutboundOutput().CloseError()
} else {
outboundRay.OutboundOutput().Close()
@ -108,14 +103,14 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn
tag := h.senderSettings.ProxySettings.Tag
handler := h.outboundManager.GetHandler(tag)
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)
stream := ray.NewRay(ctx)
stream := ray.New(ctx)
go handler.Dispatch(ctx, stream)
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 {
@ -140,7 +135,7 @@ func (h *Handler) Start() error {
return nil
}
// Close implements common.Runnable.
// Close implements common.Closable.
func (h *Handler) Close() error {
common.Close(h.mux)
return nil

View File

@ -25,10 +25,7 @@ func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error)
m := &Manager{
taggedHandler: make(map[string]core.OutboundHandler),
}
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context")
}
v := core.MustFromContext(ctx)
if err := v.RegisterFeature((*core.OutboundHandlerManager)(nil), m); err != nil {
return nil, newError("unable to register OutboundHandlerManager").Base(err)
}

View File

@ -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
//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)
}
func ProtocoSniffersFromContext(ctx context.Context) []KnownProtocols {
func ProtocolSniffersFromContext(ctx context.Context) []KnownProtocols {
if list, ok := ctx.Value(protocolsKey).([]KnownProtocols); ok {
return list
}

View File

@ -11,18 +11,16 @@ import (
"v2ray.com/core/proxy"
)
// Router is an implementation of core.Router.
type Router struct {
domainStrategy Config_DomainStrategy
rules []Rule
dns core.DNSClient
}
// NewRouter creates a new Router based on the given config.
func NewRouter(ctx context.Context, config *Config) (*Router, error) {
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context")
}
v := core.MustFromContext(ctx)
r := &Router{
domainStrategy: config.DomainStrategy,
rules: make([]Rule, len(config.Rule)),
@ -72,6 +70,7 @@ func (r *ipResolver) Resolve() []net.Address {
return r.ip
}
// PickRoute implements core.Router.
func (r *Router) PickRoute(ctx context.Context) (string, error) {
resolver := &ipResolver{
dns: r.dns,
@ -110,10 +109,12 @@ func (r *Router) PickRoute(ctx context.Context) (string, error) {
return "", core.ErrNoClue
}
// Start implements common.Runnable.
func (*Router) Start() error {
return nil
}
// Close implements common.Closable.
func (*Router) Close() error {
return nil
}

View 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
}))
}

View 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,
}

View 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 {}

View 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
View 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
View 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
View 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 {
}

View 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
View 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
View 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))
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -1,4 +1,3 @@
// Package buf provides a light-weight memory allocation mechanism.
package buf
import (
@ -12,11 +11,10 @@ type Supplier func([]byte) (int, error)
// the buffer into an internal buffer pool, in order to recreate a buffer more
// quickly.
type Buffer struct {
v []byte
pool Pool
v []byte
start int
end int
start int32
end int32
}
// Release recycles the buffer into an internal buffer pool.
@ -24,11 +22,8 @@ func (b *Buffer) Release() {
if b == nil || b.v == nil {
return
}
if b.pool != nil {
b.pool.Free(b)
}
freeBytes(b.v)
b.v = nil
b.pool = nil
b.start = 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.
func (b *Buffer) Append(data []byte) int {
nBytes := copy(b.v[b.end:], data)
b.end += nBytes
b.end += int32(nBytes)
return nBytes
}
// AppendSupplier appends the content of a BytesWriter to the buffer.
func (b *Buffer) AppendSupplier(writer Supplier) error {
nBytes, err := writer(b.v[b.end:])
b.end += nBytes
b.end += int32(nBytes)
return err
}
// 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]
}
// 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
}
@ -78,12 +73,12 @@ func (b *Buffer) Bytes() []byte {
func (b *Buffer) Reset(writer Supplier) error {
nBytes, err := writer(b.v)
b.start = 0
b.end = nBytes
b.end = int32(nBytes)
return err
}
// BytesRange returns a slice of this buffer with given from and to bounary.
func (b *Buffer) BytesRange(from, to int) []byte {
// BytesRange returns a slice of this buffer with given from and to boundary.
func (b *Buffer) BytesRange(from, to int32) []byte {
if from < 0 {
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.
func (b *Buffer) BytesFrom(from int) []byte {
func (b *Buffer) BytesFrom(from int32) []byte {
if from < 0 {
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.
func (b *Buffer) BytesTo(to int) []byte {
func (b *Buffer) BytesTo(to int32) []byte {
if to < 0 {
to += b.Len()
}
@ -110,7 +105,7 @@ func (b *Buffer) BytesTo(to int) []byte {
}
// 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 {
from += b.Len()
}
@ -125,7 +120,7 @@ func (b *Buffer) Slice(from, to int) {
}
// SliceFrom cuts the buffer at the given position.
func (b *Buffer) SliceFrom(from int) {
func (b *Buffer) SliceFrom(from int32) {
if from < 0 {
from += b.Len()
}
@ -133,7 +128,7 @@ func (b *Buffer) SliceFrom(from int) {
}
// Len returns the length of the buffer content.
func (b *Buffer) Len() int {
func (b *Buffer) Len() int32 {
if b == nil {
return 0
}
@ -147,13 +142,13 @@ func (b *Buffer) IsEmpty() bool {
// IsFull returns true if the buffer has no more room to grow.
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.
func (b *Buffer) Write(data []byte) (int, error) {
nBytes := copy(b.v[b.end:], data)
b.end += nBytes
b.end += int32(nBytes)
return nBytes, nil
}
@ -163,10 +158,10 @@ func (b *Buffer) Read(data []byte) (int, error) {
return 0, io.EOF
}
nBytes := copy(data, b.v[b.start:b.end])
if nBytes == b.Len() {
if int32(nBytes) == b.Len() {
b.Clear()
} else {
b.start += nBytes
b.start += int32(nBytes)
}
return nBytes, nil
}
@ -176,15 +171,16 @@ func (b *Buffer) String() string {
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 {
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{
v: make([]byte, size),
pool: nil,
v: pool[0].Get().([]byte),
}
}
// 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),
}
}

View File

@ -4,49 +4,60 @@ import (
"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 (
// Size of a regular buffer.
Size = 2 * 1024
)
var (
mediumPool Pool = NewSyncPool(Size)
func createAllocFunc(size int32) func() interface{} {
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
}
}
}

View File

@ -1,7 +1,6 @@
package buf_test
import (
"crypto/rand"
"testing"
. "v2ray.com/core/common/buf"
@ -17,10 +16,10 @@ func TestBufferClear(t *testing.T) {
payload := "Bytes"
buffer.Append([]byte(payload))
assert(buffer.Len(), Equals, len(payload))
assert(buffer.Len(), Equals, int32(len(payload)))
buffer.Clear()
assert(buffer.Len(), Equals, 0)
assert(buffer.Len(), Equals, int32(0))
}
func TestBufferIsEmpty(t *testing.T) {
@ -42,32 +41,6 @@ func TestBufferString(t *testing.T) {
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) {
for i := 0; i < b.N; i++ {
buffer := New()
@ -77,7 +50,7 @@ func BenchmarkNewBuffer(b *testing.B) {
func BenchmarkNewLocalBuffer(b *testing.B) {
for i := 0; i < b.N; i++ {
buffer := NewLocal(Size)
buffer := NewSize(Size)
buffer.Release()
}
}

View File

@ -36,6 +36,7 @@ func (h *copyHandler) writeTo(writer Writer, mb MultiBuffer) error {
return err
}
// SizeCounter is for counting bytes copied by Copy().
type SizeCounter struct {
Size int64
}
@ -91,7 +92,9 @@ func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
buffer.Release()
return werr
}
} else if err != nil {
}
if err != nil {
return err
}
}

View File

@ -33,7 +33,7 @@ func ReadFrom(reader io.Reader) Supplier {
}
// 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 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 {
return &seqWriter{
writer: writer,

View File

@ -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.
func ReadAllToBytes(reader io.Reader) ([]byte, error) {
mb, err := ReadAllToMultiBuffer(reader)
@ -46,7 +66,7 @@ func ReadAllToBytes(reader io.Reader) ([]byte, error) {
type MultiBuffer []*Buffer
// NewMultiBufferCap creates a new MultiBuffer instance.
func NewMultiBufferCap(capacity int) MultiBuffer {
func NewMultiBufferCap(capacity int32) MultiBuffer {
return MultiBuffer(make([]*Buffer, 0, capacity))
}
@ -57,7 +77,9 @@ func NewMultiBufferValue(b ...*Buffer) MultiBuffer {
// Append appends buffer to the end of this MultiBuffer
func (mb *MultiBuffer) Append(buf *Buffer) {
*mb = append(*mb, buf)
if buf != nil {
*mb = append(*mb, buf)
}
}
// 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 {
nBytes := copy(b[total:], bb.Bytes())
total += nBytes
if nBytes < bb.Len() {
if int32(nBytes) < bb.Len() {
break
}
}
@ -115,8 +137,8 @@ func (mb *MultiBuffer) Write(b []byte) {
}
// Len returns the total number of bytes in the MultiBuffer.
func (mb MultiBuffer) Len() int {
size := 0
func (mb MultiBuffer) Len() int32 {
size := int32(0)
for _, b := range mb {
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.
func (mb *MultiBuffer) SliceBySize(size int) MultiBuffer {
func (mb *MultiBuffer) SliceBySize(size int32) MultiBuffer {
slice := NewMultiBufferCap(10)
sliceSize := 0
sliceSize := int32(0)
endIndex := len(*mb)
for i, b := range *mb {
if b.Len()+sliceSize > size {
@ -167,7 +189,7 @@ func (mb *MultiBuffer) SliceBySize(size int) MultiBuffer {
}
*mb = (*mb)[endIndex:]
if endIndex == 0 && len(*mb) > 0 {
b := New()
b := NewSize(size)
common.Must(b.Reset(ReadFullFrom((*mb)[0], size)))
return NewMultiBufferValue(b)
}

View File

@ -1,8 +1,10 @@
package buf_test
import (
"crypto/rand"
"testing"
"v2ray.com/core/common"
. "v2ray.com/core/common/buf"
. "v2ray.com/ext/assert"
)
@ -31,5 +33,18 @@ func TestMultiBufferAppend(t *testing.T) {
b := New()
b.AppendBytes('a', '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))
}

View File

@ -12,26 +12,35 @@ type BytesToBufferReader struct {
buffer []byte
}
// NewBytesToBufferReader returns a new BytesToBufferReader.
func NewBytesToBufferReader(reader io.Reader) Reader {
return &BytesToBufferReader{
Reader: reader,
}
}
const mediumSize = 8 * 1024
const largeSize = 64 * 1024
func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) {
b := New()
err := b.Reset(ReadFrom(r.Reader))
if b.IsFull() {
r.buffer = make([]byte, mediumSize)
for i := 0; i < 64; i++ {
err := b.Reset(ReadFrom(r.Reader))
if b.IsFull() {
r.buffer = newBytes(Size + 1)
}
if !b.IsEmpty() {
return NewMultiBufferValue(b), nil
}
if err != nil {
b.Release()
return nil, err
}
}
if !b.IsEmpty() {
return NewMultiBufferValue(b), nil
}
b.Release()
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.
@ -42,29 +51,35 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) {
nBytes, err := r.Reader.Read(r.buffer)
if nBytes > 0 {
mb := NewMultiBufferCap(nBytes/Size + 1)
mb := NewMultiBufferCap(int32(nBytes/Size) + 1)
mb.Write(r.buffer[:nBytes])
if nBytes == len(r.buffer) && len(r.buffer) == mediumSize {
r.buffer = make([]byte, largeSize)
if nBytes == len(r.buffer) && nBytes < int(largeSize) {
freeBytes(r.buffer)
r.buffer = newBytes(int32(nBytes) + 1)
} else if nBytes < Size {
r.freeBuffer()
}
return mb, nil
}
return nil, err
r.freeBuffer()
if err != nil {
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 (
_ Reader = (*BufferedReader)(nil)
_ io.Reader = (*BufferedReader)(nil)
_ io.ByteReader = (*BufferedReader)(nil)
_ io.WriterTo = (*BufferedReader)(nil)
)
// BufferedReader is a Reader that keeps its internal buffer.
type BufferedReader struct {
stream Reader
leftOver MultiBuffer
buffered bool
}
// NewBufferedReader returns a new BufferedReader.
func NewBufferedReader(reader Reader) *BufferedReader {
return &BufferedReader{
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) {
r.buffered = f
}
// IsBuffered returns true if internal buffer is used.
func (r *BufferedReader) IsBuffered() bool {
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) {
var b [1]byte
_, err := r.Read(b[:])
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) {
if r.leftOver != nil {
nBytes, _ := r.leftOver.Read(b)
@ -113,6 +137,7 @@ func (r *BufferedReader) Read(b []byte) (int, error) {
return 0, err
}
// ReadMultiBuffer implements Reader.
func (r *BufferedReader) ReadMultiBuffer() (MultiBuffer, error) {
if r.leftOver != nil {
mb := r.leftOver
@ -124,7 +149,7 @@ func (r *BufferedReader) ReadMultiBuffer() (MultiBuffer, error) {
}
// 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 {
mb, err := r.stream.ReadMultiBuffer()
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 {
return 0, err
}
r.leftOver = nil
}
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) {
nBytes, err := r.writeToInternal(writer)
if errors.Cause(err) == io.EOF {

View File

@ -17,15 +17,23 @@ func TestAdaptiveReader(t *testing.T) {
reader := NewReader(bytes.NewReader(make([]byte, 1024*1024)))
b, err := reader.ReadMultiBuffer()
assert(err, IsNil)
assert(b.Len(), Equals, 2*1024)
assert(b.Len(), Equals, int32(2*1024))
b, err = reader.ReadMultiBuffer()
assert(err, IsNil)
assert(b.Len(), Equals, 8*1024)
assert(b.Len(), Equals, int32(8*1024))
b, err = reader.ReadMultiBuffer()
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) {

View File

@ -11,6 +11,7 @@ type BufferToBytesWriter struct {
io.Writer
}
// NewBufferToBytesWriter returns a new BufferToBytesWriter.
func NewBufferToBytesWriter(writer io.Writer) *BufferToBytesWriter {
return &BufferToBytesWriter{
Writer: writer,
@ -22,7 +23,7 @@ func (w *BufferToBytesWriter) WriteMultiBuffer(mb MultiBuffer) error {
defer mb.Release()
bs := mb.ToNetBuffers()
_, err := bs.WriteTo(w)
_, err := bs.WriteTo(w.Writer)
return err
}
@ -49,6 +50,7 @@ func NewBufferedWriter(writer Writer) *BufferedWriter {
}
}
// WriteByte implements io.ByteWriter.
func (w *BufferedWriter) WriteByte(c byte) error {
_, err := w.Write([]byte{c})
return err
@ -121,6 +123,7 @@ func (w *BufferedWriter) Flush() error {
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 {
w.buffered = f
if !f {

View File

@ -47,7 +47,7 @@ func TestBytesWriterReadFrom(t *testing.T) {
mb, err := cache.ReadMultiBuffer()
assert(err, IsNil)
assert(mb.Len(), Equals, size)
assert(mb.Len(), Equals, int32(size))
}
func TestDiscardBytes(t *testing.T) {

View File

@ -11,9 +11,8 @@ func Must(err error) {
}
}
// Must2 panics if the second parameter is not nil.
func Must2(v interface{}, err error) {
if err != nil {
panic(err)
}
// Must2 panics if the second parameter is not nil, otherwise returns the first parameter.
func Must2(v interface{}, err error) interface{} {
Must(err)
return v
}

View File

@ -93,6 +93,7 @@ type AuthenticationReader struct {
reader *buf.BufferedReader
sizeParser ChunkSizeDecoder
transferType protocol.TransferType
size int32
}
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)),
sizeParser: sizeParser,
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())
_, err := io.ReadFull(r.reader, sizeBytes)
if err != nil {
return 0, err
}
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()
if err != nil {
return nil, err
}
if size == r.auth.Overhead() {
if size == -2 || size == int32(r.auth.Overhead()) {
r.size = -2
return nil, io.EOF
}
var b *buf.Buffer
if size <= buf.Size {
b = buf.New()
} else {
b = buf.NewLocal(size)
if soft && size > r.reader.BufferedBytes() {
r.size = size
return nil, errSoft
}
b := buf.NewSize(size)
if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil {
b.Release()
return nil, err
@ -140,8 +154,33 @@ func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
b.Release()
return nil, err
}
b.Slice(0, len(rb))
return buf.NewMultiBufferValue(b), nil
b.Slice(0, int32(len(rb)))
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 {
@ -161,12 +200,12 @@ func NewAuthenticationWriter(auth Authenticator, sizeParser ChunkSizeEncoder, wr
}
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()
common.Must(eb.Reset(func(bb []byte) (int, error) {
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) {
_, 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 {
defer mb.Release()
payloadSize := buf.Size - w.auth.Overhead() - w.sizeParser.SizeBytes()
mb2Write := buf.NewMultiBufferCap(len(mb) + 10)
payloadSize := buf.Size - int32(w.auth.Overhead()) - w.sizeParser.SizeBytes()
mb2Write := buf.NewMultiBufferCap(int32(len(mb) + 10))
for {
b := buf.New()
@ -209,12 +248,20 @@ func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error {
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()
if b == nil {
b = buf.New()
continue
}
eb, err := w.seal(b)
b.Release()
@ -223,9 +270,6 @@ func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error {
return err
}
mb2Write.Append(eb)
if mb.IsEmpty() {
break
}
}
return w.writer.WriteMultiBuffer(mb2Write)

View File

@ -24,13 +24,15 @@ func TestAuthenticationReaderWriter(t *testing.T) {
aead, err := cipher.NewGCM(block)
assert(err, IsNil)
rawPayload := make([]byte, 8192*10)
const payloadSize = 1024 * 80
rawPayload := make([]byte, payloadSize)
rand.Read(rawPayload)
payload := buf.NewLocal(8192 * 10)
payload := buf.NewSize(payloadSize)
payload.Append(rawPayload)
assert(payload.Len(), Equals, int32(payloadSize))
cache := buf.NewLocal(160 * 1024)
cache := buf.NewSize(160 * 1024)
iv := make([]byte, 12)
rand.Read(iv)
@ -43,9 +45,8 @@ func TestAuthenticationReaderWriter(t *testing.T) {
}, PlainChunkSizeParser{}, cache, protocol.TransferTypeStream)
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(err, IsNil)
reader := NewAuthenticationReader(&AEADAuthenticator{
AEAD: aead,
@ -57,14 +58,16 @@ func TestAuthenticationReaderWriter(t *testing.T) {
var mb buf.MultiBuffer
for mb.Len() < len(rawPayload) {
for mb.Len() < payloadSize {
mb2, err := reader.ReadMultiBuffer()
assert(err, IsNil)
mb.AppendMulti(mb2)
}
mbContent := make([]byte, 8192*10)
assert(mb.Len(), Equals, int32(payloadSize))
mbContent := make([]byte, payloadSize)
mb.Read(mbContent)
assert(mbContent, Equals, rawPayload)
@ -83,7 +86,7 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) {
aead, err := cipher.NewGCM(block)
assert(err, IsNil)
cache := buf.NewLocal(1024)
cache := buf.NewSize(1024)
iv := make([]byte, 12)
rand.Read(iv)
@ -105,7 +108,7 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) {
payload.Append(pb2)
assert(writer.WriteMultiBuffer(payload), IsNil)
assert(cache.Len(), GreaterThan, 0)
assert(cache.Len(), GreaterThan, int32(0))
assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil)
assert(err, IsNil)
@ -122,12 +125,10 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) {
b1 := mb.SplitFirst()
assert(b1.String(), Equals, "abcd")
assert(mb.IsEmpty(), IsTrue)
mb, err = reader.ReadMultiBuffer()
assert(err, IsNil)
b2 := mb.SplitFirst()
assert(b2.String(), Equals, "efgh")
assert(mb.IsEmpty(), IsTrue)
_, err = reader.ReadMultiBuffer()

20
common/crypto/chunk.go Normal file → Executable file
View File

@ -8,21 +8,21 @@ import (
"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 {
SizeBytes() int
SizeBytes() int32
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 {
SizeBytes() int
SizeBytes() int32
Encode(uint16, []byte) []byte
}
type PlainChunkSizeParser struct{}
func (PlainChunkSizeParser) SizeBytes() int {
func (PlainChunkSizeParser) SizeBytes() int32 {
return 2
}
@ -38,8 +38,8 @@ type AEADChunkSizeParser struct {
Auth *AEADAuthenticator
}
func (p *AEADChunkSizeParser) SizeBytes() int {
return 2 + p.Auth.Overhead()
func (p *AEADChunkSizeParser) SizeBytes() int32 {
return 2 + int32(p.Auth.Overhead())
}
func (p *AEADChunkSizeParser) Encode(size uint16, b []byte) []byte {
@ -62,7 +62,7 @@ type ChunkStreamReader struct {
reader *buf.BufferedReader
buffer []byte
leftOverSize int
leftOverSize int32
}
func NewChunkStreamReader(sizeDecoder ChunkSizeDecoder, reader io.Reader) *ChunkStreamReader {
@ -90,7 +90,7 @@ func (r *ChunkStreamReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
if nextSize == 0 {
return nil, io.EOF
}
size = int(nextSize)
size = int32(nextSize)
}
r.leftOverSize = size
@ -125,7 +125,7 @@ func (w *ChunkStreamWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
b := buf.New()
common.Must(b.Reset(func(buffer []byte) (int, error) {
w.sizeEncoder.Encode(uint16(slice.Len()), buffer[:0])
return w.sizeEncoder.SizeBytes(), nil
return int(w.sizeEncoder.SizeBytes()), nil
}))
mb2Write.Append(b)
mb2Write.AppendMulti(slice)

View File

@ -12,7 +12,7 @@ import (
func TestChunkStreamIO(t *testing.T) {
assert := With(t)
cache := buf.NewLocal(8192)
cache := buf.NewSize(8192)
writer := NewChunkStreamWriter(PlainChunkSizeParser{}, cache)
reader := NewChunkStreamReader(PlainChunkSizeParser{}, cache)
@ -27,16 +27,16 @@ func TestChunkStreamIO(t *testing.T) {
assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil)
assert(cache.Len(), Equals, 13)
assert(cache.Len(), Equals, int32(13))
mb, err := reader.ReadMultiBuffer()
assert(err, IsNil)
assert(mb.Len(), Equals, 4)
assert(mb.Len(), Equals, int32(4))
assert(mb[0].Bytes(), Equals, []byte("abcd"))
mb, err = reader.ReadMultiBuffer()
assert(err, IsNil)
assert(mb.Len(), Equals, 3)
assert(mb.Len(), Equals, int32(3))
assert(mb[0].Bytes(), Equals, []byte("efg"))
_, err = reader.ReadMultiBuffer()

View File

@ -1,4 +1,4 @@
// 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

View File

@ -1,6 +1,6 @@
// Package dice contains common functions to generate random number.
// It also initialize math/rand with the time in seconds at launch time.
package dice
package dice // import "v2ray.com/core/common/dice"
import (
"math/rand"

32
common/dice/dice_test.go Normal file
View 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)
}
}

View File

@ -1,11 +1,13 @@
// Package errors is a drop-in replacement for Golang lib 'errors'.
package errors
package errors // import "v2ray.com/core/common/errors"
import (
"context"
"strings"
"v2ray.com/core/common/log"
"v2ray.com/core/common/serial"
"v2ray.com/core/common/session"
)
type hasInnerError interface {
@ -17,12 +19,17 @@ type hasSeverity interface {
Severity() log.Severity
}
type hasContext interface {
Context() context.Context
}
// Error is an error object with underlying error.
type Error struct {
message []interface{}
inner error
severity log.Severity
path []string
ctx context.Context
}
// Error implements error.Error().
@ -50,6 +57,27 @@ func (v *Error) Base(err error) *Error {
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 {
v.severity = s
return v
@ -103,9 +131,21 @@ func (v *Error) String() string {
// WriteToLog writes current error into log.
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{
Severity: GetSeverity(v),
Content: v,
Content: c,
})
}
@ -139,3 +179,12 @@ func GetSeverity(err error) log.Severity {
}
return log.Severity_Info
}
type sessionLog struct {
id session.ID
content interface{}
}
func (s sessionLog) String() string {
return serial.Concat("[", s.id, "] ", s.content)
}

View File

@ -21,3 +21,22 @@ type Runnable interface {
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
}

View File

@ -1,4 +1,4 @@
package log
package log // import "v2ray.com/core/common/log"
import (
"sync"
@ -24,7 +24,7 @@ type GeneralMessage struct {
// String implements Message.
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.

View File

@ -28,5 +28,5 @@ func TestLogRecord(t *testing.T) {
Content: net.ParseAddress(ip),
})
assert(logger.value, Equals, "[Error]: "+ip)
assert(logger.value, Equals, "[Error] "+ip)
}

View File

@ -23,6 +23,7 @@ type generalLogger struct {
creator WriterCreator
buffer chan Message
access *signal.Semaphore
done *signal.Done
}
// NewLogger returns a generic log handler that can handle all type of messages.
@ -31,6 +32,7 @@ func NewLogger(logWriterCreator WriterCreator) Handler {
creator: logWriterCreator,
buffer: make(chan Message, 16),
access: signal.NewSemaphore(1),
done: signal.NewDone(),
}
}
@ -49,6 +51,8 @@ func (l *generalLogger) run() {
for {
select {
case <-l.done.C():
return
case msg := <-l.buffer:
logger.Write(msg.String() + platform.LineSeparator())
dataWritten = true
@ -74,6 +78,10 @@ func (l *generalLogger) Handle(msg Message) {
}
}
func (l *generalLogger) Close() error {
return l.done.Close()
}
type consoleLogWriter struct {
logger *log.Logger
}

40
common/log/logger_test.go Normal file
View 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())
}

View File

@ -22,7 +22,7 @@ var (
)
// AddressFamily is the type of address.
type AddressFamily int
type AddressFamily byte
const (
// AddressFamilyIPv4 represents address as IPv4

View File

@ -6,9 +6,9 @@ import (
// Destination represents a network destination including address and protocol (tcp / udp).
type Destination struct {
Network Network
Port Port
Address Address
Port Port
Network Network
}
// DestinationFromAddr generates a Destination from a net address.
@ -56,7 +56,7 @@ func (d Destination) IsValid() bool {
return d.Network != Network_Unknown
}
// AsDestination converts current Enpoint into Destination.
// AsDestination converts current Endpoint into Destination.
func (p *Endpoint) AsDestination() Destination {
return Destination{
Network: p.Network,

View File

@ -1,4 +1,4 @@
// 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

View File

@ -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.
func (l NetworkList) HasNetwork(network Network) bool {
for _, value := range l.Network {

View File

@ -1,4 +1,4 @@
package platform
package platform // import "v2ray.com/core/common/platform"
import (
"os"
@ -53,7 +53,7 @@ func getExecutableDir() string {
return filepath.Dir(exec)
}
func getExecuableSubDir(dir string) func() string {
func getExecutableSubDir(dir string) func() string {
return func() string {
return filepath.Join(getExecutableDir(), dir)
}
@ -67,7 +67,7 @@ func GetAssetLocation(file string) string {
func GetPluginDirectory() string {
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
}

View File

@ -1,4 +1,4 @@
package predicate
package predicate // import "v2ray.com/core/common/predicate"
type Predicate func() bool

2
common/protocol/account.go Normal file → Executable file
View File

@ -1,6 +1,6 @@
package protocol
// Account is an user identity used for authentication.
// Account is a user identity used for authentication.
type Account interface {
Equals(Account) bool
}

204
common/protocol/address.go Normal file
View 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
}

View 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
View File

@ -10,12 +10,12 @@ const (
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 {
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 {
v := ctx.Value(userKey)
if v == nil {

View File

@ -38,24 +38,11 @@ const (
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 {
Version byte
Command RequestCommand
Option bitmask.Byte
Security Security
Security SecurityType
Port net.Port
Address net.Address
User *User
@ -88,16 +75,16 @@ type CommandSwitchAccount struct {
ValidMin byte
}
func (sc *SecurityConfig) AsSecurity() Security {
func (sc *SecurityConfig) GetSecurityType() SecurityType {
if sc == nil || sc.Type == SecurityType_AUTO {
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
}

2
common/protocol/id.go Normal file → Executable file
View File

@ -19,7 +19,7 @@ func DefaultIDHash(key []byte) hash.Hash {
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 {
uuid uuid.UUID
cmdKey [IDBytesLen]byte

View File

@ -1,6 +1,6 @@
package protocol
type TransferType int
type TransferType byte
const (
TransferTypeStream TransferType = 0

View File

@ -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

View File

@ -110,10 +110,10 @@ func (s *ServerSpec) PickUser() *User {
}
}
func (v *ServerSpec) IsValid() bool {
return v.valid.IsValid()
func (s *ServerSpec) IsValid() bool {
return s.valid.IsValid()
}
func (v *ServerSpec) Invalidate() {
v.valid.Invalidate()
func (s *ServerSpec) Invalidate() {
s.valid.Invalidate()
}

View File

@ -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

4
common/serial/bytes.go Normal file → Executable file
View File

@ -7,12 +7,12 @@ func ByteToHexString(value byte) string {
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 {
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 {
return uint32(value[0])<<24 |
uint32(value[1])<<16 |

2
common/serial/numbers.go Normal file → Executable file
View File

@ -3,7 +3,7 @@ package serial
import "strconv"
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 {
return append(b, byte(value>>8), byte(value))
}

View 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
View 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
View File

@ -4,7 +4,7 @@ import (
"sync"
)
// Done is an utility for notifications of something being done.
// Done is a utility for notifications of something being done.
type Done struct {
access sync.Mutex
c chan struct{}
@ -38,7 +38,7 @@ func (d *Done) Wait() {
<-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 {
d.access.Lock()
defer d.access.Unlock()

View File

@ -12,7 +12,7 @@ func executeAndFulfill(f func() error, done chan<- error) {
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 {
done := make(chan error, 1)
go executeAndFulfill(f, done)

2
common/signal/notifier.go Normal file → Executable file
View File

@ -1,6 +1,6 @@
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 {
c chan struct{}
}

View File

@ -50,7 +50,7 @@ func (t *PeriodicTask) Start() error {
return nil
}
// Close implements common.Runnable.
// Close implements common.Closable.
func (t *PeriodicTask) Close() error {
t.access.Lock()
defer t.access.Unlock()

14
common/uuid/uuid.go Normal file → Executable file
View File

@ -1,4 +1,4 @@
package uuid
package uuid // import "v2ray.com/core/common/uuid"
import (
"bytes"
@ -49,26 +49,26 @@ func (u *UUID) Equals(another *UUID) bool {
// Next generates a deterministic random UUID based on this UUID.
func (u *UUID) Next() UUID {
md5hash := md5.New()
md5hash.Write(u.Bytes())
md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81"))
common.Must2(md5hash.Write(u.Bytes()))
common.Must2(md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81")))
var newid UUID
for {
md5hash.Sum(newid[:0])
if !newid.Equals(u) {
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 {
var uuid UUID
common.Must2(rand.Read(uuid.Bytes()))
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) {
var uuid UUID
if len(b) != 16 {
@ -78,7 +78,7 @@ func ParseBytes(b []byte) (UUID, error) {
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) {
var uuid UUID

88
config.go Executable file
View 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,
}))
}

View File

@ -17,35 +17,13 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// Configuration serialization format.
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.
// Config is the master config of V2Ray. V2Ray takes this config as input and functions accordingly.
type Config struct {
// Inbound handler configurations. Must have at least one item.
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 []*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"`
// Transport settings.
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
}
// InboundHandlerConfig is the configuration for inbound handler.
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"`
// 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"`
// 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"`
@ -129,10 +108,11 @@ func (m *InboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.Type
return nil
}
// OutboundHandlerConfig is the configuration for outbound handler.
type OutboundHandlerConfig struct {
// Tag of this outbound handler.
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"`
// 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"`
@ -186,39 +166,36 @@ func init() {
proto.RegisterType((*Config)(nil), "v2ray.core.Config")
proto.RegisterType((*InboundHandlerConfig)(nil), "v2ray.core.InboundHandlerConfig")
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) }
var fileDescriptor0 = []byte{
// 436 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x93, 0x41, 0x6f, 0xd3, 0x30,
0x14, 0xc7, 0x71, 0x13, 0xba, 0xf6, 0x6d, 0x94, 0x60, 0x01, 0xb2, 0x06, 0x87, 0x50, 0x69, 0x53,
0xc5, 0xc1, 0x45, 0xe5, 0x82, 0x26, 0x71, 0x61, 0x08, 0xc1, 0xa4, 0xd1, 0x29, 0x45, 0x1c, 0xb8,
0x4c, 0x6e, 0xfa, 0x56, 0x45, 0x5a, 0xec, 0xc8, 0x76, 0xa7, 0xe6, 0x2b, 0xf1, 0x3d, 0xb8, 0xf1,
0x8d, 0xb8, 0xa0, 0xc4, 0x49, 0x93, 0x41, 0x0f, 0x14, 0x69, 0xa7, 0xc4, 0x79, 0xfe, 0xfd, 0xdf,
0xfb, 0x25, 0x31, 0x3c, 0xbb, 0x99, 0x68, 0x91, 0xf3, 0x58, 0xa5, 0xe3, 0x58, 0x69, 0x1c, 0xc7,
0x4a, 0x5e, 0x25, 0x4b, 0x9e, 0x69, 0x65, 0x15, 0x85, 0xba, 0xa8, 0xf1, 0xf0, 0xd5, 0x5f, 0x1b,
0xd3, 0x54, 0xc9, 0xb1, 0x41, 0x9d, 0x88, 0xeb, 0xb1, 0xcd, 0x33, 0x5c, 0x5c, 0xa6, 0x68, 0x8c,
0x58, 0xa2, 0xa3, 0x0f, 0x8f, 0xfe, 0x20, 0xac, 0x16, 0xd2, 0x64, 0x4a, 0xdb, 0x5b, 0x4d, 0x86,
0x3f, 0x3a, 0xd0, 0x3d, 0x2d, 0x1f, 0xd0, 0x13, 0xd8, 0x4b, 0xe4, 0x5c, 0xad, 0xe4, 0x82, 0x91,
0xd0, 0x1b, 0xed, 0x4f, 0x42, 0xde, 0x4c, 0xc0, 0x3f, 0xb9, 0xd2, 0x47, 0x21, 0x17, 0xd7, 0xa8,
0x1d, 0x12, 0xd5, 0x00, 0x7d, 0x0b, 0x3d, 0xb5, 0xb2, 0x0e, 0xee, 0x94, 0xf0, 0x8b, 0x36, 0x3c,
0xad, 0x6a, 0xb7, 0xe9, 0x0d, 0x42, 0xdf, 0x80, 0x27, 0xb2, 0x8c, 0xf9, 0x25, 0x79, 0xdc, 0x26,
0x9d, 0x28, 0x77, 0xa2, 0xfc, 0x4b, 0x21, 0x7a, 0xee, 0x3c, 0xa3, 0x02, 0xa1, 0x27, 0xd0, 0xdf,
0x98, 0xb1, 0xfb, 0x21, 0x19, 0xed, 0x4f, 0x9e, 0xb7, 0xf9, 0x4d, 0x91, 0x57, 0x4d, 0x9b, 0xed,
0xf4, 0x3d, 0xf4, 0x71, 0x6d, 0x51, 0x9a, 0x44, 0x49, 0xd6, 0xdd, 0xa9, 0x77, 0x03, 0x9e, 0xf9,
0x3d, 0x2f, 0xf0, 0x87, 0x3f, 0x09, 0x3c, 0xde, 0xf6, 0x8a, 0x68, 0x00, 0x9e, 0x15, 0x4b, 0x46,
0x42, 0x32, 0xea, 0x47, 0xc5, 0x2d, 0x9d, 0xc1, 0x23, 0x8d, 0x31, 0x26, 0x37, 0xa8, 0x2f, 0x0d,
0x5a, 0x9b, 0xc8, 0xa5, 0x61, 0x9d, 0x72, 0xf4, 0x7f, 0x6d, 0x1f, 0xd4, 0x01, 0xb3, 0x8a, 0xa7,
0xe7, 0x30, 0xc8, 0xb4, 0x5a, 0xe7, 0x4d, 0xa2, 0xb7, 0x53, 0xe2, 0x83, 0x92, 0xae, 0xe3, 0x86,
0xbf, 0x08, 0x3c, 0xd9, 0xfa, 0xd1, 0xb6, 0xf8, 0x4c, 0xe1, 0xa1, 0x41, 0xb9, 0xf8, 0x7f, 0x9b,
0x81, 0xc3, 0xef, 0xc8, 0x85, 0x3e, 0x85, 0x2e, 0xae, 0xb3, 0x44, 0x23, 0xf3, 0x43, 0x32, 0xf2,
0xa2, 0x6a, 0x45, 0x19, 0xec, 0x15, 0x21, 0x28, 0xdd, 0x8f, 0xd3, 0x8f, 0xea, 0xe5, 0xcb, 0x63,
0x38, 0x70, 0xb6, 0x1f, 0x94, 0x4e, 0x85, 0xa5, 0x07, 0xd0, 0xbb, 0x28, 0x4e, 0xcb, 0x7c, 0x75,
0x15, 0xdc, 0xa3, 0x3d, 0xf0, 0xcf, 0x66, 0xd3, 0xcf, 0x01, 0x79, 0x77, 0x04, 0x83, 0x58, 0xa5,
0xad, 0xa9, 0x2e, 0xc8, 0x37, 0xbf, 0xb8, 0x7e, 0xef, 0xc0, 0xd7, 0x49, 0x24, 0x72, 0x7e, 0xaa,
0x34, 0xce, 0xbb, 0xe5, 0x51, 0x7b, 0xfd, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08, 0x59, 0x1b,
0xee, 0x03, 0x00, 0x00,
// 405 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x93, 0x41, 0x6f, 0x94, 0x40,
0x1c, 0xc5, 0x33, 0x0b, 0x6e, 0xcb, 0xbf, 0x71, 0x5d, 0x27, 0x6a, 0x26, 0xd5, 0x03, 0x6e, 0x52,
0xc3, 0x69, 0x30, 0x78, 0x31, 0x4d, 0xbc, 0x58, 0x0f, 0x6a, 0xd2, 0xd4, 0x50, 0xe3, 0xc1, 0x4b,
0x33, 0x85, 0xbf, 0x84, 0xa4, 0xcc, 0x90, 0x99, 0x69, 0xb3, 0x7c, 0x25, 0xbf, 0x87, 0x37, 0xbf,
0x91, 0x17, 0x03, 0x03, 0x0b, 0xd5, 0x3d, 0xb8, 0x26, 0x3d, 0xc1, 0xf0, 0xe6, 0xf7, 0xde, 0xff,
0x01, 0x03, 0x4f, 0x6f, 0x12, 0x2d, 0x1a, 0x9e, 0xa9, 0x2a, 0xce, 0x94, 0xc6, 0x38, 0x53, 0xf2,
0x5b, 0x59, 0xf0, 0x5a, 0x2b, 0xab, 0x28, 0x0c, 0xa2, 0xc6, 0xc3, 0x97, 0x7f, 0x6d, 0xac, 0x2a,
0x25, 0x63, 0x83, 0xba, 0x14, 0x57, 0xb1, 0x6d, 0x6a, 0xcc, 0x2f, 0x2a, 0x34, 0x46, 0x14, 0xe8,
0xe8, 0xc3, 0xa3, 0x3f, 0x08, 0xab, 0x85, 0x34, 0xb5, 0xd2, 0xf6, 0x56, 0xc8, 0xea, 0xc7, 0x0c,
0xe6, 0x27, 0xdd, 0x03, 0x7a, 0x0c, 0x7b, 0xa5, 0xbc, 0x54, 0xd7, 0x32, 0x67, 0x24, 0xf4, 0xa2,
0x83, 0x24, 0xe4, 0xe3, 0x04, 0xfc, 0x83, 0x93, 0xde, 0x0b, 0x99, 0x5f, 0xa1, 0x76, 0x48, 0x3a,
0x00, 0xf4, 0x0d, 0xec, 0xab, 0x6b, 0xeb, 0xe0, 0x59, 0x07, 0x3f, 0x9f, 0xc2, 0x67, 0xbd, 0x76,
0x9b, 0xde, 0x20, 0xf4, 0x35, 0x78, 0xa2, 0xae, 0x99, 0xdf, 0x91, 0x2f, 0xa6, 0xa4, 0x2b, 0xca,
0x5d, 0x51, 0xfe, 0xb9, 0x2d, 0x7a, 0xea, 0x7a, 0xa6, 0x2d, 0x42, 0x8f, 0x21, 0xd8, 0x34, 0x63,
0xf7, 0x42, 0x12, 0x1d, 0x24, 0xcf, 0xa6, 0xfc, 0x46, 0xe4, 0x7d, 0xe8, 0xb8, 0x9d, 0xbe, 0x83,
0x00, 0xd7, 0x16, 0xa5, 0x29, 0x95, 0x64, 0xf3, 0x9d, 0xb2, 0x47, 0xf0, 0xa3, 0xbf, 0xef, 0x2d,
0xfd, 0xd5, 0x4f, 0x02, 0x8f, 0xb6, 0xbd, 0x22, 0xba, 0x04, 0xcf, 0x8a, 0x82, 0x91, 0x90, 0x44,
0x41, 0xda, 0xde, 0xd2, 0x73, 0x78, 0xa8, 0x31, 0xc3, 0xf2, 0x06, 0xf5, 0x85, 0x41, 0x6b, 0x4b,
0x59, 0x18, 0x36, 0xeb, 0x46, 0xff, 0xd7, 0xf8, 0xe5, 0x60, 0x70, 0xde, 0xf3, 0xf4, 0x14, 0x16,
0xb5, 0x56, 0xeb, 0x66, 0x74, 0xf4, 0x76, 0x72, 0xbc, 0xdf, 0xd1, 0x83, 0xdd, 0xea, 0x17, 0x81,
0xc7, 0x5b, 0x3f, 0xda, 0x96, 0x3e, 0x67, 0xf0, 0xc0, 0xa0, 0xcc, 0xff, 0xbf, 0xcd, 0xc2, 0xe1,
0x77, 0xd4, 0x85, 0x3e, 0x81, 0x39, 0xae, 0xeb, 0x52, 0x23, 0xf3, 0x43, 0x12, 0x79, 0x69, 0xbf,
0xa2, 0x0c, 0xf6, 0x5a, 0x13, 0x94, 0xee, 0xc7, 0x09, 0xd2, 0x61, 0xf9, 0xf6, 0x08, 0x16, 0x99,
0xaa, 0x26, 0x69, 0x9f, 0xc8, 0x57, 0xbf, 0xbd, 0x7e, 0x9f, 0xc1, 0x97, 0x24, 0x15, 0x0d, 0x3f,
0x51, 0x1a, 0x2f, 0xe7, 0xdd, 0x11, 0x7a, 0xf5, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x15, 0xee,
0x31, 0xc6, 0x03, 0x00, 0x00,
}

View File

@ -9,13 +9,7 @@ option java_multiple_files = true;
import "v2ray.com/core/common/serial/typed_message.proto";
import "v2ray.com/core/transport/config.proto";
// Configuration serialization format.
enum ConfigFormat {
Protobuf = 0;
JSON = 1;
}
// Master config of V2Ray. V2Ray takes this config as input and functions accordingly.
// Config is the master config of V2Ray. V2Ray takes this config as input and functions accordingly.
message Config {
// Inbound handler configurations. Must have at least one item.
repeated InboundHandlerConfig inbound = 1;
@ -25,7 +19,7 @@ message Config {
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;
// Transport settings.
@ -36,19 +30,21 @@ message Config {
repeated v2ray.core.common.serial.TypedMessage extension = 6;
}
// InboundHandlerConfig is the configuration for inbound handler.
message InboundHandlerConfig {
// Tag of the inbound handler.
// Tag of the inbound handler. The tag must be unique among all inbound handlers
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;
// Settings for inbound proxy. Must be one of the inbound proxies.
v2ray.core.common.serial.TypedMessage proxy_settings = 3;
}
// OutboundHandlerConfig is the configuration for outbound handler.
message OutboundHandlerConfig {
// Tag of this outbound handler.
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;
// Settings for this outbound proxy. Must be one of the outbound proxies.
v2ray.core.common.serial.TypedMessage proxy_settings = 3;

View File

@ -8,10 +8,19 @@ type key int
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 {
if s, ok := ctx.Value(v2rayKey).(*Instance); ok {
return s
}
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
View 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
import (
"fmt"
"v2ray.com/core/common/platform"
"v2ray.com/core/common/serial"
)
var (
version = "3.9"
version = "3.16"
build = "Custom"
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.
@ -30,10 +28,12 @@ func Version() string {
return version
}
// PrintVersion prints current version into console.
func PrintVersion() {
fmt.Printf("V2Ray %s (%s) %s%s", Version(), codename, build, platform.LineSeparator())
fmt.Printf("%s%s", intro, platform.LineSeparator())
// VersionStatement returns a list of strings representing the full version info.
func VersionStatement() []string {
return []string{
serial.Concat("V2Ray ", Version(), " (", codename, ") ", build),
intro,
}
}
/*

Some files were not shown because too many files have changed in this diff Show More