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 (
// supply the given RPC ID.
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
// supplied in the corresponding RegisterRPC call. References to RPCFuncs
// registered with RegisterConnectCall are not removed and should be removed
......
......@@ -221,18 +221,25 @@ func (g *Gateway) ForwardPort(port string) error {
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
// gateway.
func (g *Gateway) SetRateLimits(downloadSpeed, uploadSpeed int64) error {
g.mu.Lock()
defer g.mu.RUnlock()
defer g.mu.Unlock()
// Set the limit in memory.
if err := g.setRateLimits(downloadSpeed, uploadSpeed); err != nil {
return err
}
// Update the persistence struct.
g.persist.maxDownloadSpeed = downloadSpeed
g.persist.maxUploadSpeed = uploadSpeed
g.persist.MaxDownloadSpeed = downloadSpeed
g.persist.MaxUploadSpeed = uploadSpeed
return g.saveSync()
}
......@@ -303,7 +310,7 @@ func New(addr string, bootstrap bool, persistDir string) (*Gateway, error) {
}
// Create the ratelimiter and set it to the persisted limits.
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
}
// Spawn the thread to periodically save the gateway.
......
......@@ -39,8 +39,8 @@ type (
RouterURL string
// rate limit settings
maxDownloadSpeed int64
maxUploadSpeed int64
MaxDownloadSpeed int64
MaxUploadSpeed int64
}
)
......
package client
import (
"net/url"
"strconv"
"gitlab.com/NebulousLabs/Sia/modules"
"gitlab.com/NebulousLabs/Sia/node/api"
"gitlab.com/NebulousLabs/errors"
......@@ -35,3 +38,14 @@ func (c *Client) GatewayGet() (gwg api.GatewayGET, err error) {
err = c.get("/gateway", &gwg)
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
import (
"fmt"
"net/http"
"gitlab.com/NebulousLabs/Sia/modules"
......@@ -12,18 +13,52 @@ import (
type GatewayGET struct {
NetAddress modules.NetAddress `json:"netaddress"`
Peers []modules.Peer `json:"peers"`
MaxDownloadSpeed int64 `json:"maxdownloadspeed"`
MaxUploadSpeed int64 `json:"maxuploadspeed"`
}
// gatewayHandler handles the API call asking for the gatway status.
func (api *API) gatewayHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
// gatewayHandlerGET handles the API call asking for the gatway status.
func (api *API) gatewayHandlerGET(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
peers := api.gateway.Peers()
mds, mus := api.gateway.RateLimits()
// nil slices are marshalled as 'null' in JSON, whereas 0-length slices are
// marshalled as '[]'. The latter is preferred, indicating that the value
// exists but contains no elements.
if peers == nil {
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.
......
......@@ -34,7 +34,8 @@ func (api *API) buildHTTPRoutes(requiredUserAgent string, requiredPassword strin
// Gateway API Calls
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/disconnect/:netaddress", RequirePassword(api.gatewayDisconnectHandler, requiredPassword))
}
......
......@@ -16,6 +16,18 @@ var (
CreateTransactionPool: 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.
// The node has a host and all dependencies, but no other modules.
HostTemplate = NodeParams{
......@@ -63,6 +75,13 @@ func AllModules(dir string) NodeParams {
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.
func Host(dir string) NodeParams {
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 {
}
tn.Server = s
tn.Client.Address = s.APIAddress()
if !tn.params.CreateWallet || tn.params.Wallet == nil {
return nil
}
return tn.WalletUnlockPost(tn.primarySeed)
}
......@@ -170,6 +173,11 @@ func NewCleanNode(nodeParams node.NodeParams) (*TestNode, error) {
// Create TestNode
tn := &TestNode{s, *c, nodeParams, ""}
// If there is no wallet we are done.
if !nodeParams.CreateWallet && nodeParams.Wallet == nil {
return tn, nil
}
// Init wallet
wip, err := tn.WalletInitPost("", false)
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