github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/access/rest/routes/http_handler.go (about) 1 package routes 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "net/http" 8 9 "github.com/rs/zerolog" 10 11 "google.golang.org/grpc/codes" 12 "google.golang.org/grpc/status" 13 14 "github.com/onflow/flow-go/engine/access/rest/models" 15 fvmErrors "github.com/onflow/flow-go/fvm/errors" 16 "github.com/onflow/flow-go/model/flow" 17 ) 18 19 const MaxRequestSize = 2 << 20 // 2MB 20 21 // HttpHandler is custom http handler implementing custom handler function. 22 // HttpHandler function allows easier handling of errors and responses as it 23 // wraps functionality for handling error and responses outside of endpoint handling. 24 type HttpHandler struct { 25 Logger zerolog.Logger 26 Chain flow.Chain 27 } 28 29 func NewHttpHandler( 30 logger zerolog.Logger, 31 chain flow.Chain, 32 ) *HttpHandler { 33 return &HttpHandler{ 34 Logger: logger, 35 Chain: chain, 36 } 37 } 38 39 // VerifyRequest function acts as a wrapper to each request providing common handling functionality 40 // such as logging, error handling 41 func (h *HttpHandler) VerifyRequest(w http.ResponseWriter, r *http.Request) error { 42 // create a logger 43 errLog := h.Logger.With().Str("request_url", r.URL.String()).Logger() 44 45 // limit requested body size 46 r.Body = http.MaxBytesReader(w, r.Body, MaxRequestSize) 47 err := r.ParseForm() 48 if err != nil { 49 h.errorHandler(w, err, errLog) 50 return err 51 } 52 return nil 53 } 54 55 func (h *HttpHandler) errorHandler(w http.ResponseWriter, err error, errorLogger zerolog.Logger) { 56 // rest status type error should be returned with status and user message provided 57 var statusErr models.StatusError 58 if errors.As(err, &statusErr) { 59 h.errorResponse(w, statusErr.Status(), statusErr.UserMessage(), errorLogger) 60 return 61 } 62 63 // handle cadence errors 64 cadenceError := fvmErrors.Find(err, fvmErrors.ErrCodeCadenceRunTimeError) 65 if cadenceError != nil { 66 msg := fmt.Sprintf("Cadence error: %s", cadenceError.Error()) 67 h.errorResponse(w, http.StatusBadRequest, msg, errorLogger) 68 return 69 } 70 71 // handle grpc status error returned from the backend calls, we are forwarding the message to the client 72 if se, ok := status.FromError(err); ok { 73 if se.Code() == codes.NotFound { 74 msg := fmt.Sprintf("Flow resource not found: %s", se.Message()) 75 h.errorResponse(w, http.StatusNotFound, msg, errorLogger) 76 return 77 } 78 if se.Code() == codes.InvalidArgument { 79 msg := fmt.Sprintf("Invalid Flow argument: %s", se.Message()) 80 h.errorResponse(w, http.StatusBadRequest, msg, errorLogger) 81 return 82 } 83 if se.Code() == codes.Internal { 84 msg := fmt.Sprintf("Invalid Flow request: %s", se.Message()) 85 h.errorResponse(w, http.StatusBadRequest, msg, errorLogger) 86 return 87 } 88 if se.Code() == codes.Unavailable { 89 msg := fmt.Sprintf("Failed to process request: %s", se.Message()) 90 h.errorResponse(w, http.StatusServiceUnavailable, msg, errorLogger) 91 return 92 } 93 } 94 95 // stop going further - catch all error 96 msg := "internal server error" 97 errorLogger.Error().Err(err).Msg(msg) 98 h.errorResponse(w, http.StatusInternalServerError, msg, errorLogger) 99 } 100 101 // jsonResponse builds a JSON response and send it to the client 102 func (h *HttpHandler) jsonResponse(w http.ResponseWriter, code int, response interface{}, errLogger zerolog.Logger) { 103 w.Header().Set("Content-Type", "application/json; charset=UTF-8") 104 105 // serialize response to JSON and handler errors 106 encodedResponse, err := json.MarshalIndent(response, "", "\t") 107 if err != nil { 108 w.WriteHeader(http.StatusInternalServerError) 109 errLogger.Error().Err(err).Str("response", string(encodedResponse)).Msg("failed to indent response") 110 return 111 } 112 113 w.WriteHeader(code) 114 // write response to response stream 115 _, err = w.Write(encodedResponse) 116 if err != nil { 117 errLogger.Error().Err(err).Str("response", string(encodedResponse)).Msg("failed to write http response") 118 } 119 } 120 121 // errorResponse sends an HTTP error response to the client with the given return code 122 // and a model error with the given response message in the response body 123 func (h *HttpHandler) errorResponse( 124 w http.ResponseWriter, 125 returnCode int, 126 responseMessage string, 127 logger zerolog.Logger, 128 ) { 129 // create error response model 130 modelError := models.ModelError{ 131 Code: int32(returnCode), 132 Message: responseMessage, 133 } 134 h.jsonResponse(w, returnCode, modelError, logger) 135 }