diff --git a/btcjson/chainsvrwscmds.go b/btcjson/chainsvrwscmds.go
index ba8a5391..007dd83b 100644
--- a/btcjson/chainsvrwscmds.go
+++ b/btcjson/chainsvrwscmds.go
@@ -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)
diff --git a/btcjson/chainsvrwsresults.go b/btcjson/chainsvrwsresults.go
new file mode 100644
index 00000000..12968496
--- /dev/null
+++ b/btcjson/chainsvrwsresults.go
@@ -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"`
+}
diff --git a/docs/json_rpc_api.md b/docs/json_rpc_api.md
index 56ea1785..f0776cd3 100644
--- a/docs/json_rpc_api.md
+++ b/docs/json_rpc_api.md
@@ -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|
**7.2 Method Details**
@@ -815,6 +816,20 @@ user. Click the method name for further details such as parameter and return in
|Returns|Nothing|
[Return to Overview](#WSExtMethodOverview)
+***
+
+
+
+| | |
+|---|---|
+|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)`
`"sessionid": n (numeric) the session ID`
`}`|
+|Example Return|`{`
`"sessionid": 67089679842`
`}`|
+[Return to Overview](#WSExtMethodOverview)
+
### 8. Notifications (Websocket-specific)
diff --git a/rpcserverhelp.go b/rpcserverhelp.go
index bf8c7519..53256379 100644
--- a/rpcserverhelp.go
+++ b/rpcserverhelp.go
@@ -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,
diff --git a/rpcwebsocket.go b/rpcwebsocket.go
index 725314c7..04c07889 100644
--- a/rpcwebsocket.go
+++ b/rpcwebsocket.go
@@ -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) {