rpc: shuffle handleHttpRequest/handleRequest responsibilities

Make handleRequest reusable in other contexts like websockets.
This commit is contained in:
Roman Khimov 2020-04-28 22:56:19 +03:00
parent 236f3dabdd
commit 1b523be4b6

View file

@ -153,7 +153,7 @@ func (s *Server) handleHTTPRequest(w http.ResponseWriter, httpRequest *http.Requ
req := request.NewIn() req := request.NewIn()
if httpRequest.Method != "POST" { if httpRequest.Method != "POST" {
s.WriteErrorResponse( s.writeHTTPErrorResponse(
req, req,
w, w,
response.NewInvalidParamsError( response.NewInvalidParamsError(
@ -165,44 +165,32 @@ func (s *Server) handleHTTPRequest(w http.ResponseWriter, httpRequest *http.Requ
err := req.DecodeData(httpRequest.Body) err := req.DecodeData(httpRequest.Body)
if err != nil { if err != nil {
s.WriteErrorResponse(req, w, response.NewParseError("Problem parsing JSON-RPC request body", err)) s.writeHTTPErrorResponse(req, w, response.NewParseError("Problem parsing JSON-RPC request body", err))
return return
} }
s.handleRequest(w, req) resp := s.handleRequest(req)
s.writeHTTPServerResponse(req, w, resp)
} }
func (s *Server) handleRequest(w http.ResponseWriter, req *request.In) { func (s *Server) handleRequest(req *request.In) response.Raw {
reqParams, err := req.Params() reqParams, err := req.Params()
if err != nil { if err != nil {
s.WriteErrorResponse(req, w, response.NewInvalidParamsError("Problem parsing request parameters", err)) return s.packResponseToRaw(req, nil, response.NewInvalidParamsError("Problem parsing request parameters", err))
return
} }
s.log.Debug("processing rpc request", s.log.Debug("processing rpc request",
zap.String("method", req.Method), zap.String("method", req.Method),
zap.String("params", fmt.Sprintf("%v", reqParams))) zap.String("params", fmt.Sprintf("%v", reqParams)))
var (
results interface{}
resultsErr *response.Error
)
incCounter(req.Method) incCounter(req.Method)
handler, ok := rpcHandlers[req.Method] handler, ok := rpcHandlers[req.Method]
if ok { if !ok {
results, resultsErr = handler(s, *reqParams) return s.packResponseToRaw(req, nil, response.NewMethodNotFoundError(fmt.Sprintf("Method '%s' not supported", req.Method), nil))
} else {
resultsErr = response.NewMethodNotFoundError(fmt.Sprintf("Method '%s' not supported", req.Method), nil)
} }
res, resErr := handler(s, *reqParams)
if resultsErr != nil { return s.packResponseToRaw(req, res, resErr)
s.WriteErrorResponse(req, w, resultsErr)
return
}
s.WriteResponse(req, w, results)
} }
func (s *Server) getBestBlockHash(_ request.Params) (interface{}, *response.Error) { func (s *Server) getBestBlockHash(_ request.Params) (interface{}, *response.Error) {
@ -973,18 +961,33 @@ func (s *Server) blockHeightFromParam(param *request.Param) (int, *response.Erro
return num, nil return num, nil
} }
// WriteErrorResponse writes an error response to the ResponseWriter. func (s *Server) packResponseToRaw(r *request.In, result interface{}, respErr *response.Error) response.Raw {
func (s *Server) WriteErrorResponse(r *request.In, w http.ResponseWriter, jsonErr *response.Error) {
resp := response.Raw{ resp := response.Raw{
HeaderAndError: response.HeaderAndError{ HeaderAndError: response.HeaderAndError{
Header: response.Header{ Header: response.Header{
JSONRPC: r.JSONRPC, JSONRPC: r.JSONRPC,
ID: r.RawID, ID: r.RawID,
}, },
Error: jsonErr,
}, },
} }
if respErr != nil {
resp.Error = respErr
} else {
resJSON, err := json.Marshal(result)
if err != nil {
s.log.Error("failed to marshal result",
zap.Error(err),
zap.String("method", r.Method))
resp.Error = response.NewInternalServerError("failed to encode result", err)
} else {
resp.Result = resJSON
}
}
return resp
}
// logRequestError is a request error logger.
func (s *Server) logRequestError(r *request.In, jsonErr *response.Error) {
logFields := []zap.Field{ logFields := []zap.Field{
zap.Error(jsonErr.Cause), zap.Error(jsonErr.Cause),
zap.String("method", r.Method), zap.String("method", r.Method),
@ -996,35 +999,20 @@ func (s *Server) WriteErrorResponse(r *request.In, w http.ResponseWriter, jsonEr
} }
s.log.Error("Error encountered with rpc request", logFields...) s.log.Error("Error encountered with rpc request", logFields...)
w.WriteHeader(jsonErr.HTTPCode)
s.writeServerResponse(r, w, resp)
} }
// WriteResponse encodes the response and writes it to the ResponseWriter. // writeHTTPErrorResponse writes an error response to the ResponseWriter.
func (s *Server) WriteResponse(r *request.In, w http.ResponseWriter, result interface{}) { func (s *Server) writeHTTPErrorResponse(r *request.In, w http.ResponseWriter, jsonErr *response.Error) {
resJSON, err := json.Marshal(result) resp := s.packResponseToRaw(r, nil, jsonErr)
if err != nil { s.writeHTTPServerResponse(r, w, resp)
s.log.Error("Error encountered while encoding response",
zap.String("err", err.Error()),
zap.String("method", r.Method))
return
}
resp := response.Raw{
HeaderAndError: response.HeaderAndError{
Header: response.Header{
JSONRPC: r.JSONRPC,
ID: r.RawID,
},
},
Result: resJSON,
}
s.writeServerResponse(r, w, resp)
} }
func (s *Server) writeServerResponse(r *request.In, w http.ResponseWriter, resp response.Raw) { func (s *Server) writeHTTPServerResponse(r *request.In, w http.ResponseWriter, resp response.Raw) {
// Errors can happen in many places and we can only catch ALL of them here.
if resp.Error != nil {
s.logRequestError(r, resp.Error)
w.WriteHeader(resp.Error.HTTPCode)
}
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
if s.config.EnableCORSWorkaround { if s.config.EnableCORSWorkaround {
w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Origin", "*")