1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-12-27 04:27:20 -05:00

task engine for all proxies

This commit is contained in:
Darien Raymond 2016-12-30 00:32:20 +01:00
parent 690d71b16e
commit 609dbc1f13
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169
10 changed files with 304 additions and 174 deletions

View File

@ -0,0 +1,87 @@
package signal_test
import (
"errors"
"testing"
. "v2ray.com/core/common/signal"
"v2ray.com/core/testing/assert"
)
func TestErrorOrFinish2_Error(t *testing.T) {
assert := assert.On(t)
c1 := make(chan error, 1)
c2 := make(chan error, 2)
c := make(chan error, 1)
go func() {
c <- ErrorOrFinish2(c1, c2)
}()
c1 <- errors.New("test")
err := <-c
assert.String(err.Error()).Equals("test")
}
func TestErrorOrFinish2_Error2(t *testing.T) {
assert := assert.On(t)
c1 := make(chan error, 1)
c2 := make(chan error, 2)
c := make(chan error, 1)
go func() {
c <- ErrorOrFinish2(c1, c2)
}()
c2 <- errors.New("test")
err := <-c
assert.String(err.Error()).Equals("test")
}
func TestErrorOrFinish2_NoneError(t *testing.T) {
assert := assert.On(t)
c1 := make(chan error, 1)
c2 := make(chan error, 2)
c := make(chan error, 1)
go func() {
c <- ErrorOrFinish2(c1, c2)
}()
close(c1)
select {
case <-c:
t.Fail()
default:
}
close(c2)
err := <-c
assert.Error(err).IsNil()
}
func TestErrorOrFinish2_NoneError2(t *testing.T) {
assert := assert.On(t)
c1 := make(chan error, 1)
c2 := make(chan error, 2)
c := make(chan error, 1)
go func() {
c <- ErrorOrFinish2(c1, c2)
}()
close(c2)
select {
case <-c:
t.Fail()
default:
}
close(c1)
err := <-c
assert.Error(err).IsNil()
}

View File

@ -13,6 +13,7 @@ import (
v2net "v2ray.com/core/common/net" v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/retry" "v2ray.com/core/common/retry"
"v2ray.com/core/common/serial" "v2ray.com/core/common/serial"
"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" "v2ray.com/core/transport/ray"
@ -101,14 +102,17 @@ func (v *Handler) Dispatch(destination v2net.Destination, payload *buf.Buffer, r
} }
} }
go func() { requestDone := signal.ExecuteAsync(func() error {
defer input.Release()
v2writer := buf.NewWriter(conn) v2writer := buf.NewWriter(conn)
defer v2writer.Release() defer v2writer.Release()
if err := buf.PipeUntilEOF(input, v2writer); err != nil { if err := buf.PipeUntilEOF(input, v2writer); err != nil {
log.Info("Freedom: Failed to transport all TCP request: ", err) return err
} }
}() return nil
})
var reader io.Reader = conn var reader io.Reader = conn
@ -120,12 +124,21 @@ func (v *Handler) Dispatch(destination v2net.Destination, payload *buf.Buffer, r
reader = v2net.NewTimeOutReader(timeout /* seconds */, conn) reader = v2net.NewTimeOutReader(timeout /* seconds */, conn)
} }
responseDone := signal.ExecuteAsync(func() error {
defer output.Close()
v2reader := buf.NewReader(reader) v2reader := buf.NewReader(reader)
defer v2reader.Release()
if err := buf.PipeUntilEOF(v2reader, output); err != nil { if err := buf.PipeUntilEOF(v2reader, output); err != nil {
log.Info("Freedom: Failed to transport all TCP response: ", err) return err
}
return nil
})
if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil {
log.Info("Freedom: Connection ending with ", err)
} }
v2reader.Release()
ray.OutboundOutput().Close()
} }
type Factory struct{} type Factory struct{}

View File

