|
|
- package privval
-
- import (
- "io"
- "net"
- "time"
-
- cmn "github.com/tendermint/tendermint/libs/common"
- "github.com/tendermint/tendermint/libs/log"
- "github.com/tendermint/tendermint/types"
- )
-
- // SignerServiceEndpointOption sets an optional parameter on the SignerServiceEndpoint.
- type SignerServiceEndpointOption func(*SignerServiceEndpoint)
-
- // SignerServiceEndpointTimeoutReadWrite sets the read and write timeout for connections
- // from external signing processes.
- func SignerServiceEndpointTimeoutReadWrite(timeout time.Duration) SignerServiceEndpointOption {
- return func(ss *SignerServiceEndpoint) { ss.timeoutReadWrite = timeout }
- }
-
- // SignerServiceEndpointConnRetries sets the amount of attempted retries to connect.
- func SignerServiceEndpointConnRetries(retries int) SignerServiceEndpointOption {
- return func(ss *SignerServiceEndpoint) { ss.connRetries = retries }
- }
-
- // SignerServiceEndpoint dials using its dialer and responds to any
- // signature requests using its privVal.
- type SignerServiceEndpoint struct {
- cmn.BaseService
-
- chainID string
- timeoutReadWrite time.Duration
- connRetries int
- privVal types.PrivValidator
-
- dialer SocketDialer
- conn net.Conn
- }
-
- // NewSignerServiceEndpoint returns a SignerServiceEndpoint that will dial using the given
- // dialer and respond to any signature requests over the connection
- // using the given privVal.
- func NewSignerServiceEndpoint(
- logger log.Logger,
- chainID string,
- privVal types.PrivValidator,
- dialer SocketDialer,
- ) *SignerServiceEndpoint {
- se := &SignerServiceEndpoint{
- chainID: chainID,
- timeoutReadWrite: time.Second * defaultTimeoutReadWriteSeconds,
- connRetries: defaultMaxDialRetries,
- privVal: privVal,
- dialer: dialer,
- }
-
- se.BaseService = *cmn.NewBaseService(logger, "SignerServiceEndpoint", se)
- return se
- }
-
- // OnStart implements cmn.Service.
- func (se *SignerServiceEndpoint) OnStart() error {
- conn, err := se.connect()
- if err != nil {
- se.Logger.Error("OnStart", "err", err)
- return err
- }
-
- se.conn = conn
- go se.handleConnection(conn)
-
- return nil
- }
-
- // OnStop implements cmn.Service.
- func (se *SignerServiceEndpoint) OnStop() {
- if se.conn == nil {
- return
- }
-
- if err := se.conn.Close(); err != nil {
- se.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed"))
- }
- }
-
- func (se *SignerServiceEndpoint) connect() (net.Conn, error) {
- for retries := 0; retries < se.connRetries; retries++ {
- // Don't sleep if it is the first retry.
- if retries > 0 {
- time.Sleep(se.timeoutReadWrite)
- }
-
- conn, err := se.dialer()
- if err == nil {
- return conn, nil
- }
-
- se.Logger.Error("dialing", "err", err)
- }
-
- return nil, ErrDialRetryMax
- }
-
- func (se *SignerServiceEndpoint) handleConnection(conn net.Conn) {
- for {
- if !se.IsRunning() {
- return // Ignore error from listener closing.
- }
-
- // Reset the connection deadline
- deadline := time.Now().Add(se.timeoutReadWrite)
- err := conn.SetDeadline(deadline)
- if err != nil {
- return
- }
-
- req, err := readMsg(conn)
- if err != nil {
- if err != io.EOF {
- se.Logger.Error("handleConnection readMsg", "err", err)
- }
- return
- }
-
- res, err := handleRequest(req, se.chainID, se.privVal)
-
- if err != nil {
- // only log the error; we'll reply with an error in res
- se.Logger.Error("handleConnection handleRequest", "err", err)
- }
-
- err = writeMsg(conn, res)
- if err != nil {
- se.Logger.Error("handleConnection writeMsg", "err", err)
- return
- }
- }
- }
|