github.com/ZuluSpl0it/Sia@v1.3.7/node/api/routes.go (about) 1 package api 2 3 import ( 4 "net/http" 5 "strings" 6 "time" 7 8 "github.com/NebulousLabs/Sia/build" 9 "github.com/julienschmidt/httprouter" 10 ) 11 12 // buildHttpRoutes sets up and returns an * httprouter.Router. 13 // it connected the Router to the given api using the required 14 // parameters: requiredUserAgent and requiredPassword 15 func (api *API) buildHTTPRoutes(requiredUserAgent string, requiredPassword string) { 16 router := httprouter.New() 17 18 router.NotFound = http.HandlerFunc(UnrecognizedCallHandler) 19 router.RedirectTrailingSlash = false 20 21 // Consensus API Calls 22 if api.cs != nil { 23 router.GET("/consensus", api.consensusHandler) 24 router.GET("/consensus/blocks", api.consensusBlocksHandler) 25 router.POST("/consensus/validate/transactionset", api.consensusValidateTransactionsetHandler) 26 } 27 28 // Explorer API Calls 29 if api.explorer != nil { 30 router.GET("/explorer", api.explorerHandler) 31 router.GET("/explorer/blocks/:height", api.explorerBlocksHandler) 32 router.GET("/explorer/hashes/:hash", api.explorerHashHandler) 33 } 34 35 // Gateway API Calls 36 if api.gateway != nil { 37 router.GET("/gateway", api.gatewayHandler) 38 router.POST("/gateway/connect/:netaddress", RequirePassword(api.gatewayConnectHandler, requiredPassword)) 39 router.POST("/gateway/disconnect/:netaddress", RequirePassword(api.gatewayDisconnectHandler, requiredPassword)) 40 } 41 42 // Host API Calls 43 if api.host != nil { 44 // Calls directly pertaining to the host. 45 router.GET("/host", api.hostHandlerGET) // Get the host status. 46 router.POST("/host", RequirePassword(api.hostHandlerPOST, requiredPassword)) // Change the settings of the host. 47 router.POST("/host/announce", RequirePassword(api.hostAnnounceHandler, requiredPassword)) // Announce the host to the network. 48 router.GET("/host/contracts", api.hostContractInfoHandler) // Get info about contracts. 49 router.GET("/host/estimatescore", api.hostEstimateScoreGET) 50 51 // Calls pertaining to the storage manager that the host uses. 52 router.GET("/host/storage", api.storageHandler) 53 router.POST("/host/storage/folders/add", RequirePassword(api.storageFoldersAddHandler, requiredPassword)) 54 router.POST("/host/storage/folders/remove", RequirePassword(api.storageFoldersRemoveHandler, requiredPassword)) 55 router.POST("/host/storage/folders/resize", RequirePassword(api.storageFoldersResizeHandler, requiredPassword)) 56 router.POST("/host/storage/sectors/delete/:merkleroot", RequirePassword(api.storageSectorsDeleteHandler, requiredPassword)) 57 } 58 59 // Miner API Calls 60 if api.miner != nil { 61 router.GET("/miner", api.minerHandler) 62 router.GET("/miner/header", RequirePassword(api.minerHeaderHandlerGET, requiredPassword)) 63 router.POST("/miner/header", RequirePassword(api.minerHeaderHandlerPOST, requiredPassword)) 64 router.GET("/miner/start", RequirePassword(api.minerStartHandler, requiredPassword)) 65 router.GET("/miner/stop", RequirePassword(api.minerStopHandler, requiredPassword)) 66 } 67 68 // Renter API Calls 69 if api.renter != nil { 70 router.GET("/renter", api.renterHandlerGET) 71 router.POST("/renter", RequirePassword(api.renterHandlerPOST, requiredPassword)) 72 router.GET("/renter/contracts", api.renterContractsHandler) 73 router.GET("/renter/downloads", api.renterDownloadsHandler) 74 router.POST("/renter/downloads/clear", RequirePassword(api.renterClearDownloadsHandler, requiredPassword)) 75 router.GET("/renter/files", api.renterFilesHandler) 76 router.GET("/renter/file/*siapath", api.renterFileHandler) 77 router.GET("/renter/prices", api.renterPricesHandler) 78 79 // TODO: re-enable these routes once the new .sia format has been 80 // standardized and implemented. 81 // router.POST("/renter/load", RequirePassword(api.renterLoadHandler, requiredPassword)) 82 // router.POST("/renter/loadascii", RequirePassword(api.renterLoadAsciiHandler, requiredPassword)) 83 // router.GET("/renter/share", RequirePassword(api.renterShareHandler, requiredPassword)) 84 // router.GET("/renter/shareascii", RequirePassword(api.renterShareAsciiHandler, requiredPassword)) 85 86 router.POST("/renter/delete/*siapath", RequirePassword(api.renterDeleteHandler, requiredPassword)) 87 router.GET("/renter/download/*siapath", RequirePassword(api.renterDownloadHandler, requiredPassword)) 88 router.GET("/renter/downloadasync/*siapath", RequirePassword(api.renterDownloadAsyncHandler, requiredPassword)) 89 router.POST("/renter/rename/*siapath", RequirePassword(api.renterRenameHandler, requiredPassword)) 90 router.GET("/renter/stream/*siapath", api.renterStreamHandler) 91 router.POST("/renter/upload/*siapath", RequirePassword(api.renterUploadHandler, requiredPassword)) 92 93 // HostDB endpoints. 94 router.GET("/hostdb", api.hostdbHandler) 95 router.GET("/hostdb/active", api.hostdbActiveHandler) 96 router.GET("/hostdb/all", api.hostdbAllHandler) 97 router.GET("/hostdb/hosts/:pubkey", api.hostdbHostsHandler) 98 } 99 100 // Transaction pool API Calls 101 if api.tpool != nil { 102 router.GET("/tpool/fee", api.tpoolFeeHandlerGET) 103 router.GET("/tpool/raw/:id", api.tpoolRawHandlerGET) 104 router.POST("/tpool/raw", api.tpoolRawHandlerPOST) 105 router.GET("/tpool/confirmed/:id", api.tpoolConfirmedGET) 106 107 // TODO: re-enable this route once the transaction pool API has been finalized 108 //router.GET("/transactionpool/transactions", api.transactionpoolTransactionsHandler) 109 } 110 111 // Wallet API Calls 112 if api.wallet != nil { 113 router.GET("/wallet", api.walletHandler) 114 router.POST("/wallet/033x", RequirePassword(api.wallet033xHandler, requiredPassword)) 115 router.GET("/wallet/address", RequirePassword(api.walletAddressHandler, requiredPassword)) 116 router.GET("/wallet/addresses", api.walletAddressesHandler) 117 router.GET("/wallet/backup", RequirePassword(api.walletBackupHandler, requiredPassword)) 118 router.POST("/wallet/init", RequirePassword(api.walletInitHandler, requiredPassword)) 119 router.POST("/wallet/init/seed", RequirePassword(api.walletInitSeedHandler, requiredPassword)) 120 router.POST("/wallet/lock", RequirePassword(api.walletLockHandler, requiredPassword)) 121 router.POST("/wallet/seed", RequirePassword(api.walletSeedHandler, requiredPassword)) 122 router.GET("/wallet/seeds", RequirePassword(api.walletSeedsHandler, requiredPassword)) 123 router.POST("/wallet/siacoins", RequirePassword(api.walletSiacoinsHandler, requiredPassword)) 124 router.POST("/wallet/siafunds", RequirePassword(api.walletSiafundsHandler, requiredPassword)) 125 router.POST("/wallet/siagkey", RequirePassword(api.walletSiagkeyHandler, requiredPassword)) 126 router.POST("/wallet/sweep/seed", RequirePassword(api.walletSweepSeedHandler, requiredPassword)) 127 router.GET("/wallet/transaction/:id", api.walletTransactionHandler) 128 router.GET("/wallet/transactions", api.walletTransactionsHandler) 129 router.GET("/wallet/transactions/:addr", api.walletTransactionsAddrHandler) 130 router.GET("/wallet/verify/address/:addr", api.walletVerifyAddressHandler) 131 router.POST("/wallet/unlock", RequirePassword(api.walletUnlockHandler, requiredPassword)) 132 router.POST("/wallet/changepassword", RequirePassword(api.walletChangePasswordHandler, requiredPassword)) 133 } 134 135 // Apply UserAgent middleware and return the Router 136 api.router = cleanCloseHandler(RequireUserAgent(router, requiredUserAgent)) 137 return 138 } 139 140 // cleanCloseHandler wraps the entire API, ensuring that underlying conns are 141 // not leaked if the remote end closes the connection before the underlying 142 // handler finishes. 143 func cleanCloseHandler(next http.Handler) http.Handler { 144 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 145 // Close this file handle either when the function completes or when the 146 // connection is done. 147 done := make(chan struct{}) 148 go func(w http.ResponseWriter, r *http.Request) { 149 defer close(done) 150 next.ServeHTTP(w, r) 151 }(w, r) 152 select { 153 case <-done: 154 } 155 156 // Sanity check - thread should not take more than an hour to return. This 157 // must be done in a goroutine, otherwise the server will not close the 158 // underlying socket for this API call. 159 timer := time.NewTimer(time.Minute * 60) 160 go func() { 161 select { 162 case <-done: 163 timer.Stop() 164 case <-timer.C: 165 build.Severe("api call is taking more than 60 minutes to return:", r.URL.Path) 166 } 167 }() 168 }) 169 } 170 171 // RequireUserAgent is middleware that requires all requests to set a 172 // UserAgent that contains the specified string. 173 func RequireUserAgent(h http.Handler, ua string) http.Handler { 174 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 175 if !strings.Contains(req.UserAgent(), ua) && !isUnrestricted(req) { 176 WriteError(w, Error{"Browser access disabled due to security vulnerability. Use Sia-UI or siac."}, http.StatusBadRequest) 177 return 178 } 179 h.ServeHTTP(w, req) 180 }) 181 } 182 183 // RequirePassword is middleware that requires a request to authenticate with a 184 // password using HTTP basic auth. Usernames are ignored. Empty passwords 185 // indicate no authentication is required. 186 func RequirePassword(h httprouter.Handle, password string) httprouter.Handle { 187 // An empty password is equivalent to no password. 188 if password == "" { 189 return h 190 } 191 return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { 192 _, pass, ok := req.BasicAuth() 193 if !ok || pass != password { 194 w.Header().Set("WWW-Authenticate", "Basic realm=\"SiaAPI\"") 195 WriteError(w, Error{"API authentication failed."}, http.StatusUnauthorized) 196 return 197 } 198 h(w, req, ps) 199 } 200 } 201 202 // isUnrestricted checks if a request may bypass the useragent check. 203 func isUnrestricted(req *http.Request) bool { 204 return strings.HasPrefix(req.URL.Path, "/renter/stream/") 205 }