@ -17,9 +17,9 @@ import (
"v2ray.com/core/common/log" "v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net" v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/serial" "v2ray.com/core/common/serial"
"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"
) )
// Server is a HTTP proxy server. // Server is a HTTP proxy server.
@ -155,35 +155,32 @@ func (v *Server) handleConnect(request *http.Request, session *proxy.SessionInfo
} }
ray := v.packetDispatcher.DispatchToOutbound(session) ray := v.packetDispatcher.DispatchToOutbound(session)
v.transport(reader, writer, ray)
}
func (v *Server) transport(input io.Reader, output io.Writer, ray ray.InboundRay) { requestDone := signal.ExecuteAsync(func() error {
var wg sync.WaitGroup defer ray.InboundInput().Close()
wg.Add(2)
defer wg.Wait()
go func() { v2reader := buf.NewReader(reader)
v2reader := buf.NewReader(input)
defer v2reader.Release() defer v2reader.Release()
if err := buf.PipeUntilEOF(v2reader, ray.InboundInput()); err != nil { if err := buf.PipeUntilEOF(v2reader, ray.InboundInput()); err != nil {
log.Info("HTTP: Failed to transport all TCP request: ", err) return err
} }
ray.InboundInput().Close() return nil
wg.Done() })
}()
go func() { responseDone := signal.ExecuteAsync(func() error {
v2writer := buf.NewWriter(output) defer ray.InboundOutput().Release()
v2writer := buf.NewWriter(writer)
defer v2writer.Release() defer v2writer.Release()
if err := buf.PipeUntilEOF(ray.InboundOutput(), v2writer); err != nil { if err := buf.PipeUntilEOF(ray.InboundOutput(), v2writer); err != nil {
log.Info("HTTP: Failed to transport all TCP response: ", err) return err
} }
ray.InboundOutput().Release() return nil
wg.Done() })
}()
signal.ErrorOrFinish2(requestDone, responseDone)
} }
// @VisibleForTesting // @VisibleForTesting
@ -239,27 +236,26 @@ func (v *Server) handlePlainHTTP(request *http.Request, session *proxy.SessionIn
StripHopByHopHeaders(request) StripHopByHopHeaders(request)
ray := v.packetDispatcher.DispatchToOutbound(session) ray := v.packetDispatcher.DispatchToOutbound(session)
defer ray.InboundInput().Close()
defer ray.InboundOutput().Release()
var finish sync.WaitGroup requestDone := signal.ExecuteAsync(func() error {
finish.Add(1) defer ray.InboundInput().Close()
go func() {
defer finish.Done()
requestWriter := bufio.NewWriter(buf.NewBytesWriter(ray.InboundInput())) requestWriter := bufio.NewWriter(buf.NewBytesWriter(ray.InboundInput()))
defer requestWriter.Release() defer requestWriter.Release()
err := request.Write(requestWriter) err := request.Write(requestWriter)
if err != nil { if err != nil {
log.Warning("HTTP: Failed to write request: ", err) return err
return
} }
requestWriter.Flush() if err := requestWriter.Flush(); err != nil {
}() return err
}
return nil
})
responseDone := signal.ExecuteAsync(func() error {
defer ray.InboundOutput().Release()
finish.Add(1)
go func() {
defer finish.Done()
responseReader := bufio.OriginalReader(buf.NewBytesReader(ray.InboundOutput())) responseReader := bufio.OriginalReader(buf.NewBytesReader(ray.InboundOutput()))
response, err := http.ReadResponse(responseReader, request) response, err := http.ReadResponse(responseReader, request)
if err != nil { if err != nil {
@ -267,14 +263,19 @@ func (v *Server) handlePlainHTTP(request *http.Request, session *proxy.SessionIn
response = v.GenerateResponse(503, "Service Unavailable") response = v.GenerateResponse(503, "Service Unavailable")
} }
responseWriter := bufio.NewWriter(writer) responseWriter := bufio.NewWriter(writer)
err = response.Write(responseWriter) if err := response.Write(responseWriter); err != nil {
if err != nil { return err
log.Warning("HTTP: Failed to write response: ", err) }
return
if err := responseWriter.Flush(); err != nil {
return err
}
return nil
})
if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil {
log.Info("HTTP|Server: Connecton ending with ", err)
} }
responseWriter.Flush()
}()
finish.Wait()
} }
// ServerFactory is a InboundHandlerFactory. // ServerFactory is a InboundHandlerFactory.

View File

