@ -0,0 +1,298 @@ | |||||
package tmspcli | |||||
import ( | |||||
"fmt" | |||||
"net" | |||||
"sync" | |||||
"time" | |||||
context "golang.org/x/net/context" | |||||
grpc "google.golang.org/grpc" | |||||
. "github.com/tendermint/go-common" | |||||
"github.com/tendermint/tmsp/types" | |||||
) | |||||
// This is goroutine-safe, but users should beware that | |||||
// the application in general is not meant to be interfaced | |||||
// with concurrent callers. | |||||
type grpcClient struct { | |||||
QuitService | |||||
mustConnect bool | |||||
client types.TMSPApplicationClient | |||||
mtx sync.Mutex | |||||
addr string | |||||
err error | |||||
resCb func(*types.Request, *types.Response) // listens to all callbacks | |||||
} | |||||
func NewGRPCClient(addr string, mustConnect bool) (*grpcClient, error) { | |||||
cli := &grpcClient{ | |||||
addr: addr, | |||||
mustConnect: mustConnect, | |||||
} | |||||
cli.QuitService = *NewQuitService(nil, "grpcClient", cli) | |||||
_, err := cli.Start() // Just start it, it's confusing for callers to remember to start. | |||||
return cli, err | |||||
} | |||||
func dialerFunc(addr string, timeout time.Duration) (net.Conn, error) { | |||||
return Connect(addr) | |||||
} | |||||
func (cli *grpcClient) OnStart() (err error) { | |||||
cli.QuitService.OnStart() | |||||
RETRY_LOOP: | |||||
for { | |||||
conn, err_ := grpc.Dial(cli.addr, grpc.WithInsecure(), grpc.WithDialer(dialerFunc)) | |||||
if err_ != nil { | |||||
if cli.mustConnect { | |||||
err = err_ // OnStart() will return this. | |||||
return | |||||
} else { | |||||
fmt.Printf("tmsp.grpcClient failed to connect to %v. Retrying...\n", cli.addr) | |||||
time.Sleep(time.Second * 3) | |||||
continue RETRY_LOOP | |||||
} | |||||
} | |||||
cli.client = types.NewTMSPApplicationClient(conn) | |||||
return | |||||
} | |||||
} | |||||
func (cli *grpcClient) OnStop() { | |||||
cli.QuitService.OnStop() | |||||
// TODO: close client (?) | |||||
} | |||||
// Set listener for all responses | |||||
// NOTE: callback may get internally generated flush responses. | |||||
func (cli *grpcClient) SetResponseCallback(resCb Callback) { | |||||
cli.mtx.Lock() | |||||
defer cli.mtx.Unlock() | |||||
cli.resCb = resCb | |||||
} | |||||
func (cli *grpcClient) StopForError(err error) { | |||||
cli.mtx.Lock() | |||||
fmt.Printf("Stopping tmsp.grpcClient for error: %v\n", err.Error()) | |||||
if cli.err == nil { | |||||
cli.err = err | |||||
} | |||||
cli.mtx.Unlock() | |||||
cli.Stop() | |||||
} | |||||
func (cli *grpcClient) Error() error { | |||||
cli.mtx.Lock() | |||||
defer cli.mtx.Unlock() | |||||
return cli.err | |||||
} | |||||
//---------------------------------------- | |||||
// async calls are really sync. | |||||
// maybe one day, if people really want it, we use grpc streams, | |||||
// but hopefully not :D | |||||
func (cli *grpcClient) finishAsyncCall(req *types.Request, res *types.Response) *ReqRes { | |||||
reqres := NewReqRes(req) | |||||
reqres.Response = res // Set response | |||||
reqres.Done() // Release waiters | |||||
// Notify reqRes listener if set | |||||
if cb := reqres.GetCallback(); cb != nil { | |||||
cb(res) | |||||
} | |||||
// Notify client listener if set | |||||
if cli.resCb != nil { | |||||
cli.resCb(reqres.Request, res) | |||||
} | |||||
return reqres | |||||
} | |||||
func (cli *grpcClient) EchoAsync(msg string) *ReqRes { | |||||
req := types.ToRequestEcho(msg) | |||||
res, err := cli.client.Echo(context.Background(), req.GetEcho()) | |||||
if err != nil { | |||||
cli.err = err | |||||
} | |||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_Echo{res}}) | |||||
} | |||||
func (cli *grpcClient) FlushAsync() *ReqRes { | |||||
req := types.ToRequestFlush() | |||||
res, err := cli.client.Flush(context.Background(), req.GetFlush()) | |||||
if err != nil { | |||||
cli.err = err | |||||
} | |||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_Flush{res}}) | |||||
} | |||||
func (cli *grpcClient) InfoAsync() *ReqRes { | |||||
req := types.ToRequestInfo() | |||||
res, err := cli.client.Info(context.Background(), req.GetInfo()) | |||||
if err != nil { | |||||
cli.err = err | |||||
} | |||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_Info{res}}) | |||||
} | |||||
func (cli *grpcClient) SetOptionAsync(key string, value string) *ReqRes { | |||||
req := types.ToRequestSetOption(key, value) | |||||
res, err := cli.client.SetOption(context.Background(), req.GetSetOption()) | |||||
if err != nil { | |||||
cli.err = err | |||||
} | |||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_SetOption{res}}) | |||||
} | |||||
func (cli *grpcClient) AppendTxAsync(tx []byte) *ReqRes { | |||||
req := types.ToRequestAppendTx(tx) | |||||
res, err := cli.client.AppendTx(context.Background(), req.GetAppendTx()) | |||||
if err != nil { | |||||
cli.err = err | |||||
} | |||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_AppendTx{res}}) | |||||
} | |||||
func (cli *grpcClient) CheckTxAsync(tx []byte) *ReqRes { | |||||
req := types.ToRequestCheckTx(tx) | |||||
res, err := cli.client.CheckTx(context.Background(), req.GetCheckTx()) | |||||
if err != nil { | |||||
cli.err = err | |||||
} | |||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_CheckTx{res}}) | |||||
} | |||||
func (cli *grpcClient) QueryAsync(query []byte) *ReqRes { | |||||
req := types.ToRequestQuery(query) | |||||
res, err := cli.client.Query(context.Background(), req.GetQuery()) | |||||
if err != nil { | |||||
cli.err = err | |||||
} | |||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_Query{res}}) | |||||
} | |||||
func (cli *grpcClient) CommitAsync() *ReqRes { | |||||
req := types.ToRequestCommit() | |||||
res, err := cli.client.Commit(context.Background(), req.GetCommit()) | |||||
if err != nil { | |||||
cli.err = err | |||||
} | |||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_Commit{res}}) | |||||
} | |||||
func (cli *grpcClient) InitChainAsync(validators []*types.Validator) *ReqRes { | |||||
req := types.ToRequestInitChain(validators) | |||||
res, err := cli.client.InitChain(context.Background(), req.GetInitChain()) | |||||
if err != nil { | |||||
cli.err = err | |||||
} | |||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_InitChain{res}}) | |||||
} | |||||
func (cli *grpcClient) BeginBlockAsync(height uint64) *ReqRes { | |||||
req := types.ToRequestBeginBlock(height) | |||||
res, err := cli.client.BeginBlock(context.Background(), req.GetBeginBlock()) | |||||
if err != nil { | |||||
cli.err = err | |||||
} | |||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_BeginBlock{res}}) | |||||
} | |||||
func (cli *grpcClient) EndBlockAsync(height uint64) *ReqRes { | |||||
req := types.ToRequestEndBlock(height) | |||||
res, err := cli.client.EndBlock(context.Background(), req.GetEndBlock()) | |||||
if err != nil { | |||||
cli.err = err | |||||
} | |||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_EndBlock{res}}) | |||||
} | |||||
//---------------------------------------- | |||||
func (cli *grpcClient) EchoSync(msg string) (res types.Result) { | |||||
r := cli.EchoAsync(msg).Response.GetEcho() | |||||
return types.NewResultOK([]byte(r.Message), LOG) | |||||
} | |||||
func (cli *grpcClient) FlushSync() error { | |||||
return nil | |||||
} | |||||
func (cli *grpcClient) InfoSync() (res types.Result) { | |||||
r := cli.InfoAsync().Response.GetInfo() | |||||
return types.NewResultOK([]byte(r.Info), LOG) | |||||
} | |||||
func (cli *grpcClient) SetOptionSync(key string, value string) (res types.Result) { | |||||
reqres := cli.SetOptionAsync(key, value) | |||||
if cli.err != nil { | |||||
return types.ErrInternalError.SetLog(cli.err.Error()) | |||||
} | |||||
resp := reqres.Response.GetSetOption() | |||||
return types.Result{Code: OK, Data: nil, Log: resp.Log} | |||||
} | |||||
func (cli *grpcClient) AppendTxSync(tx []byte) (res types.Result) { | |||||
reqres := cli.AppendTxAsync(tx) | |||||
if cli.err != nil { | |||||
return types.ErrInternalError.SetLog(cli.err.Error()) | |||||
} | |||||
resp := reqres.Response.GetAppendTx() | |||||
return types.Result{Code: resp.Code, Data: resp.Data, Log: resp.Log} | |||||
} | |||||
func (cli *grpcClient) CheckTxSync(tx []byte) (res types.Result) { | |||||
reqres := cli.CheckTxAsync(tx) | |||||
if cli.err != nil { | |||||
return types.ErrInternalError.SetLog(cli.err.Error()) | |||||
} | |||||
resp := reqres.Response.GetCheckTx() | |||||
return types.Result{Code: resp.Code, Data: resp.Data, Log: resp.Log} | |||||
} | |||||
func (cli *grpcClient) QuerySync(query []byte) (res types.Result) { | |||||
reqres := cli.QueryAsync(query) | |||||
if cli.err != nil { | |||||
return types.ErrInternalError.SetLog(cli.err.Error()) | |||||
} | |||||
resp := reqres.Response.GetQuery() | |||||
return types.Result{Code: resp.Code, Data: resp.Data, Log: resp.Log} | |||||
} | |||||
func (cli *grpcClient) CommitSync() (res types.Result) { | |||||
reqres := cli.CommitAsync() | |||||
if cli.err != nil { | |||||
return types.ErrInternalError.SetLog(cli.err.Error()) | |||||
} | |||||
resp := reqres.Response.GetCommit() | |||||
return types.Result{Code: resp.Code, Data: resp.Data, Log: resp.Log} | |||||
} | |||||
func (cli *grpcClient) InitChainSync(validators []*types.Validator) (err error) { | |||||
cli.InitChainAsync(validators) | |||||
if cli.err != nil { | |||||
return cli.err | |||||
} | |||||
return nil | |||||
} | |||||
func (cli *grpcClient) BeginBlockSync(height uint64) (err error) { | |||||
cli.BeginBlockAsync(height) | |||||
if cli.err != nil { | |||||
return cli.err | |||||
} | |||||
return nil | |||||
} | |||||
func (cli *grpcClient) EndBlockSync(height uint64) (validators []*types.Validator, err error) { | |||||
reqres := cli.EndBlockAsync(height) | |||||
if cli.err != nil { | |||||
return nil, cli.err | |||||
} | |||||
return reqres.Response.GetEndBlock().Diffs, nil | |||||
} |
@ -1,191 +1,22 @@ | |||||
package server | package server | ||||
import ( | import ( | ||||
"bufio" | |||||
"fmt" | "fmt" | ||||
"io" | |||||
"net" | |||||
"strings" | |||||
"sync" | |||||
. "github.com/tendermint/go-common" | . "github.com/tendermint/go-common" | ||||
"github.com/tendermint/tmsp/types" | "github.com/tendermint/tmsp/types" | ||||
) | ) | ||||
// var maxNumberConnections = 2 | |||||
type Server struct { | |||||
QuitService | |||||
proto string | |||||
addr string | |||||
listener net.Listener | |||||
appMtx sync.Mutex | |||||
app types.Application | |||||
} | |||||
func NewServer(protoAddr string, app types.Application) (Service, error) { | |||||
parts := strings.SplitN(protoAddr, "://", 2) | |||||
proto, addr := parts[0], parts[1] | |||||
s := &Server{ | |||||
proto: proto, | |||||
addr: addr, | |||||
listener: nil, | |||||
app: app, | |||||
} | |||||
s.QuitService = *NewQuitService(nil, "TMSPServer", s) | |||||
_, err := s.Start() // Just start it | |||||
return s, err | |||||
} | |||||
func (s *Server) OnStart() error { | |||||
s.QuitService.OnStart() | |||||
ln, err := net.Listen(s.proto, s.addr) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
s.listener = ln | |||||
go s.acceptConnectionsRoutine() | |||||
return nil | |||||
} | |||||
func (s *Server) OnStop() { | |||||
s.QuitService.OnStop() | |||||
s.listener.Close() | |||||
} | |||||
func (s *Server) acceptConnectionsRoutine() { | |||||
// semaphore := make(chan struct{}, maxNumberConnections) | |||||
for { | |||||
// semaphore <- struct{}{} | |||||
// Accept a connection | |||||
fmt.Println("Waiting for new connection...") | |||||
conn, err := s.listener.Accept() | |||||
if err != nil { | |||||
if !s.IsRunning() { | |||||
return // Ignore error from listener closing. | |||||
} | |||||
Exit("Failed to accept connection: " + err.Error()) | |||||
} else { | |||||
fmt.Println("Accepted a new connection") | |||||
} | |||||
closeConn := make(chan error, 2) // Push to signal connection closed | |||||
responses := make(chan *types.Response, 1000) // A channel to buffer responses | |||||
// Read requests from conn and deal with them | |||||
go s.handleRequests(closeConn, conn, responses) | |||||
// Pull responses from 'responses' and write them to conn. | |||||
go s.handleResponses(closeConn, responses, conn) | |||||
go func() { | |||||
// Wait until signal to close connection | |||||
errClose := <-closeConn | |||||
if errClose != nil { | |||||
fmt.Printf("Connection error: %v\n", errClose) | |||||
} else { | |||||
fmt.Println("Connection was closed.") | |||||
} | |||||
// Close the connection | |||||
err := conn.Close() | |||||
if err != nil { | |||||
fmt.Printf("Error in closing connection: %v\n", err) | |||||
} | |||||
// <-semaphore | |||||
}() | |||||
} | |||||
} | |||||
// Read requests from conn and deal with them | |||||
func (s *Server) handleRequests(closeConn chan error, conn net.Conn, responses chan<- *types.Response) { | |||||
var count int | |||||
var bufReader = bufio.NewReader(conn) | |||||
for { | |||||
var req = &types.Request{} | |||||
err := types.ReadMessage(bufReader, req) | |||||
if err != nil { | |||||
if err == io.EOF { | |||||
closeConn <- fmt.Errorf("Connection closed by client") | |||||
} else { | |||||
closeConn <- fmt.Errorf("Error in handleValue: %v", err.Error()) | |||||
} | |||||
return | |||||
} | |||||
s.appMtx.Lock() | |||||
count++ | |||||
s.handleRequest(req, responses) | |||||
s.appMtx.Unlock() | |||||
} | |||||
} | |||||
func (s *Server) handleRequest(req *types.Request, responses chan<- *types.Response) { | |||||
switch r := req.Value.(type) { | |||||
case *types.Request_Echo: | |||||
responses <- types.ToResponseEcho(r.Echo.Message) | |||||
case *types.Request_Flush: | |||||
responses <- types.ToResponseFlush() | |||||
case *types.Request_Info: | |||||
data := s.app.Info() | |||||
responses <- types.ToResponseInfo(data) | |||||
case *types.Request_SetOption: | |||||
so := r.SetOption | |||||
logStr := s.app.SetOption(so.Key, so.Value) | |||||
responses <- types.ToResponseSetOption(logStr) | |||||
case *types.Request_AppendTx: | |||||
res := s.app.AppendTx(r.AppendTx.Tx) | |||||
responses <- types.ToResponseAppendTx(res.Code, res.Data, res.Log) | |||||
case *types.Request_CheckTx: | |||||
res := s.app.CheckTx(r.CheckTx.Tx) | |||||
responses <- types.ToResponseCheckTx(res.Code, res.Data, res.Log) | |||||
case *types.Request_Commit: | |||||
res := s.app.Commit() | |||||
responses <- types.ToResponseCommit(res.Code, res.Data, res.Log) | |||||
case *types.Request_Query: | |||||
res := s.app.Query(r.Query.Query) | |||||
responses <- types.ToResponseQuery(res.Code, res.Data, res.Log) | |||||
case *types.Request_InitChain: | |||||
if app, ok := s.app.(types.BlockchainAware); ok { | |||||
app.InitChain(r.InitChain.Validators) | |||||
responses <- types.ToResponseInitChain() | |||||
} else { | |||||
responses <- types.ToResponseInitChain() | |||||
} | |||||
case *types.Request_EndBlock: | |||||
if app, ok := s.app.(types.BlockchainAware); ok { | |||||
validators := app.EndBlock(r.EndBlock.Height) | |||||
responses <- types.ToResponseEndBlock(validators) | |||||
} else { | |||||
responses <- types.ToResponseEndBlock(nil) | |||||
} | |||||
func NewServer(protoAddr, transport string, app types.Application) (Service, error) { | |||||
var s Service | |||||
var err error | |||||
switch transport { | |||||
case "socket": | |||||
s, err = NewSocketServer(protoAddr, app) | |||||
case "grpc": | |||||
s, err = NewGRPCServer(protoAddr, types.NewGRPCApplication(app)) | |||||
default: | default: | ||||
responses <- types.ToResponseException("Unknown request") | |||||
} | |||||
} | |||||
// Pull responses from 'responses' and write them to conn. | |||||
func (s *Server) handleResponses(closeConn chan error, responses <-chan *types.Response, conn net.Conn) { | |||||
var count int | |||||
var bufWriter = bufio.NewWriter(conn) | |||||
for { | |||||
var res = <-responses | |||||
err := types.WriteMessage(res, bufWriter) | |||||
if err != nil { | |||||
closeConn <- fmt.Errorf("Error in handleValue: %v", err.Error()) | |||||
return | |||||
} | |||||
if _, ok := res.Value.(*types.Response_Flush); ok { | |||||
err = bufWriter.Flush() | |||||
if err != nil { | |||||
closeConn <- fmt.Errorf("Error in handleValue: %v", err.Error()) | |||||
return | |||||
} | |||||
} | |||||
count++ | |||||
err = fmt.Errorf("Unknown server type %s", transport) | |||||
} | } | ||||
return s, err | |||||
} | } |
@ -0,0 +1,191 @@ | |||||
package server | |||||
import ( | |||||
"bufio" | |||||
"fmt" | |||||
"io" | |||||
"net" | |||||
"strings" | |||||
"sync" | |||||
. "github.com/tendermint/go-common" | |||||
"github.com/tendermint/tmsp/types" | |||||
) | |||||
// var maxNumberConnections = 2 | |||||
type SocketServer struct { | |||||
QuitService | |||||
proto string | |||||
addr string | |||||
listener net.Listener | |||||
appMtx sync.Mutex | |||||
app types.Application | |||||
} | |||||
func NewSocketServer(protoAddr string, app types.Application) (Service, error) { | |||||
parts := strings.SplitN(protoAddr, "://", 2) | |||||
proto, addr := parts[0], parts[1] | |||||
s := &SocketServer{ | |||||
proto: proto, | |||||
addr: addr, | |||||
listener: nil, | |||||
app: app, | |||||
} | |||||
s.QuitService = *NewQuitService(nil, "TMSPServer", s) | |||||
_, err := s.Start() // Just start it | |||||
return s, err | |||||
} | |||||
func (s *SocketServer) OnStart() error { | |||||
s.QuitService.OnStart() | |||||
ln, err := net.Listen(s.proto, s.addr) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
s.listener = ln | |||||
go s.acceptConnectionsRoutine() | |||||
return nil | |||||
} | |||||
func (s *SocketServer) OnStop() { | |||||
s.QuitService.OnStop() | |||||
s.listener.Close() | |||||
} | |||||
func (s *SocketServer) acceptConnectionsRoutine() { | |||||
// semaphore := make(chan struct{}, maxNumberConnections) | |||||
for { | |||||
// semaphore <- struct{}{} | |||||
// Accept a connection | |||||
fmt.Println("Waiting for new connection...") | |||||
conn, err := s.listener.Accept() | |||||
if err != nil { | |||||
if !s.IsRunning() { | |||||
return // Ignore error from listener closing. | |||||
} | |||||
Exit("Failed to accept connection: " + err.Error()) | |||||
} else { | |||||
fmt.Println("Accepted a new connection") | |||||
} | |||||
closeConn := make(chan error, 2) // Push to signal connection closed | |||||
responses := make(chan *types.Response, 1000) // A channel to buffer responses | |||||
// Read requests from conn and deal with them | |||||
go s.handleRequests(closeConn, conn, responses) | |||||
// Pull responses from 'responses' and write them to conn. | |||||
go s.handleResponses(closeConn, responses, conn) | |||||
go func() { | |||||
// Wait until signal to close connection | |||||
errClose := <-closeConn | |||||
if errClose != nil { | |||||
fmt.Printf("Connection error: %v\n", errClose) | |||||
} else { | |||||
fmt.Println("Connection was closed.") | |||||
} | |||||
// Close the connection | |||||
err := conn.Close() | |||||
if err != nil { | |||||
fmt.Printf("Error in closing connection: %v\n", err) | |||||
} | |||||
// <-semaphore | |||||
}() | |||||
} | |||||
} | |||||
// Read requests from conn and deal with them | |||||
func (s *SocketServer) handleRequests(closeConn chan error, conn net.Conn, responses chan<- *types.Response) { | |||||
var count int | |||||
var bufReader = bufio.NewReader(conn) | |||||
for { | |||||
var req = &types.Request{} | |||||
err := types.ReadMessage(bufReader, req) | |||||
if err != nil { | |||||
if err == io.EOF { | |||||
closeConn <- fmt.Errorf("Connection closed by client") | |||||
} else { | |||||
closeConn <- fmt.Errorf("Error in handleValue: %v", err.Error()) | |||||
} | |||||
return | |||||
} | |||||
s.appMtx.Lock() | |||||
count++ | |||||
s.handleRequest(req, responses) | |||||
s.appMtx.Unlock() | |||||
} | |||||
} | |||||
func (s *SocketServer) handleRequest(req *types.Request, responses chan<- *types.Response) { | |||||
switch r := req.Value.(type) { | |||||
case *types.Request_Echo: | |||||
responses <- types.ToResponseEcho(r.Echo.Message) | |||||
case *types.Request_Flush: | |||||
responses <- types.ToResponseFlush() | |||||
case *types.Request_Info: | |||||
data := s.app.Info() | |||||
responses <- types.ToResponseInfo(data) | |||||
case *types.Request_SetOption: | |||||
so := r.SetOption | |||||
logStr := s.app.SetOption(so.Key, so.Value) | |||||
responses <- types.ToResponseSetOption(logStr) | |||||
case *types.Request_AppendTx: | |||||
res := s.app.AppendTx(r.AppendTx.Tx) | |||||
responses <- types.ToResponseAppendTx(res.Code, res.Data, res.Log) | |||||
case *types.Request_CheckTx: | |||||
res := s.app.CheckTx(r.CheckTx.Tx) | |||||
responses <- types.ToResponseCheckTx(res.Code, res.Data, res.Log) | |||||
case *types.Request_Commit: | |||||
res := s.app.Commit() | |||||
responses <- types.ToResponseCommit(res.Code, res.Data, res.Log) | |||||
case *types.Request_Query: | |||||
res := s.app.Query(r.Query.Query) | |||||
responses <- types.ToResponseQuery(res.Code, res.Data, res.Log) | |||||
case *types.Request_InitChain: | |||||
if app, ok := s.app.(types.BlockchainAware); ok { | |||||
app.InitChain(r.InitChain.Validators) | |||||
responses <- types.ToResponseInitChain() | |||||
} else { | |||||
responses <- types.ToResponseInitChain() | |||||
} | |||||
case *types.Request_EndBlock: | |||||
if app, ok := s.app.(types.BlockchainAware); ok { | |||||
validators := app.EndBlock(r.EndBlock.Height) | |||||
responses <- types.ToResponseEndBlock(validators) | |||||
} else { | |||||
responses <- types.ToResponseEndBlock(nil) | |||||
} | |||||
default: | |||||
responses <- types.ToResponseException("Unknown request") | |||||
} | |||||
} | |||||
// Pull responses from 'responses' and write them to conn. | |||||
func (s *SocketServer) handleResponses(closeConn chan error, responses <-chan *types.Response, conn net.Conn) { | |||||
var count int | |||||
var bufWriter = bufio.NewWriter(conn) | |||||
for { | |||||
var res = <-responses | |||||
err := types.WriteMessage(res, bufWriter) | |||||
if err != nil { | |||||
closeConn <- fmt.Errorf("Error in handleValue: %v", err.Error()) | |||||
return | |||||
} | |||||
if _, ok := res.Value.(*types.Response_Flush); ok { | |||||
err = bufWriter.Flush() | |||||
if err != nil { | |||||
closeConn <- fmt.Errorf("Error in handleValue: %v", err.Error()) | |||||
return | |||||
} | |||||
} | |||||
count++ | |||||
} | |||||
} |