mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-15 14:21:22 +00:00
rpc: first successful websocket event subscription
This commit is contained in:
@ -236,6 +236,7 @@ func (conR *ConsensusReactor) ResetToState(state *sm.State) {
|
|||||||
// implements events.Eventable
|
// implements events.Eventable
|
||||||
func (conR *ConsensusReactor) SetEventSwitch(evsw *events.EventSwitch) {
|
func (conR *ConsensusReactor) SetEventSwitch(evsw *events.EventSwitch) {
|
||||||
conR.evsw = evsw
|
conR.evsw = evsw
|
||||||
|
conR.conS.SetEventSwitch(evsw)
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------
|
//--------------------------------------
|
||||||
|
@ -66,6 +66,7 @@ import (
|
|||||||
. "github.com/tendermint/tendermint/common"
|
. "github.com/tendermint/tendermint/common"
|
||||||
"github.com/tendermint/tendermint/config"
|
"github.com/tendermint/tendermint/config"
|
||||||
. "github.com/tendermint/tendermint/consensus/types"
|
. "github.com/tendermint/tendermint/consensus/types"
|
||||||
|
"github.com/tendermint/tendermint/events"
|
||||||
mempl "github.com/tendermint/tendermint/mempool"
|
mempl "github.com/tendermint/tendermint/mempool"
|
||||||
sm "github.com/tendermint/tendermint/state"
|
sm "github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
@ -246,6 +247,8 @@ type ConsensusState struct {
|
|||||||
stagedBlock *types.Block // Cache last staged block.
|
stagedBlock *types.Block // Cache last staged block.
|
||||||
stagedState *sm.State // Cache result of staged block.
|
stagedState *sm.State // Cache result of staged block.
|
||||||
lastCommitVoteHeight uint // Last called commitVoteBlock() or saveCommitVoteBlock() on.
|
lastCommitVoteHeight uint // Last called commitVoteBlock() or saveCommitVoteBlock() on.
|
||||||
|
|
||||||
|
evsw *events.EventSwitch
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConsensusState(state *sm.State, blockStore *bc.BlockStore, mempoolReactor *mempl.MempoolReactor) *ConsensusState {
|
func NewConsensusState(state *sm.State, blockStore *bc.BlockStore, mempoolReactor *mempl.MempoolReactor) *ConsensusState {
|
||||||
@ -437,6 +440,8 @@ ACTION_LOOP:
|
|||||||
if cs.TryFinalizeCommit(rs.Height) {
|
if cs.TryFinalizeCommit(rs.Height) {
|
||||||
// Now at new height
|
// Now at new height
|
||||||
// cs.Step is at RoundStepNewHeight or RoundStepNewRound.
|
// cs.Step is at RoundStepNewHeight or RoundStepNewRound.
|
||||||
|
// newblock event!
|
||||||
|
cs.evsw.FireEvent("newblock", cs.state.LastBlockHash)
|
||||||
scheduleNextAction()
|
scheduleNextAction()
|
||||||
continue ACTION_LOOP
|
continue ACTION_LOOP
|
||||||
} else {
|
} else {
|
||||||
@ -1107,6 +1112,11 @@ func (cs *ConsensusState) saveCommitVoteBlock(block *types.Block, blockParts *ty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// implements events.Eventable
|
||||||
|
func (cs *ConsensusState) SetEventSwitch(evsw *events.EventSwitch) {
|
||||||
|
cs.evsw = evsw
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// total duration of given round
|
// total duration of given round
|
||||||
|
@ -4,10 +4,6 @@ import (
|
|||||||
"github.com/tendermint/tendermint/rpc"
|
"github.com/tendermint/tendermint/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: support Call && GetStorage.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var Routes = map[string]*rpc.RPCFunc{
|
var Routes = map[string]*rpc.RPCFunc{
|
||||||
"status": rpc.NewRPCFunc(Status, []string{}),
|
"status": rpc.NewRPCFunc(Status, []string{}),
|
||||||
"net_info": rpc.NewRPCFunc(NetInfo, []string{}),
|
"net_info": rpc.NewRPCFunc(NetInfo, []string{}),
|
||||||
|
@ -26,7 +26,7 @@ func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc) {
|
|||||||
func RegisterEventsHandler(mux *http.ServeMux, evsw *events.EventSwitch) {
|
func RegisterEventsHandler(mux *http.ServeMux, evsw *events.EventSwitch) {
|
||||||
// websocket endpoint
|
// websocket endpoint
|
||||||
w := NewWebsocketManager(evsw)
|
w := NewWebsocketManager(evsw)
|
||||||
http.HandleFunc("/events", w.websocketHandler) // websocket.Handler(w.eventsHandler))
|
mux.HandleFunc("/events", w.websocketHandler) // websocket.Handler(w.eventsHandler))
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
@ -191,14 +191,20 @@ func _jsonStringToArg(ty reflect.Type, arg string) (reflect.Value, error) {
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// rpc.websocket
|
// rpc.websocket
|
||||||
|
|
||||||
|
const (
|
||||||
|
WSConnectionReaperSeconds = 5
|
||||||
|
MaxFailedSendsSeconds = 10
|
||||||
|
WriteChanBufferSize = 10
|
||||||
|
)
|
||||||
|
|
||||||
// for requests coming in
|
// for requests coming in
|
||||||
type WsRequest struct {
|
type WSRequest struct {
|
||||||
Type string // subscribe or unsubscribe
|
Type string // subscribe or unsubscribe
|
||||||
Event string
|
Event string
|
||||||
}
|
}
|
||||||
|
|
||||||
// for responses going out
|
// for responses going out
|
||||||
type WsResponse struct {
|
type WSResponse struct {
|
||||||
Event string
|
Event string
|
||||||
Data interface{}
|
Data interface{}
|
||||||
Error string
|
Error string
|
||||||
@ -209,7 +215,7 @@ type WsResponse struct {
|
|||||||
type Connection struct {
|
type Connection struct {
|
||||||
id string
|
id string
|
||||||
wsCon *websocket.Conn
|
wsCon *websocket.Conn
|
||||||
writeChan chan WsResponse
|
writeChan chan WSResponse
|
||||||
quitChan chan struct{}
|
quitChan chan struct{}
|
||||||
failedSends uint
|
failedSends uint
|
||||||
}
|
}
|
||||||
@ -219,7 +225,7 @@ func NewConnection(con *websocket.Conn) *Connection {
|
|||||||
return &Connection{
|
return &Connection{
|
||||||
id: con.RemoteAddr().String(),
|
id: con.RemoteAddr().String(),
|
||||||
wsCon: con,
|
wsCon: con,
|
||||||
writeChan: make(chan WsResponse, WriteChanBuffer), // buffered. we keep track when its full
|
writeChan: make(chan WSResponse, WriteChanBufferSize), // buffered. we keep track when its full
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,15 +282,9 @@ func (w *WebsocketManager) handleWebsocket(con *websocket.Conn) {
|
|||||||
w.write(c)
|
w.write(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
WsConnectionReaperSeconds = 5
|
|
||||||
MaxFailedSendsSeconds = 10
|
|
||||||
WriteChanBuffer = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
// read from the socket and subscribe to or unsubscribe from events
|
// read from the socket and subscribe to or unsubscribe from events
|
||||||
func (w *WebsocketManager) read(con *Connection) {
|
func (w *WebsocketManager) read(con *Connection) {
|
||||||
reaper := time.Tick(time.Second * WsConnectionReaperSeconds)
|
reaper := time.Tick(time.Second * WSConnectionReaperSeconds)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-reaper:
|
case <-reaper:
|
||||||
@ -302,17 +302,17 @@ func (w *WebsocketManager) read(con *Connection) {
|
|||||||
// so kill the connection
|
// so kill the connection
|
||||||
con.quitChan <- struct{}{}
|
con.quitChan <- struct{}{}
|
||||||
}
|
}
|
||||||
var req WsRequest
|
var req WSRequest
|
||||||
err = json.Unmarshal(in, &req)
|
err = json.Unmarshal(in, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errStr := fmt.Sprintf("Error unmarshaling data: %s", err.Error())
|
errStr := fmt.Sprintf("Error unmarshaling data: %s", err.Error())
|
||||||
con.writeChan <- WsResponse{Error: errStr}
|
con.writeChan <- WSResponse{Error: errStr}
|
||||||
}
|
}
|
||||||
switch req.Type {
|
switch req.Type {
|
||||||
case "subscribe":
|
case "subscribe":
|
||||||
log.Info("New event subscription", "con id", con.id, "event", req.Event)
|
log.Info("New event subscription", "con id", con.id, "event", req.Event)
|
||||||
w.ew.AddListenerForEvent(con.id, req.Event, func(msg interface{}) {
|
w.ew.AddListenerForEvent(con.id, req.Event, func(msg interface{}) {
|
||||||
resp := WsResponse{
|
resp := WSResponse{
|
||||||
Event: req.Event,
|
Event: req.Event,
|
||||||
Data: msg,
|
Data: msg,
|
||||||
}
|
}
|
||||||
@ -334,7 +334,7 @@ func (w *WebsocketManager) read(con *Connection) {
|
|||||||
w.ew.RemoveListener(con.id)
|
w.ew.RemoveListener(con.id)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
con.writeChan <- WsResponse{Error: "Unknown request type: " + req.Type}
|
con.writeChan <- WSResponse{Error: "Unknown request type: " + req.Type}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -350,7 +350,7 @@ func (w *WebsocketManager) write(con *Connection) {
|
|||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
binary.WriteJSON(msg, buf, n, err)
|
binary.WriteJSON(msg, buf, n, err)
|
||||||
if *err != nil {
|
if *err != nil {
|
||||||
log.Error("Failed to write JSON WsResponse", "error", err)
|
log.Error("Failed to write JSON WSResponse", "error", err)
|
||||||
} else {
|
} else {
|
||||||
//websocket.Message.Send(con.wsCon, buf.Bytes())
|
//websocket.Message.Send(con.wsCon, buf.Bytes())
|
||||||
if err := con.wsCon.WriteMessage(websocket.TextMessage, buf.Bytes()); err != nil {
|
if err := con.wsCon.WriteMessage(websocket.TextMessage, buf.Bytes()); err != nil {
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/tendermint/tendermint/binary"
|
"github.com/tendermint/tendermint/binary"
|
||||||
. "github.com/tendermint/tendermint/common"
|
. "github.com/tendermint/tendermint/common"
|
||||||
"github.com/tendermint/tendermint/events"
|
"github.com/tendermint/tendermint/events"
|
||||||
|
"github.com/tendermint/tendermint/alert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StartHTTPServer(listenAddr string, funcMap map[string]*RPCFunc, evsw *events.EventSwitch) {
|
func StartHTTPServer(listenAddr string, funcMap map[string]*RPCFunc, evsw *events.EventSwitch) {
|
||||||
|
@ -85,11 +85,10 @@ func TestWSConnect(t *testing.T) {
|
|||||||
dialer := websocket.DefaultDialer
|
dialer := websocket.DefaultDialer
|
||||||
rHeader := http.Header{}
|
rHeader := http.Header{}
|
||||||
_, r, err := dialer.Dial(websocketAddr, rHeader)
|
_, r, err := dialer.Dial(websocketAddr, rHeader)
|
||||||
|
fmt.Println("respoinse:", r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
fmt.Println("respoinse:", r)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWSSubscribe(t *testing.T) {
|
func TestWSSubscribe(t *testing.T) {
|
||||||
@ -99,16 +98,13 @@ func TestWSSubscribe(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = con.WriteJSON(rpc.WsRequest{
|
err = con.WriteJSON(rpc.WSRequest{
|
||||||
Type: "subscribe",
|
Type: "subscribe",
|
||||||
Event: "newblock",
|
Event: "newblock",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
typ, p, err := con.ReadMessage()
|
typ, p, err := con.ReadMessage()
|
||||||
fmt.Println("RESPONSE:", typ, string(p), err)
|
fmt.Println("RESPONSE:", typ, string(p), err)
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user