@ -1,8 +1,6 @@
package shadowsocks package shadowsocks
import ( import (
"sync"
"v2ray.com/core/app" "v2ray.com/core/app"
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
"v2ray.com/core/common/bufio" "v2ray.com/core/common/bufio"
@ -10,6 +8,7 @@ import (
v2net "v2ray.com/core/common/net" v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/protocol" "v2ray.com/core/common/protocol"
"v2ray.com/core/common/retry" "v2ray.com/core/common/retry"
"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" "v2ray.com/core/transport/ray"
@ -38,8 +37,6 @@ func NewClient(config *ClientConfig, space app.Space, meta *proxy.OutboundHandle
// Dispatch implements OutboundHandler.Dispatch(). // Dispatch implements OutboundHandler.Dispatch().
func (v *Client) Dispatch(destination v2net.Destination, payload *buf.Buffer, ray ray.OutboundRay) { func (v *Client) Dispatch(destination v2net.Destination, payload *buf.Buffer, ray ray.OutboundRay) {
defer payload.Release() defer payload.Release()
defer ray.OutboundInput().Release()
defer ray.OutboundOutput().Close()
network := destination.Network network := destination.Network
@ -109,48 +106,38 @@ func (v *Client) Dispatch(destination v2net.Destination, payload *buf.Buffer, ra
} }
} }
var responseMutex sync.Mutex bufferedWriter.SetBuffered(false)
responseMutex.Lock()
go func() { requestDone := signal.ExecuteAsync(func() error {
defer responseMutex.Unlock() defer ray.OutboundInput().Release()
if err := buf.PipeUntilEOF(ray.OutboundInput(), bodyWriter); err != nil {
return err
}
return nil
})
responseDone := signal.ExecuteAsync(func() error {
defer ray.OutboundOutput().Close()
responseReader, err := ReadTCPResponse(user, conn) responseReader, err := ReadTCPResponse(user, conn)
if err != nil { if err != nil {
log.Warning("Shadowsocks|Client: Failed to read response: ", err) return err
return
} }
if err := buf.PipeUntilEOF(responseReader, ray.OutboundOutput()); err != nil { if err := buf.PipeUntilEOF(responseReader, ray.OutboundOutput()); err != nil {
log.Info("Shadowsocks|Client: Failed to transport all TCP response: ", err) return err
}
}()
bufferedWriter.SetBuffered(false)
if err := buf.PipeUntilEOF(ray.OutboundInput(), bodyWriter); err != nil {
log.Info("Shadowsocks|Client: Failed to trasnport all TCP request: ", err)
} }
responseMutex.Lock() return nil
})
if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil {
log.Info("Shadowsocks|Client: Connection ends with ", err)
}
} }
if request.Command == protocol.RequestCommandUDP { if request.Command == protocol.RequestCommandUDP {
timedReader := v2net.NewTimeOutReader(16, conn)
var responseMutex sync.Mutex
responseMutex.Lock()
go func() {
defer responseMutex.Unlock()
reader := &UDPReader{
Reader: timedReader,
User: user,
}
if err := buf.PipeUntilEOF(reader, ray.OutboundOutput()); err != nil {
log.Info("Shadowsocks|Client: Failed to transport all UDP response: ", err)
}
}()
writer := &UDPWriter{ writer := &UDPWriter{
Writer: conn, Writer: conn,
@ -162,11 +149,35 @@ func (v *Client) Dispatch(destination v2net.Destination, payload *buf.Buffer, ra
return return
} }
} }
requestDone := signal.ExecuteAsync(func() error {
defer ray.OutboundInput().Release()
if err := buf.PipeUntilEOF(ray.OutboundInput(), writer); err != nil { if err := buf.PipeUntilEOF(ray.OutboundInput(), writer); err != nil {
log.Info("Shadowsocks|Client: Failed to transport all UDP request: ", err) log.Info("Shadowsocks|Client: Failed to transport all UDP request: ", err)
return err
}
return nil
})
timedReader := v2net.NewTimeOutReader(16, conn)
responseDone := signal.ExecuteAsync(func() error {
defer ray.OutboundOutput().Close()
reader := &UDPReader{
Reader: timedReader,
User: user,
} }
responseMutex.Lock() if err := buf.PipeUntilEOF(reader, ray.OutboundOutput()); err != nil {
log.Info("Shadowsocks|Client: Failed to transport all UDP response: ", err)
return err
}
return nil
})
signal.ErrorOrFinish2(requestDone, responseDone)
} }
} }

View File

