agola/internal/util/http.go

125 lines
2.5 KiB
Go

package util
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"agola.io/agola/internal/errors"
)
func HTTPResponse(w http.ResponseWriter, code int, res interface{}) error {
w.Header().Set("Content-Type", "application/json")
if res != nil {
resj, err := json.Marshal(res)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return errors.WithStack(err)
}
w.WriteHeader(code)
_, err = w.Write(resj)
return errors.WithStack(err)
}
w.WriteHeader(code)
return nil
}
type ErrorResponse struct {
Code string `json:"code"`
Message string `json:"message"`
}
func ErrorResponseFromError(err error) *ErrorResponse {
if err == nil {
return nil
}
var derr *APIError
if errors.As(err, &derr) {
return &ErrorResponse{Code: string(derr.Code), Message: derr.Message}
}
// on generic error return an error response without any code
return &ErrorResponse{}
}
func HTTPError(w http.ResponseWriter, err error) bool {
if err == nil {
return false
}
response := ErrorResponseFromError(err)
resj, merr := json.Marshal(response)
if merr != nil {
w.WriteHeader(http.StatusInternalServerError)
return true
}
code := http.StatusInternalServerError
var derr *APIError
if errors.As(err, &derr) {
switch derr.Kind {
case ErrBadRequest:
code = http.StatusBadRequest
case ErrNotExist:
code = http.StatusNotFound
case ErrForbidden:
code = http.StatusForbidden
case ErrUnauthorized:
code = http.StatusUnauthorized
case ErrInternal:
code = http.StatusInternalServerError
}
}
w.WriteHeader(code)
_, _ = w.Write(resj)
return true
}
func ErrFromRemote(resp *http.Response) error {
if resp == nil {
return nil
}
if resp.StatusCode/100 == 2 {
return nil
}
response := &ErrorResponse{}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return errors.WithStack(err)
}
// Re-populate error response body so it can be parsed by the caller if needed
resp.Body = ioutil.NopCloser(bytes.NewBuffer(data))
if err := json.Unmarshal(data, &response); err != nil {
return errors.Errorf("unknown api error (status: %d)", resp.StatusCode)
}
kind := ErrInternal
switch resp.StatusCode {
case http.StatusBadRequest:
kind = ErrBadRequest
case http.StatusNotFound:
kind = ErrNotExist
case http.StatusForbidden:
kind = ErrForbidden
case http.StatusUnauthorized:
kind = ErrUnauthorized
case http.StatusInternalServerError:
kind = ErrInternal
}
return NewRemoteError(kind, response.Code, response.Message)
}