diff --git a/api/handler/util.go b/api/handler/util.go index c040cfc56..40cc57d8b 100644 --- a/api/handler/util.go +++ b/api/handler/util.go @@ -29,6 +29,19 @@ func (h *handler) logAndSendError(w http.ResponseWriter, logText string, reqInfo h.log.Error("call method", fields...) } +func (h *handler) logAndSendErrorNoHeader(w http.ResponseWriter, logText string, reqInfo *api.ReqInfo, err error, additional ...zap.Field) { + api.WriteErrorResponseNoHeader(w, reqInfo, transformToS3Error(err)) + fields := []zap.Field{ + zap.String("request_id", reqInfo.RequestID), + zap.String("method", reqInfo.API), + zap.String("bucket", reqInfo.BucketName), + zap.String("object", reqInfo.ObjectName), + zap.String("description", logText), + zap.Error(err)} + fields = append(fields, additional...) + h.log.Error("call method", fields...) +} + func transformToS3Error(err error) error { if _, ok := err.(errors.Error); ok { return err diff --git a/api/response.go b/api/response.go index 44acd952d..b85e3da1c 100644 --- a/api/response.go +++ b/api/response.go @@ -133,6 +133,13 @@ func WriteErrorResponse(w http.ResponseWriter, reqInfo *ReqInfo, err error) int return code } +// WriteErrorResponseNoHeader writes XML encoded error to the response body. +func WriteErrorResponseNoHeader(w http.ResponseWriter, reqInfo *ReqInfo, err error) { + errorResponse := getAPIErrorResponse(reqInfo, err) + encodedErrorResponse := EncodeResponse(errorResponse) + WriteResponseBody(w, encodedErrorResponse) +} + // If none of the http routes match respond with appropriate errors. func errorResponseHandler(w http.ResponseWriter, r *http.Request) { desc := fmt.Sprintf("Unknown API request at %s", r.URL.Path) @@ -172,6 +179,11 @@ func WriteResponse(w http.ResponseWriter, statusCode int, response []byte, mType return } + WriteResponseBody(w, response) +} + +// WriteResponseBody writes response into w. +func WriteResponseBody(w http.ResponseWriter, response []byte) { _, _ = w.Write(response) if flusher, ok := w.(http.Flusher); ok { flusher.Flush() @@ -188,13 +200,30 @@ func EncodeResponse(response interface{}) []byte { return bytesBuffer.Bytes() } +// EncodeResponseNoHeader encodes response without setting xml.Header. +// Should be used with periodicXMLWriter which sends xml.Header to the client +// with whitespaces to keep connection alive. +func EncodeResponseNoHeader(response interface{}) []byte { + var bytesBuffer bytes.Buffer + _ = xml.NewEncoder(&bytesBuffer).Encode(response) + return bytesBuffer.Bytes() +} + // EncodeToResponse encodes the response into ResponseWriter. func EncodeToResponse(w http.ResponseWriter, response interface{}) error { w.WriteHeader(http.StatusOK) if _, err := w.Write(xmlHeader); err != nil { return fmt.Errorf("write headers: %w", err) - } else if err = xml.NewEncoder(w).Encode(response); err != nil { + } + + return EncodeToResponseNoHeader(w, response) +} + +// EncodeToResponseNoHeader encodes the response into ResponseWriter without +// header status. +func EncodeToResponseNoHeader(w http.ResponseWriter, response interface{}) error { + if err := xml.NewEncoder(w).Encode(response); err != nil { return fmt.Errorf("encode xml response: %w", err) }