1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2025-01-04 16:37:12 -05:00

rewrite ray -> pipe

This commit is contained in:
Darien Raymond 2018-04-17 00:31:10 +02:00
parent 64ebba3cff
commit 5d23604713
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169
29 changed files with 347 additions and 598 deletions

View File

@ -2,12 +2,13 @@ package commander
import ( import (
"context" "context"
"net"
"sync" "sync"
"v2ray.com/core"
"v2ray.com/core/common" "v2ray.com/core/common"
"v2ray.com/core/common/net"
"v2ray.com/core/common/signal" "v2ray.com/core/common/signal"
"v2ray.com/core/transport/ray" "v2ray.com/core/transport/pipe"
) )
// OutboundListener is a net.Listener for listening gRPC connections. // OutboundListener is a net.Listener for listening gRPC connections.
@ -68,18 +69,18 @@ type Outbound struct {
} }
// Dispatch implements core.OutboundHandler. // Dispatch implements core.OutboundHandler.
func (co *Outbound) Dispatch(ctx context.Context, r ray.OutboundRay) { func (co *Outbound) Dispatch(ctx context.Context, link *core.Link) {
co.access.RLock() co.access.RLock()
if co.closed { if co.closed {
r.OutboundInput().CloseError() pipe.CloseError(link.Reader)
r.OutboundOutput().CloseError() pipe.CloseError(link.Writer)
co.access.RUnlock() co.access.RUnlock()
return return
} }
closeSignal := signal.NewNotifier() closeSignal := signal.NewNotifier()
c := ray.NewConnection(r.OutboundInput(), r.OutboundOutput(), ray.ConnCloseSignal(closeSignal)) c := net.NewConnection(net.ConnectionInputMulti(link.Writer), net.ConnectionOutputMulti(link.Reader), net.ConnectionOnClose(closeSignal))
co.listener.add(c) co.listener.add(c)
co.access.RUnlock() co.access.RUnlock()
<-closeSignal.Wait() <-closeSignal.Wait()

View File

@ -12,14 +12,45 @@ import (
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
"v2ray.com/core/common/net" "v2ray.com/core/common/net"
"v2ray.com/core/common/protocol" "v2ray.com/core/common/protocol"
"v2ray.com/core/common/stats"
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/transport/ray" "v2ray.com/core/transport/pipe"
) )
var ( var (
errSniffingTimeout = newError("timeout on sniffing") errSniffingTimeout = newError("timeout on sniffing")
) )
type cachedReader struct {
reader *pipe.Reader
cache buf.MultiBuffer
}
func (r *cachedReader) Cache(b *buf.Buffer) {
mb, _ := r.reader.ReadMultiBufferWithTimeout(time.Millisecond * 100)
if !mb.IsEmpty() {
r.cache.WriteMultiBuffer(mb)
}
common.Must(b.Reset(func(x []byte) (int, error) {
return r.cache.Copy(x), nil
}))
}
func (r *cachedReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
if !r.cache.IsEmpty() {
mb := r.cache
r.cache = nil
return mb, nil
}
return r.reader.ReadMultiBuffer()
}
func (r *cachedReader) CloseError() {
r.cache.Release()
r.reader.CloseError()
}
// DefaultDispatcher is a default implementation of Dispatcher. // DefaultDispatcher is a default implementation of Dispatcher.
type DefaultDispatcher struct { type DefaultDispatcher struct {
ohm core.OutboundHandlerManager ohm core.OutboundHandlerManager
@ -52,45 +83,64 @@ func (*DefaultDispatcher) Start() error {
// Close implements common.Closable. // Close implements common.Closable.
func (*DefaultDispatcher) Close() error { return nil } func (*DefaultDispatcher) Close() error { return nil }
func (d *DefaultDispatcher) getRayOption(user *protocol.User) []ray.Option { func (d *DefaultDispatcher) getLink(ctx context.Context) (*core.Link, *core.Link) {
var rayOptions []ray.Option uplinkReader, uplinkWriter := pipe.New()
downlinkReader, downlinkWriter := pipe.New()
inboundLink := &core.Link{
Reader: downlinkReader,
Writer: uplinkWriter,
}
outboundLink := &core.Link{
Reader: uplinkReader,
Writer: downlinkWriter,
}
user := protocol.UserFromContext(ctx)
if user != nil && len(user.Email) > 0 { if user != nil && len(user.Email) > 0 {
p := d.policy.ForLevel(user.Level) p := d.policy.ForLevel(user.Level)
if p.Stats.UserUplink { if p.Stats.UserUplink {
name := "user>>>" + user.Email + ">>>traffic>>>uplink" name := "user>>>" + user.Email + ">>>traffic>>>uplink"
if c, _ := core.GetOrRegisterStatCounter(d.stats, name); c != nil { if c, _ := core.GetOrRegisterStatCounter(d.stats, name); c != nil {
rayOptions = append(rayOptions, ray.WithUplinkStatCounter(c)) inboundLink.Writer = &stats.SizeStatWriter{
Counter: c,
Writer: inboundLink.Writer,
}
} }
} }
if p.Stats.UserDownlink { if p.Stats.UserDownlink {
name := "user>>>" + user.Email + ">>>traffic>>>downlink" name := "user>>>" + user.Email + ">>>traffic>>>downlink"
if c, _ := core.GetOrRegisterStatCounter(d.stats, name); c != nil { if c, _ := core.GetOrRegisterStatCounter(d.stats, name); c != nil {
rayOptions = append(rayOptions, ray.WithDownlinkStatCounter(c)) outboundLink.Writer = &stats.SizeStatWriter{
Counter: c,
Writer: outboundLink.Writer,
}
} }
} }
} }
return rayOptions return inboundLink, outboundLink
} }
// Dispatch implements core.Dispatcher. // Dispatch implements core.Dispatcher.
func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (ray.InboundRay, error) { func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (*core.Link, error) {
if !destination.IsValid() { if !destination.IsValid() {
panic("Dispatcher: Invalid destination.") panic("Dispatcher: Invalid destination.")
} }
ctx = proxy.ContextWithTarget(ctx, destination) ctx = proxy.ContextWithTarget(ctx, destination)
user := protocol.UserFromContext(ctx) inbound, outbound := d.getLink(ctx)
rayOptions := d.getRayOption(user)
outbound := ray.New(ctx, rayOptions...)
snifferList := proxyman.ProtocolSniffersFromContext(ctx) snifferList := proxyman.ProtocolSniffersFromContext(ctx)
if destination.Address.Family().IsDomain() || len(snifferList) == 0 { if destination.Address.Family().IsDomain() || len(snifferList) == 0 {
go d.routedDispatch(ctx, outbound, destination) go d.routedDispatch(ctx, outbound, destination)
} else { } else {
go func() { go func() {
domain, err := sniffer(ctx, snifferList, outbound) cReader := &cachedReader{
reader: outbound.Reader.(*pipe.Reader),
}
outbound.Reader = cReader
domain, err := sniffer(ctx, snifferList, cReader)
if err == nil { if err == nil {
newError("sniffed domain: ", domain).WithContext(ctx).WriteToLog() newError("sniffed domain: ", domain).WithContext(ctx).WriteToLog()
destination.Address = net.ParseAddress(domain) destination.Address = net.ParseAddress(domain)
@ -99,10 +149,10 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
d.routedDispatch(ctx, outbound, destination) d.routedDispatch(ctx, outbound, destination)
}() }()
} }
return outbound, nil return inbound, nil
} }
func sniffer(ctx context.Context, snifferList []proxyman.KnownProtocols, outbound ray.OutboundRay) (string, error) { func sniffer(ctx context.Context, snifferList []proxyman.KnownProtocols, cReader *cachedReader) (string, error) {
payload := buf.New() payload := buf.New()
defer payload.Release() defer payload.Release()
@ -117,7 +167,8 @@ func sniffer(ctx context.Context, snifferList []proxyman.KnownProtocols, outboun
if totalAttempt > 5 { if totalAttempt > 5 {
return "", errSniffingTimeout return "", errSniffingTimeout
} }
outbound.OutboundInput().Peek(payload)
cReader.Cache(payload)
if !payload.IsEmpty() { if !payload.IsEmpty() {
domain, err := sniffer.Sniff(payload.Bytes()) domain, err := sniffer.Sniff(payload.Bytes())
if err != ErrMoreData { if err != ErrMoreData {
@ -132,7 +183,7 @@ func sniffer(ctx context.Context, snifferList []proxyman.KnownProtocols, outboun
} }
} }
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, outbound ray.OutboundRay, destination net.Destination) { func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *core.Link, destination net.Destination) {
dispatcher := d.ohm.GetDefaultHandler() dispatcher := d.ohm.GetDefaultHandler()
if d.router != nil { if d.router != nil {
if tag, err := d.router.PickRoute(ctx); err == nil { if tag, err := d.router.PickRoute(ctx); err == nil {
@ -146,7 +197,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, outbound ray.Out
newError("default route for ", destination).WithContext(ctx).WriteToLog() newError("default route for ", destination).WithContext(ctx).WriteToLog()
} }
} }
dispatcher.Dispatch(ctx, outbound) dispatcher.Dispatch(ctx, link)
} }
func init() { func init() {

View File

@ -18,7 +18,7 @@ import (
"v2ray.com/core/common/protocol" "v2ray.com/core/common/protocol"
"v2ray.com/core/common/signal" "v2ray.com/core/common/signal"
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/transport/ray" "v2ray.com/core/transport/pipe"
) )
const ( const (
@ -41,12 +41,12 @@ func NewClientManager(p proxy.Outbound, d proxy.Dialer, c *proxyman.Multiplexing
} }
} }
func (m *ClientManager) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) error { func (m *ClientManager) Dispatch(ctx context.Context, link *core.Link) error {
m.access.Lock() m.access.Lock()
defer m.access.Unlock() defer m.access.Unlock()
for _, client := range m.clients { for _, client := range m.clients {
if client.Dispatch(ctx, outboundRay) { if client.Dispatch(ctx, link) {
return nil return nil
} }
} }
@ -56,7 +56,7 @@ func (m *ClientManager) Dispatch(ctx context.Context, outboundRay ray.OutboundRa
return newError("failed to create client").Base(err) return newError("failed to create client").Base(err)
} }
m.clients = append(m.clients, client) m.clients = append(m.clients, client)
client.Dispatch(ctx, outboundRay) client.Dispatch(ctx, link)
return nil return nil
} }
@ -76,7 +76,7 @@ func (m *ClientManager) onClientFinish() {
type Client struct { type Client struct {
sessionManager *SessionManager sessionManager *SessionManager
inboundRay ray.InboundRay link core.Link
done *signal.Done done *signal.Done
manager *ClientManager manager *ClientManager
concurrency uint32 concurrency uint32
@ -89,18 +89,22 @@ var muxCoolPort = net.Port(9527)
func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client, error) { func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client, error) {
ctx := proxy.ContextWithTarget(context.Background(), net.TCPDestination(muxCoolAddress, muxCoolPort)) ctx := proxy.ContextWithTarget(context.Background(), net.TCPDestination(muxCoolAddress, muxCoolPort))
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
pipe := ray.New(ctx) uplinkReader, upLinkWriter := pipe.New()
downlinkReader, downlinkWriter := pipe.New()
c := &Client{ c := &Client{
sessionManager: NewSessionManager(), sessionManager: NewSessionManager(),
inboundRay: pipe, link: core.Link{
Reader: downlinkReader,
Writer: upLinkWriter,
},
done: signal.NewDone(), done: signal.NewDone(),
manager: m, manager: m,
concurrency: m.config.Concurrency, concurrency: m.config.Concurrency,
} }
go func() { go func() {
if err := p.Process(ctx, pipe, dialer); err != nil { if err := p.Process(ctx, &core.Link{Reader: uplinkReader, Writer: downlinkWriter}, dialer); err != nil {
errors.New("failed to handler mux client connection").Base(err).WriteToLog() errors.New("failed to handler mux client connection").Base(err).WriteToLog()
} }
c.done.Close() c.done.Close()
@ -127,8 +131,8 @@ func (m *Client) monitor() {
select { select {
case <-m.done.Wait(): case <-m.done.Wait():
m.sessionManager.Close() m.sessionManager.Close()
m.inboundRay.InboundInput().Close() common.Close(m.link.Writer)
m.inboundRay.InboundOutput().CloseError() pipe.CloseError(m.link.Reader)
return return
case <-timer.C: case <-timer.C:
size := m.sessionManager.Size() size := m.sessionManager.Size()
@ -159,7 +163,7 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
writer.Close() writer.Close()
} }
func (m *Client) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) bool { func (m *Client) Dispatch(ctx context.Context, link *core.Link) bool {
sm := m.sessionManager sm := m.sessionManager
if sm.Size() >= int(m.concurrency) || sm.Count() >= maxTotal { if sm.Size() >= int(m.concurrency) || sm.Count() >= maxTotal {
return false return false
@ -173,9 +177,9 @@ func (m *Client) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) bool
if s == nil { if s == nil {
return false return false
} }
s.input = outboundRay.OutboundInput() s.input = link.Reader
s.output = outboundRay.OutboundOutput() s.output = link.Writer
go fetchInput(ctx, s, m.inboundRay.InboundInput()) go fetchInput(ctx, s, m.link.Writer)
return true return true
} }
@ -205,7 +209,7 @@ func (m *Client) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReade
if s, found := m.sessionManager.Get(meta.SessionID); found { if s, found := m.sessionManager.Get(meta.SessionID); found {
if err := buf.Copy(s.NewReader(reader), s.output); err != nil { if err := buf.Copy(s.NewReader(reader), s.output); err != nil {
drain(reader) drain(reader)
s.input.CloseError() pipe.CloseError(s.input)
return s.Close() return s.Close()
} }
return nil return nil
@ -216,8 +220,8 @@ func (m *Client) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReade
func (m *Client) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error { func (m *Client) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
if s, found := m.sessionManager.Get(meta.SessionID); found { if s, found := m.sessionManager.Get(meta.SessionID); found {
if meta.Option.Has(OptionError) { if meta.Option.Has(OptionError) {
s.input.CloseError() pipe.CloseError(s.input)
s.output.CloseError() pipe.CloseError(s.output)
} }
s.Close() s.Close()
} }
@ -230,7 +234,7 @@ func (m *Client) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader
func (m *Client) fetchOutput() { func (m *Client) fetchOutput() {
defer m.done.Close() defer m.done.Close()
reader := buf.NewBufferedReader(m.inboundRay.InboundOutput()) reader := buf.NewBufferedReader(m.link.Reader)
for { for {
meta, err := ReadMetadata(reader) meta, err := ReadMetadata(reader)
@ -274,19 +278,24 @@ func NewServer(ctx context.Context) *Server {
return s return s
} }
func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error) { func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (*core.Link, error) {
if dest.Address != muxCoolAddress { if dest.Address != muxCoolAddress {
return s.dispatcher.Dispatch(ctx, dest) return s.dispatcher.Dispatch(ctx, dest)
} }
ray := ray.New(ctx) uplinkReader, uplinkWriter := pipe.New()
downlinkReader, downlinkWriter := pipe.New()
worker := &ServerWorker{ worker := &ServerWorker{
dispatcher: s.dispatcher, dispatcher: s.dispatcher,
outboundRay: ray, link: &core.Link{
Reader: uplinkReader,
Writer: downlinkWriter,
},
sessionManager: NewSessionManager(), sessionManager: NewSessionManager(),
} }
go worker.run(ctx) go worker.run(ctx)
return ray, nil return &core.Link{Reader: downlinkReader, Writer: uplinkWriter}, nil
} }
func (s *Server) Start() error { func (s *Server) Start() error {
@ -299,7 +308,7 @@ func (s *Server) Close() error {
type ServerWorker struct { type ServerWorker struct {
dispatcher core.Dispatcher dispatcher core.Dispatcher
outboundRay ray.OutboundRay link *core.Link
sessionManager *SessionManager sessionManager *SessionManager
} }
@ -334,7 +343,7 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
} }
log.Record(msg) log.Record(msg)
} }
inboundRay, err := w.dispatcher.Dispatch(ctx, meta.Target) link, err := w.dispatcher.Dispatch(ctx, meta.Target)
if err != nil { if err != nil {
if meta.Option.Has(OptionData) { if meta.Option.Has(OptionData) {
drain(reader) drain(reader)
@ -342,8 +351,8 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
return newError("failed to dispatch request.").Base(err) return newError("failed to dispatch request.").Base(err)
} }
s := &Session{ s := &Session{
input: inboundRay.InboundOutput(), input: link.Reader,
output: inboundRay.InboundInput(), output: link.Writer,
parent: w.sessionManager, parent: w.sessionManager,
ID: meta.SessionID, ID: meta.SessionID,
transferType: protocol.TransferTypeStream, transferType: protocol.TransferTypeStream,
@ -352,7 +361,7 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
s.transferType = protocol.TransferTypePacket s.transferType = protocol.TransferTypePacket
} }
w.sessionManager.Add(s) w.sessionManager.Add(s)
go handle(ctx, s, w.outboundRay.OutboundOutput()) go handle(ctx, s, w.link.Writer)
if meta.Option.Has(OptionData) { if meta.Option.Has(OptionData) {
return buf.Copy(s.NewReader(reader), s.output, buf.IgnoreWriterError()) return buf.Copy(s.NewReader(reader), s.output, buf.IgnoreWriterError())
} }
@ -366,7 +375,7 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
if s, found := w.sessionManager.Get(meta.SessionID); found { if s, found := w.sessionManager.Get(meta.SessionID); found {
if err := buf.Copy(s.NewReader(reader), s.output); err != nil { if err := buf.Copy(s.NewReader(reader), s.output); err != nil {
drain(reader) drain(reader)
s.input.CloseError() pipe.CloseError(s.input)
return s.Close() return s.Close()
} }
return nil return nil
@ -377,8 +386,8 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error { func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
if s, found := w.sessionManager.Get(meta.SessionID); found { if s, found := w.sessionManager.Get(meta.SessionID); found {
if meta.Option.Has(OptionError) { if meta.Option.Has(OptionError) {
s.input.CloseError() pipe.CloseError(s.input)
s.output.CloseError() pipe.CloseError(s.output)
} }
s.Close() s.Close()
} }
@ -414,7 +423,7 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead
} }
func (w *ServerWorker) run(ctx context.Context) { func (w *ServerWorker) run(ctx context.Context) {
input := w.outboundRay.OutboundInput() input := w.link.Reader
reader := buf.NewBufferedReader(input) reader := buf.NewBufferedReader(input)
defer w.sessionManager.Close() defer w.sessionManager.Close()
@ -428,7 +437,7 @@ func (w *ServerWorker) run(ctx context.Context) {
if err != nil { if err != nil {
if errors.Cause(err) != io.EOF { if errors.Cause(err) != io.EOF {
newError("unexpected EOF").Base(err).WithContext(ctx).WriteToLog() newError("unexpected EOF").Base(err).WithContext(ctx).WriteToLog()
input.CloseError() pipe.CloseError(input)
} }
return return
} }

View File

@ -3,9 +3,9 @@ package mux
import ( import (
"sync" "sync"
"v2ray.com/core/common"
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
"v2ray.com/core/common/protocol" "v2ray.com/core/common/protocol"
"v2ray.com/core/transport/ray"
) )
type SessionManager struct { type SessionManager struct {
@ -118,8 +118,8 @@ func (m *SessionManager) Close() error {
m.closed = true m.closed = true
for _, s := range m.sessions { for _, s := range m.sessions {
s.input.Close() common.Close(s.input)
s.output.Close() common.Close(s.output)
} }
m.sessions = nil m.sessions = nil
@ -128,8 +128,8 @@ func (m *SessionManager) Close() error {
// Session represents a client connection in a Mux connection. // Session represents a client connection in a Mux connection.
type Session struct { type Session struct {
input ray.InputStream input buf.Reader
output ray.OutputStream output buf.Writer
parent *SessionManager parent *SessionManager
ID uint16 ID uint16
transferType protocol.TransferType transferType protocol.TransferType
@ -137,8 +137,8 @@ type Session struct {
// Close closes all resources associated with this session. // Close closes all resources associated with this session.
func (s *Session) Close() error { func (s *Session) Close() error {
s.output.Close() common.Close(s.output)
s.input.Close() common.Close(s.input)
s.parent.Remove(s.ID) s.parent.Remove(s.ID)
return nil return nil
} }

View File

@ -10,7 +10,7 @@ import (
"v2ray.com/core/common/net" "v2ray.com/core/common/net"
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet"
"v2ray.com/core/transport/ray" "v2ray.com/core/transport/pipe"
) )
type Handler struct { type Handler struct {
@ -74,21 +74,21 @@ func (h *Handler) Tag() string {
} }
// Dispatch implements proxy.Outbound.Dispatch. // Dispatch implements proxy.Outbound.Dispatch.
func (h *Handler) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) { func (h *Handler) Dispatch(ctx context.Context, link *core.Link) {
if h.mux != nil { if h.mux != nil {
if err := h.mux.Dispatch(ctx, outboundRay); err != nil { if err := h.mux.Dispatch(ctx, link); err != nil {
newError("failed to process outbound traffic").Base(err).WithContext(ctx).WriteToLog() newError("failed to process outbound traffic").Base(err).WithContext(ctx).WriteToLog()
outboundRay.OutboundOutput().CloseError() pipe.CloseError(link.Writer)
} }
} else { } else {
if err := h.proxy.Process(ctx, outboundRay, h); err != nil { if err := h.proxy.Process(ctx, link, h); err != nil {
// Ensure outbound ray is properly closed. // Ensure outbound ray is properly closed.
newError("failed to process outbound traffic").Base(err).WithContext(ctx).WriteToLog() newError("failed to process outbound traffic").Base(err).WithContext(ctx).WriteToLog()
outboundRay.OutboundOutput().CloseError() pipe.CloseError(link.Writer)
} else { } else {
outboundRay.OutboundOutput().Close() common.Close(link.Writer)
} }
outboundRay.OutboundInput().CloseError() pipe.CloseError(link.Reader)
} }
} }
@ -101,9 +101,12 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn
if handler != nil { if handler != nil {
newError("proxying to ", tag, " for dest ", dest).AtDebug().WithContext(ctx).WriteToLog() newError("proxying to ", tag, " for dest ", dest).AtDebug().WithContext(ctx).WriteToLog()
ctx = proxy.ContextWithTarget(ctx, dest) ctx = proxy.ContextWithTarget(ctx, dest)
stream := ray.New(ctx)
go handler.Dispatch(ctx, stream) uplinkReader, uplinkWriter := pipe.New()
return ray.NewConnection(stream.InboundOutput(), stream.InboundInput()), nil downlinkReader, downlinkWriter := pipe.New()
go handler.Dispatch(ctx, &core.Link{Reader: uplinkReader, Writer: downlinkWriter})
return net.NewConnection(net.ConnectionInputMulti(uplinkWriter), net.ConnectionOutputMulti(downlinkReader)), nil
} }
newError("failed to get outbound handler with tag: ", tag).AtWarning().WithContext(ctx).WriteToLog() newError("failed to get outbound handler with tag: ", tag).AtWarning().WithContext(ctx).WriteToLog()

View File

@ -172,6 +172,12 @@ func (mb *MultiBuffer) Write(b []byte) (int, error) {
return totalBytes, nil return totalBytes, nil
} }
// WriteMultiBuffer implements Writer.
func (mb *MultiBuffer) WriteMultiBuffer(b MultiBuffer) error {
*mb = append(*mb, b...)
return nil
}
// Len returns the total number of bytes in the MultiBuffer. // Len returns the total number of bytes in the MultiBuffer.
func (mb MultiBuffer) Len() int32 { func (mb MultiBuffer) Len() int32 {
size := int32(0) size := int32(0)

View File

@ -1,69 +1,81 @@
package ray package net
import ( import (
"io" "io"
"net" "net"
"time" "time"
"v2ray.com/core/common"
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
"v2ray.com/core/common/signal" "v2ray.com/core/common/signal"
) )
type ConnectionOption func(*connection) type ConnectionOption func(*connection)
func ConnLocalAddr(addr net.Addr) ConnectionOption { func ConnectionLocalAddr(a net.Addr) ConnectionOption {
return func(c *connection) { return func(c *connection) {
c.localAddr = addr c.local = a
} }
} }
func ConnRemoteAddr(addr net.Addr) ConnectionOption { func ConnectionRemoteAddr(a net.Addr) ConnectionOption {
return func(c *connection) { return func(c *connection) {
c.remoteAddr = addr c.remote = a
} }
} }
func ConnCloseSignal(s *signal.Notifier) ConnectionOption { func ConnectionInput(writer io.Writer) ConnectionOption {
return func(c *connection) { return func(c *connection) {
c.closeSignal = s c.writer = buf.NewWriter(writer)
} }
} }
type connection struct { func ConnectionInputMulti(writer buf.Writer) ConnectionOption {
input InputStream return func(c *connection) {
output OutputStream c.writer = writer
closed bool }
localAddr net.Addr
remoteAddr net.Addr
closeSignal *signal.Notifier
reader *buf.BufferedReader
} }
var zeroAddr net.Addr = &net.TCPAddr{IP: []byte{0, 0, 0, 0}} func ConnectionOutput(reader io.Reader) ConnectionOption {
return func(c *connection) {
c.reader = buf.NewBufferedReader(buf.NewReader(reader))
}
}
// NewConnection wraps a Ray into net.Conn. func ConnectionOutputMulti(reader buf.Reader) ConnectionOption {
func NewConnection(input InputStream, output OutputStream, options ...ConnectionOption) net.Conn { return func(c *connection) {
c.reader = buf.NewBufferedReader(reader)
}
}
func ConnectionOnClose(s *signal.Notifier) ConnectionOption {
return func(c *connection) {
c.onClose = s
}
}
func NewConnection(opts ...ConnectionOption) net.Conn {
c := &connection{ c := &connection{
input: input, done: signal.NewDone(),
output: output,
localAddr: zeroAddr,
remoteAddr: zeroAddr,
reader: buf.NewBufferedReader(input),
} }
for _, opt := range options { for _, opt := range opts {
opt(c) opt(c)
} }
return c return c
} }
// Read implements net.Conn.Read(). type connection struct {
func (c *connection) Read(b []byte) (int, error) { reader *buf.BufferedReader
if c.closed { writer buf.Writer
return 0, io.EOF done *signal.Done
onClose *signal.Notifier
local Addr
remote Addr
} }
func (c *connection) Read(b []byte) (int, error) {
return c.reader.Read(b) return c.reader.Read(b)
} }
@ -74,43 +86,44 @@ func (c *connection) ReadMultiBuffer() (buf.MultiBuffer, error) {
// Write implements net.Conn.Write(). // Write implements net.Conn.Write().
func (c *connection) Write(b []byte) (int, error) { func (c *connection) Write(b []byte) (int, error) {
if c.closed { if c.done.Done() {
return 0, io.ErrClosedPipe return 0, io.ErrClosedPipe
} }
l := len(b) l := len(b)
mb := buf.NewMultiBufferCap(int32(l)/buf.Size + 1) mb := buf.NewMultiBufferCap(int32(l)/buf.Size + 1)
mb.Write(b) common.Must2(mb.Write(b))
return l, c.output.WriteMultiBuffer(mb) return l, c.writer.WriteMultiBuffer(mb)
} }
func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error {
if c.closed { if c.done.Done() {
return io.ErrClosedPipe return io.ErrClosedPipe
} }
return c.output.WriteMultiBuffer(mb) return c.writer.WriteMultiBuffer(mb)
} }
// Close implements net.Conn.Close(). // Close implements net.Conn.Close().
func (c *connection) Close() error { func (c *connection) Close() error {
c.closed = true common.Must(c.done.Close())
c.output.Close() common.Close(c.reader)
c.input.CloseError() common.Close(c.writer)
if c.closeSignal != nil { if c.onClose != nil {
c.closeSignal.Signal() c.onClose.Signal()
} }
return nil return nil
} }
// LocalAddr implements net.Conn.LocalAddr(). // LocalAddr implements net.Conn.LocalAddr().
func (c *connection) LocalAddr() net.Addr { func (c *connection) LocalAddr() net.Addr {
return c.localAddr return c.local
} }
// RemoteAddr implements net.Conn.RemoteAddr(). // RemoteAddr implements net.Conn.RemoteAddr().
func (c *connection) RemoteAddr() net.Addr { func (c *connection) RemoteAddr() net.Addr {
return c.remoteAddr return c.remote
} }
// SetDeadline implements net.Conn.SetDeadline(). // SetDeadline implements net.Conn.SetDeadline().

26
common/stats/io.go Normal file
View File

@ -0,0 +1,26 @@
package stats
import (
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/transport/pipe"
)
type SizeStatWriter struct {
Counter core.StatCounter
Writer buf.Writer
}
func (w *SizeStatWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
w.Counter.Add(int64(mb.Len()))
return w.Writer.WriteMultiBuffer(mb)
}
func (w *SizeStatWriter) Close() error {
return common.Close(w.Writer)
}
func (w *SizeStatWriter) CloseError() {
pipe.CloseError(w.Writer)
}

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"v2ray.com/core/common/net" "v2ray.com/core/common/net"
"v2ray.com/core/transport/ray"
) )
// Dial provides an easy way for upstream caller to create net.Conn through V2Ray. // Dial provides an easy way for upstream caller to create net.Conn through V2Ray.
@ -16,5 +15,5 @@ func Dial(ctx context.Context, v *Instance, dest net.Destination) (net.Conn, err
if err != nil { if err != nil {
return nil, err return nil, err
} }
return ray.NewConnection(r.InboundOutput(), r.InboundInput()), nil return net.NewConnection(net.ConnectionInputMulti(r.Writer), net.ConnectionOutputMulti(r.Reader)), nil
} }

View File

@ -6,7 +6,6 @@ import (
"v2ray.com/core/common" "v2ray.com/core/common"
"v2ray.com/core/common/net" "v2ray.com/core/common/net"
"v2ray.com/core/transport/ray"
) )
// InboundHandler is the interface for handlers that process inbound connections. // InboundHandler is the interface for handlers that process inbound connections.
@ -23,7 +22,7 @@ type InboundHandler interface {
type OutboundHandler interface { type OutboundHandler interface {
common.Runnable common.Runnable
Tag() string Tag() string
Dispatch(ctx context.Context, outboundRay ray.OutboundRay) Dispatch(ctx context.Context, link *Link)
} }
// InboundHandlerManager is a feature that manages InboundHandlers. // InboundHandlerManager is a feature that manages InboundHandlers.

View File

@ -7,9 +7,10 @@ import (
"context" "context"
"time" "time"
"v2ray.com/core"
"v2ray.com/core/common" "v2ray.com/core/common"
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/transport/ray" "v2ray.com/core/transport/pipe"
) )
// Handler is an outbound connection that silently swallow the entire payload. // Handler is an outbound connection that silently swallow the entire payload.
@ -29,11 +30,11 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
} }
// Process implements OutboundHandler.Dispatch(). // Process implements OutboundHandler.Dispatch().
func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dialer proxy.Dialer) error { func (h *Handler) Process(ctx context.Context, link *core.Link, dialer proxy.Dialer) error {
h.response.WriteTo(outboundRay.OutboundOutput()) h.response.WriteTo(link.Writer)
// Sleep a little here to make sure the response is sent to client. // Sleep a little here to make sure the response is sent to client.
time.Sleep(time.Second) time.Sleep(time.Second)
outboundRay.OutboundOutput().CloseError() pipe.CloseError(link.Writer)
return nil return nil
} }

View File

@ -14,6 +14,7 @@ import (
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet"
"v2ray.com/core/transport/internet/udp" "v2ray.com/core/transport/internet/udp"
"v2ray.com/core/transport/pipe"
) )
type DokodemoDoor struct { type DokodemoDoor struct {
@ -70,18 +71,18 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, d.policy().Timeouts.ConnectionIdle) timer := signal.CancelAfterInactivity(ctx, cancel, d.policy().Timeouts.ConnectionIdle)
inboundRay, err := dispatcher.Dispatch(ctx, dest) link, err := dispatcher.Dispatch(ctx, dest)
if err != nil { if err != nil {
return newError("failed to dispatch request").Base(err) return newError("failed to dispatch request").Base(err)
} }
requestDone := func() error { requestDone := func() error {
defer inboundRay.InboundInput().Close() defer common.Close(link.Writer)
defer timer.SetTimeout(d.policy().Timeouts.DownlinkOnly) defer timer.SetTimeout(d.policy().Timeouts.DownlinkOnly)
chunkReader := buf.NewReader(conn) chunkReader := buf.NewReader(conn)
if err := buf.Copy(chunkReader, inboundRay.InboundInput(), buf.UpdateActivity(timer)); err != nil { if err := buf.Copy(chunkReader, link.Writer, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transport request").Base(err) return newError("failed to transport request").Base(err)
} }
@ -108,7 +109,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
} }
} }
if err := buf.Copy(inboundRay.InboundOutput(), writer, buf.UpdateActivity(timer)); err != nil { if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transport response").Base(err) return newError("failed to transport response").Base(err)
} }
@ -116,8 +117,8 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
} }
if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil { if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil {
inboundRay.InboundInput().CloseError() pipe.CloseError(link.Reader)
inboundRay.InboundOutput().CloseError() pipe.CloseError(link.Writer)
return newError("connection ends").Base(err) return newError("connection ends").Base(err)
} }

View File

@ -15,7 +15,6 @@ import (
"v2ray.com/core/common/signal" "v2ray.com/core/common/signal"
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet"
"v2ray.com/core/transport/ray"
) )
// Handler handles Freedom connections. // Handler handles Freedom connections.
@ -65,7 +64,7 @@ func (h *Handler) resolveIP(ctx context.Context, domain string) net.Address {
} }
// Process implements proxy.Outbound. // Process implements proxy.Outbound.
func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dialer proxy.Dialer) error { func (h *Handler) Process(ctx context.Context, link *core.Link, dialer proxy.Dialer) error {
destination, _ := proxy.TargetFromContext(ctx) destination, _ := proxy.TargetFromContext(ctx)
if h.config.DestinationOverride != nil { if h.config.DestinationOverride != nil {
server := h.config.DestinationOverride.Server server := h.config.DestinationOverride.Server
@ -77,8 +76,8 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
} }
newError("opening connection to ", destination).WithContext(ctx).WriteToLog() newError("opening connection to ", destination).WithContext(ctx).WriteToLog()
input := outboundRay.OutboundInput() input := link.Reader
output := outboundRay.OutboundOutput() output := link.Writer
if h.config.DomainStrategy == Config_USE_IP && destination.Address.Family().IsDomain() { if h.config.DomainStrategy == Config_USE_IP && destination.Address.Family().IsDomain() {
ip := h.resolveIP(ctx, destination.Address.Domain()) ip := h.resolveIP(ctx, destination.Address.Domain())
@ -137,8 +136,6 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
} }
if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil { if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil {
input.CloseError()
output.CloseError()
return newError("connection ends").Base(err) return newError("connection ends").Base(err)
} }

View File

@ -10,6 +10,8 @@ import (
"strings" "strings"
"time" "time"
"v2ray.com/core/transport/pipe"
"v2ray.com/core" "v2ray.com/core"
"v2ray.com/core/common" "v2ray.com/core/common"
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
@ -166,7 +168,7 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, s.policy().Timeouts.ConnectionIdle) timer := signal.CancelAfterInactivity(ctx, cancel, s.policy().Timeouts.ConnectionIdle)
ray, err := dispatcher.Dispatch(ctx, dest) link, err := dispatcher.Dispatch(ctx, dest)
if err != nil { if err != nil {
return err return err
} }
@ -176,25 +178,25 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade
if err != nil { if err != nil {
return err return err
} }
if err := ray.InboundInput().WriteMultiBuffer(payload); err != nil { if err := link.Writer.WriteMultiBuffer(payload); err != nil {
return err return err
} }
reader = nil reader = nil
} }
requestDone := func() error { requestDone := func() error {
defer ray.InboundInput().Close() defer common.Close(link.Writer)
defer timer.SetTimeout(s.policy().Timeouts.DownlinkOnly) defer timer.SetTimeout(s.policy().Timeouts.DownlinkOnly)
v2reader := buf.NewReader(conn) v2reader := buf.NewReader(conn)
return buf.Copy(v2reader, ray.InboundInput(), buf.UpdateActivity(timer)) return buf.Copy(v2reader, link.Writer, buf.UpdateActivity(timer))
} }
responseDone := func() error { responseDone := func() error {
defer timer.SetTimeout(s.policy().Timeouts.UplinkOnly) defer timer.SetTimeout(s.policy().Timeouts.UplinkOnly)
v2writer := buf.NewWriter(conn) v2writer := buf.NewWriter(conn)
if err := buf.Copy(ray.InboundOutput(), v2writer, buf.UpdateActivity(timer)); err != nil { if err := buf.Copy(link.Reader, v2writer, buf.UpdateActivity(timer)); err != nil {
return err return err
} }
@ -202,8 +204,8 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade
} }
if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil { if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil {
ray.InboundInput().CloseError() pipe.CloseError(link.Reader)
ray.InboundOutput().CloseError() pipe.CloseError(link.Writer)
return newError("connection ends").Base(err) return newError("connection ends").Base(err)
} }
@ -241,20 +243,18 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri
request.Header.Set("User-Agent", "") request.Header.Set("User-Agent", "")
} }
ray, err := dispatcher.Dispatch(ctx, dest) link, err := dispatcher.Dispatch(ctx, dest)
if err != nil { if err != nil {
return err return err
} }
input := ray.InboundInput()
output := ray.InboundOutput()
defer input.Close()
var result error = errWaitAnother var result error = errWaitAnother
requestDone := func() error { requestDone := func() error {
defer common.Close(link.Writer)
request.Header.Set("Connection", "close") request.Header.Set("Connection", "close")
requestWriter := buf.NewBufferedWriter(ray.InboundInput()) requestWriter := buf.NewBufferedWriter(link.Writer)
common.Must(requestWriter.SetBuffered(false)) common.Must(requestWriter.SetBuffered(false))
if err := request.Write(requestWriter); err != nil { if err := request.Write(requestWriter); err != nil {
return newError("failed to write whole request").Base(err).AtWarning() return newError("failed to write whole request").Base(err).AtWarning()
@ -263,7 +263,7 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri
} }
responseDone := func() error { responseDone := func() error {
responseReader := bufio.NewReaderSize(buf.NewBufferedReader(ray.InboundOutput()), buf.Size) responseReader := bufio.NewReaderSize(buf.NewBufferedReader(link.Reader), buf.Size)
response, err := http.ReadResponse(responseReader, request) response, err := http.ReadResponse(responseReader, request)
if err == nil { if err == nil {
http_proto.RemoveHopByHopHeaders(response.Header) http_proto.RemoveHopByHopHeaders(response.Header)
@ -299,8 +299,8 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri
} }
if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil { if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil {
input.CloseError() pipe.CloseError(link.Reader)
output.CloseError() pipe.CloseError(link.Writer)
return newError("connection ends").Base(err) return newError("connection ends").Base(err)
} }

View File

@ -12,7 +12,6 @@ import (
"v2ray.com/core/common/net" "v2ray.com/core/common/net"
"v2ray.com/core/common/protocol" "v2ray.com/core/common/protocol"
"v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet"
"v2ray.com/core/transport/ray"
) )
// An Inbound processes inbound connections. // An Inbound processes inbound connections.
@ -27,7 +26,7 @@ type Inbound interface {
// An Outbound process outbound connections. // An Outbound process outbound connections.
type Outbound interface { type Outbound interface {
// Process processes the given connection. The given dialer may be used to dial a system outbound connection. // Process processes the given connection. The given dialer may be used to dial a system outbound connection.
Process(context.Context, ray.OutboundRay, Dialer) error Process(context.Context, *core.Link, Dialer) error
} }
// Dialer is used by OutboundHandler for creating outbound connections. // Dialer is used by OutboundHandler for creating outbound connections.

View File

@ -12,7 +12,6 @@ import (
"v2ray.com/core/common/signal" "v2ray.com/core/common/signal"
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet"
"v2ray.com/core/transport/ray"
) )
// Client is a inbound handler for Shadowsocks protocol // Client is a inbound handler for Shadowsocks protocol
@ -38,7 +37,7 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
} }
// Process implements OutboundHandler.Process(). // Process implements OutboundHandler.Process().
func (c *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, dialer proxy.Dialer) error { func (c *Client) Process(ctx context.Context, link *core.Link, dialer proxy.Dialer) error {
destination, ok := proxy.TargetFromContext(ctx) destination, ok := proxy.TargetFromContext(ctx)
if !ok { if !ok {
return newError("target not specified") return newError("target not specified")
@ -107,7 +106,7 @@ func (c *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
requestDone := func() error { requestDone := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
return buf.Copy(outboundRay.OutboundInput(), bodyWriter, buf.UpdateActivity(timer)) return buf.Copy(link.Reader, bodyWriter, buf.UpdateActivity(timer))
} }
responseDone := func() error { responseDone := func() error {
@ -118,7 +117,7 @@ func (c *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
return err return err
} }
return buf.Copy(responseReader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer)) return buf.Copy(responseReader, link.Writer, buf.UpdateActivity(timer))
} }
if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil { if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil {
@ -138,7 +137,7 @@ func (c *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
requestDone := func() error { requestDone := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
if err := buf.Copy(outboundRay.OutboundInput(), writer, buf.UpdateActivity(timer)); err != nil { if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transport all UDP request").Base(err) return newError("failed to transport all UDP request").Base(err)
} }
return nil return nil
@ -152,7 +151,7 @@ func (c *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
User: user, User: user,
} }
if err := buf.Copy(reader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer)); err != nil { if err := buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transport all UDP response").Base(err) return newError("failed to transport all UDP response").Base(err)
} }
return nil return nil

View File

@ -14,6 +14,7 @@ import (
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet"
"v2ray.com/core/transport/internet/udp" "v2ray.com/core/transport/internet/udp"
"v2ray.com/core/transport/pipe"
) )
type Server struct { type Server struct {
@ -167,7 +168,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
ray, err := dispatcher.Dispatch(ctx, dest) link, err := dispatcher.Dispatch(ctx, dest)
if err != nil { if err != nil {
return err return err
} }
@ -182,7 +183,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
} }
{ {
payload, err := ray.InboundOutput().ReadMultiBuffer() payload, err := link.Reader.ReadMultiBuffer()
if err != nil { if err != nil {
return err return err
} }
@ -195,7 +196,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
return err return err
} }
if err := buf.Copy(ray.InboundOutput(), responseWriter, buf.UpdateActivity(timer)); err != nil { if err := buf.Copy(link.Reader, responseWriter, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transport all TCP response").Base(err) return newError("failed to transport all TCP response").Base(err)
} }
@ -204,9 +205,9 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
requestDone := func() error { requestDone := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
defer ray.InboundInput().Close() defer common.Close(link.Writer)
if err := buf.Copy(bodyReader, ray.InboundInput(), buf.UpdateActivity(timer)); err != nil { if err := buf.Copy(bodyReader, link.Writer, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transport all TCP request").Base(err) return newError("failed to transport all TCP request").Base(err)
} }
@ -214,8 +215,8 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
} }
if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil { if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil {
ray.InboundInput().CloseError() pipe.CloseError(link.Reader)
ray.InboundOutput().CloseError() pipe.CloseError(link.Writer)
return newError("connection ends").Base(err) return newError("connection ends").Base(err)
} }

View File

@ -13,7 +13,6 @@ import (
"v2ray.com/core/common/signal" "v2ray.com/core/common/signal"
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet"
"v2ray.com/core/transport/ray"
) )
// Client is a Socks5 client. // Client is a Socks5 client.
@ -40,7 +39,7 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
} }
// Process implements proxy.Outbound.Process. // Process implements proxy.Outbound.Process.
func (c *Client) Process(ctx context.Context, ray ray.OutboundRay, dialer proxy.Dialer) error { func (c *Client) Process(ctx context.Context, link *core.Link, dialer proxy.Dialer) error {
destination, ok := proxy.TargetFromContext(ctx) destination, ok := proxy.TargetFromContext(ctx)
if !ok { if !ok {
return newError("target not specified.") return newError("target not specified.")
@ -107,11 +106,11 @@ func (c *Client) Process(ctx context.Context, ray ray.OutboundRay, dialer proxy.
if request.Command == protocol.RequestCommandTCP { if request.Command == protocol.RequestCommandTCP {
requestFunc = func() error { requestFunc = func() error {
defer timer.SetTimeout(p.Timeouts.DownlinkOnly) defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
return buf.Copy(ray.OutboundInput(), buf.NewWriter(conn), buf.UpdateActivity(timer)) return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
} }
responseFunc = func() error { responseFunc = func() error {
defer timer.SetTimeout(p.Timeouts.UplinkOnly) defer timer.SetTimeout(p.Timeouts.UplinkOnly)
return buf.Copy(buf.NewReader(conn), ray.OutboundOutput(), buf.UpdateActivity(timer)) return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
} }
} else if request.Command == protocol.RequestCommandUDP { } else if request.Command == protocol.RequestCommandUDP {
udpConn, err := dialer.Dial(ctx, udpRequest.Destination()) udpConn, err := dialer.Dial(ctx, udpRequest.Destination())
@ -121,12 +120,12 @@ func (c *Client) Process(ctx context.Context, ray ray.OutboundRay, dialer proxy.
defer udpConn.Close() defer udpConn.Close()
requestFunc = func() error { requestFunc = func() error {
defer timer.SetTimeout(p.Timeouts.DownlinkOnly) defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
return buf.Copy(ray.OutboundInput(), buf.NewSequentialWriter(NewUDPWriter(request, udpConn)), buf.UpdateActivity(timer)) return buf.Copy(link.Reader, buf.NewSequentialWriter(NewUDPWriter(request, udpConn)), buf.UpdateActivity(timer))
} }
responseFunc = func() error { responseFunc = func() error {
defer timer.SetTimeout(p.Timeouts.UplinkOnly) defer timer.SetTimeout(p.Timeouts.UplinkOnly)
reader := &UDPReader{reader: udpConn} reader := &UDPReader{reader: udpConn}
return buf.Copy(reader, ray.OutboundOutput(), buf.UpdateActivity(timer)) return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
} }
} }

View File

@ -15,6 +15,7 @@ import (
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet"
"v2ray.com/core/transport/internet/udp" "v2ray.com/core/transport/internet/udp"
"v2ray.com/core/transport/pipe"
) )
// Server is a SOCKS 5 proxy server // Server is a SOCKS 5 proxy server
@ -129,20 +130,17 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, s.policy().Timeouts.ConnectionIdle) timer := signal.CancelAfterInactivity(ctx, cancel, s.policy().Timeouts.ConnectionIdle)
ray, err := dispatcher.Dispatch(ctx, dest) link, err := dispatcher.Dispatch(ctx, dest)
if err != nil { if err != nil {
return err return err
} }
input := ray.InboundInput()
output := ray.InboundOutput()
requestDone := func() error { requestDone := func() error {
defer timer.SetTimeout(s.policy().Timeouts.DownlinkOnly) defer timer.SetTimeout(s.policy().Timeouts.DownlinkOnly)
defer input.Close() defer common.Close(link.Writer)
v2reader := buf.NewReader(reader) v2reader := buf.NewReader(reader)
if err := buf.Copy(v2reader, input, buf.UpdateActivity(timer)); err != nil { if err := buf.Copy(v2reader, link.Writer, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transport all TCP request").Base(err) return newError("failed to transport all TCP request").Base(err)
} }
@ -153,7 +151,7 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
defer timer.SetTimeout(s.policy().Timeouts.UplinkOnly) defer timer.SetTimeout(s.policy().Timeouts.UplinkOnly)
v2writer := buf.NewWriter(writer) v2writer := buf.NewWriter(writer)
if err := buf.Copy(output, v2writer, buf.UpdateActivity(timer)); err != nil { if err := buf.Copy(link.Reader, v2writer, buf.UpdateActivity(timer)); err != nil {
return newError("failed to transport all TCP response").Base(err) return newError("failed to transport all TCP response").Base(err)
} }
@ -161,8 +159,8 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
} }
if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil { if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil {
input.CloseError() pipe.CloseError(link.Reader)
output.CloseError() pipe.CloseError(link.Writer)
return newError("connection ends").Base(err) return newError("connection ends").Base(err)
} }

View File

@ -22,7 +22,7 @@ import (
"v2ray.com/core/proxy/vmess" "v2ray.com/core/proxy/vmess"
"v2ray.com/core/proxy/vmess/encoding" "v2ray.com/core/proxy/vmess/encoding"
"v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet"
"v2ray.com/core/transport/ray" "v2ray.com/core/transport/pipe"
) )
type userByEmail struct { type userByEmail struct {
@ -167,8 +167,8 @@ func (h *Handler) RemoveUser(ctx context.Context, email string) error {
return nil return nil
} }
func transferRequest(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, input io.Reader, output ray.OutputStream) error { func transferRequest(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, input io.Reader, output buf.Writer) error {
defer output.Close() defer common.Close(output)
bodyReader := session.DecodeRequestBody(request, input) bodyReader := session.DecodeRequestBody(request, input)
if err := buf.Copy(bodyReader, output, buf.UpdateActivity(timer)); err != nil { if err := buf.Copy(bodyReader, output, buf.UpdateActivity(timer)); err != nil {
@ -272,17 +272,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
ray, err := dispatcher.Dispatch(ctx, request.Destination()) link, err := dispatcher.Dispatch(ctx, request.Destination())
if err != nil { if err != nil {
return newError("failed to dispatch request to ", request.Destination()).Base(err) return newError("failed to dispatch request to ", request.Destination()).Base(err)
} }
input := ray.InboundInput()
output := ray.InboundOutput()
requestDone := func() error { requestDone := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
return transferRequest(timer, session, request, reader, input) return transferRequest(timer, session, request, reader, link.Writer)
} }
responseDone := func() error { responseDone := func() error {
@ -293,12 +290,12 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i
response := &protocol.ResponseHeader{ response := &protocol.ResponseHeader{
Command: h.generateCommand(ctx, request), Command: h.generateCommand(ctx, request),
} }
return transferResponse(timer, session, request, response, output, writer) return transferResponse(timer, session, request, response, link.Reader, writer)
} }
if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil { if err := signal.ExecuteParallel(ctx, requestDone, responseDone); err != nil {
input.CloseError() pipe.CloseError(link.Reader)
output.CloseError() pipe.CloseError(link.Writer)
return newError("connection ends").Base(err) return newError("connection ends").Base(err)
} }

View File

@ -6,6 +6,8 @@ import (
"context" "context"
"time" "time"
"v2ray.com/core/transport/pipe"
"v2ray.com/core" "v2ray.com/core"
"v2ray.com/core/common" "v2ray.com/core/common"
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
@ -17,7 +19,6 @@ import (
"v2ray.com/core/proxy/vmess" "v2ray.com/core/proxy/vmess"
"v2ray.com/core/proxy/vmess/encoding" "v2ray.com/core/proxy/vmess/encoding"
"v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet"
"v2ray.com/core/transport/ray"
) )
// Handler is an outbound connection handler for VMess protocol. // Handler is an outbound connection handler for VMess protocol.
@ -42,7 +43,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
} }
// Process implements proxy.Outbound.Process(). // Process implements proxy.Outbound.Process().
func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dialer proxy.Dialer) error { func (v *Handler) Process(ctx context.Context, link *core.Link, dialer proxy.Dialer) error {
var rec *protocol.ServerSpec var rec *protocol.ServerSpec
var conn internet.Connection var conn internet.Connection
@ -95,8 +96,8 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
request.Option.Set(protocol.RequestOptionChunkMasking) request.Option.Set(protocol.RequestOptionChunkMasking)
} }
input := outboundRay.OutboundInput() input := link.Reader
output := outboundRay.OutboundOutput() output := link.Writer
session := encoding.NewClientSession(protocol.DefaultIDHash) session := encoding.NewClientSession(protocol.DefaultIDHash)
sessionPolicy := v.v.PolicyManager().ForLevel(request.User.Level) sessionPolicy := v.v.PolicyManager().ForLevel(request.User.Level)
@ -113,8 +114,8 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
} }
bodyWriter := session.EncodeRequestBody(request, writer) bodyWriter := session.EncodeRequestBody(request, writer)
{ if tReader, ok := input.(*pipe.Reader); ok {
firstPayload, err := input.ReadTimeout(time.Millisecond * 500) firstPayload, err := tReader.ReadMultiBufferWithTimeout(time.Millisecond * 500)
if err != nil && err != buf.ErrReadTimeout { if err != nil && err != buf.ErrReadTimeout {
return newError("failed to get first payload").Base(err) return newError("failed to get first payload").Base(err)
} }

View File

@ -5,18 +5,24 @@ import (
"sync" "sync"
"v2ray.com/core/common" "v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/errors" "v2ray.com/core/common/errors"
"v2ray.com/core/common/net" "v2ray.com/core/common/net"
"v2ray.com/core/transport/ray"
) )
// Link is a utility for connecting between an inbound and an outbound proxy handler.
type Link struct {
Reader buf.Reader
Writer buf.Writer
}
// Dispatcher is a feature that dispatches inbound requests to outbound handlers based on rules. // Dispatcher is a feature that dispatches inbound requests to outbound handlers based on rules.
// Dispatcher is required to be registered in a V2Ray instance to make V2Ray function properly. // Dispatcher is required to be registered in a V2Ray instance to make V2Ray function properly.
type Dispatcher interface { type Dispatcher interface {
Feature Feature
// Dispatch returns a Ray for transporting data for the given request. // Dispatch returns a Ray for transporting data for the given request.
Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error) Dispatch(ctx context.Context, dest net.Destination) (*Link, error)
} }
type syncDispatcher struct { type syncDispatcher struct {
@ -24,7 +30,7 @@ type syncDispatcher struct {
Dispatcher Dispatcher
} }
func (d *syncDispatcher) Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error) { func (d *syncDispatcher) Dispatch(ctx context.Context, dest net.Destination) (*Link, error) {
d.RLock() d.RLock()
defer d.RUnlock() defer d.RUnlock()

View File

@ -6,16 +6,16 @@ import (
"time" "time"
"v2ray.com/core" "v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
"v2ray.com/core/common/net" "v2ray.com/core/common/net"
"v2ray.com/core/common/signal" "v2ray.com/core/common/signal"
"v2ray.com/core/transport/ray"
) )
type ResponseCallback func(payload *buf.Buffer) type ResponseCallback func(payload *buf.Buffer)
type connEntry struct { type connEntry struct {
inbound ray.InboundRay link *core.Link
timer signal.ActivityUpdater timer signal.ActivityUpdater
cancel context.CancelFunc cancel context.CancelFunc
} }
@ -37,8 +37,8 @@ func (v *Dispatcher) RemoveRay(dest net.Destination) {
v.Lock() v.Lock()
defer v.Unlock() defer v.Unlock()
if conn, found := v.conns[dest]; found { if conn, found := v.conns[dest]; found {
conn.inbound.InboundInput().Close() common.Close(conn.link.Reader)
conn.inbound.InboundOutput().Close() common.Close(conn.link.Writer)
delete(v.conns, dest) delete(v.conns, dest)
} }
} }
@ -59,9 +59,9 @@ func (v *Dispatcher) getInboundRay(dest net.Destination, callback ResponseCallba
v.RemoveRay(dest) v.RemoveRay(dest)
} }
timer := signal.CancelAfterInactivity(ctx, removeRay, time.Second*4) timer := signal.CancelAfterInactivity(ctx, removeRay, time.Second*4)
inboundRay, _ := v.dispatcher.Dispatch(ctx, dest) link, _ := v.dispatcher.Dispatch(ctx, dest)
entry := &connEntry{ entry := &connEntry{
inbound: inboundRay, link: link,
timer: timer, timer: timer,
cancel: removeRay, cancel: removeRay,
} }
@ -75,7 +75,7 @@ func (v *Dispatcher) Dispatch(ctx context.Context, destination net.Destination,
newError("dispatch request to: ", destination).AtDebug().WithContext(ctx).WriteToLog() newError("dispatch request to: ", destination).AtDebug().WithContext(ctx).WriteToLog()
conn := v.getInboundRay(destination, callback) conn := v.getInboundRay(destination, callback)
outputStream := conn.inbound.InboundInput() outputStream := conn.link.Writer
if outputStream != nil { if outputStream != nil {
if err := outputStream.WriteMultiBuffer(buf.NewMultiBufferValue(payload)); err != nil { if err := outputStream.WriteMultiBuffer(buf.NewMultiBufferValue(payload)); err != nil {
newError("failed to write first UDP payload").Base(err).WithContext(ctx).WriteToLog() newError("failed to write first UDP payload").Base(err).WithContext(ctx).WriteToLog()
@ -86,7 +86,7 @@ func (v *Dispatcher) Dispatch(ctx context.Context, destination net.Destination,
} }
func handleInput(ctx context.Context, conn *connEntry, callback ResponseCallback) { func handleInput(ctx context.Context, conn *connEntry, callback ResponseCallback) {
input := conn.inbound.InboundOutput() input := conn.link.Reader
timer := conn.timer timer := conn.timer
for { for {

View File

@ -6,18 +6,19 @@ import (
"testing" "testing"
"time" "time"
"v2ray.com/core"
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
"v2ray.com/core/common/net" "v2ray.com/core/common/net"
. "v2ray.com/core/transport/internet/udp" . "v2ray.com/core/transport/internet/udp"
"v2ray.com/core/transport/ray" "v2ray.com/core/transport/pipe"
. "v2ray.com/ext/assert" . "v2ray.com/ext/assert"
) )
type TestDispatcher struct { type TestDispatcher struct {
OnDispatch func(ctx context.Context, dest net.Destination) (ray.InboundRay, error) OnDispatch func(ctx context.Context, dest net.Destination) (*core.Link, error)
} }
func (d *TestDispatcher) Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error) { func (d *TestDispatcher) Dispatch(ctx context.Context, dest net.Destination) (*core.Link, error) {
return d.OnDispatch(ctx, dest) return d.OnDispatch(ctx, dest)
} }
@ -33,23 +34,25 @@ func TestSameDestinationDispatching(t *testing.T) {
assert := With(t) assert := With(t)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
link := ray.New(ctx) uplinkReader, uplinkWriter := pipe.New()
downlinkReader, downlinkWriter := pipe.New()
go func() { go func() {
for { for {
data, err := link.OutboundInput().ReadMultiBuffer() data, err := uplinkReader.ReadMultiBuffer()
if err != nil { if err != nil {
break break
} }
err = link.OutboundOutput().WriteMultiBuffer(data) err = downlinkWriter.WriteMultiBuffer(data)
assert(err, IsNil) assert(err, IsNil)
} }
}() }()
var count uint32 var count uint32
td := &TestDispatcher{ td := &TestDispatcher{
OnDispatch: func(ctx context.Context, dest net.Destination) (ray.InboundRay, error) { OnDispatch: func(ctx context.Context, dest net.Destination) (*core.Link, error) {
atomic.AddUint32(&count, 1) atomic.AddUint32(&count, 1)
return link, nil return &core.Link{Reader: downlinkReader, Writer: uplinkWriter}, nil
}, },
} }
dest := net.UDPDestination(net.LocalHostIP, 53) dest := net.UDPDestination(net.LocalHostIP, 53)

View File

@ -6,7 +6,6 @@ import (
"time" "time"
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/signal" "v2ray.com/core/common/signal"
) )
@ -70,8 +69,6 @@ func (p *pipe) ReadMultiBuffer() (buf.MultiBuffer, error) {
} }
} }
var ErrTimeout = errors.New("Timeout on reading pipeline.")
func (p *pipe) ReadMultiBufferWithTimeout(d time.Duration) (buf.MultiBuffer, error) { func (p *pipe) ReadMultiBufferWithTimeout(d time.Duration) (buf.MultiBuffer, error) {
timer := time.After(d) timer := time.After(d)
for { for {
@ -84,7 +81,7 @@ func (p *pipe) ReadMultiBufferWithTimeout(d time.Duration) (buf.MultiBuffer, err
select { select {
case <-p.readSignal.Wait(): case <-p.readSignal.Wait():
case <-timer: case <-timer:
return nil, ErrTimeout return nil, buf.ErrReadTimeout
} }
} }
} }
@ -120,6 +117,10 @@ func (p *pipe) Close() error {
p.Lock() p.Lock()
defer p.Unlock() defer p.Unlock()
if p.state == closed || p.state == errord {
return nil
}
p.state = closed p.state = closed
p.readSignal.Signal() p.readSignal.Signal()
p.writeSignal.Signal() p.writeSignal.Signal()

View File

@ -37,6 +37,16 @@ func New(opts ...Option) (*Reader, *Writer) {
} }
} }
type closeError interface {
CloseError()
}
func CloseError(v interface{}) {
if c, ok := v.(closeError); ok {
c.CloseError()
}
}
var defaultLimit int32 = 10 * 1024 * 1024 var defaultLimit int32 = 10 * 1024 * 1024
func init() { func init() {

View File

@ -1,268 +0,0 @@
package ray
import (
"context"
"io"
"sync"
"time"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/platform"
"v2ray.com/core/common/signal"
)
type Option func(*directRay)
type addInt64 interface {
Add(int64) int64
}
func WithUplinkStatCounter(c addInt64) Option {
return func(s *directRay) {
s.Input.onDataSize = append(s.Input.onDataSize, func(delta uint64) {
c.Add(int64(delta))
})
}
}
func WithDownlinkStatCounter(c addInt64) Option {
return func(s *directRay) {
s.Output.onDataSize = append(s.Output.onDataSize, func(delta uint64) {
c.Add(int64(delta))
})
}
}
// New creates a new Ray for direct traffic transport.
func New(ctx context.Context, opts ...Option) Ray {
r := &directRay{
Input: NewStream(ctx),
Output: NewStream(ctx),
}
for _, opt := range opts {
opt(r)
}
return r
}
type directRay struct {
Input *Stream
Output *Stream
}
func (v *directRay) OutboundInput() InputStream {
return v.Input
}
func (v *directRay) OutboundOutput() OutputStream {
return v.Output
}
func (v *directRay) InboundInput() OutputStream {
return v.Input
}
func (v *directRay) InboundOutput() InputStream {
return v.Output
}
var streamSizeLimit uint64 = 10 * 1024 * 1024
func init() {
const raySizeEnvKey = "v2ray.ray.buffer.size"
size := platform.EnvFlag{
Name: raySizeEnvKey,
AltName: platform.NormalizeEnvName(raySizeEnvKey),
}.GetValueAsInt(10)
streamSizeLimit = uint64(size) * 1024 * 1024
}
// Stream is a sequential container for data in bytes.
type Stream struct {
access sync.RWMutex
data buf.MultiBuffer
size uint64
ctx context.Context
readSignal *signal.Notifier
writeSignal *signal.Notifier
onDataSize []func(uint64)
close bool
err bool
}
// NewStream creates a new Stream.
func NewStream(ctx context.Context) *Stream {
s := &Stream{
ctx: ctx,
readSignal: signal.NewNotifier(),
writeSignal: signal.NewNotifier(),
size: 0,
}
return s
}
func (s *Stream) getData() (buf.MultiBuffer, error) {
s.access.Lock()
defer s.access.Unlock()
if s.data != nil {
mb := s.data
s.data = nil
s.size = 0
return mb, nil
}
if s.err {
return nil, io.ErrClosedPipe
}
if s.close {
return nil, io.EOF
}
return nil, nil
}
// Peek fills in the given buffer with data from head of the Stream.
func (s *Stream) Peek(b *buf.Buffer) {
s.access.RLock()
defer s.access.RUnlock()
common.Must(b.Reset(func(data []byte) (int, error) {
return s.data.Copy(data), nil
}))
}
// ReadMultiBuffer reads data from the Stream.
func (s *Stream) ReadMultiBuffer() (buf.MultiBuffer, error) {
for {
mb, err := s.getData()
if err != nil {
return nil, err
}
if mb != nil {
s.readSignal.Signal()
return mb, nil
}
select {
case <-s.ctx.Done():
return nil, s.ctx.Err()
case <-s.writeSignal.Wait():
}
}
}
// ReadTimeout reads from the Stream with a specified timeout.
func (s *Stream) ReadTimeout(timeout time.Duration) (buf.MultiBuffer, error) {
for {
mb, err := s.getData()
if err != nil {
return nil, err
}
if mb != nil {
s.readSignal.Signal()
return mb, nil
}
select {
case <-s.ctx.Done():
return nil, s.ctx.Err()
case <-time.After(timeout):
return nil, buf.ErrReadTimeout
case <-s.writeSignal.Wait():
}
}
}
// Size returns the number of bytes hold in the Stream.
func (s *Stream) Size() uint64 {
s.access.RLock()
defer s.access.RUnlock()
return s.size
}
// waitForStreamSize waits until the Stream has room for more data, or any error happens.
func (s *Stream) waitForStreamSize() error {
if streamSizeLimit == 0 {
return nil
}
for s.Size() >= streamSizeLimit {
select {
case <-s.ctx.Done():
return s.ctx.Err()
case <-s.readSignal.Wait():
if s.err || s.close {
return io.ErrClosedPipe
}
}
}
return nil
}
// WriteMultiBuffer writes more data into the Stream.
func (s *Stream) WriteMultiBuffer(data buf.MultiBuffer) error {
if data.IsEmpty() {
return nil
}
if err := s.waitForStreamSize(); err != nil {
data.Release()
return err
}
s.access.Lock()
defer s.access.Unlock()
if s.err || s.close {
data.Release()
return io.ErrClosedPipe
}
if s.data == nil {
s.data = buf.NewMultiBufferCap(128)
}
dataSize := uint64(data.Len())
for _, f := range s.onDataSize {
f(dataSize)
}
s.data.AppendMulti(data)
s.size += dataSize
s.writeSignal.Signal()
return nil
}
// Close closes the stream for writing. Read() still works until EOF.
func (s *Stream) Close() error {
s.access.Lock()
s.close = true
s.readSignal.Signal()
s.writeSignal.Signal()
s.access.Unlock()
return nil
}
// CloseError closes the Stream with error. Read() will return an error afterwards.
func (s *Stream) CloseError() {
s.access.Lock()
s.err = true
if s.data != nil {
s.data.Release()
s.data = nil
s.size = 0
}
s.access.Unlock()
s.readSignal.Signal()
s.writeSignal.Signal()
}

View File

@ -1,49 +0,0 @@
package ray_test
import (
"context"
"io"
"testing"
"v2ray.com/core/common/buf"
. "v2ray.com/core/transport/ray"
. "v2ray.com/ext/assert"
)
func TestStreamIO(t *testing.T) {
assert := With(t)
stream := NewStream(context.Background())
b1 := buf.New()
b1.AppendBytes('a')
assert(stream.WriteMultiBuffer(buf.NewMultiBufferValue(b1)), IsNil)
_, err := stream.ReadMultiBuffer()
assert(err, IsNil)
stream.Close()
_, err = stream.ReadMultiBuffer()
assert(err, Equals, io.EOF)
b2 := buf.New()
b2.AppendBytes('b')
err = stream.WriteMultiBuffer(buf.NewMultiBufferValue(b2))
assert(err, Equals, io.ErrClosedPipe)
}
func TestStreamClose(t *testing.T) {
assert := With(t)
stream := NewStream(context.Background())
b1 := buf.New()
b1.AppendBytes('a')
assert(stream.WriteMultiBuffer(buf.NewMultiBufferValue(b1)), IsNil)
stream.Close()
_, err := stream.ReadMultiBuffer()
assert(err, IsNil)
_, err = stream.ReadMultiBuffer()
assert(err, Equals, io.EOF)
}

View File

@ -1,54 +0,0 @@
package ray
import (
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
)
// OutboundRay is a transport interface for outbound connections.
type OutboundRay interface {
// OutboundInput provides a stream for the input of the outbound connection.
// The outbound connection shall write all the input until it is closed.
OutboundInput() InputStream
// OutboundOutput provides a stream to retrieve the response from the
// outbound connection. The outbound connection shall close the channel
// after all responses are received and put into the channel.
OutboundOutput() OutputStream
}
// InboundRay is a transport interface for inbound connections.
type InboundRay interface {
// InboundInput provides a stream to retrieve the request from client.
// The inbound connection shall close the channel after the entire request
// is received and put into the channel.
InboundInput() OutputStream
// InboundOutput provides a stream of data for the inbound connection to write
// as response. The inbound connection shall write all the data from the
// channel until it is closed.
InboundOutput() InputStream
}
// Ray is an internal transport channel between inbound and outbound connection.
type Ray interface {
InboundRay
OutboundRay
}
type RayStream interface {
common.Closable
CloseError()
}
type InputStream interface {
buf.Reader
buf.TimeoutReader
RayStream
Peek(*buf.Buffer)
}
type OutputStream interface {
buf.Writer
RayStream
}