diff --git a/channelutil/clone.go b/channelutil/clone.go index de6ddd59..5ff3a07c 100644 --- a/channelutil/clone.go +++ b/channelutil/clone.go @@ -5,7 +5,7 @@ import ( "log" "sync" - errorutil "github.com/projectdiscovery/utils/errors" + "github.com/projectdiscovery/utils/errkit" ) // CloneOptions provides options for Cloning channels @@ -43,13 +43,13 @@ func NewCloneChannels[T any](opts *CloneOptions) *CloneChannels[T] { // Clone takes data from source channel(src) and sends them to sinks(send only channel) without being totally unfair func (s *CloneChannels[T]) Clone(ctx context.Context, src chan T, sinks ...chan<- T) error { if src == nil { - return errorutil.New("source channel is nil").WithTag("Clone", "channel") + return errkit.New("source channel is nil") } // check if all sinks are not nil for _, ch := range sinks { if ch == nil { - return errorutil.New("nil sink found").WithTag("Clone", "channel") + return errkit.New("nil sink found") } } diff --git a/channelutil/join.go b/channelutil/join.go index 4b970ea0..fe93bd57 100644 --- a/channelutil/join.go +++ b/channelutil/join.go @@ -5,7 +5,7 @@ import ( "log" "sync" - errorutil "github.com/projectdiscovery/utils/errors" + "github.com/projectdiscovery/utils/errkit" ) // JoinChannels provides method to Join channels @@ -20,14 +20,14 @@ type JoinChannels[T any] struct { // JoinChannels Joins Many Channels to Create One func (j *JoinChannels[T]) Join(ctx context.Context, sink chan T, sources ...<-chan T) error { if sink == nil { - return errorutil.New("sink cannot be nil").WithTag("join", "channel") + return errkit.New("sink cannot be nil") } if len(sources) == 0 { - return errorutil.New("sources cannot be zero").WithTag("join", "channel") + return errkit.New("sources cannot be zero") } for _, v := range sources { if v == nil { - return errorutil.New("given source is nil").WithTag("join", "channel") + return errkit.New("given source is nil") } } diff --git a/errkit/errors.go b/errkit/errors.go index 728fcab5..aacdba56 100644 --- a/errkit/errors.go +++ b/errkit/errors.go @@ -41,7 +41,7 @@ var ( // EnableTimestamp controls whether error timestamps are included EnableTimestamp = env.GetEnvOrDefault("ENABLE_ERR_TIMESTAMP", false) // EnableTrace controls whether error stack traces are included - EnableTrace = env.GetEnvOrDefault("ENABLE_ERR_TRACE", false) + EnableTrace = env.GetEnvOrDefault("ERRKIT_ENABLE_TRACE", false) ) // ErrorX is a custom error type that can handle all known types of errors @@ -220,19 +220,18 @@ func FromError(err error) *ErrorX { return nucleiErr } -// New creates a new error with the given message -// it follows slog pattern of adding and expects in the same way +// New creates a new error with the given message and slog attributes // // Example: // // this is correct (√) -// errkit.New("this is a nuclei error","address",host) +// errkit.New("connection failed", "address", host, "port", port) // -// this is not readable/recommended (x) -// errkit.New("this is a nuclei error",slog.String("address",host)) +// this is also correct (√) +// errkit.New("timeout occurred") // -// this is wrong (x) -// errkit.New("this is a nuclei error %s",host) +// this is not recommended (x) - use Newf instead +// errkit.New("error on host %s", host) func New(msg string, args ...interface{}) *ErrorX { e := &ErrorX{} e.init() @@ -243,27 +242,46 @@ func New(msg string, args ...interface{}) *ErrorX { return e } -// Msgf adds a message to the error -// it follows slog pattern of adding and expects in the same way +// Newf creates a new error with a formatted message // // Example: // -// this is correct (√) -// myError.Msgf("dial error","network","tcp") +// errkit.Newf("connection failed on %s:%d", host, port) +func Newf(format string, args ...interface{}) *ErrorX { + e := &ErrorX{} + e.init() + msg := fmt.Sprintf(format, args...) + e.append(errors.New(msg)) + return e +} + +// Msg adds a plain message to the error // -// this is not readable/recommended (x) -// myError.Msgf(slog.String("address",host)) +// Example: // -// this is wrong (x) -// myError.Msgf("this is a nuclei error %s",host) -func (e *ErrorX) Msgf(format string, args ...interface{}) { +// myError.Msg("connection failed") +func (e *ErrorX) Msg(message string) { if e == nil { return } - if len(args) == 0 { - e.append(errors.New(format)) + e.append(errors.New(message)) +} + +// Msgf adds a formatted message to the error +// +// Example: +// +// this is correct (√) +// myError.Msgf("dial error on %s:%d", host, port) +// +// this is also correct (√) +// myError.Msgf("connection failed") +func (e *ErrorX) Msgf(format string, args ...interface{}) { + if e == nil { + return } - e.append(fmt.Errorf(format, args...)) + msg := fmt.Sprintf(format, args...) + e.append(errors.New(msg)) } // SetClass sets the class of the error diff --git a/errkit/errors_test.go b/errkit/errors_test.go index a27cb168..235e9351 100644 --- a/errkit/errors_test.go +++ b/errkit/errors_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/pkg/errors" - errorutil "github.com/projectdiscovery/utils/errors" "github.com/stretchr/testify/require" "go.uber.org/multierr" @@ -71,14 +70,15 @@ func TestErrorIs(t *testing.T) { } } -func TestErrorUtil(t *testing.T) { - utilErr := errorutil.New("got err while executing http://206.189.19.240:8000/wp-content/plugins/wp-automatic/inc/csv.php <- POST http://206.189.19.240:8000/wp-content/plugins/wp-automatic/inc/csv.php giving up after 2 attempts: Post \"http://206.189.19.240:8000/wp-content/plugins/wp-automatic/inc/csv.php\": [:RUNTIME] ztls fallback failed <- dial tcp 206.189.19.240:8000: connect: connection refused") - x := ErrorX{} - parseError(&x, utilErr) - if len(x.errs) != 3 { - t.Fatal("expected 3 errors") - } -} +// TestErrorUtil commented out to avoid circular dependency with errorutil +// func TestErrorUtil(t *testing.T) { +// utilErr := errorutil.New("got err while executing http://206.189.19.240:8000/wp-content/plugins/wp-automatic/inc/csv.php <- POST http://206.189.19.240:8000/wp-content/plugins/wp-automatic/inc/csv.php giving up after 2 attempts: Post \"http://206.189.19.240:8000/wp-content/plugins/wp-automatic/inc/csv.php\": [:RUNTIME] ztls fallback failed <- dial tcp 206.189.19.240:8000: connect: connection refused") +// x := ErrorX{} +// parseError(&x, utilErr) +// if len(x.errs) != 3 { +// t.Fatal("expected 3 errors") +// } +// } func TestErrKindCheck(t *testing.T) { x := New("port closed or filtered").SetKind(ErrKindNetworkPermanent) diff --git a/errkit/helpers.go b/errkit/helpers.go index 8bff426a..06ad3a62 100644 --- a/errkit/helpers.go +++ b/errkit/helpers.go @@ -82,7 +82,7 @@ func Wrap(err error, message string) error { } x := &ErrorX{} parseError(x, err) - x.Msgf("%s", message) + x.Msg(message) return x } @@ -148,7 +148,7 @@ func WithMessage(err error, message string) error { } x := &ErrorX{} parseError(x, err) - x.Msgf("%s", message) + x.Msg(message) return x } diff --git a/errors/enriched.go b/errors/enriched.go index 62145390..b7add997 100644 --- a/errors/enriched.go +++ b/errors/enriched.go @@ -1,164 +1,173 @@ +// Package errorutil provides error handling utilities. +// +// Deprecated: This package is deprecated and will be removed in a future version. +// Use github.com/projectdiscovery/utils/errkit instead, which provides better +// error handling with proper Go error chain support and optional stack traces. package errorutil import ( - "bytes" "errors" - "fmt" - "runtime/debug" - "strings" -) -// ShowStackTrace in Error Message -var ShowStackTrace bool = false + "github.com/projectdiscovery/utils/errkit" +) // ErrCallback function to handle given error +// +// Deprecated: Use errkit.ErrorX and its structured logging capabilities instead. type ErrCallback func(level ErrorLevel, err string, tags ...string) // enrichedError is enriched version of normal error -// with tags, stacktrace and other methods +// with tags and other methods +// +// Deprecated: Use errkit.ErrorX instead. type enrichedError struct { - errString string - wrappedErr error - StackTrace string - Tags []string - Level ErrorLevel - - //OnError is called when Error() method is triggered - OnError ErrCallback + errorX *errkit.ErrorX + level ErrorLevel + tags []string + callback ErrCallback + wrappedErrs []error // Keep original errors for compatibility } -// withTag assignes tag to Error +// WithTag assignes tag to Error +// +// Deprecated: Use errkit.ErrorX instead. func (e *enrichedError) WithTag(tag ...string) Error { - if e.Tags == nil { - e.Tags = tag + if e.tags == nil { + e.tags = tag } else { - e.Tags = append(e.Tags, tag...) + e.tags = append(e.tags, tag...) } return e } -// withLevel assinges level to Error +// WithLevel assinges level to Error +// +// Deprecated: Use errkit.ErrorX instead. func (e *enrichedError) WithLevel(level ErrorLevel) Error { - e.Level = level + e.level = level return e } // Unwrap returns the underlying error +// +// Deprecated: Use errkit.ErrorX instead. func (e *enrichedError) Unwrap() error { - return e.wrappedErr + if e.errorX != nil { + // Return the original error that was used to create this enrichedError + return e.errorX.Cause() + } + return nil } -// returns formated *enrichedError string +// Error returns formatted *enrichedError string +// +// Deprecated: Use errkit.ErrorX instead. func (e *enrichedError) Error() string { defer func() { - if e.OnError != nil { - e.OnError(e.Level, e.errString, e.Tags...) + if e.callback != nil { + errStr := "" + if e.errorX != nil { + errStr = e.errorX.Error() + } + e.callback(e.level, errStr, e.tags...) } }() - var buff bytes.Buffer - label := fmt.Sprintf("[%v:%v]", strings.Join(e.Tags, ","), e.Level.String()) - buff.WriteString(fmt.Sprintf("%v %v", label, e.errString)) - if ShowStackTrace { - e.captureStack() - buff.WriteString(fmt.Sprintf("Stacktrace:\n%v", e.StackTrace)) + if e.errorX == nil { + return "" } - return buff.String() + + return e.errorX.Error() } -// wraps given error +// Wrap wraps given error +// +// Deprecated: Use errkit.ErrorX instead. func (e *enrichedError) Wrap(err ...error) Error { for _, v := range err { if v == nil { continue } - if e.wrappedErr == nil { - e.wrappedErr = v - } else { - // wraps the existing wrapped error (maintains the error chain) - e.wrappedErr = &enrichedError{ - errString: v.Error(), - wrappedErr: e.wrappedErr, - Level: e.Level, - } - } - - // preserve its props if it's an enriched one - if ee, ok := v.(*enrichedError); ok { - if len(ee.Tags) > 0 { - if e.Tags == nil { - e.Tags = make([]string, 0) - } - e.Tags = append(e.Tags, ee.Tags...) - } + // Store original error for compatibility + e.wrappedErrs = append(e.wrappedErrs, v) - if ee.StackTrace != "" { - e.StackTrace += ee.StackTrace - } + if e.errorX == nil { + // Create a new ErrorX starting with this error + e.errorX = errkit.FromError(v) + } else { + // Add this error to the existing ErrorX + e.errorX.Msgf("%s", v.Error()) } } return e } -// Wrapf wraps given message +// Msgf wraps given message +// +// Deprecated: Use errkit.ErrorX instead. func (e *enrichedError) Msgf(format string, args ...any) Error { - // wraps with '<-` as delimeter - msg := fmt.Sprintf(format, args...) - if e.errString == "" { - e.errString = msg - } else { - e.errString = fmt.Sprintf("%v <- %v", msg, e.errString) + if e.errorX == nil { + e.errorX = errkit.New("error") } + // Pass format and args directly to errkit.Msgf + e.errorX.Msgf(format, args...) return e } // Equal returns true if error matches anyone of given errors +// +// Deprecated: Use errkit.ErrorX instead. func (e *enrichedError) Equal(err ...error) bool { for _, v := range err { - if ee, ok := v.(*enrichedError); ok { - if e.errString == ee.errString { - return true - } - } else { - // not an enriched error but a simple error - if e.errString == v.Error() { - return true - } + if e.Is(v) { + return true } + } + return false +} - // also check if the err is in the wrapped chain - if errors.Is(e, v) { +// Is implements the errors.Is interface for Go's error chain traversal +func (e *enrichedError) Is(target error) bool { + // First check our wrapped errors for exact matches + for _, wrappedErr := range e.wrappedErrs { + if errors.Is(wrappedErr, target) { return true } } - + + // Then check errkit's Is method + if e.errorX != nil { + return e.errorX.Is(target) + } + return false } // WithCallback executes callback when error is triggered +// +// Deprecated: Use errkit.ErrorX instead. func (e *enrichedError) WithCallback(handle ErrCallback) Error { - e.OnError = handle + e.callback = handle return e } -// captureStack -func (e *enrichedError) captureStack() { - // can be furthur improved to format - // ref https://github.com/go-errors/errors/blob/33d496f939bc762321a636d4035e15c302eb0b00/stackframe.go - e.StackTrace = string(debug.Stack()) -} - -// New +// New creates a new error +// +// Deprecated: Use errkit.New instead. func New(format string, args ...any) Error { + errorX := errkit.New(format, args...) ee := &enrichedError{ - errString: fmt.Sprintf(format, args...), - Level: Runtime, + errorX: errorX, + level: Runtime, } return ee } +// NewWithErr creates a new error with an existing error +// +// Deprecated: Use errkit.FromError instead. func NewWithErr(err error) Error { if err == nil { return nil @@ -166,25 +175,27 @@ func NewWithErr(err error) Error { if ee, ok := err.(*enrichedError); ok { return &enrichedError{ - errString: ee.errString, - wrappedErr: err, - StackTrace: ee.StackTrace, - Tags: append([]string{}, ee.Tags...), - Level: ee.Level, - OnError: ee.OnError, + errorX: ee.errorX, + level: ee.level, + tags: append([]string{}, ee.tags...), + callback: ee.callback, + wrappedErrs: append([]error{}, ee.wrappedErrs...), } } + errorX := errkit.FromError(err) return &enrichedError{ - errString: err.Error(), - wrappedErr: err, - Level: Runtime, + errorX: errorX, + level: Runtime, + wrappedErrs: []error{err}, // Store the original error } } // NewWithTag creates an error with tag +// +// Deprecated: Use errkit.New instead. func NewWithTag(tag string, format string, args ...any) Error { ee := New(format, args...) _ = ee.WithTag(tag) return ee -} +} \ No newline at end of file diff --git a/errors/err_test.go b/errors/err_test.go index 6d6341c1..55e01564 100644 --- a/errors/err_test.go +++ b/errors/err_test.go @@ -39,23 +39,6 @@ func TestWrapWithNil(t *testing.T) { } } -func TestStackTrace(t *testing.T) { - err := errorutil.New("base error") - relay := func(err error) error { - return err - } - errx := relay(err) - - t.Run("teststack", func(t *testing.T) { - if strings.Contains(errx.Error(), "captureStack") { - t.Errorf("stacktrace should be disabled by default") - } - errorutil.ShowStackTrace = true - if !strings.Contains(errx.Error(), "captureStack") { - t.Errorf("missing stacktrace got %v", errx.Error()) - } - }) -} func TestErrorCallback(t *testing.T) { callbackExecuted := false @@ -72,8 +55,8 @@ func TestErrorCallback(t *testing.T) { errval := err.Error() - if !strings.Contains(errval, "callback") || !strings.Contains(errval, "got error") || !strings.Contains(errval, "RUNTIME") { - t.Errorf("error content missing expected values `callback,got error and Runtime` in error value but got %v", errval) + if !strings.Contains(errval, "got error") { + t.Errorf("error content missing expected value `got error` in error value but got %v", errval) } if !callbackExecuted { diff --git a/errors/err_with_fmt.go b/errors/err_with_fmt.go index bff847f3..ddd3cc1f 100644 --- a/errors/err_with_fmt.go +++ b/errors/err_with_fmt.go @@ -1,3 +1,7 @@ +// Package errorutil provides error handling utilities. +// +// Deprecated: This package is deprecated and will be removed in a future version. +// Use github.com/projectdiscovery/utils/errkit instead. package errorutil import ( @@ -5,19 +9,29 @@ import ( ) // ErrWithFmt is a simplified version of err holding a default format +// +// Deprecated: Use errkit.ErrorX instead. type ErrWithFmt struct { fmt string } -// Wrapf wraps given message +// Msgf wraps given message +// +// Deprecated: Use errkit.ErrorX instead. func (e *ErrWithFmt) Msgf(args ...any) error { return fmt.Errorf(e.fmt, args...) } +// Error implements error interface +// +// Deprecated: Use errkit.ErrorX instead. func (e *ErrWithFmt) Error() { panic("ErrWithFmt is a format holder") } +// NewWithFmt creates a new ErrWithFmt +// +// Deprecated: Use errkit.New instead. func NewWithFmt(fmt string) ErrWithFmt { if fmt == "" { panic("format can't be empty") diff --git a/errors/errinterface.go b/errors/errinterface.go index baf6fa90..00697d06 100644 --- a/errors/errinterface.go +++ b/errors/errinterface.go @@ -1,22 +1,44 @@ +// Package errorutil provides error handling utilities. +// +// Deprecated: This package is deprecated and will be removed in a future version. +// Use github.com/projectdiscovery/utils/errkit instead. package errorutil // Error is enriched version of normal error -// with tags, stacktrace and other methods +// with tags and other methods +// +// Deprecated: Use errkit.ErrorX instead. type Error interface { // WithTag assigns tag[s] to Error + // + // Deprecated: Use errkit.ErrorX instead. WithTag(tag ...string) Error // WithLevel assigns given ErrorLevel + // + // Deprecated: Use errkit.ErrorX instead. WithLevel(level ErrorLevel) Error // Error is interface method of 'error' + // + // Deprecated: Use errkit.ErrorX instead. Error() string // Unwrap returns the underlying error + // + // Deprecated: Use errkit.ErrorX instead. Unwrap() error // Wraps existing error with errors (skips if passed error is nil) + // + // Deprecated: Use errkit.ErrorX instead. Wrap(err ...error) Error // Msgf wraps error with given message + // + // Deprecated: Use errkit.ErrorX instead. Msgf(format string, args ...any) Error // Equal Checks Equality of errors + // + // Deprecated: Use errkit.ErrorX instead. Equal(err ...error) bool // WithCallback execute ErrCallback function when Error is triggered + // + // Deprecated: Use errkit.ErrorX instead. WithCallback(handle ErrCallback) Error } diff --git a/errors/errlevel.go b/errors/errlevel.go index a078f758..0686570f 100644 --- a/errors/errlevel.go +++ b/errors/errlevel.go @@ -1,13 +1,32 @@ +// Package errorutil provides error handling utilities. +// +// Deprecated: This package is deprecated and will be removed in a future version. +// Use github.com/projectdiscovery/utils/errkit instead. package errorutil +// ErrorLevel represents the severity level of an error +// +// Deprecated: Use errkit.ErrKind instead. type ErrorLevel uint const ( + // Panic level error + // + // Deprecated: Use errkit.ErrKind instead. Panic ErrorLevel = iota + // Fatal level error + // + // Deprecated: Use errkit.ErrKind instead. Fatal - Runtime // Default + // Runtime level error (Default) + // + // Deprecated: Use errkit.ErrKind instead. + Runtime ) +// String returns string representation of ErrorLevel +// +// Deprecated: Use errkit.ErrKind instead. func (l ErrorLevel) String() string { switch l { case Panic: diff --git a/errors/errors.go b/errors/errors.go index 1f888b09..c599da07 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -1,3 +1,7 @@ +// Package errorutil provides error handling utilities. +// +// Deprecated: This package is deprecated and will be removed in a future version. +// Use github.com/projectdiscovery/utils/errkit instead. package errorutil import ( @@ -11,6 +15,8 @@ import ( // IsAny checks if err is not nil and matches any one of errxx errors // if match successful returns true else false +// +// Deprecated: Use standard library errors.Is instead. func IsAny(err error, errxx ...error) bool { if err == nil { return false @@ -45,6 +51,8 @@ func IsAny(err error, errxx ...error) bool { // WrapfWithNil returns nil if error is nil but if err is not nil // wraps error with given msg unlike errors.Wrapf +// +// Deprecated: Use errkit.FromError instead. func WrapfWithNil(err error, format string, args ...any) Error { if err == nil { return nil @@ -55,6 +63,8 @@ func WrapfWithNil(err error, format string, args ...any) Error { // WrapwithNil returns nil if err is nil but wraps it with given // errors continuously if it is not nil +// +// Deprecated: Use errkit.FromError instead. func WrapwithNil(err error, errx ...error) Error { if err == nil { return nil @@ -64,6 +74,8 @@ func WrapwithNil(err error, errx ...error) Error { } // IsTimeout checks if error is timeout error +// +// Deprecated: Use standard library errors.Is with context.DeadlineExceeded instead. func IsTimeout(err error) bool { var net net.Error return (errors.As(err, &net) && net.Timeout()) || errors.Is(err, context.DeadlineExceeded) || errors.Is(err, os.ErrDeadlineExceeded) diff --git a/file/clean.go b/file/clean.go index 8ee01c4c..1bc5ce09 100644 --- a/file/clean.go +++ b/file/clean.go @@ -4,7 +4,7 @@ import ( "os" "path/filepath" - errorutil "github.com/projectdiscovery/utils/errors" + "github.com/projectdiscovery/utils/errkit" ) var ( @@ -61,7 +61,7 @@ func ResolveNClean(inputPath string, baseDir ...string) (string, error) { return abs, nil } } - return "", errorutil.NewWithErr(os.ErrNotExist).Msgf("failed to resolve path: %s", inputPath) + return "", errkit.Wrapf(os.ErrNotExist, "failed to resolve path: %s", inputPath) } // ResolveNCleanOrDefault resolves the path and cleans it diff --git a/maps/synclock_map.go b/maps/synclock_map.go index 36954767..04a981bc 100644 --- a/maps/synclock_map.go +++ b/maps/synclock_map.go @@ -4,11 +4,11 @@ import ( "sync" "sync/atomic" - errorutil "github.com/projectdiscovery/utils/errors" + "github.com/projectdiscovery/utils/errkit" ) var ( - ErrReadOnly = errorutil.New("map is currently in read-only mode").WithTag("syncLockMap") + ErrReadOnly = errkit.New("map is currently in read-only mode") ) // SyncLock adds sync and lock capabilities to generic map diff --git a/proxy/proxy.go b/proxy/proxy.go index 36fcba7e..f39f54c5 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -8,7 +8,7 @@ import ( "strings" "time" - errorutil "github.com/projectdiscovery/utils/errors" + "github.com/projectdiscovery/utils/errkit" "github.com/remeh/sizedwaitgroup" ) @@ -76,7 +76,7 @@ func GetAnyAliveProxy(timeoutInSec int, proxies ...string) (string, error) { } // all proxies are dead - return "", errorutil.NewWithTag("proxyutils", "all proxies are dead got : %v", strings.Join(errstack, " : ")) + return "", errkit.Newf("all proxies are dead got : %v", strings.Join(errstack, " : ")) } // dial and test if proxy is open @@ -96,7 +96,7 @@ func GetProxyURL(proxyAddr string) (url.URL, error) { if url, err := url.Parse(proxyAddr); err == nil && isSupportedProtocol(url.Scheme) { return *url, nil } - return url.URL{}, errorutil.New("invalid proxy format (It should be http[s]/socks5://[username:password@]host:port)").WithTag("proxyutils") + return url.URL{}, errkit.New("invalid proxy format (It should be http[s]/socks5://[username:password@]host:port)") } // isSupportedProtocol checks given protocols are supported diff --git a/reader/conn_read.go b/reader/conn_read.go index 526e7a3f..95424bb4 100644 --- a/reader/conn_read.go +++ b/reader/conn_read.go @@ -9,7 +9,7 @@ import ( "github.com/docker/go-units" contextutil "github.com/projectdiscovery/utils/context" - errorutil "github.com/projectdiscovery/utils/errors" + "github.com/projectdiscovery/utils/errkit" ) var ( @@ -59,11 +59,11 @@ func ConnReadN(ctx context.Context, reader io.Reader, N int64) ([]byte, error) { // read from pipe and return bin, err2 := io.ReadAll(pr) if err2 != nil { - return nil, errorutil.NewWithErr(err2).Msgf("something went wrong while reading from pipe") + return nil, errkit.Wrapf(err2, "something went wrong while reading from pipe") } if readErr != nil { - if errorutil.IsTimeout(readErr) && len(bin) > 0 { + if errors.Is(readErr, context.DeadlineExceeded) && len(bin) > 0 { // if error is a timeout error and we have some data already // then return data and ignore error return bin, nil @@ -72,7 +72,7 @@ func ConnReadN(ctx context.Context, reader io.Reader, N int64) ([]byte, error) { // then return data and ignore error return bin, nil } else { - return nil, errorutil.WrapfWithNil(readErr, "reader: error while reading from connection") + return nil, errkit.Wrap(readErr, "reader: error while reading from connection") } } else { return bin, nil diff --git a/reader/conn_read_test.go b/reader/conn_read_test.go index c86755e0..b61a77ca 100644 --- a/reader/conn_read_test.go +++ b/reader/conn_read_test.go @@ -69,15 +69,16 @@ func TestConnReadN(t *testing.T) { t.Run("Read From Connection which times out", func(t *testing.T) { conn, err := tls.Dial("tcp", "projectdiscovery.io:443", &tls.Config{InsecureSkipVerify: true}) - _ = conn.SetReadDeadline(time.Now().Add(5 * time.Second)) + _ = conn.SetReadDeadline(time.Now().Add(1 * time.Second)) // shorter deadline to force timeout require.Nil(t, err, "could not connect to projectdiscovery.io over tls") defer func() { _ = conn.Close() }() - _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: projectdiscovery.io\r\n\r\n")) + _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: projectdiscovery.io\r\nConnection: close\r\n\r\n")) require.Nil(t, err, "could not write to connection") - data, err := ConnReadNWithTimeout(conn, -1, timeout) - require.Nilf(t, err, "could not read from connection: %s", err) - require.NotEmpty(t, data, "could not read from connection") + data, err := ConnReadNWithTimeout(conn, -1, 1*time.Second) // shorter timeout + // The function should return data and ignore timeout error if data was received + require.Nil(t, err, "should ignore timeout error when data is available") + require.NotEmpty(t, data, "should have received some data before timeout") }) } diff --git a/update/gh.go b/update/gh.go index 4c386404..06f5a956 100644 --- a/update/gh.go +++ b/update/gh.go @@ -18,13 +18,13 @@ import ( "github.com/cheggaaa/pb/v3" "github.com/google/go-github/v30/github" "github.com/projectdiscovery/gologger" - errorutil "github.com/projectdiscovery/utils/errors" + "github.com/projectdiscovery/utils/errkit" "golang.org/x/oauth2" ) var ( extIfFound = ".exe" - ErrNoAssetFound = errorutil.NewWithFmt("update: could not find release asset for your platform (%s/%s)") + ErrNoAssetFound = errkit.New("update: could not find release asset for your platform (%s/%s)") SkipCheckSumValidation = false // by default checksum of gh assets is verified with checksums file present in release ) @@ -51,7 +51,7 @@ func NewghReleaseDownloader(RepoName string) (*GHReleaseDownloader, error) { if strings.Contains(RepoName, "/") { arr := strings.Split(RepoName, "/") if len(arr) != 2 { - return nil, errorutil.NewWithTag("update", "invalid repo name %v", RepoName) + return nil, errkit.Newf("invalid repo name %v", RepoName) } orgName = arr[0] repoName = arr[1] @@ -63,7 +63,7 @@ func NewghReleaseDownloader(RepoName string) (*GHReleaseDownloader, error) { Timeout: DownloadUpdateTimeout, } if orgName == "" { - return nil, errorutil.NewWithTag("update", "organization name cannot be empty") + return nil, errkit.New("organization name cannot be empty") } if token := os.Getenv("GITHUB_TOKEN"); token != "" { httpClient = oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})) @@ -103,7 +103,7 @@ func (d *GHReleaseDownloader) DownloadTool() (*bytes.Buffer, error) { bin, err := io.ReadAll(resp.Body) if err != nil { - return nil, errorutil.NewWithErr(err).Msgf("failed to read response body") + return nil, errkit.Wrapf(err, "failed to read response body") } return bytes.NewBuffer(bin), nil } @@ -125,23 +125,23 @@ func (d *GHReleaseDownloader) GetReleaseChecksums() (map[string]string, error) { } } if checksumFileAssetID == 0 { - return nil, errorutil.NewWithTag("update", "checksum file not in release assets") + return nil, errkit.New("checksum file not in release assets") } resp, err := d.downloadAssetwithID(int64(checksumFileAssetID)) if err != nil { - return nil, errorutil.NewWithErr(err).Msgf("failed to download checksum file") + return nil, errkit.Wrapf(err, "failed to download checksum file") } defer func() { _ = resp.Body.Close() }() bin, err := io.ReadAll(resp.Body) if err != nil { - return nil, errorutil.NewWithErr(err).Msgf("failed to read checksum file") + return nil, errkit.Wrapf(err, "failed to read checksum file") } data := strings.TrimSpace(string(bin)) if data == "" { - return nil, errorutil.NewWithTag("checksum", "something went wrong checksum file is emtpy") + return nil, errkit.New("something went wrong checksum file is emtpy") } m := map[string]string{} for _, v := range strings.Split(data, "\n") { @@ -181,14 +181,17 @@ func (d *GHReleaseDownloader) GetExecutableFromAsset() ([]byte, error) { gotChecksumbytes := sha256.Sum256(buff.Bytes()) gotchecksum := hex.EncodeToString(gotChecksumbytes[:]) if expectedChecksum != gotchecksum { - return nil, errorutil.NewWithTag("checksum", "asset file corrupted: checksum mismatch expected %v but got %v", expectedChecksum, gotchecksum) + return nil, errkit.Newf("asset file corrupted: checksum mismatch expected %v but got %v", expectedChecksum, gotchecksum) } else { gologger.Info().Msgf("Verified Integrity of %v", d.fullAssetName) } } _ = UnpackAssetWithCallback(d.Format, bytes.NewReader(buff.Bytes()), getToolCallback) - return bin, errorutil.WrapfWithNil(err, "executable not found in archive") // Note: WrapfWithNil wraps msg if err != nil + if err != nil { + return bin, errkit.Wrap(err, "executable not found in archive") + } + return bin, nil } // DownloadAssetWithName downloads asset with given name @@ -200,11 +203,11 @@ func (d *GHReleaseDownloader) DownloadAssetWithName(assetname string, showProgre } } if assetID == 0 { - return nil, errorutil.New("release asset %v not found", assetname) + return nil, errkit.Newf("release asset %v not found", assetname) } resp, err := d.downloadAssetwithID(int64(assetID)) if err != nil { - return nil, errorutil.NewWithErr(err).Msgf("failed to download asset %v", assetname) + return nil, errkit.Wrapf(err, "failed to download asset %v", assetname) } defer func() { _ = resp.Body.Close() @@ -219,7 +222,7 @@ func (d *GHReleaseDownloader) DownloadAssetWithName(assetname string, showProgre bin, err := io.ReadAll(resp.Body) if err != nil { - return nil, errorutil.NewWithErr(err).Msgf("failed to read resp body") + return nil, errkit.Wrapf(err, "failed to read resp body") } return bytes.NewBuffer(bin), nil } @@ -230,7 +233,7 @@ func (d *GHReleaseDownloader) DownloadSourceWithCallback(showProgressBar bool, c resp, err := d.httpClient.Get(downloadURL) if err != nil { - return errorutil.NewWithErr(err).Msgf("failed to source of %v", d.repoName) + return errkit.Wrapf(err, "failed to source of %v", d.repoName) } defer func() { _ = resp.Body.Close() @@ -244,7 +247,7 @@ func (d *GHReleaseDownloader) DownloadSourceWithCallback(showProgressBar bool, c bin, err := io.ReadAll(resp.Body) if err != nil { - return errorutil.NewWithErr(err).Msgf("failed to read resp body") + return errkit.Wrapf(err, "failed to read resp body") } return UnpackAssetWithCallback(Zip, bytes.NewReader(bin), callback) } @@ -253,15 +256,14 @@ func (d *GHReleaseDownloader) DownloadSourceWithCallback(showProgressBar bool, c func (d *GHReleaseDownloader) getLatestRelease() error { release, resp, err := d.client.Repositories.GetLatestRelease(context.Background(), d.organization, d.repoName) if err != nil { - errx := errorutil.NewWithErr(err) if resp != nil && resp.StatusCode == http.StatusNotFound { - errx = errx.Msgf("repo %v/%v not found got %v", d.organization, d.repoName) + return errkit.Wrapf(err, "repo %v/%v not found", d.organization, d.repoName) } else if _, ok := err.(*github.RateLimitError); ok { - errx = errx.Msgf("hit github ratelimit while downloading latest release") + return errkit.Wrapf(err, "hit github ratelimit while downloading latest release") } else if resp != nil && (resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusUnauthorized) { - errx = errx.Msgf("gh auth failed try unsetting GITHUB_TOKEN env variable") + return errkit.Wrapf(err, "gh auth failed try unsetting GITHUB_TOKEN env variable") } - return errx + return err } d.Latest = release return nil @@ -306,7 +308,7 @@ loop: // handle if id is zero (no asset found) if d.AssetID == 0 { - return ErrNoAssetFound.Msgf(runtime.GOOS, runtime.GOARCH) + return errkit.Newf("update: could not find release asset for your platform (%s/%s)", runtime.GOOS, runtime.GOARCH) } return nil } @@ -319,13 +321,13 @@ func (d *GHReleaseDownloader) downloadAssetwithID(id int64) (*http.Response, err } resp, err := d.httpClient.Get(rdurl) if err != nil { - return nil, errorutil.NewWithErr(err).Msgf("failed to download release asset") + return nil, errkit.Wrapf(err, "failed to download release asset") } if resp.StatusCode != http.StatusOK { - return nil, errorutil.New("something went wrong got %v while downloading asset, expected status 200", resp.StatusCode) + return nil, errkit.Newf("something went wrong got %v while downloading asset, expected status 200", resp.StatusCode) } if resp.Body == nil { - return nil, errorutil.New("something went wrong got response without body") + return nil, errkit.New("something went wrong got response without body") } return resp, nil } @@ -333,7 +335,7 @@ func (d *GHReleaseDownloader) downloadAssetwithID(id int64) (*http.Response, err // UnpackAssetWithCallback unpacks asset and executes callback function on every file in data func UnpackAssetWithCallback(format AssetFormat, data *bytes.Reader, callback AssetFileCallback) error { if format != Zip && format != Tar { - return errorutil.NewWithTag("unpack", "github asset format not supported. only zip and tar are supported") + return errkit.New("github asset format not supported. only zip and tar are supported") } if format == Zip { zipReader, err := zip.NewReader(data, data.Size()) diff --git a/update/update.go b/update/update.go index 752e1bbd..039c238f 100644 --- a/update/update.go +++ b/update/update.go @@ -18,7 +18,7 @@ import ( "github.com/minio/selfupdate" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/machineid" - errorutil "github.com/projectdiscovery/utils/errors" + "github.com/projectdiscovery/utils/errkit" ) const ( @@ -125,7 +125,7 @@ func GetToolVersionCallback(toolName, version string) func() (string, error) { } resp, err := DefaultHttpClient.Get(updateURL) if err != nil { - return "", errorutil.NewWithErr(err).Msgf("http Get %v failed", updateURL).WithTag("updater") + return "", errkit.Wrapf(err, "http Get %v failed", updateURL) } if resp.Body != nil { defer func() { @@ -133,19 +133,19 @@ func GetToolVersionCallback(toolName, version string) func() (string, error) { }() } if resp.StatusCode != 200 { - return "", errorutil.NewWithTag("updater", "version check failed expected status 200 but got %v for GET %v", resp.StatusCode, updateURL) + return "", errkit.Newf("version check failed expected status 200 but got %v for GET %v", resp.StatusCode, updateURL) } body, err := io.ReadAll(resp.Body) if err != nil { - return "", errorutil.NewWithErr(err).Msgf("failed to get response body of GET %v", updateURL).WithTag("updater") + return "", errkit.Wrapf(err, "failed to get response body of GET %v", updateURL) } var toolDetails Tool if err := json.Unmarshal(body, &toolDetails); err != nil { - return "", errorutil.NewWithErr(err).Msgf("failed to unmarshal %v", string(body)).WithTag("updater") + return "", errkit.Wrapf(err, "failed to unmarshal %v", string(body)) } if toolDetails.Version == "" { msg := fmt.Sprintf("something went wrong, expected version string but got empty string for GET `%v` response `%v`", updateURL, string(body)) - return "", errorutil.New("%s", msg) + return "", errkit.Newf("%s", msg) } return toolDetails.Version, nil } diff --git a/url/parsers.go b/url/parsers.go index 582b076c..d56eb54a 100644 --- a/url/parsers.go +++ b/url/parsers.go @@ -4,7 +4,7 @@ import ( "net/url" "strings" - errorutil "github.com/projectdiscovery/utils/errors" + "github.com/projectdiscovery/utils/errkit" stringsutil "github.com/projectdiscovery/utils/strings" ) @@ -42,7 +42,7 @@ func ParseURL(inputURL string, unsafe bool) (*URL, error) { // logical bug url is not relative but host is empty if u.Host == "" { - return nil, errorutil.NewWithTag("urlutil", "failed to parse url `%v`", inputURL).Msgf("got empty host when url is not relative") + return nil, errkit.Newf("failed to parse url `%v`: got empty host when url is not relative", inputURL) } // # Normalization 1: if value of u.Host does not look like a common domain @@ -86,10 +86,10 @@ func ParseAbsoluteURL(inputURL string, unsafe bool) (*URL, error) { return nil, err } if u.IsRelative { - return nil, errorutil.NewWithTag("urlutil", "expected absolute url but got relative url input=%v,path=%v", inputURL, u.Path) + return nil, errkit.Newf("expected absolute url but got relative url input=%v,path=%v", inputURL, u.Path) } if u.Host == "" { - return nil, errorutil.NewWithTag("urlutil", "something went wrong got empty host for absolute url=%v", inputURL) + return nil, errkit.Newf("something went wrong got empty host for absolute url=%v", inputURL) } return u, nil } @@ -127,7 +127,7 @@ func absoluteURLParser(u *URL) (*URL, error) { // we use u.Original because u.fetchParams() parses fragments and parameters // from u.Original (this is done to preserve query order in params and other edgecases) if u.Original == "" { - return nil, errorutil.NewWithTag("urlutil", "failed to parse url got empty input") + return nil, errkit.New("failed to parse url got empty input") } // Note: we consider //scanme.sh as valid (since all browsers accept this