OSDN Git Service

add API struct (#468)
authorYongfeng LI <wliyongfeng@gmail.com>
Fri, 23 Mar 2018 07:42:26 +0000 (15:42 +0800)
committerGitHub <noreply@github.com>
Fri, 23 Mar 2018 07:42:26 +0000 (15:42 +0800)
* rename rpc_reactor to api

* create API struct

* move server to API

* move buildHandler to API

* refactor initServer

* remove BlockchainReactor Handler

blockchain/api.go [new file with mode: 0644]
blockchain/reactor.go
blockchain/rpc_reactor.go [deleted file]
node/node.go

diff --git a/blockchain/api.go b/blockchain/api.go
new file mode 100644 (file)
index 0000000..6438e74
--- /dev/null
@@ -0,0 +1,246 @@
+package blockchain
+
+import (
+       "crypto/tls"
+       "net"
+       "net/http"
+       "sync"
+       "time"
+
+       "github.com/kr/secureheader"
+       log "github.com/sirupsen/logrus"
+       cmn "github.com/tendermint/tmlibs/common"
+
+       "github.com/bytom/blockchain/accesstoken"
+       cfg "github.com/bytom/config"
+       "github.com/bytom/dashboard"
+       "github.com/bytom/errors"
+       "github.com/bytom/net/http/authn"
+       "github.com/bytom/net/http/httpjson"
+       "github.com/bytom/net/http/static"
+)
+
+var (
+       errNotAuthenticated = errors.New("not authenticated")
+       httpReadTimeout     = 2 * time.Minute
+       httpWriteTimeout    = time.Hour
+)
+
+type waitHandler struct {
+       h  http.Handler
+       wg sync.WaitGroup
+}
+
+func (wh *waitHandler) Set(h http.Handler) {
+       wh.h = h
+       wh.wg.Done()
+}
+
+func (wh *waitHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+       wh.wg.Wait()
+       wh.h.ServeHTTP(w, req)
+}
+
+type API struct {
+       bcr     *BlockchainReactor
+       server  *http.Server
+       handler http.Handler
+}
+
+func (a *API) initServer(config *cfg.Config) {
+       // The waitHandler accepts incoming requests, but blocks until its underlying
+       // handler is set, when the second phase is complete.
+       var coreHandler waitHandler
+       coreHandler.wg.Add(1)
+       mux := http.NewServeMux()
+       mux.Handle("/", &coreHandler)
+
+       var handler http.Handler = mux
+
+       if config.Auth.Disable == false {
+               handler = AuthHandler(handler, a.bcr.wallet.Tokens)
+       }
+       handler = RedirectHandler(handler)
+
+       secureheader.DefaultConfig.PermitClearLoopback = true
+       secureheader.DefaultConfig.HTTPSRedirect = false
+       secureheader.DefaultConfig.Next = handler
+
+       a.server = &http.Server{
+               // Note: we should not set TLSConfig here;
+               // we took care of TLS with the listener in maybeUseTLS.
+               Handler:      secureheader.DefaultConfig,
+               ReadTimeout:  httpReadTimeout,
+               WriteTimeout: httpWriteTimeout,
+               // Disable HTTP/2 for now until the Go implementation is more stable.
+               // https://github.com/golang/go/issues/16450
+               // https://github.com/golang/go/issues/17071
+               TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
+       }
+
+       coreHandler.Set(a)
+}
+
+func (a *API) StartServer(address string) {
+       log.WithField("api address:", address).Info("Rpc listen")
+       listener, err := net.Listen("tcp", address)
+       if err != nil {
+               cmn.Exit(cmn.Fmt("Failed to register tcp port: %v", err))
+       }
+
+       // The `Serve` call has to happen in its own goroutine because
+       // it's blocking and we need to proceed to the rest of the core setup after
+       // we call it.
+       go func() {
+               if err := a.server.Serve(listener); err != nil {
+                       log.WithField("error", errors.Wrap(err, "Serve")).Error("Rpc server")
+               }
+       }()
+}
+
+func NewAPI(bcr *BlockchainReactor, config *cfg.Config) *API {
+       api := &API{
+               bcr: bcr,
+       }
+       api.buildHandler()
+       api.initServer(config)
+
+       return api
+}
+
+func (a *API) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+       a.handler.ServeHTTP(rw, req)
+}
+
+// buildHandler is in charge of all the rpc handling.
+func (a *API) buildHandler() {
+       m := http.NewServeMux()
+       if a.bcr.wallet != nil && a.bcr.wallet.AccountMgr != nil && a.bcr.wallet.AssetReg != nil {
+               m.Handle("/create-account", jsonHandler(a.bcr.createAccount))
+               m.Handle("/update-account-tags", jsonHandler(a.bcr.updateAccountTags))
+               m.Handle("/create-account-receiver", jsonHandler(a.bcr.createAccountReceiver))
+               m.Handle("/list-accounts", jsonHandler(a.bcr.listAccounts))
+               m.Handle("/list-addresses", jsonHandler(a.bcr.listAddresses))
+               m.Handle("/delete-account", jsonHandler(a.bcr.deleteAccount))
+               m.Handle("/validate-address", jsonHandler(a.bcr.validateAddress))
+
+               m.Handle("/create-asset", jsonHandler(a.bcr.createAsset))
+               m.Handle("/update-asset-alias", jsonHandler(a.bcr.updateAssetAlias))
+               m.Handle("/update-asset-tags", jsonHandler(a.bcr.updateAssetTags))
+               m.Handle("/list-assets", jsonHandler(a.bcr.listAssets))
+
+               m.Handle("/create-key", jsonHandler(a.bcr.pseudohsmCreateKey))
+               m.Handle("/list-keys", jsonHandler(a.bcr.pseudohsmListKeys))
+               m.Handle("/delete-key", jsonHandler(a.bcr.pseudohsmDeleteKey))
+               m.Handle("/reset-key-password", jsonHandler(a.bcr.pseudohsmResetPassword))
+
+               m.Handle("/get-transaction", jsonHandler(a.bcr.getTransaction))
+               m.Handle("/list-transactions", jsonHandler(a.bcr.listTransactions))
+               m.Handle("/list-balances", jsonHandler(a.bcr.listBalances))
+       } else {
+               log.Warn("Please enable wallet")
+       }
+
+       m.Handle("/", alwaysError(errors.New("not Found")))
+
+       m.Handle("/build-transaction", jsonHandler(a.bcr.build))
+       m.Handle("/sign-transaction", jsonHandler(a.bcr.pseudohsmSignTemplates))
+       m.Handle("/submit-transaction", jsonHandler(a.bcr.submit))
+       m.Handle("/sign-submit-transaction", jsonHandler(a.bcr.signSubmit))
+
+       m.Handle("/create-transaction-feed", jsonHandler(a.bcr.createTxFeed))
+       m.Handle("/get-transaction-feed", jsonHandler(a.bcr.getTxFeed))
+       m.Handle("/update-transaction-feed", jsonHandler(a.bcr.updateTxFeed))
+       m.Handle("/delete-transaction-feed", jsonHandler(a.bcr.deleteTxFeed))
+       m.Handle("/list-transaction-feeds", jsonHandler(a.bcr.listTxFeeds))
+       m.Handle("/list-unspent-outputs", jsonHandler(a.bcr.listUnspentOutputs))
+       m.Handle("/info", jsonHandler(a.bcr.info))
+
+       m.Handle("/create-access-token", jsonHandler(a.bcr.createAccessToken))
+       m.Handle("/list-access-tokens", jsonHandler(a.bcr.listAccessTokens))
+       m.Handle("/delete-access-token", jsonHandler(a.bcr.deleteAccessToken))
+       m.Handle("/check-access-token", jsonHandler(a.bcr.checkAccessToken))
+
+       m.Handle("/block-hash", jsonHandler(a.bcr.getBestBlockHash))
+
+       m.Handle("/export-private-key", jsonHandler(a.bcr.walletExportKey))
+       m.Handle("/import-private-key", jsonHandler(a.bcr.walletImportKey))
+       m.Handle("/import-key-progress", jsonHandler(a.bcr.keyImportProgress))
+
+       m.Handle("/get-block-header-by-hash", jsonHandler(a.bcr.getBlockHeaderByHash))
+       m.Handle("/get-block-header-by-height", jsonHandler(a.bcr.getBlockHeaderByHeight))
+       m.Handle("/get-block", jsonHandler(a.bcr.getBlock))
+       m.Handle("/get-block-count", jsonHandler(a.bcr.getBlockCount))
+       m.Handle("/get-block-transactions-count-by-hash", jsonHandler(a.bcr.getBlockTransactionsCountByHash))
+       m.Handle("/get-block-transactions-count-by-height", jsonHandler(a.bcr.getBlockTransactionsCountByHeight))
+
+       m.Handle("/net-info", jsonHandler(a.bcr.getNetInfo))
+
+       m.Handle("/is-mining", jsonHandler(a.bcr.isMining))
+       m.Handle("/gas-rate", jsonHandler(a.bcr.gasRate))
+       m.Handle("/getwork", jsonHandler(a.bcr.getWork))
+       m.Handle("/submitwork", jsonHandler(a.bcr.submitWork))
+
+       latencyHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+               if l := latency(m, req); l != nil {
+                       defer l.RecordSince(time.Now())
+               }
+               m.ServeHTTP(w, req)
+       })
+       handler := maxBytes(latencyHandler) // TODO(tessr): consider moving this to non-core specific mux
+       handler = webAssetsHandler(handler)
+
+       a.handler = handler
+}
+
+// json Handler
+func jsonHandler(f interface{}) http.Handler {
+       h, err := httpjson.Handler(f, errorFormatter.Write)
+       if err != nil {
+               panic(err)
+       }
+       return h
+}
+
+// error Handler
+func alwaysError(err error) http.Handler {
+       return jsonHandler(func() error { return err })
+}
+
+func webAssetsHandler(next http.Handler) http.Handler {
+       mux := http.NewServeMux()
+       mux.Handle("/dashboard/", http.StripPrefix("/dashboard/", static.Handler{
+               Assets:  dashboard.Files,
+               Default: "index.html",
+       }))
+       mux.Handle("/", next)
+
+       return mux
+}
+
+//AuthHandler access token auth Handler
+func AuthHandler(handler http.Handler, accessTokens *accesstoken.CredentialStore) http.Handler {
+       authenticator := authn.NewAPI(accessTokens)
+
+       return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+               // TODO(tessr): check that this path exists; return early if this path isn't legit
+               req, err := authenticator.Authenticate(req)
+               if err != nil {
+                       log.WithField("error", errors.Wrap(err, "Serve")).Error("Authenticate fail")
+                       err = errors.Sub(errNotAuthenticated, err)
+                       errorFormatter.Write(req.Context(), rw, err)
+                       return
+               }
+               handler.ServeHTTP(rw, req)
+       })
+}
+
+func RedirectHandler(next http.Handler) http.Handler {
+       return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+               if req.URL.Path == "/" {
+                       http.Redirect(w, req, "/dashboard/", http.StatusFound)
+                       return
+               }
+               next.ServeHTTP(w, req)
+       })
+}
index fe4c7a1..a4b4bd4 100755 (executable)
@@ -67,7 +67,6 @@ type BlockchainReactor struct {
        mining        *cpuminer.CPUMiner
        miningPool    *miningpool.MiningPool
        sw            *p2p.Switch
-       Handler       http.Handler
        evsw          types.EventSwitch
        newBlockCh    chan *bc.Hash
        miningEnable  bool
@@ -124,7 +123,6 @@ func NewBlockchainReactor(chain *protocol.Chain, txPool *protocol.TxPool, sw *p2
 // OnStart implements BaseService
 func (bcr *BlockchainReactor) OnStart() error {
        bcr.BaseReactor.OnStart()
-       bcr.BuildHandler()
 
        if bcr.miningEnable {
                bcr.mining.Start()
diff --git a/blockchain/rpc_reactor.go b/blockchain/rpc_reactor.go
deleted file mode 100644 (file)
index d576e0f..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-package blockchain
-
-import (
-       "net/http"
-       "time"
-
-       log "github.com/sirupsen/logrus"
-
-       "github.com/bytom/blockchain/accesstoken"
-       "github.com/bytom/dashboard"
-       "github.com/bytom/errors"
-       "github.com/bytom/net/http/authn"
-       "github.com/bytom/net/http/httpjson"
-       "github.com/bytom/net/http/static"
-)
-
-var (
-       errNotAuthenticated = errors.New("not authenticated")
-)
-
-// json Handler
-func jsonHandler(f interface{}) http.Handler {
-       h, err := httpjson.Handler(f, errorFormatter.Write)
-       if err != nil {
-               panic(err)
-       }
-       return h
-}
-
-// error Handler
-func alwaysError(err error) http.Handler {
-       return jsonHandler(func() error { return err })
-}
-
-func webAssetsHandler(next http.Handler) http.Handler {
-       mux := http.NewServeMux()
-       mux.Handle("/dashboard/", http.StripPrefix("/dashboard/", static.Handler{
-               Assets:  dashboard.Files,
-               Default: "index.html",
-       }))
-       mux.Handle("/", next)
-
-       return mux
-}
-
-// BuildHandler is in charge of all the rpc handling.
-func (bcr *BlockchainReactor) BuildHandler() {
-       m := http.NewServeMux()
-       if bcr.wallet != nil && bcr.wallet.AccountMgr != nil && bcr.wallet.AssetReg != nil {
-               m.Handle("/create-account", jsonHandler(bcr.createAccount))
-               m.Handle("/update-account-tags", jsonHandler(bcr.updateAccountTags))
-               m.Handle("/create-account-receiver", jsonHandler(bcr.createAccountReceiver))
-               m.Handle("/list-accounts", jsonHandler(bcr.listAccounts))
-               m.Handle("/list-addresses", jsonHandler(bcr.listAddresses))
-               m.Handle("/delete-account", jsonHandler(bcr.deleteAccount))
-               m.Handle("/validate-address", jsonHandler(bcr.validateAddress))
-
-               m.Handle("/create-asset", jsonHandler(bcr.createAsset))
-               m.Handle("/update-asset-alias", jsonHandler(bcr.updateAssetAlias))
-               m.Handle("/update-asset-tags", jsonHandler(bcr.updateAssetTags))
-               m.Handle("/list-assets", jsonHandler(bcr.listAssets))
-
-               m.Handle("/create-key", jsonHandler(bcr.pseudohsmCreateKey))
-               m.Handle("/list-keys", jsonHandler(bcr.pseudohsmListKeys))
-               m.Handle("/delete-key", jsonHandler(bcr.pseudohsmDeleteKey))
-               m.Handle("/reset-key-password", jsonHandler(bcr.pseudohsmResetPassword))
-
-               m.Handle("/get-transaction", jsonHandler(bcr.getTransaction))
-               m.Handle("/list-transactions", jsonHandler(bcr.listTransactions))
-               m.Handle("/list-balances", jsonHandler(bcr.listBalances))
-       } else {
-               log.Warn("Please enable wallet")
-       }
-
-       m.Handle("/", alwaysError(errors.New("not Found")))
-
-       m.Handle("/build-transaction", jsonHandler(bcr.build))
-       m.Handle("/sign-transaction", jsonHandler(bcr.pseudohsmSignTemplates))
-       m.Handle("/submit-transaction", jsonHandler(bcr.submit))
-       m.Handle("/sign-submit-transaction", jsonHandler(bcr.signSubmit))
-
-       m.Handle("/create-transaction-feed", jsonHandler(bcr.createTxFeed))
-       m.Handle("/get-transaction-feed", jsonHandler(bcr.getTxFeed))
-       m.Handle("/update-transaction-feed", jsonHandler(bcr.updateTxFeed))
-       m.Handle("/delete-transaction-feed", jsonHandler(bcr.deleteTxFeed))
-       m.Handle("/list-transaction-feeds", jsonHandler(bcr.listTxFeeds))
-       m.Handle("/list-unspent-outputs", jsonHandler(bcr.listUnspentOutputs))
-       m.Handle("/info", jsonHandler(bcr.info))
-
-       m.Handle("/create-access-token", jsonHandler(bcr.createAccessToken))
-       m.Handle("/list-access-tokens", jsonHandler(bcr.listAccessTokens))
-       m.Handle("/delete-access-token", jsonHandler(bcr.deleteAccessToken))
-       m.Handle("/check-access-token", jsonHandler(bcr.checkAccessToken))
-
-       m.Handle("/block-hash", jsonHandler(bcr.getBestBlockHash))
-
-       m.Handle("/export-private-key", jsonHandler(bcr.walletExportKey))
-       m.Handle("/import-private-key", jsonHandler(bcr.walletImportKey))
-       m.Handle("/import-key-progress", jsonHandler(bcr.keyImportProgress))
-
-       m.Handle("/get-block-header-by-hash", jsonHandler(bcr.getBlockHeaderByHash))
-       m.Handle("/get-block-header-by-height", jsonHandler(bcr.getBlockHeaderByHeight))
-       m.Handle("/get-block", jsonHandler(bcr.getBlock))
-       m.Handle("/get-block-count", jsonHandler(bcr.getBlockCount))
-       m.Handle("/get-block-transactions-count-by-hash", jsonHandler(bcr.getBlockTransactionsCountByHash))
-       m.Handle("/get-block-transactions-count-by-height", jsonHandler(bcr.getBlockTransactionsCountByHeight))
-
-       m.Handle("/net-info", jsonHandler(bcr.getNetInfo))
-
-       m.Handle("/is-mining", jsonHandler(bcr.isMining))
-       m.Handle("/gas-rate", jsonHandler(bcr.gasRate))
-       m.Handle("/getwork", jsonHandler(bcr.getWork))
-       m.Handle("/submitwork", jsonHandler(bcr.submitWork))
-
-       latencyHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-               if l := latency(m, req); l != nil {
-                       defer l.RecordSince(time.Now())
-               }
-               m.ServeHTTP(w, req)
-       })
-       handler := maxBytes(latencyHandler) // TODO(tessr): consider moving this to non-core specific mux
-       handler = webAssetsHandler(handler)
-
-       bcr.Handler = handler
-}
-
-//AuthHandler access token auth Handler
-func AuthHandler(handler http.Handler, accessTokens *accesstoken.CredentialStore) http.Handler {
-       authenticator := authn.NewAPI(accessTokens)
-
-       return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
-               // TODO(tessr): check that this path exists; return early if this path isn't legit
-               req, err := authenticator.Authenticate(req)
-               if err != nil {
-                       log.WithField("error", errors.Wrap(err, "Serve")).Error("Authenticate fail")
-                       err = errors.Sub(errNotAuthenticated, err)
-                       errorFormatter.Write(req.Context(), rw, err)
-                       return
-               }
-               handler.ServeHTTP(rw, req)
-       })
-}
index 6bc374e..5cc5da7 100755 (executable)
@@ -2,15 +2,11 @@ package node
 
 import (
        "context"
-       "crypto/tls"
-       "net"
        "net/http"
        _ "net/http/pprof"
        "strings"
-       "sync"
        "time"
 
-       "github.com/kr/secureheader"
        log "github.com/sirupsen/logrus"
        "github.com/tendermint/go-crypto"
        "github.com/tendermint/go-wire"
@@ -28,7 +24,6 @@ import (
        w "github.com/bytom/blockchain/wallet"
        cfg "github.com/bytom/config"
        "github.com/bytom/env"
-       "github.com/bytom/errors"
        "github.com/bytom/p2p"
        "github.com/bytom/protocol"
        "github.com/bytom/types"
@@ -37,8 +32,6 @@ import (
 )
 
 const (
-       httpReadTimeout          = 2 * time.Minute
-       httpWriteTimeout         = time.Hour
        webAddress               = "http://127.0.0.1:9888"
        expireReservationsPeriod = time.Second
 )
@@ -56,67 +49,8 @@ type Node struct {
 
        evsw         types.EventSwitch // pub/sub for services
        bcReactor    *bc.BlockchainReactor
-       server       *http.Server
        accessTokens *accesstoken.CredentialStore
-}
-
-func RedirectHandler(next http.Handler) http.Handler {
-       return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-               if req.URL.Path == "/" {
-                       http.Redirect(w, req, "/dashboard/", http.StatusFound)
-                       return
-               }
-               next.ServeHTTP(w, req)
-       })
-}
-
-type waitHandler struct {
-       h  http.Handler
-       wg sync.WaitGroup
-}
-
-func (wh *waitHandler) Set(h http.Handler) {
-       wh.h = h
-       wh.wg.Done()
-}
-
-func (wh *waitHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
-       wh.wg.Wait()
-       wh.h.ServeHTTP(w, req)
-}
-
-func (n *Node) initServer() {
-       // The waitHandler accepts incoming requests, but blocks until its underlying
-       // handler is set, when the second phase is complete.
-       var coreHandler waitHandler
-       coreHandler.wg.Add(1)
-       mux := http.NewServeMux()
-       mux.Handle("/", &coreHandler)
-
-       var handler http.Handler = mux
-
-       if n.config.Auth.Disable == false {
-               handler = bc.AuthHandler(handler, n.accessTokens)
-       }
-       handler = RedirectHandler(handler)
-
-       secureheader.DefaultConfig.PermitClearLoopback = true
-       secureheader.DefaultConfig.HTTPSRedirect = false
-       secureheader.DefaultConfig.Next = handler
-
-       n.server = &http.Server{
-               // Note: we should not set TLSConfig here;
-               // we took care of TLS with the listener in maybeUseTLS.
-               Handler:      secureheader.DefaultConfig,
-               ReadTimeout:  httpReadTimeout,
-               WriteTimeout: httpWriteTimeout,
-               // Disable HTTP/2 for now until the Go implementation is more stable.
-               // https://github.com/golang/go/issues/16450
-               // https://github.com/golang/go/issues/17071
-               TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
-       }
-
-       coreHandler.Set(n.bcReactor.Handler)
+       api          *bc.API
 }
 
 func NewNode(config *cfg.Config) *Node {
@@ -273,24 +207,11 @@ func lanchWebBroser(lanch bool) {
        }
 }
 
-func (n *Node) initAndstartServer() {
-       n.initServer()
+func (n *Node) initAndstartApiServer() {
+       n.api = bc.NewAPI(n.bcReactor, n.config)
 
        listenAddr := env.String("LISTEN", n.config.ApiAddress)
-       log.WithField("api address:", n.config.ApiAddress).Info("Rpc listen")
-       listener, err := net.Listen("tcp", *listenAddr)
-       if err != nil {
-               cmn.Exit(cmn.Fmt("Failed to register tcp port: %v", err))
-       }
-
-       // The `Serve` call has to happen in its own goroutine because
-       // it's blocking and we need to proceed to the rest of the core setup after
-       // we call it.
-       go func() {
-               if err := n.server.Serve(listener); err != nil {
-                       log.WithField("error", errors.Wrap(err, "Serve")).Error("Rpc server")
-               }
-       }()
+       n.api.StartServer(*listenAddr)
 }
 
 func (n *Node) OnStart() error {
@@ -316,7 +237,7 @@ func (n *Node) OnStart() error {
                }
        }
 
-       n.initAndstartServer()
+       n.initAndstartApiServer()
        lanchWebBroser(!n.config.Web.Closed)
 
        return nil