rpc: add CORS workaround for RPC

This one enables our RPC to be called from the browser if there is a
need. It's insecure and not standards-compliant, thus this behaviour is
configurable is not enabled by default. It makes our node with this workaround
enabled compatible with neo-mon monitoring.

Originally debugged by @anatoly-bogatyrev in #464.
This commit is contained in:
Roman Khimov 2019-11-01 13:23:46 +03:00
parent e5205d26a3
commit b46dd295bc
14 changed files with 72 additions and 22 deletions

View file

@ -247,7 +247,7 @@ func startServer(ctx *cli.Context) error {
}
server := network.NewServer(serverConfig, chain)
rpcServer := rpc.NewServer(chain, cfg.ApplicationConfiguration.RPCPort, server)
rpcServer := rpc.NewServer(chain, cfg.ApplicationConfiguration.RPC, server)
errChan := make(chan error)
monitoring := metrics.NewMetricsService(cfg.ApplicationConfiguration.Monitoring)

View file

@ -67,7 +67,6 @@ type (
// ApplicationConfiguration config specific to the node.
ApplicationConfiguration struct {
DBConfiguration storage.DBConfiguration `yaml:"DBConfiguration"`
RPCPort uint16 `yaml:"RPCPort"`
NodePort uint16 `yaml:"NodePort"`
Relay bool `yaml:"Relay"`
DialTimeout time.Duration `yaml:"DialTimeout"`
@ -75,6 +74,15 @@ type (
MaxPeers int `yaml:"MaxPeers"`
MinPeers int `yaml:"MinPeers"`
Monitoring metrics.PrometheusConfig `yaml:"Monitoring"`
RPC RPCConfig `yaml:"RPC"`
}
// RPCConfig is an RPC service configuration information (to be moved to the rpc package, see #423).
RPCConfig struct {
Enabled bool `yaml:"Enabled"`
EnableCORSWorkaround bool `yaml:"EnableCORSWorkaround"`
Address string `yaml:"Address"`
Port uint16 `yaml:"Port"`
}
// NetMode describes the mode the blockchain will operate on.

View file

@ -42,13 +42,16 @@ ApplicationConfiguration:
# DB: 0
# BoltDBOptions:
# FilePath: "./chains/mainnet.bolt"
RPCPort: 20332
NodePort: 20333
Relay: true
DialTimeout: 3
ProtoTickInterval: 2
MaxPeers: 50
MinPeers: 5
RPC:
Enabled: true
EnableCORSWorkaround: false
Port: 20332
Monitoring:
Enabled: true
Port: 2112

View file

@ -30,13 +30,16 @@ ApplicationConfiguration:
# DB: 0
# BoltDBOptions:
# FilePath: "./chains/privnet.bolt"
RPCPort: 20336
NodePort: 20337
Relay: true
DialTimeout: 3
ProtoTickInterval: 2
MaxPeers: 50
MinPeers: 3
RPC:
Enabled: true
EnableCORSWorkaround: false
Port: 20336
Monitoring:
Enabled: true
Port: 2112

View file

@ -27,13 +27,16 @@ ApplicationConfiguration:
# DB: 0
# BoltDBOptions:
# FilePath: "./chains/privnet.bolt"
RPCPort: 20333
NodePort: 20334
Relay: true
DialTimeout: 3
ProtoTickInterval: 2
MaxPeers: 50
MinPeers: 3
RPC:
Enabled: true
EnableCORSWorkaround: false
Port: 20333
Monitoring:
Enabled: true
Port: 2112

View file

@ -27,13 +27,16 @@ ApplicationConfiguration:
# DB: 0
# BoltDBOptions:
# FilePath: "./chains/privnet.bolt"
RPCPort: 20335
NodePort: 20336
Relay: true
DialTimeout: 3
ProtoTickInterval: 2
MaxPeers: 50
MinPeers: 3
RPC:
Enabled: true
EnableCORSWorkaround: false
Port: 20335
Monitoring:
Enabled: true
Port: 2112

View file

@ -27,13 +27,16 @@ ApplicationConfiguration:
# DB: 0
# BoltDBOptions:
# FilePath: "./chains/privnet.bolt"
RPCPort: 20334
NodePort: 20335
Relay: true
DialTimeout: 3
ProtoTickInterval: 2
MaxPeers: 50
MinPeers: 3
RPC:
Enabled: true
EnableCORSWorkaround: false
Port: 20334
Monitoring:
Enabled: true
Port: 2112

View file

@ -33,13 +33,16 @@ ApplicationConfiguration:
# DB: 0
# BoltDBOptions:
# FilePath: "./chains/privnet.bolt"
RPCPort: 20331
NodePort: 20332
Relay: true
DialTimeout: 3
ProtoTickInterval: 2
MaxPeers: 50
MinPeers: 3
RPC:
Enabled: true
EnableCORSWorkaround: false
Port: 20331
Monitoring:
Enabled: true
Port: 2112

View file

@ -42,13 +42,16 @@ ApplicationConfiguration:
# DB: 0
# BoltDBOptions:
# FilePath: "./chains/testnet.bolt"
RPCPort: 20332
NodePort: 20333
Relay: true
DialTimeout: 3
ProtoTickInterval: 2
MaxPeers: 50
MinPeers: 5
RPC:
Enabled: true
EnableCORSWorkaround: false
Port: 20332
Monitoring:
Enabled: true
Port: 2112

View file

@ -32,13 +32,16 @@ ApplicationConfiguration:
# DB: 0
# BoltDBOptions:
# FilePath: "./chains/unit_testnet.bolt"
RPCPort: 20332
NodePort: 20333
Relay: true
DialTimeout: 3
ProtoTickInterval: 2
MaxPeers: 50
MinPeers: 1
RPC:
Enabled: true
EnableCORSWorkaround: false
Port: 20332
Monitoring:
Enabled: false #since it's not useful for unit tests.
Port: 2112

2
go.mod
View file

@ -15,6 +15,8 @@ require (
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.9 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/mr-tron/base58 v1.1.2
github.com/nspcc-dev/rfc6979 v0.1.0
github.com/onsi/gomega v1.4.2 // indirect

View file

@ -17,10 +17,11 @@ type (
// Request represents a standard JSON-RPC 2.0
// request: http://www.jsonrpc.org/specification#request_object.
Request struct {
JSONRPC string `json:"jsonrpc"`
Method string `json:"method"`
RawParams json.RawMessage `json:"params,omitempty"`
RawID json.RawMessage `json:"id,omitempty"`
JSONRPC string `json:"jsonrpc"`
Method string `json:"method"`
RawParams json.RawMessage `json:"params,omitempty"`
RawID json.RawMessage `json:"id,omitempty"`
enableCORSWorkaround bool
}
// Response represents a standard JSON-RPC 2.0
@ -34,9 +35,10 @@ type (
)
// NewRequest creates a new Request struct.
func NewRequest() *Request {
func NewRequest(corsWorkaround bool) *Request {
return &Request{
JSONRPC: jsonRPCVersion,
JSONRPC: jsonRPCVersion,
enableCORSWorkaround: corsWorkaround,
}
}
@ -110,6 +112,11 @@ func (r Request) WriteResponse(w http.ResponseWriter, result interface{}) {
func (r Request) writeServerResponse(w http.ResponseWriter, response Response) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
if r.enableCORSWorkaround {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With")
}
encoder := json.NewEncoder(w)
err := encoder.Encode(response)

View file

@ -7,6 +7,7 @@ import (
"net/http"
"strconv"
"github.com/CityOfZion/neo-go/config"
"github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto"
@ -24,6 +25,7 @@ type (
Server struct {
*http.Server
chain core.Blockchainer
config config.RPCConfig
coreServer *network.Server
}
)
@ -35,12 +37,15 @@ var (
)
// NewServer creates a new Server struct.
func NewServer(chain core.Blockchainer, port uint16, coreServer *network.Server) Server {
func NewServer(chain core.Blockchainer, conf config.RPCConfig, coreServer *network.Server) Server {
httpServer := &http.Server{
Addr: conf.Address + ":" + strconv.FormatUint(uint64(conf.Port), 10),
}
return Server{
Server: &http.Server{
Addr: ":" + strconv.FormatUint(uint64(port), 10),
},
Server: httpServer,
chain: chain,
config: conf,
coreServer: coreServer,
}
}
@ -48,6 +53,10 @@ func NewServer(chain core.Blockchainer, port uint16, coreServer *network.Server)
// Start creates a new JSON-RPC server
// listening on the configured port.
func (s *Server) Start(errChan chan error) {
if !s.config.Enabled {
log.Info("RPC server is not enabled")
return
}
s.Handler = http.HandlerFunc(s.requestHandler)
log.WithFields(log.Fields{
"endpoint": s.Addr,
@ -66,7 +75,7 @@ func (s *Server) Shutdown() error {
}
func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request) {
req := NewRequest()
req := NewRequest(s.config.EnableCORSWorkaround)
if httpRequest.Method != "POST" {
req.WriteErrorResponse(

View file

@ -167,7 +167,7 @@ func initServerWithInMemoryChain(ctx context.Context, t *testing.T) (*core.Block
serverConfig := network.NewServerConfig(cfg)
server := network.NewServer(serverConfig, chain)
rpcServer := NewServer(chain, cfg.ApplicationConfiguration.RPCPort, server)
rpcServer := NewServer(chain, cfg.ApplicationConfiguration.RPC, server)
handler := http.HandlerFunc(rpcServer.requestHandler)
return chain, handler