// jsonRPCRead is the RPC wrapper around the jsonRead function to handle reading // and responding to RPC messages. func jsonRPCRead(w http.ResponseWriter, r *http.Request, s *rpcServer) { if atomic.LoadInt32(&s.shutdown) != 0 { return } body, err := btcjson.GetRaw(r.Body) if err != nil { rpcsLog.Errorf("Error getting json message: %v", err) return } var reply btcjson.Reply cmd, jsonErr := parseCmd(body) if cmd != nil { // Unmarshaling at least a valid JSON-RPC message succeeded. // Use the provided id for errors. id := cmd.Id() reply.Id = &id } if jsonErr != nil { reply.Error = jsonErr } else { reply = standardCmdReply(cmd, s) } rpcsLog.Tracef("reply: %v", reply) msg, err := btcjson.MarshallAndSend(reply, w) if err != nil { rpcsLog.Errorf(msg) return } rpcsLog.Debugf(msg) }
// respondToAnyCmd checks that a parsed command is a standard or // extension JSON-RPC command and runs the proper handler to reply to // the command. Any and all responses are sent to the wallet from // this function. func respondToAnyCmd(cmd btcjson.Cmd, s *rpcServer, wallet walletChan) { // Lookup the websocket extension for the command and if it doesn't // exist fallback to handling the command as a standard command. wsHandler, ok := wsHandlers[cmd.Method()] if !ok { reply := standardCmdReply(cmd, s) mreply, _ := json.Marshal(reply) wallet <- mreply return } // Call the appropriate handler which responds unless there was an // error in which case the error is marshalled and sent here. if err := wsHandler(s, cmd, wallet); err != nil { var reply btcjson.Reply jsonErr, ok := err.(btcjson.Error) if ok { reply.Error = &jsonErr mreply, _ := json.Marshal(reply) wallet <- mreply return } // In the case where we did not have a btcjson // error to begin with, make a new one to send, // but this really should not happen. jsonErr = btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } reply.Error = &jsonErr mreply, _ := json.Marshal(reply) wallet <- mreply } }
// websocketJSONHandler parses and handles a marshalled json message, // sending the marshalled reply to a wallet notification channel. func (s *rpcServer) websocketJSONHandler(wallet walletChan, msg []byte) { s.wg.Add(1) defer s.wg.Done() cmd, jsonErr := parseCmd(msg) if jsonErr != nil { var reply btcjson.Reply if cmd != nil { // Unmarshaling at least a valid JSON-RPC message succeeded. // Use the provided id for errors. id := cmd.Id() reply.Id = &id } reply.Error = jsonErr mreply, _ := json.Marshal(reply) wallet <- mreply return } respondToAnyCmd(cmd, s, wallet) }
// websocketJSONHandler parses and handles a marshalled json message, // sending the marshalled reply to a wallet notification channel. func (s *rpcServer) websocketJSONHandler(r chan *btcjson.Reply, c handlerChans, msg string) error { var resp *btcjson.Reply cmd, jsonErr := parseCmd([]byte(msg)) if jsonErr != nil { resp = &btcjson.Reply{} if cmd != nil { // Unmarshaling at least a valid JSON-RPC message succeeded. // Use the provided id for errors. Requests with no IDs // should be ignored. id := cmd.Id() if id == nil { return nil } resp.Id = &id } resp.Error = jsonErr } else { // The first command must be the "authenticate" command if the // connection is not already authenticated. s.ws.Lock() rc := s.ws.connections[c.n] if _, ok := cmd.(*btcws.AuthenticateCmd); ok { // Validate the provided credentials. err := websocketAuthenticate(cmd, rc, s.authsha[:]) if err != nil { s.ws.Unlock() return err } // Generate an empty response to send for the successful // authentication. id := cmd.Id() resp = &btcjson.Reply{ Id: &id, Result: nil, Error: nil, } } else if !rc.authenticated { rpcsLog.Warnf("Unauthenticated websocket message " + "received") s.ws.Unlock() return ErrBadAuth } s.ws.Unlock() } // Find and run handler in new goroutine. go func() { if resp == nil { resp = respondToAnyCmd(cmd, s, c) } select { case <-c.disconnected: return default: r <- resp } }() return nil }