Browse Source

report transportation error to observer

Shelikhoo 4 years ago
parent
commit
a553ccc4c4

+ 26 - 0
app/observatory/explainErrors.go

@@ -0,0 +1,26 @@
+package observatory
+
+import "github.com/v2fly/v2ray-core/v4/common/errors"
+
+type errorCollector struct {
+	errors *errors.Error
+}
+
+func (e *errorCollector) SubmitError(err error) {
+	if e.errors == nil {
+		e.errors = newError("underlying connection error").Base(err)
+		return
+	}
+	e.errors = e.errors.Base(newError("underlying connection error").Base(err))
+}
+
+func newErrorCollector() *errorCollector {
+	return &errorCollector{}
+}
+
+func (e *errorCollector) UnderlyingError() error {
+	if e.errors == nil {
+		return newError("failed to produce report")
+	}
+	return e.errors
+}

+ 12 - 3
app/observatory/observer.go

@@ -8,6 +8,7 @@ import (
 	core "github.com/v2fly/v2ray-core/v4"
 	core "github.com/v2fly/v2ray-core/v4"
 	"github.com/v2fly/v2ray-core/v4/common"
 	"github.com/v2fly/v2ray-core/v4/common"
 	v2net "github.com/v2fly/v2ray-core/v4/common/net"
 	v2net "github.com/v2fly/v2ray-core/v4/common/net"
+	"github.com/v2fly/v2ray-core/v4/common/session"
 	"github.com/v2fly/v2ray-core/v4/common/signal/done"
 	"github.com/v2fly/v2ray-core/v4/common/signal/done"
 	"github.com/v2fly/v2ray-core/v4/common/task"
 	"github.com/v2fly/v2ray-core/v4/common/task"
 	"github.com/v2fly/v2ray-core/v4/features/extension"
 	"github.com/v2fly/v2ray-core/v4/features/extension"
@@ -81,6 +82,8 @@ func (o *Observer) updateStatus(outbounds []string) {
 }
 }
 
 
 func (o *Observer) probe(outbound string) ProbeResult {
 func (o *Observer) probe(outbound string) ProbeResult {
+	errorCollectorForRequest := newErrorCollector()
+
 	httpTransport := http.Transport{
 	httpTransport := http.Transport{
 		Proxy: func(*http.Request) (*url.URL, error) {
 		Proxy: func(*http.Request) (*url.URL, error) {
 			return nil, nil
 			return nil, nil
@@ -93,7 +96,8 @@ func (o *Observer) probe(outbound string) ProbeResult {
 				if err != nil {
 				if err != nil {
 					return newError("cannot understand address").Base(err)
 					return newError("cannot understand address").Base(err)
 				}
 				}
-				conn, err := tagged.Dialer(o.ctx, dest, outbound)
+				trackedCtx := session.TrackedConnectionError(o.ctx, errorCollectorForRequest)
+				conn, err := tagged.Dialer(trackedCtx, dest, outbound)
 				if err != nil {
 				if err != nil {
 					return newError("cannot dial remote address", dest).Base(err)
 					return newError("cannot dial remote address", dest).Base(err)
 				}
 				}
@@ -130,8 +134,13 @@ func (o *Observer) probe(outbound string) ProbeResult {
 		return nil
 		return nil
 	})
 	})
 	if err != nil {
 	if err != nil {
-		newError("the outbound ", outbound, "is dead:").Base(err).AtInfo().WriteToLog()
-		return ProbeResult{Alive: false, LastErrorReason: err.Error()}
+		fullerr := newError("underlying connection failed").Base(errorCollectorForRequest.UnderlyingError())
+		fullerr = newError("with outbound handler report").Base(fullerr)
+		fullerr = newError("GET request failed:", err).Base(fullerr)
+		fullerr = newError("the outbound ", outbound, "is dead:").Base(fullerr)
+		fullerr = fullerr.AtInfo()
+		fullerr.WriteToLog()
+		return ProbeResult{Alive: false, LastErrorReason: fullerr.Error()}
 	}
 	}
 	newError("the outbound ", outbound, "is alive:", GETTime.Seconds()).AtInfo().WriteToLog()
 	newError("the outbound ", outbound, "is alive:", GETTime.Seconds()).AtInfo().WriteToLog()
 	return ProbeResult{Alive: true, Delay: GETTime.Milliseconds()}
 	return ProbeResult{Alive: true, Delay: GETTime.Milliseconds()}

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

@@ -134,13 +134,17 @@ func (h *Handler) Tag() string {
 func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
 func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
 	if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) {
 	if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) {
 		if err := h.mux.Dispatch(ctx, link); err != nil {
 		if err := h.mux.Dispatch(ctx, link); err != nil {
-			newError("failed to process mux outbound traffic").Base(err).WriteToLog(session.ExportIDToError(ctx))
+			err := newError("failed to process mux outbound traffic").Base(err)
+			session.SubmitOutboundErrorToOriginator(ctx, err)
+			err.WriteToLog(session.ExportIDToError(ctx))
 			common.Interrupt(link.Writer)
 			common.Interrupt(link.Writer)
 		}
 		}
 	} else {
 	} else {
 		if err := h.proxy.Process(ctx, link, 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).WriteToLog(session.ExportIDToError(ctx))
+			err := newError("failed to process outbound traffic").Base(err)
+			session.SubmitOutboundErrorToOriginator(ctx, err)
+			err.WriteToLog(session.ExportIDToError(ctx))
 			common.Interrupt(link.Writer)
 			common.Interrupt(link.Writer)
 		} else {
 		} else {
 			common.Must(common.Close(link.Writer))
 			common.Must(common.Close(link.Writer))

+ 16 - 0
common/session/context.go

@@ -13,6 +13,7 @@ const (
 	contentSessionKey
 	contentSessionKey
 	muxPreferedSessionKey
 	muxPreferedSessionKey
 	sockoptSessionKey
 	sockoptSessionKey
+	trackedConnectionErrorKey
 )
 )
 
 
 // ContextWithID returns a new context with the given ID.
 // ContextWithID returns a new context with the given ID.
@@ -116,3 +117,18 @@ func SetForcedOutboundTagToContext(ctx context.Context, tag string) context.Cont
 	ContentFromContext(ctx).SetAttribute("forcedOutboundTag", tag)
 	ContentFromContext(ctx).SetAttribute("forcedOutboundTag", tag)
 	return ctx
 	return ctx
 }
 }
+
+type TrackedRequestErrorFeedback interface {
+	SubmitError(err error)
+}
+
+func SubmitOutboundErrorToOriginator(ctx context.Context, err error) {
+	if errorTracker := ctx.Value(trackedConnectionErrorKey); errorTracker != nil {
+		errorTracker := errorTracker.(TrackedRequestErrorFeedback)
+		errorTracker.SubmitError(err)
+	}
+}
+
+func TrackedConnectionError(ctx context.Context, tracker TrackedRequestErrorFeedback) context.Context {
+	return context.WithValue(ctx, trackedConnectionErrorKey, tracker)
+}