浏览代码

smart error propagation

Darien Raymond 8 年之前
父节点
当前提交
e480091388

+ 13 - 2
app/proxyman/inbound/worker.go

@@ -11,6 +11,7 @@ import (
 	"v2ray.com/core/app/dispatcher"
 	"v2ray.com/core/app/log"
 	"v2ray.com/core/common/buf"
+	"v2ray.com/core/common/errors"
 	v2net "v2ray.com/core/common/net"
 	"v2ray.com/core/proxy"
 	"v2ray.com/core/transport/internet"
@@ -53,7 +54,12 @@ func (w *tcpWorker) callback(conn internet.Connection) {
 	ctx = proxy.ContextWithInboundEntryPoint(ctx, v2net.TCPDestination(w.address, w.port))
 	ctx = proxy.ContextWithSource(ctx, v2net.DestinationFromAddr(conn.RemoteAddr()))
 	if err := w.proxy.Process(ctx, v2net.Network_TCP, conn, w.dispatcher); err != nil {
-		log.Info("Proxyman|TCPWorker: Connection ends with ", err)
+		err := errors.Base(err).Message("Proxyman|TCPWorker: Connection ends.")
+		if errors.IsActionRequired(err) {
+			log.Warning(err)
+		} else {
+			log.Info(err)
+		}
 	}
 	cancel()
 	conn.Close()
@@ -213,7 +219,12 @@ func (w *udpWorker) callback(b *buf.Buffer, source v2net.Destination, originalDe
 			ctx = proxy.ContextWithSource(ctx, source)
 			ctx = proxy.ContextWithInboundEntryPoint(ctx, v2net.UDPDestination(w.address, w.port))
 			if err := w.proxy.Process(ctx, v2net.Network_UDP, conn, w.dispatcher); err != nil {
-				log.Info("Proxyman|UDPWorker: Connection ends with ", err)
+				err = errors.Base(err).Message("Proxyman|UDPWorker: Connection ends.")
+				if errors.IsActionRequired(err) {
+					log.Warning(err)
+				} else {
+					log.Info(err)
+				}
 			}
 			w.removeConn(source)
 			cancel()

+ 6 - 1
app/proxyman/outbound/handler.go

@@ -67,7 +67,12 @@ func (h *Handler) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) {
 	err := h.proxy.Process(ctx, outboundRay, h)
 	// Ensure outbound ray is properly closed.
 	if err != nil && errors.Cause(err) != io.EOF {
-		log.Info("Proxyman|OutboundHandler: Failed to process outbound traffic: ", err)
+		err = errors.Base(err).Message("Proxyman|OutboundHandler: Failed to process outbound traffic.")
+		if errors.IsActionRequired(err) {
+			log.Warning(err)
+		} else {
+			log.Info(err)
+		}
 		outboundRay.OutboundOutput().CloseError()
 	} else {
 		outboundRay.OutboundOutput().Close()

+ 37 - 9
common/errors/errors.go

@@ -12,10 +12,15 @@ type hasInnerError interface {
 	Inner() error
 }
 
+type actionRequired interface {
+	ActionRequired() bool
+}
+
 // Error is an error object with underlying error.
 type Error struct {
-	message string
-	inner   error
+	message        string
+	inner          error
+	actionRequired bool
 }
 
 // Error implements error.Error().
@@ -31,6 +36,10 @@ func (v *Error) Inner() error {
 	return v.inner
 }
 
+func (v *Error) ActionRequired() bool {
+	return v.actionRequired
+}
+
 // New returns a new error object with message formed from given arguments.
 func New(msg ...interface{}) error {
 	return &Error{
@@ -56,19 +65,36 @@ func Cause(err error) error {
 	}
 	for {
 		inner, ok := err.(hasInnerError)
-		if !ok {
+		if !ok || inner.Inner() == nil {
 			break
 		}
-		if inner.Inner() == nil {
+		err = inner.Inner()
+	}
+	return err
+}
+
+func IsActionRequired(err error) bool {
+	for err != nil {
+		if ar, ok := err.(actionRequired); ok && ar.ActionRequired() {
+			return true
+		}
+		inner, ok := err.(hasInnerError)
+		if !ok || inner.Inner() == nil {
 			break
 		}
 		err = inner.Inner()
 	}
-	return err
+	return false
 }
 
 type ErrorBuilder struct {
 	error
+	actionRequired bool
+}
+
+func (v ErrorBuilder) RequireUserAction() ErrorBuilder {
+	v.actionRequired = true
+	return v
 }
 
 // Message returns an error object with given message and base error.
@@ -78,8 +104,9 @@ func (v ErrorBuilder) Message(msg ...interface{}) error {
 	}
 
 	return &Error{
-		message: serial.Concat(msg...) + " > " + v.error.Error(),
-		inner:   v.error,
+		message:        serial.Concat(msg...) + " > " + v.error.Error(),
+		inner:          v.error,
+		actionRequired: v.actionRequired,
 	}
 }
 
@@ -89,7 +116,8 @@ func (v ErrorBuilder) Format(format string, values ...interface{}) error {
 		return nil
 	}
 	return &Error{
-		message: fmt.Sprintf(format, values...) + " > " + v.error.Error(),
-		inner:   v.error,
+		message:        fmt.Sprintf(format, values...) + " > " + v.error.Error(),
+		inner:          v.error,
+		actionRequired: v.actionRequired,
 	}
 }

+ 26 - 0
common/errors/errors_test.go

@@ -0,0 +1,26 @@
+package errors_test
+
+import (
+	"io"
+	"testing"
+
+	. "v2ray.com/core/common/errors"
+	"v2ray.com/core/testing/assert"
+)
+
+func TestActionRequired(t *testing.T) {
+	assert := assert.On(t)
+
+	err := New("TestError")
+	assert.Bool(IsActionRequired(err)).IsFalse()
+
+	err = Base(io.EOF).Message("TestError2")
+	assert.Bool(IsActionRequired(err)).IsFalse()
+
+	err = Base(io.EOF).RequireUserAction().Message("TestError3")
+	assert.Bool(IsActionRequired(err)).IsTrue()
+
+	err = Base(io.EOF).RequireUserAction().Message("TestError4")
+	err = Base(err).Message("TestError5")
+	assert.Bool(IsActionRequired(err)).IsTrue()
+}

+ 1 - 1
proxy/shadowsocks/client.go

@@ -60,7 +60,7 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
 		return nil
 	})
 	if err != nil {
-		return errors.Base(err).Message("Shadowsocks|Client: Failed to find an available destination.")
+		return errors.Base(err).RequireUserAction().Message("Shadowsocks|Client: Failed to find an available destination.")
 	}
 	log.Info("Shadowsocks|Client: Tunneling request to ", destination, " via ", server.Destination())
 

+ 1 - 1
proxy/vmess/outbound/outbound.go

@@ -61,7 +61,7 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
 		return nil
 	})
 	if err != nil {
-		return errors.Base(err).Message("VMess|Outbound: Failed to find an available destination.")
+		return errors.Base(err).RequireUserAction().Message("VMess|Outbound: Failed to find an available destination.")
 	}
 	defer conn.Close()
 

+ 11 - 2
transport/internet/tcp_hub.go

@@ -3,6 +3,7 @@ package internet
 import (
 	"net"
 
+	"v2ray.com/core/app/log"
 	"v2ray.com/core/common/errors"
 	v2net "v2ray.com/core/common/net"
 	"v2ray.com/core/common/retry"
@@ -85,13 +86,21 @@ func (v *TCPHub) start() {
 			default:
 				conn, err := v.listener.Accept()
 				if err != nil {
-					return errors.Base(err).Message("Internet|Listener: Failed to accept new TCP connection.")
+					return errors.Base(err).RequireUserAction().Message("Internet|Listener: Failed to accept new TCP connection.")
 				}
 				newConn = conn
 				return nil
 			}
 		})
-		if err == nil && newConn != nil {
+		if err != nil {
+			if errors.IsActionRequired(err) {
+				log.Warning(err)
+			} else {
+				log.Info(err)
+			}
+			continue
+		}
+		if newConn != nil {
 			go v.connCallback(newConn)
 		}
 	}