mirror of
https://github.com/v2fly/v2ray-core.git
synced 2025-01-02 07:26:24 -05:00
Merge branch 'master' of github.com:v2ray/v2ray-core into domainsocket
This commit is contained in:
commit
c542c043f3
@ -1,7 +1,7 @@
|
||||
sudo: required
|
||||
language: go
|
||||
go:
|
||||
- 1.9.4
|
||||
- "1.10.1"
|
||||
go_import_path: v2ray.com/core
|
||||
git:
|
||||
depth: 5
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
@ -45,7 +42,7 @@ type UDPNameServer struct {
|
||||
address net.Destination
|
||||
requests map[uint16]*PendingRequest
|
||||
udpServer *udp.Dispatcher
|
||||
nextCleanup time.Time
|
||||
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)
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
48
app/log/command/command.go
Normal file
48
app/log/command/command.go
Normal file
@ -0,0 +1,48 @@
|
||||
package command
|
||||
|
||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg command -path App,Log,Command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
|
||||
"v2ray.com/core"
|
||||
"v2ray.com/core/app/log"
|
||||
"v2ray.com/core/common"
|
||||
)
|
||||
|
||||
type LoggerServer struct {
|
||||
V *core.Instance
|
||||
}
|
||||
|
||||
func (s *LoggerServer) RestartLogger(ctx context.Context, request *RestartLoggerRequest) (*RestartLoggerResponse, error) {
|
||||
logger := s.V.GetFeature((*log.Instance)(nil))
|
||||
if logger == nil {
|
||||
return nil, newError("unable to get logger instance")
|
||||
}
|
||||
if err := logger.Close(); err != nil {
|
||||
return nil, newError("failed to close logger").Base(err)
|
||||
}
|
||||
if err := logger.Start(); err != nil {
|
||||
return nil, newError("failed to start logger").Base(err)
|
||||
}
|
||||
return &RestartLoggerResponse{}, nil
|
||||
}
|
||||
|
||||
type service struct {
|
||||
v *core.Instance
|
||||
}
|
||||
|
||||
func (s *service) Register(server *grpc.Server) {
|
||||
RegisterLoggerServiceServer(server, &LoggerServer{
|
||||
V: s.v,
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
|
||||
s := core.MustFromContext(ctx)
|
||||
return &service{v: s}, nil
|
||||
}))
|
||||
}
|
38
app/log/command/command_test.go
Normal file
38
app/log/command/command_test.go
Normal file
@ -0,0 +1,38 @@
|
||||
package command_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"v2ray.com/core"
|
||||
"v2ray.com/core/app/dispatcher"
|
||||
"v2ray.com/core/app/log"
|
||||
. "v2ray.com/core/app/log/command"
|
||||
"v2ray.com/core/app/proxyman"
|
||||
_ "v2ray.com/core/app/proxyman/inbound"
|
||||
_ "v2ray.com/core/app/proxyman/outbound"
|
||||
"v2ray.com/core/common/serial"
|
||||
. "v2ray.com/ext/assert"
|
||||
)
|
||||
|
||||
func TestLoggerRestart(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
v, err := core.New(&core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{}),
|
||||
serial.ToTypedMessage(&dispatcher.Config{}),
|
||||
serial.ToTypedMessage(&proxyman.InboundConfig{}),
|
||||
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
|
||||
},
|
||||
})
|
||||
|
||||
assert(err, IsNil)
|
||||
assert(v.Start(), IsNil)
|
||||
|
||||
server := &LoggerServer{
|
||||
V: v,
|
||||
}
|
||||
_, err = server.RestartLogger(context.Background(), &RestartLoggerRequest{})
|
||||
assert(err, IsNil)
|
||||
}
|
144
app/log/command/config.pb.go
Normal file
144
app/log/command/config.pb.go
Normal file
@ -0,0 +1,144 @@
|
||||
package command
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
func (m *Config) Reset() { *m = Config{} }
|
||||
func (m *Config) String() string { return proto.CompactTextString(m) }
|
||||
func (*Config) ProtoMessage() {}
|
||||
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
type RestartLoggerRequest struct {
|
||||
}
|
||||
|
||||
func (m *RestartLoggerRequest) Reset() { *m = RestartLoggerRequest{} }
|
||||
func (m *RestartLoggerRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*RestartLoggerRequest) ProtoMessage() {}
|
||||
func (*RestartLoggerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
type RestartLoggerResponse struct {
|
||||
}
|
||||
|
||||
func (m *RestartLoggerResponse) Reset() { *m = RestartLoggerResponse{} }
|
||||
func (m *RestartLoggerResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*RestartLoggerResponse) ProtoMessage() {}
|
||||
func (*RestartLoggerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Config)(nil), "v2ray.core.app.log.command.Config")
|
||||
proto.RegisterType((*RestartLoggerRequest)(nil), "v2ray.core.app.log.command.RestartLoggerRequest")
|
||||
proto.RegisterType((*RestartLoggerResponse)(nil), "v2ray.core.app.log.command.RestartLoggerResponse")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for LoggerService service
|
||||
|
||||
type LoggerServiceClient interface {
|
||||
RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error)
|
||||
}
|
||||
|
||||
type loggerServiceClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewLoggerServiceClient(cc *grpc.ClientConn) LoggerServiceClient {
|
||||
return &loggerServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) {
|
||||
out := new(RestartLoggerResponse)
|
||||
err := grpc.Invoke(ctx, "/v2ray.core.app.log.command.LoggerService/RestartLogger", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for LoggerService service
|
||||
|
||||
type LoggerServiceServer interface {
|
||||
RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error)
|
||||
}
|
||||
|
||||
func RegisterLoggerServiceServer(s *grpc.Server, srv LoggerServiceServer) {
|
||||
s.RegisterService(&_LoggerService_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _LoggerService_RestartLogger_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RestartLoggerRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(LoggerServiceServer).RestartLogger(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/v2ray.core.app.log.command.LoggerService/RestartLogger",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(LoggerServiceServer).RestartLogger(ctx, req.(*RestartLoggerRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _LoggerService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "v2ray.core.app.log.command.LoggerService",
|
||||
HandlerType: (*LoggerServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "RestartLogger",
|
||||
Handler: _LoggerService_RestartLogger_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "v2ray.com/core/app/log/command/config.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("v2ray.com/core/app/log/command/config.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 210 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x2e, 0x33, 0x2a, 0x4a,
|
||||
0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0xcf,
|
||||
0xc9, 0x4f, 0xd7, 0x4f, 0xce, 0xcf, 0xcd, 0x4d, 0xcc, 0x4b, 0xd1, 0x4f, 0xce, 0xcf, 0x4b, 0xcb,
|
||||
0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x82, 0x29, 0x2e, 0x4a, 0xd5, 0x4b, 0x2c,
|
||||
0x28, 0xd0, 0xcb, 0xc9, 0x4f, 0xd7, 0x83, 0x2a, 0x54, 0xe2, 0xe0, 0x62, 0x73, 0x06, 0xab, 0x55,
|
||||
0x12, 0xe3, 0x12, 0x09, 0x4a, 0x2d, 0x2e, 0x49, 0x2c, 0x2a, 0xf1, 0xc9, 0x4f, 0x4f, 0x4f, 0x2d,
|
||||
0x0a, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x51, 0x12, 0xe7, 0x12, 0x45, 0x13, 0x2f, 0x2e, 0xc8,
|
||||
0xcf, 0x2b, 0x4e, 0x35, 0x6a, 0x67, 0xe4, 0xe2, 0x85, 0x08, 0x05, 0xa7, 0x16, 0x95, 0x65, 0x26,
|
||||
0xa7, 0x0a, 0x95, 0x71, 0xf1, 0xa2, 0x28, 0x15, 0x32, 0xd0, 0xc3, 0x6d, 0xb5, 0x1e, 0x36, 0xdb,
|
||||
0xa4, 0x0c, 0x49, 0xd0, 0x01, 0x71, 0x87, 0x12, 0x83, 0x93, 0x07, 0x97, 0x5c, 0x72, 0x7e, 0x2e,
|
||||
0x1e, 0x9d, 0x01, 0x8c, 0x51, 0xec, 0x50, 0xe6, 0x2a, 0x26, 0xa9, 0x30, 0xa3, 0xa0, 0xc4, 0x4a,
|
||||
0x3d, 0x67, 0x90, 0x3a, 0xc7, 0x82, 0x02, 0x3d, 0x9f, 0xfc, 0x74, 0x3d, 0x67, 0x88, 0x64, 0x12,
|
||||
0x1b, 0x38, 0xc4, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x37, 0xc7, 0xfc, 0xda, 0x60, 0x01,
|
||||
0x00, 0x00,
|
||||
}
|
18
app/log/command/config.proto
Normal file
18
app/log/command/config.proto
Normal file
@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package v2ray.core.app.log.command;
|
||||
option csharp_namespace = "V2Ray.Core.App.Log.Command";
|
||||
option go_package = "command";
|
||||
option java_package = "com.v2ray.core.app.log.command";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Config {
|
||||
}
|
||||
|
||||
message RestartLoggerRequest {}
|
||||
|
||||
message RestartLoggerResponse{}
|
||||
|
||||
service LoggerService {
|
||||
rpc RestartLogger(RestartLoggerRequest) returns (RestartLoggerResponse) {}
|
||||
}
|
7
app/log/command/errors.generated.go
Normal file
7
app/log/command/errors.generated.go
Normal file
@ -0,0 +1,7 @@
|
||||
package command
|
||||
|
||||
import "v2ray.com/core/common/errors"
|
||||
|
||||
func newError(values ...interface{}) *errors.Error {
|
||||
return errors.New(values...).Path("App", "Log", "Command")
|
||||
}
|
@ -6,11 +6,12 @@ import (
|
||||
"context"
|
||||
"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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -9,30 +9,28 @@ 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 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
|
||||
}
|
||||
|
37
app/policy/manager_test.go
Normal file
37
app/policy/manager_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package policy_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"v2ray.com/core"
|
||||
. "v2ray.com/core/app/policy"
|
||||
. "v2ray.com/ext/assert"
|
||||
)
|
||||
|
||||
func TestPolicy(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
manager, err := New(context.Background(), &Config{
|
||||
Level: map[uint32]*Policy{
|
||||
0: {
|
||||
Timeout: &Policy_Timeout{
|
||||
Handshake: &Second{
|
||||
Value: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
assert(err, IsNil)
|
||||
|
||||
pDefault := core.DefaultPolicy()
|
||||
|
||||
p0 := manager.ForLevel(0)
|
||||
assert(p0.Timeouts.Handshake, Equals, 2*time.Second)
|
||||
assert(p0.Timeouts.ConnectionIdle, Equals, pDefault.Timeouts.ConnectionIdle)
|
||||
|
||||
p1 := manager.ForLevel(1)
|
||||
assert(p1.Timeouts.Handshake, Equals, pDefault.Timeouts.Handshake)
|
||||
}
|
11
app/proxyman/command/command.go
Normal file → Executable file
11
app/proxyman/command/command.go
Normal file → Executable file
@ -11,7 +11,7 @@ import (
|
||||
|
||||
// InboundOperation is the interface for operations that applies to inbound handlers.
|
||||
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
|
||||
}))
|
||||
}
|
||||
|
@ -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"
|
||||
)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
@ -204,7 +211,9 @@ func (w *udpWorker) getConnection(id connID) (*udpConn, bool) {
|
||||
func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest net.Destination) {
|
||||
id := connID{
|
||||
src: source,
|
||||
dest: originalDest,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ const (
|
||||
|
||||
const (
|
||||
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
|
||||
}
|
||||
|
||||
b.AppendBytes(byte(f.SessionStatus), byte(f.Option))
|
||||
|
||||
if f.SessionStatus == SessionStatusNew {
|
||||
switch f.Target.Network {
|
||||
case net.Network_TCP:
|
||||
b = append(b, byte(TargetNetworkTCP))
|
||||
b.AppendBytes(byte(TargetNetworkTCP))
|
||||
case net.Network_UDP:
|
||||
b = append(b, byte(TargetNetworkUDP))
|
||||
b.AppendBytes(byte(TargetNetworkUDP))
|
||||
}
|
||||
length++
|
||||
|
||||
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 err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
serial.Uint16ToBytes(uint16(length), lengthBytes[:0])
|
||||
return length + 2, nil
|
||||
}
|
||||
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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
51
app/stats/command/command.go
Normal file
51
app/stats/command/command.go
Normal file
@ -0,0 +1,51 @@
|
||||
package command
|
||||
|
||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg command -path App,Stats,Command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
"v2ray.com/core"
|
||||
"v2ray.com/core/common"
|
||||
)
|
||||
|
||||
type statsServer struct {
|
||||
stats core.StatManager
|
||||
}
|
||||
|
||||
func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
|
||||
c := s.stats.GetCounter(request.Name)
|
||||
if c == nil {
|
||||
return nil, newError(request.Name, " not found.")
|
||||
}
|
||||
var value int64
|
||||
if request.Reset_ {
|
||||
value = c.Set(0)
|
||||
} else {
|
||||
value = c.Value()
|
||||
}
|
||||
return &GetStatsResponse{
|
||||
Stat: &Stat{
|
||||
Name: request.Name,
|
||||
Value: value,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type service struct {
|
||||
v *core.Instance
|
||||
}
|
||||
|
||||
func (s *service) Register(server *grpc.Server) {
|
||||
RegisterStatsServiceServer(server, &statsServer{
|
||||
stats: s.v.Stats(),
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
|
||||
s := core.MustFromContext(ctx)
|
||||
return &service{v: s}, nil
|
||||
}))
|
||||
}
|
198
app/stats/command/command.pb.go
Normal file
198
app/stats/command/command.pb.go
Normal file
@ -0,0 +1,198 @@
|
||||
package command
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type GetStatsRequest struct {
|
||||
// Name of the stat counter.
|
||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||
// Whether or not to reset the counter to fetching its value.
|
||||
Reset_ bool `protobuf:"varint,2,opt,name=reset" json:"reset,omitempty"`
|
||||
}
|
||||
|
||||
func (m *GetStatsRequest) Reset() { *m = GetStatsRequest{} }
|
||||
func (m *GetStatsRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*GetStatsRequest) ProtoMessage() {}
|
||||
func (*GetStatsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *GetStatsRequest) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *GetStatsRequest) GetReset_() bool {
|
||||
if m != nil {
|
||||
return m.Reset_
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Stat struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||
Value int64 `protobuf:"varint,2,opt,name=value" json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Stat) Reset() { *m = Stat{} }
|
||||
func (m *Stat) String() string { return proto.CompactTextString(m) }
|
||||
func (*Stat) ProtoMessage() {}
|
||||
func (*Stat) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *Stat) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Stat) GetValue() int64 {
|
||||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type GetStatsResponse struct {
|
||||
Stat *Stat `protobuf:"bytes,1,opt,name=stat" json:"stat,omitempty"`
|
||||
}
|
||||
|
||||
func (m *GetStatsResponse) Reset() { *m = GetStatsResponse{} }
|
||||
func (m *GetStatsResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*GetStatsResponse) ProtoMessage() {}
|
||||
func (*GetStatsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
func (m *GetStatsResponse) GetStat() *Stat {
|
||||
if m != nil {
|
||||
return m.Stat
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
func (m *Config) Reset() { *m = Config{} }
|
||||
func (m *Config) String() string { return proto.CompactTextString(m) }
|
||||
func (*Config) ProtoMessage() {}
|
||||
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*GetStatsRequest)(nil), "v2ray.core.app.stats.command.GetStatsRequest")
|
||||
proto.RegisterType((*Stat)(nil), "v2ray.core.app.stats.command.Stat")
|
||||
proto.RegisterType((*GetStatsResponse)(nil), "v2ray.core.app.stats.command.GetStatsResponse")
|
||||
proto.RegisterType((*Config)(nil), "v2ray.core.app.stats.command.Config")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for StatsService service
|
||||
|
||||
type StatsServiceClient interface {
|
||||
GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error)
|
||||
}
|
||||
|
||||
type statsServiceClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewStatsServiceClient(cc *grpc.ClientConn) StatsServiceClient {
|
||||
return &statsServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) {
|
||||
out := new(GetStatsResponse)
|
||||
err := grpc.Invoke(ctx, "/v2ray.core.app.stats.command.StatsService/GetStats", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for StatsService service
|
||||
|
||||
type StatsServiceServer interface {
|
||||
GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error)
|
||||
}
|
||||
|
||||
func RegisterStatsServiceServer(s *grpc.Server, srv StatsServiceServer) {
|
||||
s.RegisterService(&_StatsService_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _StatsService_GetStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetStatsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StatsServiceServer).GetStats(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/v2ray.core.app.stats.command.StatsService/GetStats",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StatsServiceServer).GetStats(ctx, req.(*GetStatsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _StatsService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "v2ray.core.app.stats.command.StatsService",
|
||||
HandlerType: (*StatsServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetStats",
|
||||
Handler: _StatsService_GetStats_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "v2ray.com/core/app/stats/command/command.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("v2ray.com/core/app/stats/command/command.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 267 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x3f, 0x4b, 0x03, 0x31,
|
||||
0x14, 0xc0, 0xbd, 0x5a, 0xeb, 0xf9, 0x14, 0x94, 0xe0, 0x50, 0xa4, 0xc3, 0x91, 0xa9, 0x8b, 0xef,
|
||||
0xe4, 0x04, 0x17, 0x27, 0xbd, 0x41, 0x10, 0x07, 0x49, 0xc1, 0xc1, 0x2d, 0xc6, 0xa7, 0x14, 0xcd,
|
||||
0x25, 0x26, 0xe9, 0x41, 0xf1, 0x1b, 0xf9, 0x29, 0x25, 0xb9, 0x1e, 0x82, 0xe0, 0xe1, 0x94, 0xf7,
|
||||
0x92, 0xdf, 0xef, 0xfd, 0x21, 0x80, 0x6d, 0xe5, 0xe4, 0x1a, 0x95, 0xd1, 0xa5, 0x32, 0x8e, 0x4a,
|
||||
0x69, 0x6d, 0xe9, 0x83, 0x0c, 0xbe, 0x54, 0x46, 0x6b, 0xd9, 0x3c, 0xf7, 0x27, 0x5a, 0x67, 0x82,
|
||||
0x61, 0xb3, 0x9e, 0x77, 0x84, 0xd2, 0x5a, 0x4c, 0x2c, 0x6e, 0x18, 0x7e, 0x09, 0x87, 0x37, 0x14,
|
||||
0x16, 0xf1, 0x4e, 0xd0, 0xc7, 0x8a, 0x7c, 0x60, 0x0c, 0xc6, 0x8d, 0xd4, 0x34, 0xcd, 0x8a, 0x6c,
|
||||
0xbe, 0x27, 0x52, 0xcc, 0x8e, 0x61, 0xc7, 0x91, 0xa7, 0x30, 0x1d, 0x15, 0xd9, 0x3c, 0x17, 0x5d,
|
||||
0xc2, 0xcf, 0x60, 0x1c, 0xcd, 0xbf, 0x8c, 0x56, 0xbe, 0xaf, 0x28, 0x19, 0xdb, 0xa2, 0x4b, 0xf8,
|
||||
0x2d, 0x1c, 0xfd, 0xb4, 0xf3, 0xd6, 0x34, 0x9e, 0xd8, 0x05, 0x8c, 0xe3, 0x4c, 0xc9, 0xde, 0xaf,
|
||||
0x38, 0x0e, 0xcd, 0x8b, 0x51, 0x15, 0x89, 0xe7, 0x39, 0x4c, 0x6a, 0xd3, 0xbc, 0x2c, 0x5f, 0xab,
|
||||
0x4f, 0x38, 0x48, 0x25, 0x17, 0xe4, 0xda, 0xa5, 0x22, 0xf6, 0x06, 0x79, 0xdf, 0x85, 0x9d, 0x0e,
|
||||
0xd7, 0xfb, 0xb5, 0xfc, 0x09, 0xfe, 0x17, 0xef, 0x86, 0xe7, 0x5b, 0xd7, 0x77, 0x50, 0x28, 0xa3,
|
||||
0x07, 0xb5, 0xfb, 0xec, 0x71, 0x77, 0x13, 0x7e, 0x8d, 0x66, 0x0f, 0x95, 0x90, 0x6b, 0xac, 0x23,
|
||||
0x79, 0x65, 0x6d, 0xda, 0xc8, 0x63, 0xdd, 0x3d, 0x3f, 0x4d, 0xd2, 0xa7, 0x9d, 0x7f, 0x07, 0x00,
|
||||
0x00, 0xff, 0xff, 0x10, 0x3a, 0x8a, 0xf3, 0xe6, 0x01, 0x00, 0x00,
|
||||
}
|
29
app/stats/command/command.proto
Normal file
29
app/stats/command/command.proto
Normal file
@ -0,0 +1,29 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package v2ray.core.app.stats.command;
|
||||
option csharp_namespace = "V2Ray.Core.App.Stats.Command";
|
||||
option go_package = "command";
|
||||
option java_package = "com.v2ray.core.app.stats.command";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message GetStatsRequest {
|
||||
// Name of the stat counter.
|
||||
string name = 1;
|
||||
// Whether or not to reset the counter to fetching its value.
|
||||
bool reset = 2;
|
||||
}
|
||||
|
||||
message Stat {
|
||||
string name = 1;
|
||||
int64 value = 2;
|
||||
}
|
||||
|
||||
message GetStatsResponse {
|
||||
Stat stat = 1;
|
||||
}
|
||||
|
||||
service StatsService {
|
||||
rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {}
|
||||
}
|
||||
|
||||
message Config {}
|
7
app/stats/command/errors.generated.go
Normal file
7
app/stats/command/errors.generated.go
Normal file
@ -0,0 +1,7 @@
|
||||
package command
|
||||
|
||||
import "v2ray.com/core/common/errors"
|
||||
|
||||
func newError(values ...interface{}) *errors.Error {
|
||||
return errors.New(values...).Path("App", "Stats", "Command")
|
||||
}
|
13
app/stats/config.go
Normal file
13
app/stats/config.go
Normal file
@ -0,0 +1,13 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"v2ray.com/core/common"
|
||||
)
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return NewManager(ctx, config.(*Config))
|
||||
}))
|
||||
}
|
42
app/stats/config.pb.go
Normal file
42
app/stats/config.pb.go
Normal file
@ -0,0 +1,42 @@
|
||||
package stats
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
func (m *Config) Reset() { *m = Config{} }
|
||||
func (m *Config) String() string { return proto.CompactTextString(m) }
|
||||
func (*Config) ProtoMessage() {}
|
||||
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Config)(nil), "v2ray.core.app.stats.Config")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("v2ray.com/core/app/stats/config.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 123 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x33, 0x2a, 0x4a,
|
||||
0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0x2f,
|
||||
0x2e, 0x49, 0x2c, 0x29, 0xd6, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f,
|
||||
0xc9, 0x17, 0x12, 0x81, 0x29, 0x2b, 0x4a, 0xd5, 0x4b, 0x2c, 0x28, 0xd0, 0x03, 0x2b, 0x51, 0xe2,
|
||||
0xe0, 0x62, 0x73, 0x06, 0xab, 0x72, 0xb2, 0xe2, 0x92, 0x48, 0xce, 0xcf, 0xd5, 0xc3, 0xa6, 0x2a,
|
||||
0x80, 0x31, 0x8a, 0x15, 0xcc, 0x58, 0xc5, 0x24, 0x12, 0x66, 0x14, 0x94, 0x58, 0xa9, 0xe7, 0x0c,
|
||||
0x92, 0x77, 0x2c, 0x28, 0xd0, 0x0b, 0x06, 0x09, 0x27, 0xb1, 0x81, 0xad, 0x30, 0x06, 0x04, 0x00,
|
||||
0x00, 0xff, 0xff, 0x88, 0x24, 0xc6, 0x41, 0x8b, 0x00, 0x00, 0x00,
|
||||
}
|
11
app/stats/config.proto
Normal file
11
app/stats/config.proto
Normal file
@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package v2ray.core.app.stats;
|
||||
option csharp_namespace = "V2Ray.Core.App.Stats";
|
||||
option go_package = "stats";
|
||||
option java_package = "com.v2ray.core.app.stats";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Config {
|
||||
|
||||
}
|
5
app/stats/errors.generated.go
Normal file
5
app/stats/errors.generated.go
Normal file
@ -0,0 +1,5 @@
|
||||
package stats
|
||||
|
||||
import "v2ray.com/core/common/errors"
|
||||
|
||||
func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Stats") }
|
83
app/stats/stats.go
Normal file
83
app/stats/stats.go
Normal file
@ -0,0 +1,83 @@
|
||||
package stats
|
||||
|
||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg stats -path App,Stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"v2ray.com/core"
|
||||
)
|
||||
|
||||
// Counter is an implementation of core.StatCounter.
|
||||
type Counter struct {
|
||||
value int64
|
||||
}
|
||||
|
||||
// Value implements core.StatCounter.
|
||||
func (c *Counter) Value() int64 {
|
||||
return atomic.LoadInt64(&c.value)
|
||||
}
|
||||
|
||||
// Set implements core.StatCounter.
|
||||
func (c *Counter) Set(newValue int64) int64 {
|
||||
return atomic.SwapInt64(&c.value, newValue)
|
||||
}
|
||||
|
||||
// Add implements core.StatCounter.
|
||||
func (c *Counter) Add(delta int64) int64 {
|
||||
return atomic.AddInt64(&c.value, delta)
|
||||
}
|
||||
|
||||
// Manager is an implementation of core.StatManager.
|
||||
type Manager struct {
|
||||
access sync.RWMutex
|
||||
counters map[string]*Counter
|
||||
}
|
||||
|
||||
func NewManager(ctx context.Context, config *Config) (*Manager, error) {
|
||||
m := &Manager{
|
||||
counters: make(map[string]*Counter),
|
||||
}
|
||||
|
||||
v := core.FromContext(ctx)
|
||||
if v != nil {
|
||||
if err := v.RegisterFeature((*core.StatManager)(nil), m); err != nil {
|
||||
return nil, newError("failed to register StatManager").Base(err)
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *Manager) RegisterCounter(name string) (core.StatCounter, error) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
|
||||
if _, found := m.counters[name]; found {
|
||||
return nil, newError("Counter ", name, " already registered.")
|
||||
}
|
||||
newError("create new counter ", name).AtDebug().WriteToLog()
|
||||
c := new(Counter)
|
||||
m.counters[name] = c
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (m *Manager) GetCounter(name string) core.StatCounter {
|
||||
m.access.RLock()
|
||||
defer m.access.RUnlock()
|
||||
|
||||
if c, found := m.counters[name]; found {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Close() error {
|
||||
return nil
|
||||
}
|
32
app/stats/stats_test.go
Normal file
32
app/stats/stats_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package stats_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"v2ray.com/core"
|
||||
. "v2ray.com/core/app/stats"
|
||||
"v2ray.com/core/common"
|
||||
. "v2ray.com/ext/assert"
|
||||
)
|
||||
|
||||
func TestInternface(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
assert((*Manager)(nil), Implements, (*core.StatManager)(nil))
|
||||
}
|
||||
|
||||
func TestStatsCounter(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
raw, err := common.CreateObject(context.Background(), &Config{})
|
||||
assert(err, IsNil)
|
||||
|
||||
m := raw.(core.StatManager)
|
||||
c, err := m.RegisterCounter("test.counter")
|
||||
assert(err, IsNil)
|
||||
|
||||
assert(c.Add(1), Equals, int64(1))
|
||||
assert(c.Set(0), Equals, int64(1))
|
||||
assert(c.Value(), Equals, int64(0))
|
||||
}
|
59
clock.go
59
clock.go
@ -1,59 +0,0 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Clock is a V2Ray feature that returns current time.
|
||||
type Clock interface {
|
||||
Feature
|
||||
|
||||
// Now returns current time.
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
type syncClock struct {
|
||||
sync.RWMutex
|
||||
Clock
|
||||
}
|
||||
|
||||
func (c *syncClock) Now() time.Time {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
if c.Clock == nil {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
return c.Clock.Now()
|
||||
}
|
||||
|
||||
func (c *syncClock) Start() error {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
if c.Clock == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Clock.Start()
|
||||
}
|
||||
|
||||
func (c *syncClock) Close() error {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
if c.Clock == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Clock.Close()
|
||||
}
|
||||
|
||||
func (c *syncClock) Set(clock Clock) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.Clock = clock
|
||||
}
|
44
commander.go
44
commander.go
@ -1,44 +0,0 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Commander is a feature that accepts commands from external source.
|
||||
type Commander interface {
|
||||
Feature
|
||||
}
|
||||
|
||||
type syncCommander struct {
|
||||
sync.RWMutex
|
||||
Commander
|
||||
}
|
||||
|
||||
func (c *syncCommander) Start() error {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
if c.Commander == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Commander.Start()
|
||||
}
|
||||
|
||||
func (c *syncCommander) Close() error {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
if c.Commander == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Commander.Close()
|
||||
}
|
||||
|
||||
func (c *syncCommander) Set(commander Commander) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.Commander = commander
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
package buf
|
||||
// Package buf provides a light-weight memory allocation mechanism.
|
||||
package buf // import "v2ray.com/core/common/buf"
|
||||
|
||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg buf -path Buf
|
||||
|
@ -1,4 +1,3 @@
|
||||
// Package buf provides a light-weight memory allocation mechanism.
|
||||
package buf
|
||||
|
||||
import (
|
||||
@ -13,10 +12,9 @@ type Supplier func([]byte) (int, error)
|
||||
// quickly.
|
||||
type Buffer struct {
|
||||
v []byte
|
||||
pool Pool
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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()
|
||||
for i := 0; i < 64; i++ {
|
||||
err := b.Reset(ReadFrom(r.Reader))
|
||||
if b.IsFull() {
|
||||
r.buffer = make([]byte, mediumSize)
|
||||
r.buffer = newBytes(Size + 1)
|
||||
}
|
||||
if !b.IsEmpty() {
|
||||
return NewMultiBufferValue(b), nil
|
||||
}
|
||||
if err != 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
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
20
common/crypto/chunk.go
Normal file → Executable 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)
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
32
common/dice/dice_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package dice_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
. "v2ray.com/core/common/dice"
|
||||
)
|
||||
|
||||
func BenchmarkRoll1(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Roll(1)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRoll20(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Roll(20)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIntn1(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
rand.Intn(1)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIntn20(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
rand.Intn(20)
|
||||
}
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
// Package errors is a drop-in replacement for Golang lib 'errors'.
|
||||
package errors
|
||||
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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
40
common/log/logger_test.go
Normal file
@ -0,0 +1,40 @@
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/buf"
|
||||
. "v2ray.com/core/common/log"
|
||||
. "v2ray.com/ext/assert"
|
||||
)
|
||||
|
||||
func TestFileLogger(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
f, err := ioutil.TempFile("", "vtest")
|
||||
assert(err, IsNil)
|
||||
path := f.Name()
|
||||
common.Must(f.Close())
|
||||
|
||||
creator, err := CreateFileLogWriter(path)
|
||||
assert(err, IsNil)
|
||||
|
||||
handler := NewLogger(creator)
|
||||
handler.Handle(&GeneralMessage{Content: "Test Log"})
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
common.Must(common.Close(handler))
|
||||
|
||||
f, err = os.Open(path)
|
||||
assert(err, IsNil)
|
||||
|
||||
b, err := buf.ReadAllToBytes(f)
|
||||
assert(err, IsNil)
|
||||
assert(string(b), HasSubstring, "Test Log")
|
||||
|
||||
common.Must(f.Close())
|
||||
}
|
@ -22,7 +22,7 @@ var (
|
||||
)
|
||||
|
||||
// AddressFamily is the type of address.
|
||||
type AddressFamily int
|
||||
type AddressFamily byte
|
||||
|
||||
const (
|
||||
// AddressFamilyIPv4 represents address as IPv4
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
2
common/protocol/account.go
Normal file → Executable 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
204
common/protocol/address.go
Normal file
@ -0,0 +1,204 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"v2ray.com/core/common/buf"
|
||||
"v2ray.com/core/common/net"
|
||||
)
|
||||
|
||||
type AddressOption func(*AddressParser)
|
||||
|
||||
func PortThenAddress() AddressOption {
|
||||
return func(p *AddressParser) {
|
||||
p.portFirst = true
|
||||
}
|
||||
}
|
||||
|
||||
func AddressFamilyByte(b byte, f net.AddressFamily) AddressOption {
|
||||
return func(p *AddressParser) {
|
||||
p.addrTypeMap[b] = f
|
||||
p.addrByteMap[f] = b
|
||||
}
|
||||
}
|
||||
|
||||
type AddressTypeParser func(byte) byte
|
||||
|
||||
func WithAddressTypeParser(atp AddressTypeParser) AddressOption {
|
||||
return func(p *AddressParser) {
|
||||
p.typeParser = atp
|
||||
}
|
||||
}
|
||||
|
||||
type AddressParser struct {
|
||||
addrTypeMap map[byte]net.AddressFamily
|
||||
addrByteMap map[net.AddressFamily]byte
|
||||
portFirst bool
|
||||
typeParser AddressTypeParser
|
||||
}
|
||||
|
||||
func NewAddressParser(options ...AddressOption) *AddressParser {
|
||||
p := &AddressParser{
|
||||
addrTypeMap: make(map[byte]net.AddressFamily, 8),
|
||||
addrByteMap: make(map[net.AddressFamily]byte, 8),
|
||||
}
|
||||
for _, opt := range options {
|
||||
opt(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *AddressParser) readPort(b *buf.Buffer, reader io.Reader) (net.Port, error) {
|
||||
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return net.PortFromBytes(b.BytesFrom(-2)), nil
|
||||
}
|
||||
|
||||
func maybeIPPrefix(b byte) bool {
|
||||
return b == '[' || (b >= '0' && b <= '9')
|
||||
}
|
||||
|
||||
func isValidDomain(d string) bool {
|
||||
for _, c := range d {
|
||||
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '.' || c == '_') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Address, error) {
|
||||
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addrType := b.Byte(b.Len() - 1)
|
||||
if p.typeParser != nil {
|
||||
addrType = p.typeParser(addrType)
|
||||
}
|
||||
|
||||
addrFamily, valid := p.addrTypeMap[addrType]
|
||||
if !valid {
|
||||
return nil, newError("unknown address type: ", addrType)
|
||||
}
|
||||
|
||||
switch addrFamily {
|
||||
case net.AddressFamilyIPv4:
|
||||
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return net.IPAddress(b.BytesFrom(-4)), nil
|
||||
case net.AddressFamilyIPv6:
|
||||
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return net.IPAddress(b.BytesFrom(-16)), nil
|
||||
case net.AddressFamilyDomain:
|
||||
if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
domainLength := int32(b.Byte(b.Len() - 1))
|
||||
if err := b.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
domain := string(b.BytesFrom(-domainLength))
|
||||
if maybeIPPrefix(domain[0]) {
|
||||
addr := net.ParseAddress(domain)
|
||||
if addr.Family().IsIPv4() || addrFamily.IsIPv6() {
|
||||
return addr, nil
|
||||
}
|
||||
}
|
||||
if !isValidDomain(domain) {
|
||||
return nil, newError("invalid domain name: ", domain)
|
||||
}
|
||||
return net.DomainAddress(domain), nil
|
||||
default:
|
||||
panic("impossible case")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AddressParser) ReadAddressPort(buffer *buf.Buffer, input io.Reader) (net.Address, net.Port, error) {
|
||||
if buffer == nil {
|
||||
buffer = buf.New()
|
||||
defer buffer.Release()
|
||||
}
|
||||
|
||||
if p.portFirst {
|
||||
port, err := p.readPort(buffer, input)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
addr, err := p.readAddress(buffer, input)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return addr, port, nil
|
||||
}
|
||||
|
||||
addr, err := p.readAddress(buffer, input)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
port, err := p.readPort(buffer, input)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return addr, port, nil
|
||||
}
|
||||
|
||||
func (p *AddressParser) writePort(writer io.Writer, port net.Port) error {
|
||||
_, err := writer.Write(port.Bytes(nil))
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *AddressParser) writeAddress(writer io.Writer, address net.Address) error {
|
||||
tb, valid := p.addrByteMap[address.Family()]
|
||||
if !valid {
|
||||
return newError("unknown address family", address.Family())
|
||||
}
|
||||
|
||||
switch address.Family() {
|
||||
case net.AddressFamilyIPv4, net.AddressFamilyIPv6:
|
||||
if _, err := writer.Write([]byte{tb}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := writer.Write(address.IP()); err != nil {
|
||||
return err
|
||||
}
|
||||
case net.AddressFamilyDomain:
|
||||
domain := address.Domain()
|
||||
if isDomainTooLong(domain) {
|
||||
return newError("Super long domain is not supported: ", domain)
|
||||
}
|
||||
if _, err := writer.Write([]byte{tb, byte(len(domain))}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := writer.Write([]byte(domain)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AddressParser) WriteAddressPort(writer io.Writer, addr net.Address, port net.Port) error {
|
||||
if p.portFirst {
|
||||
if err := p.writePort(writer, port); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.writeAddress(writer, addr); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := p.writeAddress(writer, addr); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.writePort(writer, port); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
118
common/protocol/address_test.go
Normal file
118
common/protocol/address_test.go
Normal file
@ -0,0 +1,118 @@
|
||||
package protocol_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"v2ray.com/core/common/buf"
|
||||
"v2ray.com/core/common/net"
|
||||
. "v2ray.com/core/common/protocol"
|
||||
. "v2ray.com/ext/assert"
|
||||
)
|
||||
|
||||
func TestAddressReading(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
data := []struct {
|
||||
Options []AddressOption
|
||||
Input []byte
|
||||
Address net.Address
|
||||
Port net.Port
|
||||
Error bool
|
||||
}{
|
||||
{
|
||||
Options: []AddressOption{},
|
||||
Input: []byte{},
|
||||
Error: true,
|
||||
},
|
||||
{
|
||||
Options: []AddressOption{},
|
||||
Input: []byte{0, 0, 0, 0, 0},
|
||||
Error: true,
|
||||
},
|
||||
{
|
||||
Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)},
|
||||
Input: []byte{1, 0, 0, 0, 0, 0, 53},
|
||||
Address: net.IPAddress([]byte{0, 0, 0, 0}),
|
||||
Port: net.Port(53),
|
||||
},
|
||||
{
|
||||
Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)},
|
||||
Input: []byte{1, 0, 0, 0, 0},
|
||||
Error: true,
|
||||
},
|
||||
{
|
||||
Options: []AddressOption{AddressFamilyByte(0x04, net.AddressFamilyIPv6)},
|
||||
Input: []byte{4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 80},
|
||||
Address: net.IPAddress([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}),
|
||||
Port: net.Port(80),
|
||||
},
|
||||
{
|
||||
Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)},
|
||||
Input: []byte{3, 9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 80},
|
||||
Address: net.DomainAddress("v2ray.com"),
|
||||
Port: net.Port(80),
|
||||
},
|
||||
{
|
||||
Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)},
|
||||
Input: []byte{3, 9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0},
|
||||
Error: true,
|
||||
},
|
||||
{
|
||||
Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)},
|
||||
Input: []byte{3, 7, 56, 46, 56, 46, 56, 46, 56, 0, 80},
|
||||
Address: net.ParseAddress("8.8.8.8"),
|
||||
Port: net.Port(80),
|
||||
},
|
||||
{
|
||||
Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)},
|
||||
Input: []byte{3, 7, 10, 46, 56, 46, 56, 46, 56, 0, 80},
|
||||
Error: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range data {
|
||||
b := buf.New()
|
||||
parser := NewAddressParser(tc.Options...)
|
||||
addr, port, err := parser.ReadAddressPort(b, bytes.NewReader(tc.Input))
|
||||
b.Release()
|
||||
if tc.Error {
|
||||
assert(err, IsNotNil)
|
||||
} else {
|
||||
assert(addr, Equals, tc.Address)
|
||||
assert(port, Equals, tc.Port)
|
||||
assert(err, IsNil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddressWriting(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
data := []struct {
|
||||
Options []AddressOption
|
||||
Address net.Address
|
||||
Port net.Port
|
||||
Bytes []byte
|
||||
Error bool
|
||||
}{
|
||||
{
|
||||
Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)},
|
||||
Address: net.LocalHostIP,
|
||||
Port: net.Port(80),
|
||||
Bytes: []byte{1, 127, 0, 0, 1, 0, 80},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range data {
|
||||
parser := NewAddressParser(tc.Options...)
|
||||
|
||||
b := buf.New()
|
||||
err := parser.WriteAddressPort(b, tc.Address, tc.Port)
|
||||
if tc.Error {
|
||||
assert(err, IsNotNil)
|
||||
} else {
|
||||
assert(b.Bytes(), Equals, tc.Bytes)
|
||||
}
|
||||
}
|
||||
}
|
4
common/protocol/context.go
Normal file → Executable file
4
common/protocol/context.go
Normal file → Executable file
@ -10,12 +10,12 @@ const (
|
||||
userKey key = iota
|
||||
)
|
||||
|
||||
// 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 {
|
||||
|
@ -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
2
common/protocol/id.go
Normal file → Executable file
@ -19,7 +19,7 @@ func DefaultIDHash(key []byte) hash.Hash {
|
||||
return hmac.New(md5.New, key)
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -1,6 +1,6 @@
|
||||
package protocol
|
||||
|
||||
type TransferType int
|
||||
type TransferType byte
|
||||
|
||||
const (
|
||||
TransferTypeStream TransferType = 0
|
||||
|
@ -1,3 +1,3 @@
|
||||
package protocol
|
||||
package protocol // import "v2ray.com/core/common/protocol"
|
||||
|
||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg protocol -path Protocol
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
4
common/serial/bytes.go
Normal file → Executable 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
2
common/serial/numbers.go
Normal file → Executable 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))
|
||||
}
|
||||
|
28
common/serial/string_test.go
Normal file
28
common/serial/string_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
package serial_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
. "v2ray.com/core/common/serial"
|
||||
. "v2ray.com/ext/assert"
|
||||
)
|
||||
|
||||
func TestToString(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
s := "a"
|
||||
data := []struct {
|
||||
Value interface{}
|
||||
String string
|
||||
}{
|
||||
{Value: s, String: s},
|
||||
{Value: &s, String: s},
|
||||
{Value: errors.New("t"), String: "t"},
|
||||
{Value: []byte{'b', 'c'}, String: "[62,63]"},
|
||||
}
|
||||
|
||||
for _, c := range data {
|
||||
assert(ToString(c.Value), Equals, c.String)
|
||||
}
|
||||
}
|
40
common/session/session.go
Normal file
40
common/session/session.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Package session provides functions for sessions of incoming requests.
|
||||
package session // import "v2ray.com/core/common/session"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// ID of a session.
|
||||
type ID uint32
|
||||
|
||||
// NewID generates a new ID. The generated ID is high likely to be unique, but not cryptographically secure.
|
||||
// The generated ID will never be 0.
|
||||
func NewID() ID {
|
||||
for {
|
||||
id := ID(rand.Uint32())
|
||||
if id != 0 {
|
||||
return id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type sessionKey int
|
||||
|
||||
const (
|
||||
idSessionKey sessionKey = iota
|
||||
)
|
||||
|
||||
// ContextWithID returns a new context with the given ID.
|
||||
func ContextWithID(ctx context.Context, id ID) context.Context {
|
||||
return context.WithValue(ctx, idSessionKey, id)
|
||||
}
|
||||
|
||||
// IDFromContext returns ID in this context, or 0 if not contained.
|
||||
func IDFromContext(ctx context.Context) ID {
|
||||
if id, ok := ctx.Value(idSessionKey).(ID); ok {
|
||||
return id
|
||||
}
|
||||
return 0
|
||||
}
|
4
common/signal/done.go
Normal file → Executable file
4
common/signal/done.go
Normal file → Executable file
@ -4,7 +4,7 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 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()
|
||||
|
@ -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
2
common/signal/notifier.go
Normal file → Executable 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{}
|
||||
}
|
||||
|
@ -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
14
common/uuid/uuid.go
Normal file → Executable 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
88
config.go
Executable file
@ -0,0 +1,88 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/buf"
|
||||
)
|
||||
|
||||
// ConfigFormat is a configurable format of V2Ray config file.
|
||||
type ConfigFormat struct {
|
||||
Name string
|
||||
Extension []string
|
||||
Loader ConfigLoader
|
||||
}
|
||||
|
||||
// ConfigLoader is a utility to load V2Ray config from external source.
|
||||
type ConfigLoader func(input io.Reader) (*Config, error)
|
||||
|
||||
var (
|
||||
configLoaderByName = make(map[string]*ConfigFormat)
|
||||
configLoaderByExt = make(map[string]*ConfigFormat)
|
||||
)
|
||||
|
||||
// RegisterConfigLoader add a new ConfigLoader.
|
||||
func RegisterConfigLoader(format *ConfigFormat) error {
|
||||
name := strings.ToLower(format.Name)
|
||||
if _, found := configLoaderByName[name]; found {
|
||||
return newError(format.Name, " already registered.")
|
||||
}
|
||||
configLoaderByName[name] = format
|
||||
|
||||
for _, ext := range format.Extension {
|
||||
lext := strings.ToLower(ext)
|
||||
if f, found := configLoaderByExt[lext]; found {
|
||||
return newError(ext, " already registered to ", f.Name)
|
||||
}
|
||||
configLoaderByExt[lext] = format
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getExtension(filename string) string {
|
||||
idx := strings.LastIndexByte(filename, '.')
|
||||
if idx == -1 {
|
||||
return ""
|
||||
}
|
||||
return filename[idx+1:]
|
||||
}
|
||||
|
||||
// LoadConfig loads config with given format from given source.
|
||||
func LoadConfig(formatName string, filename string, input io.Reader) (*Config, error) {
|
||||
ext := getExtension(filename)
|
||||
if len(ext) > 0 {
|
||||
if f, found := configLoaderByExt[ext]; found {
|
||||
return f.Loader(input)
|
||||
}
|
||||
}
|
||||
|
||||
if f, found := configLoaderByName[formatName]; found {
|
||||
return f.Loader(input)
|
||||
}
|
||||
|
||||
return nil, newError("Unable to load config in ", formatName).AtWarning()
|
||||
}
|
||||
|
||||
func loadProtobufConfig(input io.Reader) (*Config, error) {
|
||||
config := new(Config)
|
||||
data, err := buf.ReadAllToBytes(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := proto.Unmarshal(data, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(RegisterConfigLoader(&ConfigFormat{
|
||||
Name: "Protobuf",
|
||||
Extension: []string{"pb"},
|
||||
Loader: loadProtobufConfig,
|
||||
}))
|
||||
}
|
91
config.pb.go
91
config.pb.go
@ -17,35 +17,13 @@ var _ = math.Inf
|
||||
// proto package needs to be updated.
|
||||
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,
|
||||
}
|
||||
|
18
config.proto
18
config.proto
@ -9,13 +9,7 @@ option java_multiple_files = true;
|
||||
import "v2ray.com/core/common/serial/typed_message.proto";
|
||||
import "v2ray.com/core/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;
|
||||
|
11
context.go
11
context.go
@ -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
18
core.go
Normal file → Executable file
@ -12,16 +12,14 @@ package core
|
||||
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg core -path Core
|
||||
|
||||
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
Loading…
Reference in New Issue
Block a user