|
|
@ -25,8 +25,11 @@ import ( |
|
|
|
|
|
|
|
|
|
|
|
"errors" |
|
|
|
"errors" |
|
|
|
"net" |
|
|
|
"net" |
|
|
|
|
|
|
|
"net/http" |
|
|
|
"time" |
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"io" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
"github.com/ethereum/go-ethereum/core/types" |
|
|
|
"github.com/ethereum/go-ethereum/core/types" |
|
|
|
) |
|
|
|
) |
|
|
@ -266,39 +269,64 @@ type ListenerStoppedError struct { |
|
|
|
msg string |
|
|
|
msg string |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (self ListenerStoppedError) Timout() bool { |
|
|
|
func (self ListenerStoppedError) Error() string { |
|
|
|
return false |
|
|
|
return self.msg |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (self ListenerStoppedError) Temporary() bool { |
|
|
|
var listenerStoppedError = ListenerStoppedError{"Listener stopped"} |
|
|
|
return false |
|
|
|
|
|
|
|
|
|
|
|
type StoppableTCPListener struct { |
|
|
|
|
|
|
|
*net.TCPListener |
|
|
|
|
|
|
|
stop *chan struct{} // closed when the listener must stop
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (self ListenerStoppedError) Error() string { |
|
|
|
// Wraps the default handler and checks if the RPC service was stopped. In that case it returns an
|
|
|
|
return self.msg |
|
|
|
// error indicating that the service was stopped. This will only happen for connections which are
|
|
|
|
|
|
|
|
// kept open (HTTP keep-alive) when the RPC service was shutdown.
|
|
|
|
|
|
|
|
func NewStoppableHandler(h http.Handler, stop *chan struct{}) http.Handler { |
|
|
|
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
|
|
|
select { |
|
|
|
|
|
|
|
case <-*stop: |
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json") |
|
|
|
|
|
|
|
jsonerr := &RpcErrorObject{-32603, "RPC service stopt"} |
|
|
|
|
|
|
|
send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
h.ServeHTTP(w, r) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type ControllableTCPListener struct { |
|
|
|
// Stop the listener and all accepted and still active connections.
|
|
|
|
*net.TCPListener |
|
|
|
func (self *StoppableTCPListener) Stop() { |
|
|
|
stop chan struct{} |
|
|
|
close(*self.stop) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var listenerStoppedError ListenerStoppedError |
|
|
|
func NewStoppableTCPListener(addr string) (*StoppableTCPListener, error) { |
|
|
|
|
|
|
|
wl, err := net.Listen("tcp", addr) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (self *ControllableTCPListener) Stop() { |
|
|
|
if tcpl, ok := wl.(*net.TCPListener); ok { |
|
|
|
close(self.stop) |
|
|
|
stop := make(chan struct{}) |
|
|
|
|
|
|
|
l := &StoppableTCPListener{tcpl, &stop} |
|
|
|
|
|
|
|
return l, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil, errors.New("Unable to create TCP listener for RPC service") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (self *ControllableTCPListener) Accept() (net.Conn, error) { |
|
|
|
func (self *StoppableTCPListener) Accept() (net.Conn, error) { |
|
|
|
for { |
|
|
|
for { |
|
|
|
self.SetDeadline(time.Now().Add(time.Duration(500 * time.Millisecond))) |
|
|
|
self.SetDeadline(time.Now().Add(time.Duration(1 * time.Second))) |
|
|
|
c, err := self.TCPListener.AcceptTCP() |
|
|
|
c, err := self.TCPListener.AcceptTCP() |
|
|
|
|
|
|
|
|
|
|
|
select { |
|
|
|
select { |
|
|
|
case <-self.stop: |
|
|
|
case <-*self.stop: |
|
|
|
|
|
|
|
c.Close() |
|
|
|
self.TCPListener.Close() |
|
|
|
self.TCPListener.Close() |
|
|
|
return nil, listenerStoppedError |
|
|
|
return nil, listenerStoppedError |
|
|
|
default: // keep on going
|
|
|
|
default: |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
@ -307,20 +335,21 @@ func (self *ControllableTCPListener) Accept() (net.Conn, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return c, err |
|
|
|
return &ClosableConnection{c, self.stop}, err |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func NewControllableTCPListener(addr string) (*ControllableTCPListener, error) { |
|
|
|
type ClosableConnection struct { |
|
|
|
wl, err := net.Listen("tcp", addr) |
|
|
|
*net.TCPConn |
|
|
|
if err != nil { |
|
|
|
closed *chan struct{} |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if tcpl, ok := wl.(*net.TCPListener); ok { |
|
|
|
func (self *ClosableConnection) Read(b []byte) (n int, err error) { |
|
|
|
l := &ControllableTCPListener{tcpl, make(chan struct{})} |
|
|
|
select { |
|
|
|
return l, nil |
|
|
|
case <-*self.closed: |
|
|
|
|
|
|
|
self.TCPConn.Close() |
|
|
|
|
|
|
|
return 0, io.EOF |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
return self.TCPConn.Read(b) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return nil, errors.New("Unable to create TCP listener for RPC") |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|