Skip to content

Commit

Permalink
Write service timing header when error (#5669)
Browse files Browse the repository at this point in the history
  • Loading branch information
yeya24 authored Nov 28, 2023
1 parent 2d7b13a commit 9dbd731
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 16 deletions.
31 changes: 23 additions & 8 deletions pkg/frontend/transport/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,20 +234,20 @@ func (f *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
f.reportQueryStats(r, userID, queryString, queryResponseTime, stats, err, statusCode, resp)
}

hs := w.Header()
if f.cfg.QueryStatsEnabled {
writeServiceTimingHeader(queryResponseTime, hs, stats)
}

if err != nil {
writeError(w, err)
writeError(w, err, hs)
return
}

hs := w.Header()
for h, vs := range resp.Header {
hs[h] = vs
}

if f.cfg.QueryStatsEnabled {
writeServiceTimingHeader(queryResponseTime, hs, stats)
}

w.WriteHeader(resp.StatusCode)
// log copy response body error so that we will know even though success response code returned
bytesCopied, err := io.Copy(w, resp.Body)
Expand Down Expand Up @@ -422,7 +422,7 @@ func formatQueryString(queryString url.Values) (fields []interface{}) {
return fields
}

func writeError(w http.ResponseWriter, err error) {
func writeError(w http.ResponseWriter, err error, additionalHeaders http.Header) {
switch err {
case context.Canceled:
err = errCanceled
Expand All @@ -433,7 +433,22 @@ func writeError(w http.ResponseWriter, err error) {
err = errRequestEntityTooLarge
}
}
server.WriteError(w, err)

resp, ok := httpgrpc.HTTPResponseFromError(err)
if ok {
for k, values := range additionalHeaders {
resp.Headers = append(resp.Headers, &httpgrpc.Header{Key: k, Values: values})
}
_ = server.WriteResponse(w, resp)
} else {
headers := w.Header()
for k, values := range additionalHeaders {
for _, value := range values {
headers.Set(k, value)
}
}
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

func writeServiceTimingHeader(queryResponseTime time.Duration, headers http.Header, stats *querier_stats.QueryStats) {
Expand Down
28 changes: 20 additions & 8 deletions pkg/frontend/transport/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,31 @@ func (f roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {

func TestWriteError(t *testing.T) {
for _, test := range []struct {
status int
err error
status int
err error
additionalHeaders http.Header
}{
{http.StatusInternalServerError, errors.New("unknown")},
{http.StatusGatewayTimeout, context.DeadlineExceeded},
{StatusClientClosedRequest, context.Canceled},
{http.StatusBadRequest, httpgrpc.Errorf(http.StatusBadRequest, "")},
{http.StatusRequestEntityTooLarge, errors.New("http: request body too large")},
{http.StatusInternalServerError, errors.New("unknown"), http.Header{"User-Agent": []string{"Golang"}}},
{http.StatusInternalServerError, errors.New("unknown"), nil},
{http.StatusGatewayTimeout, context.DeadlineExceeded, nil},
{StatusClientClosedRequest, context.Canceled, nil},
{StatusClientClosedRequest, context.Canceled, http.Header{"User-Agent": []string{"Golang"}}},
{StatusClientClosedRequest, context.Canceled, http.Header{"User-Agent": []string{"Golang"}, "Content-Type": []string{"application/json"}}},
{http.StatusBadRequest, httpgrpc.Errorf(http.StatusBadRequest, ""), http.Header{}},
{http.StatusRequestEntityTooLarge, errors.New("http: request body too large"), http.Header{}},
} {
t.Run(test.err.Error(), func(t *testing.T) {
w := httptest.NewRecorder()
writeError(w, test.err)
writeError(w, test.err, test.additionalHeaders)
require.Equal(t, test.status, w.Result().StatusCode)
expectedAdditionalHeaders := test.additionalHeaders
if expectedAdditionalHeaders != nil {
for key, value := range w.Header() {
if values, ok := expectedAdditionalHeaders[key]; ok {
require.Equal(t, values, value)
}
}
}
})
}
}
Expand Down

0 comments on commit 9dbd731

Please sign in to comment.