| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | package rpc | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2015-03-29 21:26:47 +02:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2015-03-17 20:14:19 -04:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2015-05-18 10:31:03 -05:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/logger" | 
					
						
							| 
									
										
										
										
											2015-04-15 00:09:13 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/logger/glog" | 
					
						
							| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/xeth" | 
					
						
							| 
									
										
										
										
											2015-03-29 21:56:04 +02:00
										 |  |  | 	"github.com/rs/cors" | 
					
						
							| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-19 10:01:50 +02:00
										 |  |  | var rpclistener *stoppableTCPListener | 
					
						
							| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-09 23:25:46 +01:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	jsonrpcver       = "2.0" | 
					
						
							|  |  |  | 	maxSizeReqLength = 1024 * 1024 // 1MB | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-29 21:26:47 +02:00
										 |  |  | func Start(pipe *xeth.XEth, config RpcConfig) error { | 
					
						
							| 
									
										
										
										
											2015-04-16 19:23:57 +02:00
										 |  |  | 	if rpclistener != nil { | 
					
						
							|  |  |  | 		if fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort) != rpclistener.Addr().String() { | 
					
						
							|  |  |  | 			return fmt.Errorf("RPC service already running on %s ", rpclistener.Addr().String()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil // RPC service already running on given host/port | 
					
						
							| 
									
										
										
										
											2015-04-16 12:56:51 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-19 10:01:50 +02:00
										 |  |  | 	l, err := newStoppableTCPListener(fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort)) | 
					
						
							| 
									
										
										
										
											2015-03-29 21:26:47 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-04-27 09:18:55 -05:00
										 |  |  | 		glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", config.ListenAddress, config.ListenPort, err) | 
					
						
							| 
									
										
										
										
											2015-03-29 21:26:47 +02:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-16 12:56:51 +02:00
										 |  |  | 	rpclistener = l | 
					
						
							| 
									
										
										
										
											2015-03-29 21:56:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var handler http.Handler | 
					
						
							|  |  |  | 	if len(config.CorsDomain) > 0 { | 
					
						
							|  |  |  | 		var opts cors.Options | 
					
						
							|  |  |  | 		opts.AllowedMethods = []string{"POST"} | 
					
						
							| 
									
										
										
										
											2015-05-18 10:31:03 -05:00
										 |  |  | 		opts.AllowedOrigins = strings.Split(config.CorsDomain, " ") | 
					
						
							| 
									
										
										
										
											2015-03-29 21:56:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		c := cors.New(opts) | 
					
						
							| 
									
										
										
										
											2015-04-19 10:01:50 +02:00
										 |  |  | 		handler = newStoppableHandler(c.Handler(JSONRPC(pipe)), l.stop) | 
					
						
							| 
									
										
										
										
											2015-03-29 21:56:04 +02:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2015-04-19 10:01:50 +02:00
										 |  |  | 		handler = newStoppableHandler(JSONRPC(pipe), l.stop) | 
					
						
							| 
									
										
										
										
											2015-03-29 21:56:04 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go http.Serve(l, handler) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-29 21:26:47 +02:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-16 12:56:51 +02:00
										 |  |  | func Stop() error { | 
					
						
							| 
									
										
										
										
											2015-04-16 19:23:57 +02:00
										 |  |  | 	if rpclistener != nil { | 
					
						
							|  |  |  | 		rpclistener.Stop() | 
					
						
							|  |  |  | 		rpclistener = nil | 
					
						
							| 
									
										
										
										
											2015-04-16 12:56:51 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | // JSONRPC returns a handler that implements the Ethereum JSON-RPC API. | 
					
						
							| 
									
										
										
										
											2015-03-27 09:36:18 +01:00
										 |  |  | func JSONRPC(pipe *xeth.XEth) http.Handler { | 
					
						
							|  |  |  | 	api := NewEthereumApi(pipe) | 
					
						
							| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | 
					
						
							| 
									
										
										
										
											2015-03-29 21:56:04 +02:00
										 |  |  | 		w.Header().Set("Content-Type", "application/json") | 
					
						
							| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 		// Limit request size to resist DoS | 
					
						
							| 
									
										
										
										
											2015-03-09 23:25:46 +01:00
										 |  |  | 		if req.ContentLength > maxSizeReqLength { | 
					
						
							| 
									
										
										
										
											2015-03-12 19:07:03 -05:00
										 |  |  | 			jsonerr := &RpcErrorObject{-32700, "Request too large"} | 
					
						
							| 
									
										
										
										
											2015-03-19 18:06:26 -04:00
										 |  |  | 			send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) | 
					
						
							| 
									
										
										
										
											2015-03-09 23:25:46 +01:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-17 20:14:19 -04:00
										 |  |  | 		// Read request body | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 		defer req.Body.Close() | 
					
						
							|  |  |  | 		body, err := ioutil.ReadAll(req.Body) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			jsonerr := &RpcErrorObject{-32700, "Could not read request body"} | 
					
						
							| 
									
										
										
										
											2015-03-19 18:06:26 -04:00
										 |  |  | 			send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) | 
					
						
							| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 		// Try to parse the request as a single | 
					
						
							|  |  |  | 		var reqSingle RpcRequest | 
					
						
							|  |  |  | 		if err := json.Unmarshal(body, &reqSingle); err == nil { | 
					
						
							|  |  |  | 			response := RpcResponse(api, &reqSingle) | 
					
						
							| 
									
										
										
										
											2015-05-14 12:39:57 -05:00
										 |  |  | 			if reqSingle.Id != nil { | 
					
						
							|  |  |  | 				send(w, &response) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-03-12 19:07:03 -05:00
										 |  |  | 			return | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// 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)) | 
					
						
							| 
									
										
										
										
											2015-05-14 12:39:57 -05:00
										 |  |  | 			resCount := 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 			for i, request := range reqBatch { | 
					
						
							|  |  |  | 				response := RpcResponse(api, &request) | 
					
						
							| 
									
										
										
										
											2015-05-14 12:39:57 -05:00
										 |  |  | 				// this leaves nil entries in the response batch for later removal | 
					
						
							|  |  |  | 				if request.Id != nil { | 
					
						
							|  |  |  | 					resBatch[i] = response | 
					
						
							|  |  |  | 					resCount = resCount + 1 | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-05-14 12:39:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// make response omitting nil entries | 
					
						
							|  |  |  | 			respBatchComp := make([]*interface{}, resCount) | 
					
						
							|  |  |  | 			for _, v := range resBatch { | 
					
						
							|  |  |  | 				if v != nil { | 
					
						
							| 
									
										
										
										
											2015-05-14 15:50:39 -05:00
										 |  |  | 					respBatchComp[len(respBatchComp)-resCount] = v | 
					
						
							| 
									
										
										
										
											2015-05-14 12:39:57 -05:00
										 |  |  | 					resCount = resCount - 1 | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			send(w, respBatchComp) | 
					
						
							| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 		// Not a batch or single request, error | 
					
						
							|  |  |  | 		jsonerr := &RpcErrorObject{-32600, "Could not decode request"} | 
					
						
							| 
									
										
										
										
											2015-03-19 18:06:26 -04:00
										 |  |  | 		send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) | 
					
						
							| 
									
										
										
										
											2015-03-09 23:00:27 +01:00
										 |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} { | 
					
						
							|  |  |  | 	var reply, response interface{} | 
					
						
							|  |  |  | 	reserr := api.GetRequestReply(request, &reply) | 
					
						
							|  |  |  | 	switch reserr.(type) { | 
					
						
							|  |  |  | 	case nil: | 
					
						
							| 
									
										
										
										
											2015-03-17 17:46:22 -04:00
										 |  |  | 		response = &RpcSuccessResponse{Jsonrpc: jsonrpcver, Id: request.Id, Result: reply} | 
					
						
							| 
									
										
										
										
											2015-05-11 11:53:53 +03:00
										 |  |  | 	case *NotImplementedError, *NotAvailableError: | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 		jsonerr := &RpcErrorObject{-32601, reserr.Error()} | 
					
						
							| 
									
										
										
										
											2015-03-17 17:46:22 -04:00
										 |  |  | 		response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} | 
					
						
							| 
									
										
										
										
											2015-03-28 21:42:44 +01:00
										 |  |  | 	case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError: | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 		jsonerr := &RpcErrorObject{-32602, reserr.Error()} | 
					
						
							| 
									
										
										
										
											2015-03-17 17:46:22 -04:00
										 |  |  | 		response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		jsonerr := &RpcErrorObject{-32603, reserr.Error()} | 
					
						
							| 
									
										
										
										
											2015-03-17 17:46:22 -04:00
										 |  |  | 		response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-15 00:09:13 +02:00
										 |  |  | 	glog.V(logger.Detail).Infof("Generated response: %T %s", response, response) | 
					
						
							| 
									
										
										
										
											2015-03-17 17:38:05 -04:00
										 |  |  | 	return &response | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-03-17 20:14:19 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-19 18:06:26 -04:00
										 |  |  | func send(writer io.Writer, v interface{}) (n int, err error) { | 
					
						
							| 
									
										
										
										
											2015-03-17 20:14:19 -04:00
										 |  |  | 	var payload []byte | 
					
						
							|  |  |  | 	payload, err = json.MarshalIndent(v, "", "\t") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-04-27 09:18:55 -05:00
										 |  |  | 		glog.V(logger.Error).Infoln("Error marshalling JSON", err) | 
					
						
							| 
									
										
										
										
											2015-03-17 20:14:19 -04:00
										 |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-15 00:09:13 +02:00
										 |  |  | 	glog.V(logger.Detail).Infof("Sending payload: %s", payload) | 
					
						
							| 
									
										
										
										
											2015-03-17 20:14:19 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return writer.Write(payload) | 
					
						
							|  |  |  | } |