|
|
|
@ -1,6 +1,8 @@ |
|
|
|
|
package rpc |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"encoding/json" |
|
|
|
|
"io/ioutil" |
|
|
|
|
"net/http" |
|
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/logger" |
|
|
|
@ -16,54 +18,71 @@ const ( |
|
|
|
|
|
|
|
|
|
// JSONRPC returns a handler that implements the Ethereum JSON-RPC API.
|
|
|
|
|
func JSONRPC(pipe *xeth.XEth, dataDir string) http.Handler { |
|
|
|
|
var json JsonWrapper |
|
|
|
|
var jsw JsonWrapper |
|
|
|
|
api := NewEthereumApi(pipe, dataDir) |
|
|
|
|
|
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { |
|
|
|
|
// TODO this needs to be configurable
|
|
|
|
|
w.Header().Set("Access-Control-Allow-Origin", "*") |
|
|
|
|
|
|
|
|
|
rpchttplogger.DebugDetailln("Handling request") |
|
|
|
|
|
|
|
|
|
// Limit request size to resist DoS
|
|
|
|
|
if req.ContentLength > maxSizeReqLength { |
|
|
|
|
jsonerr := &RpcErrorObject{-32700, "Request too large"} |
|
|
|
|
json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) |
|
|
|
|
jsw.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
reqParsed, reqerr := json.ParseRequestBody(req) |
|
|
|
|
switch reqerr.(type) { |
|
|
|
|
case nil: |
|
|
|
|
break |
|
|
|
|
case *DecodeParamError, *InsufficientParamsError, *ValidationError: |
|
|
|
|
jsonerr := &RpcErrorObject{-32602, reqerr.Error()} |
|
|
|
|
json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) |
|
|
|
|
return |
|
|
|
|
default: |
|
|
|
|
jsonerr := &RpcErrorObject{-32700, "Could not parse request"} |
|
|
|
|
json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) |
|
|
|
|
return |
|
|
|
|
defer req.Body.Close() |
|
|
|
|
body, err := ioutil.ReadAll(req.Body) |
|
|
|
|
if err != nil { |
|
|
|
|
jsonerr := &RpcErrorObject{-32700, "Could not read request body"} |
|
|
|
|
jsw.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var response interface{} |
|
|
|
|
reserr := api.GetRequestReply(&reqParsed, &response) |
|
|
|
|
switch reserr.(type) { |
|
|
|
|
case nil: |
|
|
|
|
break |
|
|
|
|
case *NotImplementedError: |
|
|
|
|
jsonerr := &RpcErrorObject{-32601, reserr.Error()} |
|
|
|
|
json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Error: jsonerr}) |
|
|
|
|
// Try to parse the request as a single
|
|
|
|
|
var reqSingle RpcRequest |
|
|
|
|
if err := json.Unmarshal(body, &reqSingle); err == nil { |
|
|
|
|
response := RpcResponse(api, &reqSingle) |
|
|
|
|
jsw.Send(w, &response) |
|
|
|
|
return |
|
|
|
|
case *DecodeParamError, *InsufficientParamsError, *ValidationError: |
|
|
|
|
jsonerr := &RpcErrorObject{-32602, reserr.Error()} |
|
|
|
|
json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Error: jsonerr}) |
|
|
|
|
return |
|
|
|
|
default: |
|
|
|
|
jsonerr := &RpcErrorObject{-32603, reserr.Error()} |
|
|
|
|
json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Error: jsonerr}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Try to parse the request to batch
|
|
|
|
|
var reqBatch []RpcRequest |
|
|
|
|
if err := json.Unmarshal(body, &reqBatch); err == nil { |
|
|
|
|
// Build response batch
|
|
|
|
|
resBatch := make([]*interface{}, len(reqBatch)) |
|
|
|
|
for i, request := range reqBatch { |
|
|
|
|
response := RpcResponse(api, &request) |
|
|
|
|
resBatch[i] = response |
|
|
|
|
} |
|
|
|
|
jsw.Send(w, resBatch) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
rpchttplogger.DebugDetailf("Generated response: %T %s", response, response) |
|
|
|
|
json.Send(w, &RpcSuccessResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Result: response}) |
|
|
|
|
// Not a batch or single request, error
|
|
|
|
|
jsonerr := &RpcErrorObject{-32600, "Could not decode request"} |
|
|
|
|
jsw.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} { |
|
|
|
|
var reply, response interface{} |
|
|
|
|
reserr := api.GetRequestReply(request, &reply) |
|
|
|
|
switch reserr.(type) { |
|
|
|
|
case nil: |
|
|
|
|
response = &RpcSuccessResponse{JsonRpc: jsonrpcver, ID: request.ID, Result: reply} |
|
|
|
|
case *NotImplementedError: |
|
|
|
|
jsonerr := &RpcErrorObject{-32601, reserr.Error()} |
|
|
|
|
response = &RpcErrorResponse{JsonRpc: jsonrpcver, ID: request.ID, Error: jsonerr} |
|
|
|
|
case *DecodeParamError, *InsufficientParamsError, *ValidationError: |
|
|
|
|
jsonerr := &RpcErrorObject{-32602, reserr.Error()} |
|
|
|
|
response = &RpcErrorResponse{JsonRpc: jsonrpcver, ID: request.ID, Error: jsonerr} |
|
|
|
|
default: |
|
|
|
|
jsonerr := &RpcErrorObject{-32603, reserr.Error()} |
|
|
|
|
response = &RpcErrorResponse{JsonRpc: jsonrpcver, ID: request.ID, Error: jsonerr} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
rpchttplogger.DebugDetailf("Generated response: %T %s", response, response) |
|
|
|
|
return &response |
|
|
|
|
} |
|
|
|
|