Add a websocket session RPC.

This commit is contained in:
Josh Rickmar 2015-09-15 14:03:48 -04:00
parent 6ac46f9e5f
commit 3c9d18d641
5 changed files with 68 additions and 4 deletions

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014 The btcsuite developers
// Copyright (c) 2014-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -56,6 +56,15 @@ func NewNotifyNewTransactionsCmd(verbose *bool) *NotifyNewTransactionsCmd {
}
}
// SessionCmd defines the session JSON-RPC command.
type SessionCmd struct{}
// NewSessionCmd returns a new instance which can be used to issue a session
// JSON-RPC command.
func NewSessionCmd() *SessionCmd {
return &SessionCmd{}
}
// StopNotifyNewTransactionsCmd defines the stopnotifynewtransactions JSON-RPC command.
type StopNotifyNewTransactionsCmd struct{}
@ -158,6 +167,7 @@ func init() {
MustRegisterCmd("notifynewtransactions", (*NotifyNewTransactionsCmd)(nil), flags)
MustRegisterCmd("notifyreceived", (*NotifyReceivedCmd)(nil), flags)
MustRegisterCmd("notifyspent", (*NotifySpentCmd)(nil), flags)
MustRegisterCmd("session", (*SessionCmd)(nil), flags)
MustRegisterCmd("stopnotifyblocks", (*StopNotifyBlocksCmd)(nil), flags)
MustRegisterCmd("stopnotifynewtransactions", (*StopNotifyNewTransactionsCmd)(nil), flags)
MustRegisterCmd("stopnotifyspent", (*StopNotifySpentCmd)(nil), flags)

View file

@ -0,0 +1,10 @@
// Copyright (c) 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package btcjson
// SessionResult models the data from the session command.
type SessionResult struct {
SessionID uint64 `json:"sessionid"`
}

View file

@ -685,6 +685,7 @@ user. Click the method name for further details such as parameter and return in
|8|[rescan](#rescan)|Rescan block chain for transactions to addresses and spent transaction outpoints.|[recvtx](#recvtx), [redeemingtx](#redeemingtx), [rescanprogress](#rescanprogress), and [rescanfinished](#rescanfinished) |
|9|[notifynewtransactions](#notifynewtransactions)|Send notifications for all new transactions as they are accepted into the mempool.|[txaccepted](#txaccepted) or [txacceptedverbose](#txacceptedverbose)|
|10|[stopnotifynewtransactions](#stopnotifynewtransactions)|Stop sending either a txaccepted or a txacceptedverbose notification when a new transaction is accepted into the mempool.|None|
|11|[session](#session)|Return details regarding a websocket client's current connection.|None|
<a name="WSExtMethodDetails" />
**7.2 Method Details**<br />
@ -815,6 +816,20 @@ user. Click the method name for further details such as parameter and return in
|Returns|Nothing|
[Return to Overview](#WSExtMethodOverview)<br />
***
<a name="session"/>
| | |
|---|---|
|Method|session|
|Notifications|None|
|Parameters|None|
|Description|Return a JSON object with details regarding a websocket client's current connection to the RPC server. This currently only includes the session ID, a random unsigned 64-bit integer that is created for each newly connected client. Session IDs may be used to verify that the current connection was not lost and subsequently reestablished.|
|Returns|`{ (json object)`<br />&nbsp;&nbsp;`"sessionid": n (numeric) the session ID`<br />`}`|
|Example Return|`{`<br />&nbsp;&nbsp;`"sessionid": 67089679842`<br />`}`|
[Return to Overview](#WSExtMethodOverview)<br />
<a name="Notifications" />
### 8. Notifications (Websocket-specific)

View file

@ -525,6 +525,10 @@ var helpDescsEnUS = map[string]string{
// -------- Websocket-specific help --------
// Session help.
"session--synopsis": "Return details regarding a websocket client's current connection session.",
"sessionresult-sessionid": "The unique session ID for a client's websocket connection.",
// NotifyBlocksCmd help.
"notifyblocks--synopsis": "Request notifications for whenever a block is connected or disconnected from the main (best) chain.",
@ -616,6 +620,7 @@ var rpcResultTypes = map[string][]interface{}{
"verifymessage": []interface{}{(*bool)(nil)},
// Websocket commands.
"session": []interface{}{(*btcjson.SessionResult)(nil)},
"notifyblocks": nil,
"stopnotifyblocks": nil,
"notifynewtransactions": nil,

View file

@ -54,6 +54,7 @@ var wsHandlersBeforeInit = map[string]wsCommandHandler{
"notifynewtransactions": handleNotifyNewTransactions,
"notifyreceived": handleNotifyReceived,
"notifyspent": handleNotifySpent,
"session": handleSession,
"stopnotifyblocks": handleStopNotifyBlocks,
"stopnotifynewtransactions": handleStopNotifyNewTransactions,
"stopnotifyspent": handleStopNotifySpent,
@ -94,7 +95,12 @@ func (s *rpcServer) WebsocketHandler(conn *websocket.Conn, remoteAddr string,
// Create a new websocket client to handle the new websocket connection
// and wait for it to shutdown. Once it has shutdown (and hence
// disconnected), remove it and any notifications it registered for.
client := newWebsocketClient(s, conn, remoteAddr, authenticated, isAdmin)
client, err := newWebsocketClient(s, conn, remoteAddr, authenticated, isAdmin)
if err != nil {
rpcsLog.Errorf("Failed to serve client %s: %v", remoteAddr, err)
conn.Close()
return
}
s.ntfnMgr.AddClient(client)
client.Start()
client.WaitForShutdown()
@ -869,6 +875,11 @@ type wsClient struct {
// false means its access is only to the limited set of RPC calls.
isAdmin bool
// sessionID is a random ID generated for each client when connected.
// These IDs may be queried by a client using the session RPC. A change
// to the session ID indicates that the client reconnected.
sessionID uint64
// verboseTxUpdates specifies whether a client has requested verbose
// information about all new transactions.
verboseTxUpdates bool
@ -1383,13 +1394,19 @@ func (c *wsClient) WaitForShutdown() {
// incoming and outgoing messages in separate goroutines complete with queueing
// and asynchrous handling for long-running operations.
func newWebsocketClient(server *rpcServer, conn *websocket.Conn,
remoteAddr string, authenticated bool, isAdmin bool) *wsClient {
remoteAddr string, authenticated bool, isAdmin bool) (*wsClient, error) {
return &wsClient{
sessionID, err := wire.RandomUint64()
if err != nil {
return nil, err
}
client := &wsClient{
conn: conn,
addr: remoteAddr,
authenticated: authenticated,
isAdmin: isAdmin,
sessionID: sessionID,
server: server,
addrRequests: make(map[string]struct{}),
spentRequests: make(map[wire.OutPoint]struct{}),
@ -1398,6 +1415,7 @@ func newWebsocketClient(server *rpcServer, conn *websocket.Conn,
sendChan: make(chan wsResponse, websocketSendBufferSize),
quit: make(chan struct{}),
}
return client, nil
}
// handleWebsocketHelp implements the help command for websocket connections.
@ -1454,6 +1472,12 @@ func handleNotifyBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) {
return nil, nil
}
// handleSession implements the session command extension for websocket
// connections.
func handleSession(wsc *wsClient, icmd interface{}) (interface{}, error) {
return &btcjson.SessionResult{SessionID: wsc.sessionID}, nil
}
// handleStopNotifyBlocks implements the stopnotifyblocks command extension for
// websocket connections.
func handleStopNotifyBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) {