@ -1,8 +1,6 @@
package shadowsocks package shadowsocks
import ( import (
"sync"
"v2ray.com/core/app" "v2ray.com/core/app"
"v2ray.com/core/app/dispatcher" "v2ray.com/core/app/dispatcher"
"v2ray.com/core/common" "v2ray.com/core/common"
@ -12,6 +10,7 @@ import (
"v2ray.com/core/common/log" "v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net" v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/protocol" "v2ray.com/core/common/protocol"
"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/internet/udp" "v2ray.com/core/transport/internet/udp"
@ -177,11 +176,10 @@ func (v *Server) handleConnection(conn internet.Connection) {
Inbound: v.meta, Inbound: v.meta,
}) })
defer ray.InboundOutput().Release() defer ray.InboundOutput().Release()
defer ray.InboundInput().Close()
var writeFinish sync.Mutex requestDone := signal.ExecuteAsync(func() error {
writeFinish.Lock() defer ray.InboundOutput().Release()
go func() {
defer writeFinish.Unlock()
bufferedWriter := bufio.NewWriter(conn) bufferedWriter := bufio.NewWriter(conn)
defer bufferedWriter.Release() defer bufferedWriter.Release()
@ -189,26 +187,38 @@ func (v *Server) handleConnection(conn internet.Connection) {
responseWriter, err := WriteTCPResponse(request, bufferedWriter) responseWriter, err := WriteTCPResponse(request, bufferedWriter)
if err != nil { if err != nil {
log.Warning("Shadowsocks|Server: Failed to write response: ", err) log.Warning("Shadowsocks|Server: Failed to write response: ", err)
return return err
} }
defer responseWriter.Release() defer responseWriter.Release()
if payload, err := ray.InboundOutput().Read(); err == nil { payload, err := ray.InboundOutput().Read()
if err != nil {
return err
}
responseWriter.Write(payload) responseWriter.Write(payload)
bufferedWriter.SetBuffered(false) bufferedWriter.SetBuffered(false)
if err := buf.PipeUntilEOF(ray.InboundOutput(), responseWriter); err != nil { if err := buf.PipeUntilEOF(ray.InboundOutput(), responseWriter); err != nil {
log.Info("Shadowsocks|Server: Failed to transport all TCP response: ", err) log.Info("Shadowsocks|Server: Failed to transport all TCP response: ", err)
return err
} }
}
}() return nil
})
responseDone := signal.ExecuteAsync(func() error {
defer ray.InboundInput().Close()
if err := buf.PipeUntilEOF(bodyReader, ray.InboundInput()); err != nil { if err := buf.PipeUntilEOF(bodyReader, ray.InboundInput()); err != nil {
log.Info("Shadowsocks|Server: Failed to transport all TCP request: ", err) log.Info("Shadowsocks|Server: Failed to transport all TCP request: ", err)
return err
} }
ray.InboundInput().Close() return nil
})
writeFinish.Lock() if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil {
log.Info("Shadowsocks|Server: Connection ends with ", err)
}
} }
type ServerFactory struct{} type ServerFactory struct{}

View File

@ -15,6 +15,7 @@ import (
"v2ray.com/core/common/log" "v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net" v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/serial" "v2ray.com/core/common/serial"
"v2ray.com/core/common/signal"
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/proxy/socks/protocol" "v2ray.com/core/proxy/socks/protocol"
"v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet"
@ -299,26 +300,36 @@ func (v *Server) transport(reader io.Reader, writer io.Writer, session *proxy.Se
input := ray.InboundInput() input := ray.InboundInput()
output := ray.InboundOutput() output := ray.InboundOutput()
requestDone := signal.ExecuteAsync(func() error {
defer input.Close() defer input.Close()
defer output.Release()
go func() {
v2reader := buf.NewReader(reader) v2reader := buf.NewReader(reader)
defer v2reader.Release() defer v2reader.Release()
if err := buf.PipeUntilEOF(v2reader, input); err != nil { if err := buf.PipeUntilEOF(v2reader, input); err != nil {
log.Info("Socks|Server: Failed to transport all TCP request: ", err) log.Info("Socks|Server: Failed to transport all TCP request: ", err)
return err
} }
input.Close() return nil
}() })
responseDone := signal.ExecuteAsync(func() error {
defer output.Release()
v2writer := buf.NewWriter(writer) v2writer := buf.NewWriter(writer)
defer v2writer.Release() defer v2writer.Release()
if err := buf.PipeUntilEOF(output, v2writer); err != nil { if err := buf.PipeUntilEOF(output, v2writer); err != nil {
log.Info("Socks|Server: Failed to transport all TCP response: ", err) log.Info("Socks|Server: Failed to transport all TCP response: ", err)
return err
}
return nil
})
if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil {
log.Info("Socks|Server: Connection ends with ", err)
} }
output.Release()
} }
type ServerFactory struct{} type ServerFactory struct{}

