Update dependencies

- uses newer version of go-ethereum required for go1.11
This commit is contained in:
Rob Mulholand
2018-09-13 16:14:35 -05:00
parent 939ead0c82
commit 560305f601
2356 changed files with 331656 additions and 128304 deletions
+49 -8
View File
@@ -25,6 +25,7 @@ import (
"fmt"
"net"
"net/url"
"os"
"reflect"
"strconv"
"strings"
@@ -60,7 +61,7 @@ const (
// The approach taken here is to maintain a per-subscription linked list buffer
// shrinks on demand. If the buffer reaches the size below, the subscription is
// dropped.
maxClientSubscriptionBuffer = 8000
maxClientSubscriptionBuffer = 20000
)
// BatchElem is an element in a batch request.
@@ -171,6 +172,8 @@ func DialContext(ctx context.Context, rawurl string) (*Client, error) {
return DialHTTP(rawurl)
case "ws", "wss":
return DialWebsocket(ctx, rawurl, "")
case "stdio":
return DialStdIO(ctx)
case "":
return DialIPC(ctx, rawurl)
default:
@@ -178,13 +181,51 @@ func DialContext(ctx context.Context, rawurl string) (*Client, error) {
}
}
type StdIOConn struct{}
func (io StdIOConn) Read(b []byte) (n int, err error) {
return os.Stdin.Read(b)
}
func (io StdIOConn) Write(b []byte) (n int, err error) {
return os.Stdout.Write(b)
}
func (io StdIOConn) Close() error {
return nil
}
func (io StdIOConn) LocalAddr() net.Addr {
return &net.UnixAddr{Name: "stdio", Net: "stdio"}
}
func (io StdIOConn) RemoteAddr() net.Addr {
return &net.UnixAddr{Name: "stdio", Net: "stdio"}
}
func (io StdIOConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (io StdIOConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (io StdIOConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func DialStdIO(ctx context.Context) (*Client, error) {
return newClient(ctx, func(_ context.Context) (net.Conn, error) {
return StdIOConn{}, nil
})
}
func newClient(initctx context.Context, connectFunc func(context.Context) (net.Conn, error)) (*Client, error) {
conn, err := connectFunc(initctx)
if err != nil {
return nil, err
}
_, isHTTP := conn.(*httpConn)
c := &Client{
writeConn: conn,
isHTTP: isHTTP,
@@ -263,7 +304,7 @@ func (c *Client) CallContext(ctx context.Context, result interface{}, method str
return err
}
// dispatch has accepted the request and will close the channel it when it quits.
// dispatch has accepted the request and will close the channel when it quits.
switch resp, err := op.wait(ctx); {
case err != nil:
return err
@@ -524,13 +565,13 @@ func (c *Client) dispatch(conn net.Conn) {
}
case err := <-c.readErr:
log.Debug(fmt.Sprintf("<-readErr: %v", err))
log.Debug("<-readErr", "err", err)
c.closeRequestOps(err)
conn.Close()
reading = false
case newconn := <-c.reconnected:
log.Debug(fmt.Sprintf("<-reconnected: (reading=%t) %v", reading, conn.RemoteAddr()))
log.Debug("<-reconnected", "reading", reading, "remote", conn.RemoteAddr())
if reading {
// Wait for the previous read loop to exit. This is a rare case.
conn.Close()
@@ -587,7 +628,7 @@ func (c *Client) closeRequestOps(err error) {
func (c *Client) handleNotification(msg *jsonrpcMessage) {
if !strings.HasSuffix(msg.Method, notificationMethodSuffix) {
log.Debug(fmt.Sprint("dropping non-subscription message: ", msg))
log.Debug("dropping non-subscription message", "msg", msg)
return
}
var subResult struct {
@@ -595,7 +636,7 @@ func (c *Client) handleNotification(msg *jsonrpcMessage) {
Result json.RawMessage `json:"result"`
}
if err := json.Unmarshal(msg.Params, &subResult); err != nil {
log.Debug(fmt.Sprint("dropping invalid subscription message: ", msg))
log.Debug("dropping invalid subscription message", "msg", msg)
return
}
if c.subs[subResult.ID] != nil {
@@ -606,7 +647,7 @@ func (c *Client) handleNotification(msg *jsonrpcMessage) {
func (c *Client) handleResponse(msg *jsonrpcMessage) {
op := c.respWait[string(msg.ID)]
if op == nil {
log.Debug(fmt.Sprintf("unsolicited response %v", msg))
log.Debug("unsolicited response", "msg", msg)
return
}
delete(c.respWait, string(msg.ID))
+1 -1
View File
@@ -66,7 +66,7 @@ func subscribeBlocks(client *rpc.Client, subch chan Block) {
defer cancel()
// Subscribe to new blocks.
sub, err := client.EthSubscribe(ctx, subch, "newBlocks")
sub, err := client.EthSubscribe(ctx, subch, "newHeads")
if err != nil {
fmt.Println("subscribe error:", err)
return
+2 -2
View File
@@ -58,7 +58,7 @@ An example server which uses the JSON codec:
return a + b
}
func (s *CalculatorService Div(a, b int) (int, error) {
func (s *CalculatorService) Div(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("divide by zero")
}
@@ -92,7 +92,7 @@ An example method:
Subscriptions are deleted when:
- the user sends an unsubscribe request
- the connection which was used to create the subscription is closed. This can be initiated
by the client and server. The server will close the connection on an write error or when
by the client and server. The server will close the connection on a write error or when
the queue of buffered notifications gets too big.
*/
package rpc
+102
View File
@@ -0,0 +1,102 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package rpc
import (
"net"
"github.com/ethereum/go-ethereum/log"
)
// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules
func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeouts HTTPTimeouts) (net.Listener, *Server, error) {
// Generate the whitelist based on the allowed modules
whitelist := make(map[string]bool)
for _, module := range modules {
whitelist[module] = true
}
// Register all the APIs exposed by the services
handler := NewServer()
for _, api := range apis {
if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) {
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
return nil, nil, err
}
log.Debug("HTTP registered", "namespace", api.Namespace)
}
}
// All APIs registered, start the HTTP listener
var (
listener net.Listener
err error
)
if listener, err = net.Listen("tcp", endpoint); err != nil {
return nil, nil, err
}
go NewHTTPServer(cors, vhosts, timeouts, handler).Serve(listener)
return listener, handler, err
}
// StartWSEndpoint starts a websocket endpoint
func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *Server, error) {
// Generate the whitelist based on the allowed modules
whitelist := make(map[string]bool)
for _, module := range modules {
whitelist[module] = true
}
// Register all the APIs exposed by the services
handler := NewServer()
for _, api := range apis {
if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) {
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
return nil, nil, err
}
log.Debug("WebSocket registered", "service", api.Service, "namespace", api.Namespace)
}
}
// All APIs registered, start the HTTP listener
var (
listener net.Listener
err error
)
if listener, err = net.Listen("tcp", endpoint); err != nil {
return nil, nil, err
}
go NewWSServer(wsOrigins, handler).Serve(listener)
return listener, handler, err
}
// StartIPCEndpoint starts an IPC endpoint.
func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, error) {
// Register all the APIs exposed by the services.
handler := NewServer()
for _, api := range apis {
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
return nil, nil, err
}
log.Debug("IPC registered", "namespace", api.Namespace)
}
// All APIs registered, start the IPC listener.
listener, err := ipcListen(ipcEndpoint)
if err != nil {
return nil, nil, err
}
go handler.ServeListener(listener)
return listener, handler, nil
}
+81 -10
View File
@@ -27,16 +27,17 @@ import (
"mime"
"net"
"net/http"
"strings"
"sync"
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/rs/cors"
"strings"
)
const (
contentType = "application/json"
maxHTTPRequestContentLength = 1024 * 128
contentType = "application/json"
maxRequestContentLength = 1024 * 128
)
var nullAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:0")
@@ -66,6 +67,38 @@ func (hc *httpConn) Close() error {
return nil
}
// HTTPTimeouts represents the configuration params for the HTTP RPC server.
type HTTPTimeouts struct {
// ReadTimeout is the maximum duration for reading the entire
// request, including the body.
//
// Because ReadTimeout does not let Handlers make per-request
// decisions on each request body's acceptable deadline or
// upload rate, most users will prefer to use
// ReadHeaderTimeout. It is valid to use them both.
ReadTimeout time.Duration
// WriteTimeout is the maximum duration before timing out
// writes of the response. It is reset whenever a new
// request's header is read. Like ReadTimeout, it does not
// let Handlers make decisions on a per-request basis.
WriteTimeout time.Duration
// IdleTimeout is the maximum amount of time to wait for the
// next request when keep-alives are enabled. If IdleTimeout
// is zero, the value of ReadTimeout is used. If both are
// zero, ReadHeaderTimeout is used.
IdleTimeout time.Duration
}
// DefaultHTTPTimeouts represents the default timeout values used if further
// configuration is not provided.
var DefaultHTTPTimeouts = HTTPTimeouts{
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
}
// DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP
// using the provided HTTP Client.
func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) {
@@ -90,10 +123,19 @@ func DialHTTP(endpoint string) (*Client, error) {
func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error {
hc := c.writeConn.(*httpConn)
respBody, err := hc.doRequest(ctx, msg)
if respBody != nil {
defer respBody.Close()
}
if err != nil {
if respBody != nil {
buf := new(bytes.Buffer)
if _, err2 := buf.ReadFrom(respBody); err2 == nil {
return fmt.Errorf("%v %v", err, buf.String())
}
}
return err
}
defer respBody.Close()
var respmsg jsonrpcMessage
if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil {
return err
@@ -132,6 +174,9 @@ func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadClos
if err != nil {
return nil, err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return resp.Body, errors.New(resp.Status)
}
return resp.Body, nil
}
@@ -149,11 +194,31 @@ func (t *httpReadWriteNopCloser) Close() error {
// NewHTTPServer creates a new HTTP RPC server around an API provider.
//
// Deprecated: Server implements http.Handler
func NewHTTPServer(cors []string, vhosts []string, srv *Server) *http.Server {
func NewHTTPServer(cors []string, vhosts []string, timeouts HTTPTimeouts, srv *Server) *http.Server {
// Wrap the CORS-handler within a host-handler
handler := newCorsHandler(srv, cors)
handler = newVHostHandler(vhosts, handler)
return &http.Server{Handler: handler}
// Make sure timeout values are meaningful
if timeouts.ReadTimeout < time.Second {
log.Warn("Sanitizing invalid HTTP read timeout", "provided", timeouts.ReadTimeout, "updated", DefaultHTTPTimeouts.ReadTimeout)
timeouts.ReadTimeout = DefaultHTTPTimeouts.ReadTimeout
}
if timeouts.WriteTimeout < time.Second {
log.Warn("Sanitizing invalid HTTP write timeout", "provided", timeouts.WriteTimeout, "updated", DefaultHTTPTimeouts.WriteTimeout)
timeouts.WriteTimeout = DefaultHTTPTimeouts.WriteTimeout
}
if timeouts.IdleTimeout < time.Second {
log.Warn("Sanitizing invalid HTTP idle timeout", "provided", timeouts.IdleTimeout, "updated", DefaultHTTPTimeouts.IdleTimeout)
timeouts.IdleTimeout = DefaultHTTPTimeouts.IdleTimeout
}
// Bundle and start the HTTP server
return &http.Server{
Handler: handler,
ReadTimeout: timeouts.ReadTimeout,
WriteTimeout: timeouts.WriteTimeout,
IdleTimeout: timeouts.IdleTimeout,
}
}
// ServeHTTP serves JSON-RPC requests over HTTP.
@@ -169,11 +234,17 @@ func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// All checks passed, create a codec that reads direct from the request body
// untilEOF and writes the response to w and order the server to process a
// single request.
codec := NewJSONCodec(&httpReadWriteNopCloser{r.Body, w})
ctx := r.Context()
ctx = context.WithValue(ctx, "remote", r.RemoteAddr)
ctx = context.WithValue(ctx, "scheme", r.Proto)
ctx = context.WithValue(ctx, "local", r.Host)
body := io.LimitReader(r.Body, maxRequestContentLength)
codec := NewJSONCodec(&httpReadWriteNopCloser{body, w})
defer codec.Close()
w.Header().Set("content-type", contentType)
srv.ServeSingleRequest(codec, OptionMethodInvocation)
srv.ServeSingleRequest(ctx, codec, OptionMethodInvocation)
}
// validateRequest returns a non-zero response code and error message if the
@@ -182,8 +253,8 @@ func validateRequest(r *http.Request) (int, error) {
if r.Method == http.MethodPut || r.Method == http.MethodDelete {
return http.StatusMethodNotAllowed, errors.New("method not allowed")
}
if r.ContentLength > maxHTTPRequestContentLength {
err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxHTTPRequestContentLength)
if r.ContentLength > maxRequestContentLength {
err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength)
return http.StatusRequestEntityTooLarge, err
}
mt, _, err := mime.ParseMediaType(r.Header.Get("content-type"))
+1 -1
View File
@@ -32,7 +32,7 @@ func TestHTTPErrorResponseWithPut(t *testing.T) {
}
func TestHTTPErrorResponseWithMaxContentLength(t *testing.T) {
body := make([]rune, maxHTTPRequestContentLength+1)
body := make([]rune, maxRequestContentLength+1)
testHTTPErrorResponse(t,
http.MethodPost, contentType, string(body), http.StatusRequestEntityTooLarge)
}
+1 -1
View File
@@ -21,7 +21,7 @@ import (
"net"
)
// NewInProcClient attaches an in-process connection to the given RPC server.
// DialInProc attaches an in-process connection to the given RPC server.
func DialInProc(handler *Server) *Client {
initctx := context.Background()
c, _ := newClient(initctx, func(context.Context) (net.Conn, error) {
+6 -9
View File
@@ -18,26 +18,23 @@ package rpc
import (
"context"
"fmt"
"net"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/netutil"
)
// CreateIPCListener creates an listener, on Unix platforms this is a unix socket, on
// Windows this is a named pipe
func CreateIPCListener(endpoint string) (net.Listener, error) {
return ipcListen(endpoint)
}
// ServeListener accepts connections on l, serving JSON-RPC on them.
func (srv *Server) ServeListener(l net.Listener) error {
for {
conn, err := l.Accept()
if err != nil {
if netutil.IsTemporaryError(err) {
log.Warn("RPC accept error", "err", err)
continue
} else if err != nil {
return err
}
log.Trace(fmt.Sprint("accepted conn", conn.RemoteAddr()))
log.Trace("Accepted connection", "addr", conn.RemoteAddr())
go srv.ServeCodec(NewJSONCodec(conn), OptionMethodInvocation|OptionSubscriptions)
}
}
+31 -23
View File
@@ -76,13 +76,13 @@ type jsonNotification struct {
// jsonCodec reads and writes JSON-RPC messages to the underlying connection. It
// also has support for parsing arguments and serializing (result) objects.
type jsonCodec struct {
closer sync.Once // close closed channel once
closed chan interface{} // closed on Close
decMu sync.Mutex // guards d
d *json.Decoder // decodes incoming requests
encMu sync.Mutex // guards e
e *json.Encoder // encodes responses
rw io.ReadWriteCloser // connection
closer sync.Once // close closed channel once
closed chan interface{} // closed on Close
decMu sync.Mutex // guards the decoder
decode func(v interface{}) error // decoder to allow multiple transports
encMu sync.Mutex // guards the encoder
encode func(v interface{}) error // encoder to allow multiple transports
rw io.ReadWriteCloser // connection
}
func (err *jsonError) Error() string {
@@ -96,11 +96,29 @@ func (err *jsonError) ErrorCode() int {
return err.Code
}
// NewJSONCodec creates a new RPC server codec with support for JSON-RPC 2.0
// NewCodec creates a new RPC server codec with support for JSON-RPC 2.0 based
// on explicitly given encoding and decoding methods.
func NewCodec(rwc io.ReadWriteCloser, encode, decode func(v interface{}) error) ServerCodec {
return &jsonCodec{
closed: make(chan interface{}),
encode: encode,
decode: decode,
rw: rwc,
}
}
// NewJSONCodec creates a new RPC server codec with support for JSON-RPC 2.0.
func NewJSONCodec(rwc io.ReadWriteCloser) ServerCodec {
d := json.NewDecoder(rwc)
d.UseNumber()
return &jsonCodec{closed: make(chan interface{}), d: d, e: json.NewEncoder(rwc), rw: rwc}
enc := json.NewEncoder(rwc)
dec := json.NewDecoder(rwc)
dec.UseNumber()
return &jsonCodec{
closed: make(chan interface{}),
encode: enc.Encode,
decode: dec.Decode,
rw: rwc,
}
}
// isBatch returns true when the first non-whitespace characters is '['
@@ -123,14 +141,12 @@ func (c *jsonCodec) ReadRequestHeaders() ([]rpcRequest, bool, Error) {
defer c.decMu.Unlock()
var incomingMsg json.RawMessage
if err := c.d.Decode(&incomingMsg); err != nil {
if err := c.decode(&incomingMsg); err != nil {
return nil, false, &invalidRequestError{err.Error()}
}
if isBatch(incomingMsg) {
return parseBatchRequest(incomingMsg)
}
return parseRequest(incomingMsg)
}
@@ -304,9 +320,6 @@ func parsePositionalArguments(rawArgs json.RawMessage, types []reflect.Type) ([]
// CreateResponse will create a JSON-RPC success response with the given id and reply as result.
func (c *jsonCodec) CreateResponse(id interface{}, reply interface{}) interface{} {
if isHexNum(reflect.TypeOf(reply)) {
return &jsonSuccessResponse{Version: jsonrpcVersion, Id: id, Result: fmt.Sprintf(`%#x`, reply)}
}
return &jsonSuccessResponse{Version: jsonrpcVersion, Id: id, Result: reply}
}
@@ -324,11 +337,6 @@ func (c *jsonCodec) CreateErrorResponseWithInfo(id interface{}, err Error, info
// CreateNotification will create a JSON-RPC notification with the given subscription id and event as params.
func (c *jsonCodec) CreateNotification(subid, namespace string, event interface{}) interface{} {
if isHexNum(reflect.TypeOf(event)) {
return &jsonNotification{Version: jsonrpcVersion, Method: namespace + notificationMethodSuffix,
Params: jsonSubscription{Subscription: subid, Result: fmt.Sprintf(`%#x`, event)}}
}
return &jsonNotification{Version: jsonrpcVersion, Method: namespace + notificationMethodSuffix,
Params: jsonSubscription{Subscription: subid, Result: event}}
}
@@ -338,7 +346,7 @@ func (c *jsonCodec) Write(res interface{}) error {
c.encMu.Lock()
defer c.encMu.Unlock()
return c.e.Encode(res)
return c.encode(res)
}
// Close the underlying connection
+14 -17
View File
@@ -25,8 +25,8 @@ import (
"sync"
"sync/atomic"
mapset "github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/log"
"gopkg.in/fatih/set.v0"
)
const MetadataApi = "rpc"
@@ -46,7 +46,7 @@ const (
func NewServer() *Server {
server := &Server{
services: make(serviceRegistry),
codecs: set.New(),
codecs: mapset.NewSet(),
run: 1,
}
@@ -94,11 +94,12 @@ func (s *Server) RegisterName(name string, rcvr interface{}) error {
methods, subscriptions := suitableCallbacks(rcvrVal, svc.typ)
// already a previous service register under given sname, merge methods/subscriptions
if len(methods) == 0 && len(subscriptions) == 0 {
return fmt.Errorf("Service %T doesn't have any suitable methods/subscriptions to expose", rcvr)
}
// already a previous service register under given name, merge methods/subscriptions
if regsvc, present := s.services[name]; present {
if len(methods) == 0 && len(subscriptions) == 0 {
return fmt.Errorf("Service %T doesn't have any suitable methods/subscriptions to expose", rcvr)
}
for _, m := range methods {
regsvc.callbacks[formatName(m.method.Name)] = m
}
@@ -111,10 +112,6 @@ func (s *Server) RegisterName(name string, rcvr interface{}) error {
svc.name = name
svc.callbacks, svc.subscriptions = methods, subscriptions
if len(svc.callbacks) == 0 && len(svc.subscriptions) == 0 {
return fmt.Errorf("Service %T doesn't have any suitable methods/subscriptions to expose", rcvr)
}
s.services[svc.name] = svc
return nil
}
@@ -125,7 +122,7 @@ func (s *Server) RegisterName(name string, rcvr interface{}) error {
// If singleShot is true it will process a single request, otherwise it will handle
// requests until the codec returns an error when reading a request (in most cases
// an EOF). It executes requests in parallel when singleShot is false.
func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecOption) error {
func (s *Server) serveRequest(ctx context.Context, codec ServerCodec, singleShot bool, options CodecOption) error {
var pend sync.WaitGroup
defer func() {
@@ -140,11 +137,12 @@ func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecO
s.codecsMu.Unlock()
}()
ctx, cancel := context.WithCancel(context.Background())
// ctx, cancel := context.WithCancel(context.Background())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// if the codec supports notification include a notifier that callbacks can use
// to send notification to clients. It is thight to the codec/connection. If the
// to send notification to clients. It is tied to the codec/connection. If the
// connection is closed the notifier will stop and cancels all active subscriptions.
if options&OptionSubscriptions == OptionSubscriptions {
ctx = context.WithValue(ctx, notifierKey{}, newNotifier(codec))
@@ -215,14 +213,14 @@ func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecO
// stopped. In either case the codec is closed.
func (s *Server) ServeCodec(codec ServerCodec, options CodecOption) {
defer codec.Close()
s.serveRequest(codec, false, options)
s.serveRequest(context.Background(), codec, false, options)
}
// ServeSingleRequest reads and processes a single RPC request from the given codec. It will not
// close the codec unless a non-recoverable error has occurred. Note, this method will return after
// a single request has been processed!
func (s *Server) ServeSingleRequest(codec ServerCodec, options CodecOption) {
s.serveRequest(codec, true, options)
func (s *Server) ServeSingleRequest(ctx context.Context, codec ServerCodec, options CodecOption) {
s.serveRequest(ctx, codec, true, options)
}
// Stop will stop reading new requests, wait for stopPendingRequestTimeout to allow pending requests to finish,
@@ -312,7 +310,6 @@ func (s *Server) handle(ctx context.Context, codec ServerCodec, req *serverReque
if len(reply) == 0 {
return codec.CreateResponse(req.id, nil), nil
}
if req.callb.errPos >= 0 { // test if method returned an error
if !reply[req.callb.errPos].IsNil() {
e := reply[req.callb.errPos].Interface().(error)
+2 -2
View File
@@ -23,8 +23,8 @@ import (
"strings"
"sync"
mapset "github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/common/hexutil"
"gopkg.in/fatih/set.v0"
)
// API describes the set of methods offered over the RPC interface
@@ -73,7 +73,7 @@ type Server struct {
run int32
codecsMu sync.Mutex
codecs *set.Set
codecs mapset.Set
}
// rpcRequest represents a raw incoming RPC request
-15
View File
@@ -22,7 +22,6 @@ import (
crand "crypto/rand"
"encoding/binary"
"encoding/hex"
"math/big"
"math/rand"
"reflect"
"strings"
@@ -105,20 +104,6 @@ func formatName(name string) string {
return string(ret)
}
var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem()
// Indication if this type should be serialized in hex
func isHexNum(t reflect.Type) bool {
if t == nil {
return false
}
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t == bigIntType
}
// suitableCallbacks iterates over the methods of the given type. It will determine if a method satisfies the criteria
// for a RPC callback or a subscription callback and adds it to the collection of callbacks or subscriptions. See server
// documentation for a summary of these criteria.
+34 -6
View File
@@ -17,8 +17,10 @@
package rpc
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/http"
@@ -27,11 +29,28 @@ import (
"strings"
"time"
mapset "github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/log"
"golang.org/x/net/websocket"
"gopkg.in/fatih/set.v0"
)
// websocketJSONCodec is a custom JSON codec with payload size enforcement and
// special number parsing.
var websocketJSONCodec = websocket.Codec{
// Marshal is the stock JSON marshaller used by the websocket library too.
Marshal: func(v interface{}) ([]byte, byte, error) {
msg, err := json.Marshal(v)
return msg, websocket.TextFrame, err
},
// Unmarshal is a specialized unmarshaller to properly convert numbers.
Unmarshal: func(msg []byte, payloadType byte, v interface{}) error {
dec := json.NewDecoder(bytes.NewReader(msg))
dec.UseNumber()
return dec.Decode(v)
},
}
// WebsocketHandler returns a handler that serves JSON-RPC to WebSocket connections.
//
// allowedOrigins should be a comma-separated list of allowed origin URLs.
@@ -40,7 +59,16 @@ func (srv *Server) WebsocketHandler(allowedOrigins []string) http.Handler {
return websocket.Server{
Handshake: wsHandshakeValidator(allowedOrigins),
Handler: func(conn *websocket.Conn) {
srv.ServeCodec(NewJSONCodec(conn), OptionMethodInvocation|OptionSubscriptions)
// Create a custom encode/decode pair to enforce payload size and number encoding
conn.MaxPayloadBytes = maxRequestContentLength
encoder := func(v interface{}) error {
return websocketJSONCodec.Send(conn, v)
}
decoder := func(v interface{}) error {
return websocketJSONCodec.Receive(conn, v)
}
srv.ServeCodec(NewCodec(conn, encoder, decoder), OptionMethodInvocation|OptionSubscriptions)
},
}
}
@@ -56,7 +84,7 @@ func NewWSServer(allowedOrigins []string, srv *Server) *http.Server {
// websocket upgrade process. When a '*' is specified as an allowed origins all
// connections are accepted.
func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http.Request) error {
origins := set.New()
origins := mapset.NewSet()
allowAllOrigins := false
for _, origin := range allowedOrigins {
@@ -69,18 +97,18 @@ func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http
}
// allow localhost if no allowedOrigins are specified.
if len(origins.List()) == 0 {
if len(origins.ToSlice()) == 0 {
origins.Add("http://localhost")
if hostname, err := os.Hostname(); err == nil {
origins.Add("http://" + strings.ToLower(hostname))
}
}
log.Debug(fmt.Sprintf("Allowed origin(s) for WS RPC interface %v\n", origins.List()))
log.Debug(fmt.Sprintf("Allowed origin(s) for WS RPC interface %v\n", origins.ToSlice()))
f := func(cfg *websocket.Config, req *http.Request) error {
origin := strings.ToLower(req.Header.Get("Origin"))
if allowAllOrigins || origins.Has(origin) {
if allowAllOrigins || origins.Contains(origin) {
return nil
}
log.Warn(fmt.Sprintf("origin '%s' not allowed on WS-RPC interface\n", origin))