mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Server: Write internal server error on missing write (#57813)
This commit is contained in:
@@ -8,12 +8,11 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNonGrafanaError = errutil.NewBase(errutil.StatusInternal, "core.MalformedError")
|
var ErrNonGrafanaError = errutil.NewBase(errutil.StatusInternal, "core.MalformedError")
|
||||||
var defaultLogger = log.New("request-errors")
|
var defaultLogger = log.New("requestErrors")
|
||||||
|
|
||||||
// ErrorOptions is a container for functional options passed to [Write].
|
// ErrorOptions is a container for functional options passed to [Write].
|
||||||
type ErrorOptions struct {
|
type ErrorOptions struct {
|
||||||
@@ -23,9 +22,9 @@ type ErrorOptions struct {
|
|||||||
|
|
||||||
// Write writes an error to the provided [http.ResponseWriter] with the
|
// Write writes an error to the provided [http.ResponseWriter] with the
|
||||||
// appropriate HTTP status and JSON payload from [errutil.Error].
|
// appropriate HTTP status and JSON payload from [errutil.Error].
|
||||||
// Write also logs the provided error to either the contextlogger,
|
// Write also logs the provided error to either the "request-errors"
|
||||||
// the "request-errors" logger, or the logger provided as a functional
|
// logger, or the logger provided as a functional option using
|
||||||
// option using [WithLogger].
|
// [WithLogger].
|
||||||
// When passing errors that are not [errors.As] compatible with
|
// When passing errors that are not [errors.As] compatible with
|
||||||
// [errutil.Error], [ErrNonGrafanaError] will be used to create a
|
// [errutil.Error], [ErrNonGrafanaError] will be used to create a
|
||||||
// generic 500 Internal Server Error payload by default, this is
|
// generic 500 Internal Server Error payload by default, this is
|
||||||
@@ -45,8 +44,8 @@ func Write(ctx context.Context, err error, w http.ResponseWriter, opts ...func(E
|
|||||||
logError(ctx, gErr, opt)
|
logError(ctx, gErr, opt)
|
||||||
|
|
||||||
pub := gErr.Public()
|
pub := gErr.Public()
|
||||||
w.WriteHeader(pub.StatusCode)
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(pub.StatusCode)
|
||||||
err = json.NewEncoder(w).Encode(pub)
|
err = json.NewEncoder(w).Encode(pub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
defaultLogger.FromContext(ctx).Error("error while writing error", "error", err)
|
defaultLogger.FromContext(ctx).Error("error while writing error", "error", err)
|
||||||
@@ -68,9 +67,6 @@ func WithLogger(opt ErrorOptions, logger log.Logger) ErrorOptions {
|
|||||||
|
|
||||||
func logError(ctx context.Context, e errutil.Error, opt ErrorOptions) {
|
func logError(ctx context.Context, e errutil.Error, opt ErrorOptions) {
|
||||||
var logger log.Logger = defaultLogger
|
var logger log.Logger = defaultLogger
|
||||||
if reqCtx := contexthandler.FromContext(ctx); reqCtx != nil && reqCtx.Logger != nil {
|
|
||||||
logger = reqCtx.Logger
|
|
||||||
}
|
|
||||||
if opt.logger != nil {
|
if opt.logger != nil {
|
||||||
logger = opt.logger
|
logger = opt.logger
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
|
"github.com/grafana/grafana/pkg/util/errutil/errhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Context represents the runtime context of current request of Macaron instance.
|
// Context represents the runtime context of current request of Macaron instance.
|
||||||
@@ -34,6 +37,8 @@ type Context struct {
|
|||||||
template *template.Template
|
template *template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errMissingWrite = errutil.NewBase(errutil.StatusInternal, "web.missingWrite")
|
||||||
|
|
||||||
func (ctx *Context) run() {
|
func (ctx *Context) run() {
|
||||||
h := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
h := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||||
for i := len(ctx.mws) - 1; i >= 0; i-- {
|
for i := len(ctx.mws) - 1; i >= 0; i-- {
|
||||||
@@ -47,7 +52,11 @@ func (ctx *Context) run() {
|
|||||||
// This indicates nearly always that a middleware is misbehaving and not calling its next.ServeHTTP().
|
// This indicates nearly always that a middleware is misbehaving and not calling its next.ServeHTTP().
|
||||||
// In rare cases where a blank http.StatusOK without any body is wished, explicitly state that using w.WriteStatus(http.StatusOK)
|
// In rare cases where a blank http.StatusOK without any body is wished, explicitly state that using w.WriteStatus(http.StatusOK)
|
||||||
if !rw.Written() {
|
if !rw.Written() {
|
||||||
panic("chain did not write HTTP response")
|
errhttp.Write(
|
||||||
|
ctx.Req.Context(),
|
||||||
|
errMissingWrite.Errorf("chain did not write HTTP response: %s", ctx.Req.URL.Path),
|
||||||
|
rw,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ package web
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -94,3 +97,17 @@ func TestContext_RemoteAddr(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContext_noHandler(t *testing.T) {
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
method := http.MethodGet
|
||||||
|
c := &Context{
|
||||||
|
Req: httptest.NewRequest(method, "/", nil),
|
||||||
|
Resp: NewResponseWriter(method, recorder),
|
||||||
|
}
|
||||||
|
|
||||||
|
c.run()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusInternalServerError, recorder.Code)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user