View File

@ -137,7 +137,6 @@ func transferRequest(session *encoding.ServerSession, request *protocol.RequestH
defer bodyReader.Release() defer bodyReader.Release()
if err := buf.PipeUntilEOF(bodyReader, output); err != nil { if err := buf.PipeUntilEOF(bodyReader, output); err != nil {
log.Debug("VMess|Inbound: Error when sending data to outbound: ", err)
return err return err
} }
return nil return nil
@ -160,7 +159,6 @@ func transferResponse(session *encoding.ServerSession, request *protocol.Request
} }
if err := buf.PipeUntilEOF(input, bodyWriter); err != nil { if err := buf.PipeUntilEOF(input, bodyWriter); err != nil {
log.Debug("VMess|Inbound: Error when sending data to downstream: ", err)
return err return err
} }
} }
@ -201,13 +199,13 @@ func (v *VMessInboundHandler) HandleConnection(connection internet.Connection) {
if err != nil { if err != nil {
if errors.Cause(err) != io.EOF { if errors.Cause(err) != io.EOF {
log.Access(connection.RemoteAddr(), "", log.AccessRejected, err) log.Access(connection.RemoteAddr(), "", log.AccessRejected, err)
log.Info("VMessIn: Invalid request from ", connection.RemoteAddr(), ": ", err) log.Info("VMess|Inbound: Invalid request from ", connection.RemoteAddr(), ": ", err)
} }
connection.SetReusable(false) connection.SetReusable(false)
return return
} }
log.Access(connection.RemoteAddr(), request.Destination(), log.AccessAccepted, "") log.Access(connection.RemoteAddr(), request.Destination(), log.AccessAccepted, "")
log.Info("VMessIn: Received request for ", request.Destination()) log.Info("VMess|Inbound: Received request for ", request.Destination())
connection.SetReusable(request.Option.Has(protocol.RequestOptionConnectionReuse)) connection.SetReusable(request.Option.Has(protocol.RequestOptionConnectionReuse))
@ -245,13 +243,14 @@ func (v *VMessInboundHandler) HandleConnection(connection internet.Connection) {
return transferResponse(session, request, response, output, writer) return transferResponse(session, request, response, output, writer)
}) })
err = signal.ErrorOrFinish2(requestDone, responseDone) if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil {
if err != nil { log.Info("VMess|Inbound: Connection ending with ", err)
connection.SetReusable(false) connection.SetReusable(false)
return return
} }
if err := writer.Flush(); err != nil { if err := writer.Flush(); err != nil {
log.Info("VMess|Inbound: Failed to flush remain data: ", err)
connection.SetReusable(false) connection.SetReusable(false)
return return
} }

View File

