Commit ecf83720 authored by Christopher Schinnerl's avatar Christopher Schinnerl

add /gateway POST endpoint

parent 5852bc98
Pipeline #38510867 failed with stages
in 29 minutes and 46 seconds
...@@ -156,6 +156,13 @@ type ( ...@@ -156,6 +156,13 @@ type (
// supply the given RPC ID. // supply the given RPC ID.
RegisterRPC(string, RPCFunc) RegisterRPC(string, RPCFunc)
// RateLimits returns the currently set bandwidth limits of the gateway.
RateLimits() (int64, int64)
// SetRateLimits changes the rate limits for the peer-connections of the
// gateway.
SetRateLimits(downloadSpeed, uploadSpeed int64) error
// UnregisterRPC unregisters an RPC and removes all references to the RPCFunc // UnregisterRPC unregisters an RPC and removes all references to the RPCFunc
// supplied in the corresponding RegisterRPC call. References to RPCFuncs // supplied in the corresponding RegisterRPC call. References to RPCFuncs
// registered with RegisterConnectCall are not removed and should be removed // registered with RegisterConnectCall are not removed and should be removed
......
...@@ -221,18 +221,25 @@ func (g *Gateway) ForwardPort(port string) error { ...@@ -221,18 +221,25 @@ func (g *Gateway) ForwardPort(port string) error {
return g.managedForwardPort(port) return g.managedForwardPort(port)
} }
// RateLimits returns the currently set bandwidth limits of the gateway.
func (g *Gateway) RateLimits() (int64, int64) {
g.mu.RLock()
defer g.mu.RUnlock()
return g.persist.MaxDownloadSpeed, g.persist.MaxUploadSpeed
}
// SetRateLimits changes the rate limits for the peer-connections of the // SetRateLimits changes the rate limits for the peer-connections of the
// gateway. // gateway.
func (g *Gateway) SetRateLimits(downloadSpeed, uploadSpeed int64) error { func (g *Gateway) SetRateLimits(downloadSpeed, uploadSpeed int64) error {
g.mu.Lock() g.mu.Lock()
defer g.mu.RUnlock() defer g.mu.Unlock()
// Set the limit in memory. // Set the limit in memory.
if err := g.setRateLimits(downloadSpeed, uploadSpeed); err != nil { if err := g.setRateLimits(downloadSpeed, uploadSpeed); err != nil {
return err return err
} }
// Update the persistence struct. // Update the persistence struct.
g.persist.maxDownloadSpeed = downloadSpeed g.persist.MaxDownloadSpeed = downloadSpeed
g.persist.maxUploadSpeed = uploadSpeed g.persist.MaxUploadSpeed = uploadSpeed
return g.saveSync() return g.saveSync()
} }
...@@ -303,7 +310,7 @@ func New(addr string, bootstrap bool, persistDir string) (*Gateway, error) { ...@@ -303,7 +310,7 @@ func New(addr string, bootstrap bool, persistDir string) (*Gateway, error) {
} }
// Create the ratelimiter and set it to the persisted limits. // Create the ratelimiter and set it to the persisted limits.
g.rl = ratelimit.NewRateLimit(0, 0, 0) g.rl = ratelimit.NewRateLimit(0, 0, 0)
if err := g.setRateLimits(g.persist.maxDownloadSpeed, g.persist.maxUploadSpeed); err != nil { if err := g.setRateLimits(g.persist.MaxDownloadSpeed, g.persist.MaxUploadSpeed); err != nil {
return nil, err return nil, err
} }
// Spawn the thread to periodically save the gateway. // Spawn the thread to periodically save the gateway.
......
...@@ -39,8 +39,8 @@ type ( ...@@ -39,8 +39,8 @@ type (
RouterURL string RouterURL string
// rate limit settings // rate limit settings
maxDownloadSpeed int64 MaxDownloadSpeed int64
maxUploadSpeed int64 MaxUploadSpeed int64
} }
) )
......
package client package client
import ( import (
"net/url"
"strconv"
"gitlab.com/NebulousLabs/Sia/modules" "gitlab.com/NebulousLabs/Sia/modules"
"gitlab.com/NebulousLabs/Sia/node/api" "gitlab.com/NebulousLabs/Sia/node/api"
"gitlab.com/NebulousLabs/errors" "gitlab.com/NebulousLabs/errors"
...@@ -35,3 +38,14 @@ func (c *Client) GatewayGet() (gwg api.GatewayGET, err error) { ...@@ -35,3 +38,14 @@ func (c *Client) GatewayGet() (gwg api.GatewayGET, err error) {
err = c.get("/gateway", &gwg) err = c.get("/gateway", &gwg)
return return
} }
// GatewayRateLimitPost uses the /gateway endpoint to change the gateway's
// bandwidth rate limit. downloadSpeed and uploadSpeed are interpreted as
// bytes/second.
func (c *Client) GatewayRateLimitPost(downloadSpeed, uploadSpeed int64) (err error) {
values := url.Values{}
values.Set("maxdownloadspeed", strconv.FormatInt(downloadSpeed, 10))
values.Set("maxuploadspeed", strconv.FormatInt(uploadSpeed, 10))
err = c.post("/gateway", values.Encode(), nil)
return
}
package api package api
import ( import (
"fmt"
"net/http" "net/http"
"gitlab.com/NebulousLabs/Sia/modules" "gitlab.com/NebulousLabs/Sia/modules"
...@@ -12,18 +13,52 @@ import ( ...@@ -12,18 +13,52 @@ import (
type GatewayGET struct { type GatewayGET struct {
NetAddress modules.NetAddress `json:"netaddress"` NetAddress modules.NetAddress `json:"netaddress"`
Peers []modules.Peer `json:"peers"` Peers []modules.Peer `json:"peers"`
MaxDownloadSpeed int64 `json:"maxdownloadspeed"`
MaxUploadSpeed int64 `json:"maxuploadspeed"`
} }
// gatewayHandler handles the API call asking for the gatway status. // gatewayHandlerGET handles the API call asking for the gatway status.
func (api *API) gatewayHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { func (api *API) gatewayHandlerGET(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
peers := api.gateway.Peers() peers := api.gateway.Peers()
mds, mus := api.gateway.RateLimits()
// nil slices are marshalled as 'null' in JSON, whereas 0-length slices are // nil slices are marshalled as 'null' in JSON, whereas 0-length slices are
// marshalled as '[]'. The latter is preferred, indicating that the value // marshalled as '[]'. The latter is preferred, indicating that the value
// exists but contains no elements. // exists but contains no elements.
if peers == nil { if peers == nil {
peers = make([]modules.Peer, 0) peers = make([]modules.Peer, 0)
} }
WriteJSON(w, GatewayGET{api.gateway.Address(), peers}) WriteJSON(w, GatewayGET{api.gateway.Address(), peers, mds, mus})
}
// gatewayHandlerPOST handles the API call changing gateway specific settings.
func (api *API) gatewayHandlerPOST(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
maxDownloadSpeed, maxUploadSpeed := api.gateway.RateLimits()
// Scan the download speed limit. (optional parameter)
if d := req.FormValue("maxdownloadspeed"); d != "" {
var downloadSpeed int64
if _, err := fmt.Sscan(d, &downloadSpeed); err != nil {
WriteError(w, Error{"unable to parse downloadspeed: " + err.Error()}, http.StatusBadRequest)
return
}
maxDownloadSpeed = downloadSpeed
}
// Scan the upload speed limit. (optional parameter)
if u := req.FormValue("maxuploadspeed"); u != "" {
var uploadSpeed int64
if _, err := fmt.Sscan(u, &uploadSpeed); err != nil {
WriteError(w, Error{"unable to parse uploadspeed: " + err.Error()}, http.StatusBadRequest)
return
}
maxUploadSpeed = uploadSpeed
}
// Try to set the limits.
err := api.gateway.SetRateLimits(maxDownloadSpeed, maxUploadSpeed)
if err != nil {
WriteError(w, Error{"failed to set new rate limit: " + err.Error()}, http.StatusBadRequest)
return
}
WriteSuccess(w)
} }
// gatewayConnectHandler handles the API call to add a peer to the gateway. // gatewayConnectHandler handles the API call to add a peer to the gateway.
......
...@@ -34,7 +34,8 @@ func (api *API) buildHTTPRoutes(requiredUserAgent string, requiredPassword strin ...@@ -34,7 +34,8 @@ func (api *API) buildHTTPRoutes(requiredUserAgent string, requiredPassword strin
// Gateway API Calls // Gateway API Calls
if api.gateway != nil { if api.gateway != nil {
router.GET("/gateway", api.gatewayHandler) router.GET("/gateway", api.gatewayHandlerGET)
router.POST("/gateway", api.gatewayHandlerPOST)
router.POST("/gateway/connect/:netaddress", RequirePassword(api.gatewayConnectHandler, requiredPassword)) router.POST("/gateway/connect/:netaddress", RequirePassword(api.gatewayConnectHandler, requiredPassword))
router.POST("/gateway/disconnect/:netaddress", RequirePassword(api.gatewayDisconnectHandler, requiredPassword)) router.POST("/gateway/disconnect/:netaddress", RequirePassword(api.gatewayDisconnectHandler, requiredPassword))
} }
......
...@@ -16,6 +16,18 @@ var ( ...@@ -16,6 +16,18 @@ var (
CreateTransactionPool: true, CreateTransactionPool: true,
CreateWallet: true, CreateWallet: true,
} }
// GatewayTemplate is a template for a Sia node that has a functioning
// Gateway. The node has a Gateway but no other modules.
GatewayTemplate = NodeParams{
CreateConsensusSet: false,
CreateExplorer: false,
CreateGateway: true,
CreateHost: false,
CreateMiner: false,
CreateRenter: false,
CreateTransactionPool: false,
CreateWallet: false,
}
// HostTemplate is a template for a Sia node that has a functioning host. // HostTemplate is a template for a Sia node that has a functioning host.
// The node has a host and all dependencies, but no other modules. // The node has a host and all dependencies, but no other modules.
HostTemplate = NodeParams{ HostTemplate = NodeParams{
...@@ -63,6 +75,13 @@ func AllModules(dir string) NodeParams { ...@@ -63,6 +75,13 @@ func AllModules(dir string) NodeParams {
return template return template
} }
// Gateway returns a GatewayTemplate filled out with the provided dir.
func Gateway(dir string) NodeParams {
template := GatewayTemplate
template.Dir = dir
return template
}
// Host returns a HostTemplate filled out with the provided dir. // Host returns a HostTemplate filled out with the provided dir.
func Host(dir string) NodeParams { func Host(dir string) NodeParams {
template := HostTemplate template := HostTemplate
......
package gateway
import (
"os"
"gitlab.com/NebulousLabs/Sia/siatest"
)
// gatewayTestDir creates a temporary testing directory for a gateway. This
// should only every be called once per test. Otherwise it will delete the
// directory again.
func gatewayTestDir(testName string) string {
path := siatest.TestDir("gateway", testName)
if err := os.MkdirAll(path, 0777); err != nil {
panic(err)
}
return path
}
package gateway
import (
"testing"
"gitlab.com/NebulousLabs/Sia/node"
"gitlab.com/NebulousLabs/Sia/siatest"
)
// TestGatewayRatelimit makes sure that we can set the gateway's ratelimits
// using the API and that they are persisted correctly.
func TestGatewayRatelimit(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
testDir := gatewayTestDir(t.Name())
// Create a new server
testNode, err := siatest.NewCleanNode(node.Gateway(testDir))
if err != nil {
t.Fatal(err)
}
defer func() {
if err := testNode.Close(); err != nil {
t.Fatal(err)
}
}()
// Get the current ratelimit.
gg, err := testNode.GatewayGet()
if err != nil {
t.Fatal(err)
}
// Speeds should be 0 which means it's not being rate limited.
if gg.MaxDownloadSpeed != 0 || gg.MaxUploadSpeed != 0 {
t.Fatalf("Limits should be 0 but were %v and %v", gg.MaxDownloadSpeed, gg.MaxUploadSpeed)
}
// Change the limits.
ds := int64(100)
us := int64(200)
if err := testNode.GatewayRateLimitPost(ds, us); err != nil {
t.Fatal(err)
}
// Get the ratelimit again.
gg, err = testNode.GatewayGet()
if err != nil {
t.Fatal(err)
}
// Limit should be set correctly.
if gg.MaxDownloadSpeed != ds || gg.MaxUploadSpeed != us {
t.Fatalf("Limits should be %v/%v but are %v/%v",
ds, us, gg.MaxDownloadSpeed, gg.MaxUploadSpeed)
}
// Restart the node.
if err := testNode.RestartNode(); err != nil {
t.Fatal(err)
}
// Get the ratelimit again.
gg, err = testNode.GatewayGet()
if err != nil {
t.Fatal(err)
}
// Limit should've been persisted correctly.
if gg.MaxDownloadSpeed != ds || gg.MaxUploadSpeed != us {
t.Fatalf("Limits should be %v/%v but are %v/%v",
ds, us, gg.MaxDownloadSpeed, gg.MaxUploadSpeed)
}
}
...@@ -122,6 +122,9 @@ func (tn *TestNode) StartNode() error { ...@@ -122,6 +122,9 @@ func (tn *TestNode) StartNode() error {
} }
tn.Server = s tn.Server = s
tn.Client.Address = s.APIAddress() tn.Client.Address = s.APIAddress()
if !tn.params.CreateWallet || tn.params.Wallet == nil {
return nil
}
return tn.WalletUnlockPost(tn.primarySeed) return tn.WalletUnlockPost(tn.primarySeed)
} }
...@@ -170,6 +173,11 @@ func NewCleanNode(nodeParams node.NodeParams) (*TestNode, error) { ...@@ -170,6 +173,11 @@ func NewCleanNode(nodeParams node.NodeParams) (*TestNode, error) {
// Create TestNode // Create TestNode
tn := &TestNode{s, *c, nodeParams, ""} tn := &TestNode{s, *c, nodeParams, ""}
// If there is no wallet we are done.
if !nodeParams.CreateWallet && nodeParams.Wallet == nil {
return tn, nil
}
// Init wallet // Init wallet
wip, err := tn.WalletInitPost("", false) wip, err := tn.WalletInitPost("", false)
if err != nil { if err != nil {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment