|  | @@ -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,73 +80,65 @@ 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)
 |  | 
 | 
											
												
													
														|  | -	go v.handleResponse(session, conn, request, rec.Destination(), output, &responseFinish)
 |  | 
 | 
											
												
													
														|  | 
 |  | +	requestDone := signal.ExecuteAsync(func() error {
 | 
											
												
													
														|  | 
 |  | +		defer input.Release()
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	requestFinish.Lock()
 |  | 
 | 
											
												
													
														|  | -	responseFinish.Lock()
 |  | 
 | 
											
												
													
														|  | -	return
 |  | 
 | 
											
												
													
														|  | -}
 |  | 
 | 
											
												
													
														|  | 
 |  | +		writer := bufio.NewWriter(conn)
 | 
											
												
													
														|  | 
 |  | +		defer writer.Release()
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -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()
 |  | 
 | 
											
												
													
														|  | 
 |  | +		session.EncodeRequestHeader(request, writer)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	writer := bufio.NewWriter(conn)
 |  | 
 | 
											
												
													
														|  | -	defer writer.Release()
 |  | 
 | 
											
												
													
														|  | -	session.EncodeRequestHeader(request, writer)
 |  | 
 | 
											
												
													
														|  | 
 |  | +		bodyWriter := session.EncodeRequestBody(request, writer)
 | 
											
												
													
														|  | 
 |  | +		defer bodyWriter.Release()
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	bodyWriter := session.EncodeRequestBody(request, writer)
 |  | 
 | 
											
												
													
														|  | -	defer bodyWriter.Release()
 |  | 
 | 
											
												
													
														|  | 
 |  | +		if !payload.IsEmpty() {
 | 
											
												
													
														|  | 
 |  | +			if err := bodyWriter.Write(payload); err != nil {
 | 
											
												
													
														|  | 
 |  | +				return err
 | 
											
												
													
														|  | 
 |  | +			}
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +		writer.SetBuffered(false)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	if !payload.IsEmpty() {
 |  | 
 | 
											
												
													
														|  | -		if err := bodyWriter.Write(payload); err != nil {
 |  | 
 | 
											
												
													
														|  | -			log.Info("VMess|Outbound: Failed to write payload. Disabling connection reuse.", err)
 |  | 
 | 
											
												
													
														|  | -			conn.SetReusable(false)
 |  | 
 | 
											
												
													
														|  | 
 |  | +		if err := buf.PipeUntilEOF(input, bodyWriter); err != nil {
 | 
											
												
													
														|  | 
 |  | +			return err
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  | -		payload.Release()
 |  | 
 | 
											
												
													
														|  | -	}
 |  | 
 | 
											
												
													
														|  | -	writer.SetBuffered(false)
 |  | 
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	if err := buf.PipeUntilEOF(input, bodyWriter); err != nil {
 |  | 
 | 
											
												
													
														|  | -		conn.SetReusable(false)
 |  | 
 | 
											
												
													
														|  | -	}
 |  | 
 | 
											
												
													
														|  | 
 |  | +		if request.Option.Has(protocol.RequestOptionChunkStream) {
 | 
											
												
													
														|  | 
 |  | +			if err := bodyWriter.Write(buf.NewLocal(8)); err != nil {
 | 
											
												
													
														|  | 
 |  | +				return err
 | 
											
												
													
														|  | 
 |  | +			}
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +		return nil
 | 
											
												
													
														|  | 
 |  | +	})
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +	responseDone := signal.ExecuteAsync(func() error {
 | 
											
												
													
														|  | 
 |  | +		defer output.Close()
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +		reader := bufio.NewReader(conn)
 | 
											
												
													
														|  | 
 |  | +		defer reader.Release()
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	if request.Option.Has(protocol.RequestOptionChunkStream) {
 |  | 
 | 
											
												
													
														|  | -		err := bodyWriter.Write(buf.NewLocal(8))
 |  | 
 | 
											
												
													
														|  | 
 |  | +		header, err := session.DecodeResponseHeader(reader)
 | 
											
												
													
														|  |  		if err != nil {
 |  |  		if err != nil {
 | 
											
												
													
														|  | -			conn.SetReusable(false)
 |  | 
 | 
											
												
													
														|  | 
 |  | +			return err
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  | -	}
 |  | 
 | 
											
												
													
														|  | -	return
 |  | 
 | 
											
												
													
														|  | -}
 |  | 
 | 
											
												
													
														|  | 
 |  | +		v.handleCommand(rec.Destination(), header.Command)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -func (v *VMessOutboundHandler) handleResponse(session *encoding.ClientSession, conn internet.Connection, request *protocol.RequestHeader, dest v2net.Destination, output buf.Writer, finish *sync.Mutex) {
 |  | 
 | 
											
												
													
														|  | -	defer finish.Unlock()
 |  | 
 | 
											
												
													
														|  | 
 |  | +		conn.SetReusable(header.Option.Has(protocol.ResponseOptionConnectionReuse))
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	reader := bufio.NewReader(conn)
 |  | 
 | 
											
												
													
														|  | -	defer reader.Release()
 |  | 
 | 
											
												
													
														|  | 
 |  | +		reader.SetBuffered(false)
 | 
											
												
													
														|  | 
 |  | +		bodyReader := session.DecodeResponseBody(request, reader)
 | 
											
												
													
														|  | 
 |  | +		defer bodyReader.Release()
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	header, err := session.DecodeResponseHeader(reader)
 |  | 
 | 
											
												
													
														|  | -	if err != nil {
 |  | 
 | 
											
												
													
														|  | -		conn.SetReusable(false)
 |  | 
 | 
											
												
													
														|  | -		log.Warning("VMess|Outbound: Failed to read response from ", request.Destination(), ": ", err)
 |  | 
 | 
											
												
													
														|  | -		return
 |  | 
 | 
											
												
													
														|  | -	}
 |  | 
 | 
											
												
													
														|  | -	v.handleCommand(dest, header.Command)
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -	conn.SetReusable(header.Option.Has(protocol.ResponseOptionConnectionReuse))
 |  | 
 | 
											
												
													
														|  | 
 |  | +		if err := buf.PipeUntilEOF(bodyReader, output); err != nil {
 | 
											
												
													
														|  | 
 |  | +			return err
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	reader.SetBuffered(false)
 |  | 
 | 
											
												
													
														|  | -	bodyReader := session.DecodeResponseBody(request, reader)
 |  | 
 | 
											
												
													
														|  | -	defer bodyReader.Release()
 |  | 
 | 
											
												
													
														|  | 
 |  | +		return nil
 | 
											
												
													
														|  | 
 |  | +	})
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	if err := buf.PipeUntilEOF(bodyReader, output); err != nil {
 |  | 
 | 
											
												
													
														|  | 
 |  | +	if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil {
 | 
											
												
													
														|  | 
 |  | +		log.Info("VMess|Outbound: Connection ending with ", err)
 | 
											
												
													
														|  |  		conn.SetReusable(false)
 |  |  		conn.SetReusable(false)
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 |