@ -1,8 +1,6 @@
package outbound package outbound
import ( import (
"sync"
"v2ray.com/core/app" "v2ray.com/core/app"
"v2ray.com/core/common" "v2ray.com/core/common"
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
@ -12,6 +10,7 @@ import (
"v2ray.com/core/common/protocol" "v2ray.com/core/common/protocol"
"v2ray.com/core/common/retry" "v2ray.com/core/common/retry"
"v2ray.com/core/common/serial" "v2ray.com/core/common/serial"
"v2ray.com/core/common/signal"
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/proxy/vmess" "v2ray.com/core/proxy/vmess"
"v2ray.com/core/proxy/vmess/encoding" "v2ray.com/core/proxy/vmess/encoding"
@ -28,6 +27,7 @@ type VMessOutboundHandler struct {
// Dispatch implements OutboundHandler.Dispatch(). // Dispatch implements OutboundHandler.Dispatch().
func (v *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *buf.Buffer, ray ray.OutboundRay) { func (v *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *buf.Buffer, ray ray.OutboundRay) {
defer payload.Release()
defer ray.OutboundInput().Release() defer ray.OutboundInput().Release()
defer ray.OutboundOutput().Close() defer ray.OutboundOutput().Close()
@ -80,25 +80,14 @@ func (v *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *buf.B
input := ray.OutboundInput() input := ray.OutboundInput()
output := ray.OutboundOutput() output := ray.OutboundOutput()
var requestFinish, responseFinish sync.Mutex
requestFinish.Lock()
responseFinish.Lock()
session := encoding.NewClientSession(protocol.DefaultIDHash) session := encoding.NewClientSession(protocol.DefaultIDHash)
go v.handleRequest(session, conn, request, payload, input, &requestFinish) requestDone := signal.ExecuteAsync(func() error {
go v.handleResponse(session, conn, request, rec.Destination(), output, &responseFinish) defer input.Release()
requestFinish.Lock()
responseFinish.Lock()
return
}
func (v *VMessOutboundHandler) handleRequest(session *encoding.ClientSession, conn internet.Connection, request *protocol.RequestHeader, payload *buf.Buffer, input buf.Reader, finish *sync.Mutex) {
defer finish.Unlock()
writer := bufio.NewWriter(conn) writer := bufio.NewWriter(conn)
defer writer.Release() defer writer.Release()
session.EncodeRequestHeader(request, writer) session.EncodeRequestHeader(request, writer)
bodyWriter := session.EncodeRequestBody(request, writer) bodyWriter := session.EncodeRequestBody(request, writer)
@ -106,39 +95,34 @@ func (v *VMessOutboundHandler) handleRequest(session *encoding.ClientSession, co
if !payload.IsEmpty() { if !payload.IsEmpty() {
if err := bodyWriter.Write(payload); err != nil { if err := bodyWriter.Write(payload); err != nil {
log.Info("VMess|Outbound: Failed to write payload. Disabling connection reuse.", err) return err
conn.SetReusable(false)
} }
payload.Release()
} }
writer.SetBuffered(false) writer.SetBuffered(false)
if err := buf.PipeUntilEOF(input, bodyWriter); err != nil { if err := buf.PipeUntilEOF(input, bodyWriter); err != nil {
conn.SetReusable(false) return err
} }
if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Option.Has(protocol.RequestOptionChunkStream) {
err := bodyWriter.Write(buf.NewLocal(8)) if err := bodyWriter.Write(buf.NewLocal(8)); err != nil {
if err != nil { return err
conn.SetReusable(false)
} }
} }
return return nil
} })
func (v *VMessOutboundHandler) handleResponse(session *encoding.ClientSession, conn internet.Connection, request *protocol.RequestHeader, dest v2net.Destination, output buf.Writer, finish *sync.Mutex) { responseDone := signal.ExecuteAsync(func() error {
defer finish.Unlock() defer output.Close()
reader := bufio.NewReader(conn) reader := bufio.NewReader(conn)
defer reader.Release() defer reader.Release()
header, err := session.DecodeResponseHeader(reader) header, err := session.DecodeResponseHeader(reader)
if err != nil { if err != nil {
conn.SetReusable(false) return err
log.Warning("VMess|Outbound: Failed to read response from ", request.Destination(), ": ", err)
return
} }
v.handleCommand(dest, header.Command) v.handleCommand(rec.Destination(), header.Command)
conn.SetReusable(header.Option.Has(protocol.ResponseOptionConnectionReuse)) conn.SetReusable(header.Option.Has(protocol.ResponseOptionConnectionReuse))
@ -147,6 +131,14 @@ func (v *VMessOutboundHandler) handleResponse(session *encoding.ClientSession, c
defer bodyReader.Release() defer bodyReader.Release()
if err := buf.PipeUntilEOF(bodyReader, output); err != nil { if err := buf.PipeUntilEOF(bodyReader, output); err != nil {
return err
}
return nil
})
if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil {
log.Info("VMess|Outbound: Connection ending with ", err)
conn.SetReusable(false) conn.SetReusable(false)
} }

View File

@ -65,6 +65,8 @@ func (v *Stream) Read() (*buf.Buffer, error) {
return b, nil return b, nil
case <-v.srcClose: case <-v.srcClose:
return nil, io.EOF return nil, io.EOF
case <-v.destClose:
return nil, io.ErrClosedPipe
} }
} }
} }
@ -97,7 +99,7 @@ func (v *Stream) Close() {
close(v.srcClose) close(v.srcClose)
} }
func (v *Stream) Release() { func (v *Stream) ForceClose() {
defer swallowPanic() defer swallowPanic()
close(v.destClose) close(v.destClose)
@ -114,6 +116,8 @@ func (v *Stream) Release() {
} }
} }
func (v *Stream) Release() {}
func swallowPanic() { func swallowPanic() {
recover() recover()
} }

View File

@ -36,9 +36,11 @@ type Ray interface {
type InputStream interface { type InputStream interface {
buf.Reader buf.Reader
Close() Close()
ForceClose()
} }
type OutputStream interface { type OutputStream interface {
buf.Writer buf.Writer
Close() Close()
ForceClose()
} }