pdns-auth-proxy/vendor/github.com/jarcoal/httpmock/internal/stack_tracer.go

92 lines
1.8 KiB
Go

package internal
import (
"bytes"
"fmt"
"net/http"
"runtime"
"strings"
)
type StackTracer struct {
CustomFn func(...interface{})
Err error
}
// Error implements error interface.
func (s StackTracer) Error() string {
if s.Err == nil {
return ""
}
return s.Err.Error()
}
// Unwrap implements the interface needed by errors.Unwrap.
func (s StackTracer) Unwrap() error {
return s.Err
}
// CheckStackTracer checks for specific error returned by
// NewNotFoundResponder function or Trace Responder method.
func CheckStackTracer(req *http.Request, err error) error {
if nf, ok := err.(StackTracer); ok {
if nf.CustomFn != nil {
pc := make([]uintptr, 128)
npc := runtime.Callers(2, pc)
pc = pc[:npc]
var mesg bytes.Buffer
var netHTTPBegin, netHTTPEnd bool
// Start recording at first net/http call if any...
for {
frames := runtime.CallersFrames(pc)
var lastFn string
for {
frame, more := frames.Next()
if !netHTTPEnd {
if netHTTPBegin {
netHTTPEnd = !strings.HasPrefix(frame.Function, "net/http.")
} else {
netHTTPBegin = strings.HasPrefix(frame.Function, "net/http.")
}
}
if netHTTPEnd {
if lastFn != "" {
if mesg.Len() == 0 {
if nf.Err != nil {
mesg.WriteString(nf.Err.Error())
} else {
fmt.Fprintf(&mesg, "%s %s", req.Method, req.URL)
}
mesg.WriteString("\nCalled from ")
} else {
mesg.WriteString("\n ")
}
fmt.Fprintf(&mesg, "%s()\n at %s:%d", lastFn, frame.File, frame.Line)
}
}
lastFn = frame.Function
if !more {
break
}
}
// At least one net/http frame found
if mesg.Len() > 0 {
break
}
netHTTPEnd = true // retry without looking at net/http frames
}
nf.CustomFn(mesg.String())
}
err = nf.Err
}
return err
}