mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-19 00:36:38 +00:00
Add better error page
This commit is contained in:
@@ -24,10 +24,13 @@ func (l *Logger) Middleware(next http.Handler) http.Handler {
|
||||
// headers that we wish to use to gather that information on the request.
|
||||
sourceIp := extractSourceIP(r)
|
||||
|
||||
// Generate request ID early so it can be used by error pages.
|
||||
requestID := xid.New().String()
|
||||
|
||||
// Create a mutable struct to capture data from downstream handlers.
|
||||
// We pass a pointer in the context - the pointer itself flows down immutably,
|
||||
// but the struct it points to can be mutated by inner handlers.
|
||||
capturedData := &proxy.CapturedData{}
|
||||
capturedData := &proxy.CapturedData{RequestID: requestID}
|
||||
ctx := proxy.WithCapturedData(r.Context(), capturedData)
|
||||
|
||||
start := time.Now()
|
||||
@@ -41,7 +44,7 @@ func (l *Logger) Middleware(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
entry := logEntry{
|
||||
ID: xid.New().String(),
|
||||
ID: requestID,
|
||||
ServiceId: capturedData.GetServiceId(),
|
||||
AccountID: string(capturedData.GetAccountId()),
|
||||
Host: host,
|
||||
|
||||
@@ -19,10 +19,18 @@ const (
|
||||
// to pass data back up the middleware chain.
|
||||
type CapturedData struct {
|
||||
mu sync.RWMutex
|
||||
RequestID string
|
||||
ServiceId string
|
||||
AccountId types.AccountID
|
||||
}
|
||||
|
||||
// GetRequestID safely gets the request ID
|
||||
func (c *CapturedData) GetRequestID() string {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.RequestID
|
||||
}
|
||||
|
||||
// SetServiceId safely sets the service ID
|
||||
func (c *CapturedData) SetServiceId(serviceId string) {
|
||||
c.mu.Lock()
|
||||
|
||||
@@ -41,8 +41,10 @@ func NewReverseProxy(transport http.RoundTripper, logger *log.Logger) *ReversePr
|
||||
func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
target, serviceId, accountID, exists := p.findTargetForRequest(r)
|
||||
if !exists {
|
||||
requestID := getRequestID(r)
|
||||
web.ServeErrorPage(w, r, http.StatusNotFound, "Service Not Found",
|
||||
"The requested service could not be found. Please check the URL, try refreshing, or check if the peer is running. If that doesn't work, see our documentation for help.")
|
||||
"The requested service could not be found. Please check the URL, try refreshing, or check if the peer is running. If that doesn't work, see our documentation for help.",
|
||||
requestID, web.ErrorStatus{Proxy: true, Peer: false, Destination: false})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -71,37 +73,51 @@ func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// proxyErrorHandler handles errors from the reverse proxy and serves
|
||||
// user-friendly error pages instead of raw error responses.
|
||||
func proxyErrorHandler(w http.ResponseWriter, r *http.Request, err error) {
|
||||
title, message, code := classifyProxyError(err)
|
||||
web.ServeErrorPage(w, r, code, title, message)
|
||||
requestID := getRequestID(r)
|
||||
title, message, code, status := classifyProxyError(err)
|
||||
web.ServeErrorPage(w, r, code, title, message, requestID, status)
|
||||
}
|
||||
|
||||
// classifyProxyError determines the appropriate error title, message, and HTTP
|
||||
// status code based on the error type.
|
||||
func classifyProxyError(err error) (title, message string, code int) {
|
||||
// getRequestID retrieves the request ID from context or returns empty string.
|
||||
func getRequestID(r *http.Request) string {
|
||||
if capturedData := CapturedDataFromContext(r.Context()); capturedData != nil {
|
||||
return capturedData.GetRequestID()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// classifyProxyError determines the appropriate error title, message, HTTP
|
||||
// status code, and component status based on the error type.
|
||||
func classifyProxyError(err error) (title, message string, code int, status web.ErrorStatus) {
|
||||
switch {
|
||||
case errors.Is(err, context.DeadlineExceeded):
|
||||
return "Request Timeout",
|
||||
"The request timed out while trying to reach the service. Please refresh the page and try again.",
|
||||
http.StatusGatewayTimeout
|
||||
http.StatusGatewayTimeout,
|
||||
web.ErrorStatus{Proxy: true, Peer: true, Destination: false}
|
||||
|
||||
case errors.Is(err, context.Canceled):
|
||||
return "Request Canceled",
|
||||
"The request was canceled before it could be completed. Please refresh the page and try again.",
|
||||
http.StatusBadGateway
|
||||
http.StatusBadGateway,
|
||||
web.ErrorStatus{Proxy: true, Peer: true, Destination: false}
|
||||
|
||||
case errors.Is(err, roundtrip.ErrNoAccountID):
|
||||
return "Configuration Error",
|
||||
"The request could not be processed due to a configuration issue. Please refresh the page and try again.",
|
||||
http.StatusInternalServerError
|
||||
http.StatusInternalServerError,
|
||||
web.ErrorStatus{Proxy: false, Peer: false, Destination: false}
|
||||
|
||||
case strings.Contains(err.Error(), "connection refused"):
|
||||
return "Service Unavailable",
|
||||
"The connection to the service was refused. Please verify that the service is running and try again.",
|
||||
http.StatusBadGateway
|
||||
http.StatusBadGateway,
|
||||
web.ErrorStatus{Proxy: true, Peer: true, Destination: false}
|
||||
|
||||
default:
|
||||
return "Peer Not Connected",
|
||||
"The connection to the peer could not be established. Please ensure the peer is running and connected to the NetBird network.",
|
||||
http.StatusBadGateway
|
||||
http.StatusBadGateway,
|
||||
web.ErrorStatus{Proxy: true, Peer: false, Destination: false}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user