mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-22 01:57:12 -05:00
reusable connection
This commit is contained in:
parent
3a6bf38686
commit
0fac2084c7
@ -48,8 +48,8 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
|
|||||||
|
|
||||||
_, err := io.ReadFull(reader, buffer.Value[:protocol.IDBytesLen])
|
_, err := io.ReadFull(reader, buffer.Value[:protocol.IDBytesLen])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Raw: Failed to read request header: ", err)
|
log.Info("Raw: Failed to read request header: ", err)
|
||||||
return nil, err
|
return nil, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
user, timestamp, valid := this.userValidator.Get(buffer.Value[:protocol.IDBytesLen])
|
user, timestamp, valid := this.userValidator.Get(buffer.Value[:protocol.IDBytesLen])
|
||||||
@ -77,7 +77,7 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
|
|||||||
}
|
}
|
||||||
|
|
||||||
if request.Version != Version {
|
if request.Version != Version {
|
||||||
log.Warning("Raw: Invalid protocol version ", request.Version)
|
log.Info("Raw: Invalid protocol version ", request.Version)
|
||||||
return nil, protocol.ErrorInvalidVersion
|
return nil, protocol.ErrorInvalidVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/v2ray/v2ray-core/common/retry"
|
"github.com/v2ray/v2ray-core/common/retry"
|
||||||
"github.com/v2ray/v2ray-core/proxy"
|
"github.com/v2ray/v2ray-core/proxy"
|
||||||
"github.com/v2ray/v2ray-core/proxy/internal"
|
"github.com/v2ray/v2ray-core/proxy/internal"
|
||||||
"github.com/v2ray/v2ray-core/transport/dialer"
|
"github.com/v2ray/v2ray-core/transport/hub"
|
||||||
"github.com/v2ray/v2ray-core/transport/ray"
|
"github.com/v2ray/v2ray-core/transport/ray"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *
|
|||||||
destination = this.ResolveIP(destination)
|
destination = this.ResolveIP(destination)
|
||||||
}
|
}
|
||||||
err := retry.Timed(5, 100).On(func() error {
|
err := retry.Timed(5, 100).On(func() error {
|
||||||
rawConn, err := dialer.Dial(destination)
|
rawConn, err := hub.DialWithoutCache(destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package inbound
|
package inbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/v2ray/v2ray-core/app"
|
"github.com/v2ray/v2ray-core/app"
|
||||||
@ -124,7 +125,7 @@ func (this *VMessInboundHandler) Listen(address v2net.Address, port v2net.Port)
|
|||||||
func (this *VMessInboundHandler) HandleConnection(connection *hub.Connection) {
|
func (this *VMessInboundHandler) HandleConnection(connection *hub.Connection) {
|
||||||
defer connection.Close()
|
defer connection.Close()
|
||||||
|
|
||||||
connReader := v2net.NewTimeOutReader(16, connection)
|
connReader := v2net.NewTimeOutReader(8, connection)
|
||||||
defer connReader.Release()
|
defer connReader.Release()
|
||||||
|
|
||||||
reader := v2io.NewBufferedReader(connReader)
|
reader := v2io.NewBufferedReader(connReader)
|
||||||
@ -135,13 +136,19 @@ func (this *VMessInboundHandler) HandleConnection(connection *hub.Connection) {
|
|||||||
|
|
||||||
request, err := session.DecodeRequestHeader(reader)
|
request, err := session.DecodeRequestHeader(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Access(connection.RemoteAddr(), "", log.AccessRejected, err)
|
if err != io.EOF {
|
||||||
log.Warning("VMessIn: Invalid request from ", connection.RemoteAddr(), ": ", err)
|
log.Access(connection.RemoteAddr(), "", log.AccessRejected, err)
|
||||||
|
log.Warning("VMessIn: Invalid request from ", connection.RemoteAddr(), ": ", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Access(connection.RemoteAddr(), request.Destination(), log.AccessAccepted, "")
|
log.Access(connection.RemoteAddr(), request.Destination(), log.AccessAccepted, "")
|
||||||
log.Debug("VMessIn: Received request for ", request.Destination())
|
log.Debug("VMessIn: Received request for ", request.Destination())
|
||||||
|
|
||||||
|
if request.Option.IsChunkStream() {
|
||||||
|
connection.SetReusable(true)
|
||||||
|
}
|
||||||
|
|
||||||
ray := this.packetDispatcher.DispatchToOutbound(request.Destination())
|
ray := this.packetDispatcher.DispatchToOutbound(request.Destination())
|
||||||
input := ray.InboundInput()
|
input := ray.InboundInput()
|
||||||
output := ray.InboundOutput()
|
output := ray.InboundOutput()
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/v2ray/v2ray-core/proxy"
|
"github.com/v2ray/v2ray-core/proxy"
|
||||||
"github.com/v2ray/v2ray-core/proxy/internal"
|
"github.com/v2ray/v2ray-core/proxy/internal"
|
||||||
vmessio "github.com/v2ray/v2ray-core/proxy/vmess/io"
|
vmessio "github.com/v2ray/v2ray-core/proxy/vmess/io"
|
||||||
"github.com/v2ray/v2ray-core/transport/dialer"
|
"github.com/v2ray/v2ray-core/transport/hub"
|
||||||
"github.com/v2ray/v2ray-core/transport/ray"
|
"github.com/v2ray/v2ray-core/transport/ray"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *al
|
|||||||
Option: protocol.RequestOptionChunkStream,
|
Option: protocol.RequestOptionChunkStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := dialer.Dial(destination)
|
conn, err := hub.Dial(destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to open ", destination, ": ", err)
|
log.Error("Failed to open ", destination, ": ", err)
|
||||||
return err
|
return err
|
||||||
@ -49,6 +49,9 @@ func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *al
|
|||||||
log.Info("VMessOut: Tunneling request to ", request.Address, " via ", destination)
|
log.Info("VMessOut: Tunneling request to ", request.Address, " via ", destination)
|
||||||
|
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
if request.Option.IsChunkStream() {
|
||||||
|
conn.SetReusable(true)
|
||||||
|
}
|
||||||
|
|
||||||
input := ray.OutboundInput()
|
input := ray.OutboundInput()
|
||||||
output := ray.OutboundOutput()
|
output := ray.OutboundOutput()
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"port": 50030,
|
"port": 50030,
|
||||||
"listen": "127.0.0.1",
|
"listen": "127.0.0.1",
|
||||||
|
"log": {
|
||||||
|
"loglevel": "debug"
|
||||||
|
},
|
||||||
"inbound": {
|
"inbound": {
|
||||||
"protocol": "dokodemo-door",
|
"protocol": "dokodemo-door",
|
||||||
"settings": {
|
"settings": {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"port": 50031,
|
"port": 50031,
|
||||||
"listen": "127.0.0.1",
|
"listen": "127.0.0.1",
|
||||||
"log": {
|
"log": {
|
||||||
"loglevel": "warning"
|
"loglevel": "debug"
|
||||||
},
|
},
|
||||||
"inbound": {
|
"inbound": {
|
||||||
"protocol": "vmess",
|
"protocol": "vmess",
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
package dialer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrorInvalidHost = errors.New("Invalid Host.")
|
|
||||||
)
|
|
||||||
|
|
||||||
func Dial(dest v2net.Destination) (net.Conn, error) {
|
|
||||||
if dest.Address().IsDomain() {
|
|
||||||
dialer := &net.Dialer{
|
|
||||||
Timeout: time.Second * 60,
|
|
||||||
DualStack: true,
|
|
||||||
}
|
|
||||||
network := "tcp"
|
|
||||||
if dest.IsUDP() {
|
|
||||||
network = "udp"
|
|
||||||
}
|
|
||||||
return dialer.Dial(network, dest.NetAddr())
|
|
||||||
}
|
|
||||||
|
|
||||||
ip := dest.Address().IP()
|
|
||||||
if dest.IsTCP() {
|
|
||||||
return net.DialTCP("tcp", nil, &net.TCPAddr{
|
|
||||||
IP: ip,
|
|
||||||
Port: int(dest.Port()),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
return net.DialUDP("udp", nil, &net.UDPAddr{
|
|
||||||
IP: ip,
|
|
||||||
Port: int(dest.Port()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,9 +7,14 @@ import (
|
|||||||
|
|
||||||
type ConnectionHandler func(*Connection)
|
type ConnectionHandler func(*Connection)
|
||||||
|
|
||||||
|
type ConnectionManager interface {
|
||||||
|
Recycle(string, net.Conn)
|
||||||
|
}
|
||||||
|
|
||||||
type Connection struct {
|
type Connection struct {
|
||||||
|
dest string
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
listener *TCPHub
|
listener ConnectionManager
|
||||||
reusable bool
|
reusable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,22 +38,12 @@ func (this *Connection) Close() error {
|
|||||||
return ErrorClosedConnection
|
return ErrorClosedConnection
|
||||||
}
|
}
|
||||||
if this.Reusable() {
|
if this.Reusable() {
|
||||||
this.listener.Recycle(this.conn)
|
this.listener.Recycle(this.dest, this.conn)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return this.conn.Close()
|
return this.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Connection) Release() {
|
|
||||||
if this == nil || this.listener == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Close()
|
|
||||||
this.conn = nil
|
|
||||||
this.listener = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Connection) LocalAddr() net.Addr {
|
func (this *Connection) LocalAddr() net.Addr {
|
||||||
return this.conn.LocalAddr()
|
return this.conn.LocalAddr()
|
||||||
}
|
}
|
||||||
|
107
transport/hub/connection_cache.go
Normal file
107
transport/hub/connection_cache.go
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package hub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AwaitingConnection struct {
|
||||||
|
conn net.Conn
|
||||||
|
expire time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AwaitingConnection) Expired() bool {
|
||||||
|
return this.expire.Before(time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionCache struct {
|
||||||
|
sync.Mutex
|
||||||
|
cache map[string][]*AwaitingConnection
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnectionCache() *ConnectionCache {
|
||||||
|
c := &ConnectionCache{
|
||||||
|
cache: make(map[string][]*AwaitingConnection),
|
||||||
|
}
|
||||||
|
go c.Cleanup()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ConnectionCache) Cleanup() {
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Second * 4)
|
||||||
|
this.Lock()
|
||||||
|
for key, value := range this.cache {
|
||||||
|
size := len(value)
|
||||||
|
changed := false
|
||||||
|
for i := 0; i < size; {
|
||||||
|
if value[i].Expired() {
|
||||||
|
value[i].conn.Close()
|
||||||
|
value[i] = value[size-1]
|
||||||
|
size--
|
||||||
|
changed = true
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if changed {
|
||||||
|
for i := size; i < len(value); i++ {
|
||||||
|
value[i] = nil
|
||||||
|
}
|
||||||
|
value = value[:size]
|
||||||
|
this.cache[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ConnectionCache) Recycle(dest string, conn net.Conn) {
|
||||||
|
this.Lock()
|
||||||
|
defer this.Unlock()
|
||||||
|
|
||||||
|
aconn := &AwaitingConnection{
|
||||||
|
conn: conn,
|
||||||
|
expire: time.Now().Add(time.Second * 4),
|
||||||
|
}
|
||||||
|
|
||||||
|
var list []*AwaitingConnection
|
||||||
|
if v, found := this.cache[dest]; found {
|
||||||
|
v = append(v, aconn)
|
||||||
|
list = v
|
||||||
|
} else {
|
||||||
|
list = []*AwaitingConnection{aconn}
|
||||||
|
}
|
||||||
|
this.cache[dest] = list
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindFirstValid(list []*AwaitingConnection) int {
|
||||||
|
for idx, conn := range list {
|
||||||
|
if !conn.Expired() {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
conn.conn.Close()
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ConnectionCache) Get(dest string) net.Conn {
|
||||||
|
this.Lock()
|
||||||
|
defer this.Unlock()
|
||||||
|
|
||||||
|
list, found := this.cache[dest]
|
||||||
|
if !found {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
firstValid := FindFirstValid(list)
|
||||||
|
if firstValid == -1 {
|
||||||
|
delete(this.cache, dest)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
res := list[firstValid].conn
|
||||||
|
list = list[firstValid+1:]
|
||||||
|
this.cache[dest] = list
|
||||||
|
return res
|
||||||
|
}
|
63
transport/hub/dialer.go
Normal file
63
transport/hub/dialer.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package hub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/v2ray/v2ray-core/common/log"
|
||||||
|
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrorInvalidHost = errors.New("Invalid Host.")
|
||||||
|
|
||||||
|
globalCache = NewConnectionCache()
|
||||||
|
)
|
||||||
|
|
||||||
|
func Dial(dest v2net.Destination) (*Connection, error) {
|
||||||
|
destStr := dest.String()
|
||||||
|
conn := globalCache.Get(destStr)
|
||||||
|
if conn == nil {
|
||||||
|
var err error
|
||||||
|
log.Debug("Hub: Dialling new connection to ", dest)
|
||||||
|
conn, err = DialWithoutCache(dest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debug("Hub: Reusing connection to ", dest)
|
||||||
|
}
|
||||||
|
return &Connection{
|
||||||
|
dest: destStr,
|
||||||
|
conn: conn,
|
||||||
|
listener: globalCache,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DialWithoutCache(dest v2net.Destination) (net.Conn, error) {
|
||||||
|
if dest.Address().IsDomain() {
|
||||||
|
dialer := &net.Dialer{
|
||||||
|
Timeout: time.Second * 60,
|
||||||
|
DualStack: true,
|
||||||
|
}
|
||||||
|
network := "tcp"
|
||||||
|
if dest.IsUDP() {
|
||||||
|
network = "udp"
|
||||||
|
}
|
||||||
|
return dialer.Dial(network, dest.NetAddr())
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := dest.Address().IP()
|
||||||
|
if dest.IsTCP() {
|
||||||
|
return net.DialTCP("tcp", nil, &net.TCPAddr{
|
||||||
|
IP: ip,
|
||||||
|
Port: int(dest.Port()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return net.DialUDP("udp", nil, &net.UDPAddr{
|
||||||
|
IP: ip,
|
||||||
|
Port: int(dest.Port()),
|
||||||
|
})
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package dialer_test
|
package hub_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -7,7 +7,7 @@ import (
|
|||||||
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
|
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
|
||||||
"github.com/v2ray/v2ray-core/testing/assert"
|
"github.com/v2ray/v2ray-core/testing/assert"
|
||||||
"github.com/v2ray/v2ray-core/testing/servers/tcp"
|
"github.com/v2ray/v2ray-core/testing/servers/tcp"
|
||||||
. "github.com/v2ray/v2ray-core/transport/dialer"
|
. "github.com/v2ray/v2ray-core/transport/hub"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDialDomain(t *testing.T) {
|
func TestDialDomain(t *testing.T) {
|
@ -49,24 +49,14 @@ func ListenTCP(address v2net.Address, port v2net.Port, callback ConnectionHandle
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *TCPHub) Close() {
|
func (this *TCPHub) Close() {
|
||||||
this.Lock()
|
|
||||||
defer this.Unlock()
|
|
||||||
|
|
||||||
this.accepting = false
|
this.accepting = false
|
||||||
this.listener.Close()
|
this.listener.Close()
|
||||||
this.listener = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *TCPHub) start() {
|
func (this *TCPHub) start() {
|
||||||
this.accepting = true
|
this.accepting = true
|
||||||
for this.accepting {
|
for this.accepting {
|
||||||
this.Lock()
|
|
||||||
if !this.accepting {
|
|
||||||
this.Unlock()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
conn, err := this.listener.Accept()
|
conn, err := this.listener.Accept()
|
||||||
this.Unlock()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if this.accepting {
|
if this.accepting {
|
||||||
@ -75,6 +65,7 @@ func (this *TCPHub) start() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
go this.connCallback(&Connection{
|
go this.connCallback(&Connection{
|
||||||
|
dest: conn.RemoteAddr().String(),
|
||||||
conn: conn,
|
conn: conn,
|
||||||
listener: this,
|
listener: this,
|
||||||
})
|
})
|
||||||
@ -82,9 +73,10 @@ func (this *TCPHub) start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Private
|
// @Private
|
||||||
func (this *TCPHub) Recycle(conn net.Conn) {
|
func (this *TCPHub) Recycle(dest string, conn net.Conn) {
|
||||||
if this.accepting {
|
if this.accepting {
|
||||||
go this.connCallback(&Connection{
|
go this.connCallback(&Connection{
|
||||||
|
dest: dest,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
listener: this,
|
listener: this,
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user