From 70378f3a798610c56a2e48b4c3d4efc6def6b0da Mon Sep 17 00:00:00 2001 From: ersonp Date: Sat, 15 Jun 2024 13:53:38 +0530 Subject: [PATCH 01/25] refactor: Rename `skyrev` and `skyfwd` to `net` Rename the `skyrev` package to `con` and `skyfwd` to `pub` under the `net` command. This change improves the clarity and consistency of the codebase. --- .../{skyrev/root.go => net/connect.go} | 25 ++++++++++--------- .../{skyfwd/root.go => net/publish.go} | 23 ++++++++--------- cmd/skywire-cli/commands/net/root.go | 18 +++++++++++++ cmd/skywire-cli/commands/root.go | 7 +++--- 4 files changed, 45 insertions(+), 28 deletions(-) rename cmd/skywire-cli/commands/{skyrev/root.go => net/connect.go} (71%) rename cmd/skywire-cli/commands/{skyfwd/root.go => net/publish.go} (69%) create mode 100644 cmd/skywire-cli/commands/net/root.go diff --git a/cmd/skywire-cli/commands/skyrev/root.go b/cmd/skywire-cli/commands/net/connect.go similarity index 71% rename from cmd/skywire-cli/commands/skyrev/root.go rename to cmd/skywire-cli/commands/net/connect.go index b8fccc903e..58a74e6670 100644 --- a/cmd/skywire-cli/commands/skyrev/root.go +++ b/cmd/skywire-cli/commands/net/connect.go @@ -1,5 +1,5 @@ -// Package skyrev cmd/skywire-cli/commands/skyfwd/root.go -package skyrev +// Package net cmd/skywire-cli/commands/net/connect.go +package net import ( "bytes" @@ -25,18 +25,19 @@ var ( ) func init() { - RootCmd.Flags().IntVarP(&remotePort, "remote", "r", 0, "remote port to read from") - RootCmd.Flags().StringVarP(&remotePk, "pk", "k", "", "remote public key to connect to") - RootCmd.Flags().IntVarP(&localPort, "port", "p", 0, "local port to reverse proxy") - RootCmd.Flags().BoolVarP(&lsPorts, "ls", "l", false, "list configured connections") - RootCmd.Flags().StringVarP(&disconnect, "stop", "d", "", "disconnect from specified ") + conCmd.Flags().IntVarP(&remotePort, "remote", "r", 0, "remote port to read from") + conCmd.Flags().StringVarP(&remotePk, "pk", "k", "", "remote public key to connect to") + conCmd.Flags().IntVarP(&localPort, "port", "p", 0, "local port to reverse proxy") + conCmd.Flags().BoolVarP(&lsPorts, "ls", "l", false, "list configured connections") + conCmd.Flags().StringVarP(&disconnect, "stop", "d", "", "disconnect from specified ") + RootCmd.AddCommand(conCmd) } -// RootCmd contains commands that interact with the skyforwarding -var RootCmd = &cobra.Command{ - Use: "rev", - Short: "reverse proxy skyfwd", - Long: "connect or disconnect from remote ports", +// conCmd contains commands to connect to a published port on the skywire network +var conCmd = &cobra.Command{ + Use: "con", + Short: "Connect to a published port on the skywire network", + Long: "Connect to a published port on the skywire network\nConnect to a remote port on the skywire network. This will allow you to access the remote port via the skywire network.", Args: cobra.MinimumNArgs(0), Run: func(cmd *cobra.Command, args []string) { diff --git a/cmd/skywire-cli/commands/skyfwd/root.go b/cmd/skywire-cli/commands/net/publish.go similarity index 69% rename from cmd/skywire-cli/commands/skyfwd/root.go rename to cmd/skywire-cli/commands/net/publish.go index 3cd4420897..624b04e08f 100644 --- a/cmd/skywire-cli/commands/skyfwd/root.go +++ b/cmd/skywire-cli/commands/net/publish.go @@ -1,5 +1,5 @@ -// Package skyfwd cmd/skywire-cli/commands/skyfwd/root.go -package skyfwd +// Package net cmd/skywire-cli/commands/net/publish.go +package net import ( "bytes" @@ -17,21 +17,20 @@ import ( var ( portNo int deregister bool - lsPorts bool ) func init() { - RootCmd.PersistentFlags().IntVarP(&portNo, "port", "p", 0, "local port of the external (http) app") - RootCmd.PersistentFlags().BoolVarP(&deregister, "deregister", "d", false, "deregister local port of the external (http) app") - RootCmd.PersistentFlags().BoolVarP(&lsPorts, "ls", "l", false, "list registered local ports") - + pubCmd.PersistentFlags().IntVarP(&portNo, "port", "p", 0, "local port of the external (http) app") + pubCmd.PersistentFlags().BoolVarP(&deregister, "deregister", "d", false, "deregister local port of the external (http) app") + pubCmd.PersistentFlags().BoolVarP(&lsPorts, "ls", "l", false, "list published local ports") + RootCmd.AddCommand(pubCmd) } -// RootCmd contains commands that interact with the skyforwarding -var RootCmd = &cobra.Command{ - Use: "fwd", - Short: "Control skyforwarding", - Long: "Control skyforwarding\n forward local ports over skywire", +// pubCmd contains commands to publish over the skywire network +var pubCmd = &cobra.Command{ + Use: "pub", + Short: "Publish over skywire network", + Long: "Publish over skywire network\nPublish a local port over the skywire network. This will allow other nodes to access the local port via the skywire network.", Args: cobra.MinimumNArgs(0), Run: func(cmd *cobra.Command, args []string) { diff --git a/cmd/skywire-cli/commands/net/root.go b/cmd/skywire-cli/commands/net/root.go new file mode 100644 index 0000000000..71d0933efb --- /dev/null +++ b/cmd/skywire-cli/commands/net/root.go @@ -0,0 +1,18 @@ +// Package net cmd/skywire-cli/commands/net/root.go +package net + +import ( + "github.com/spf13/cobra" + + clirpc "github.com/skycoin/skywire/cmd/skywire-cli/commands/rpc" +) + +func init() { + RootCmd.PersistentFlags().StringVar(&clirpc.Addr, "rpc", "localhost:3435", "RPC server address") +} + +// RootCmd contains commands that interact with the skywire network +var RootCmd = &cobra.Command{ + Use: "net", + Short: "Publish and connect to skywire network", +} diff --git a/cmd/skywire-cli/commands/root.go b/cmd/skywire-cli/commands/root.go index dee47484c0..f3249c6288 100644 --- a/cmd/skywire-cli/commands/root.go +++ b/cmd/skywire-cli/commands/root.go @@ -23,8 +23,8 @@ import ( clireward "github.com/skycoin/skywire/cmd/skywire-cli/commands/reward" clirewards "github.com/skycoin/skywire/cmd/skywire-cli/commands/rewards" cliroute "github.com/skycoin/skywire/cmd/skywire-cli/commands/route" - cliskyfwd "github.com/skycoin/skywire/cmd/skywire-cli/commands/skyfwd" - cliskyrev "github.com/skycoin/skywire/cmd/skywire-cli/commands/skyrev" + + clinet "github.com/skycoin/skywire/cmd/skywire-cli/commands/net" clisurvey "github.com/skycoin/skywire/cmd/skywire-cli/commands/survey" clitp "github.com/skycoin/skywire/cmd/skywire-cli/commands/tp" cliut "github.com/skycoin/skywire/cmd/skywire-cli/commands/ut" @@ -40,8 +40,7 @@ func init() { clivisor.RootCmd, clivpn.RootCmd, cliut.RootCmd, - cliskyfwd.RootCmd, - cliskyrev.RootCmd, + clinet.RootCmd, clireward.RootCmd, clirewards.RootCmd, clisurvey.RootCmd, From 65a4123c559ea8d0f9f919520e0fd98a3db3d2d2 Mon Sep 17 00:00:00 2001 From: ersonp Date: Sat, 15 Jun 2024 15:54:49 +0530 Subject: [PATCH 02/25] refactor: Rename `forward` to `connect` --- cmd/skywire-cli/commands/net/connect.go | 10 ++-- pkg/app/appnet/{forwarding.go => connect.go} | 62 ++++++++++---------- pkg/visor/api.go | 16 ++--- pkg/visor/rpc.go | 2 +- pkg/visor/rpc_client.go | 6 +- pkg/visor/visor.go | 2 +- 6 files changed, 49 insertions(+), 49 deletions(-) rename pkg/app/appnet/{forwarding.go => connect.go} (72%) diff --git a/cmd/skywire-cli/commands/net/connect.go b/cmd/skywire-cli/commands/net/connect.go index 58a74e6670..6031496f57 100644 --- a/cmd/skywire-cli/commands/net/connect.go +++ b/cmd/skywire-cli/commands/net/connect.go @@ -47,7 +47,7 @@ var conCmd = &cobra.Command{ } if lsPorts { - forwardConns, err := rpcClient.List() + connectConns, err := rpcClient.List() internal.Catch(cmd.Flags(), err) var b bytes.Buffer @@ -55,13 +55,13 @@ var conCmd = &cobra.Command{ _, err = fmt.Fprintln(w, "id\tlocal_port\tremote_port") internal.Catch(cmd.Flags(), err) - for _, forwardConn := range forwardConns { - _, err = fmt.Fprintf(w, "%s\t%s\t%s\n", forwardConn.ID, strconv.Itoa(int(forwardConn.LocalPort)), - strconv.Itoa(int(forwardConn.RemotePort))) + for _, connectConn := range connectConns { + _, err = fmt.Fprintf(w, "%s\t%s\t%s\n", connectConn.ID, strconv.Itoa(int(connectConn.LocalPort)), + strconv.Itoa(int(connectConn.RemotePort))) internal.Catch(cmd.Flags(), err) } internal.Catch(cmd.Flags(), w.Flush()) - internal.PrintOutput(cmd.Flags(), forwardConns, b.String()) + internal.PrintOutput(cmd.Flags(), connectConns, b.String()) os.Exit(0) } diff --git a/pkg/app/appnet/forwarding.go b/pkg/app/appnet/connect.go similarity index 72% rename from pkg/app/appnet/forwarding.go rename to pkg/app/appnet/connect.go index fdc9d48d83..da28e4f8b4 100644 --- a/pkg/app/appnet/forwarding.go +++ b/pkg/app/appnet/connect.go @@ -17,42 +17,42 @@ import ( // nolint: gochecknoglobals var ( - forwardConns = make(map[uuid.UUID]*ForwardConn) - forwardConnsMu sync.Mutex + connectConns = make(map[uuid.UUID]*ConnectConn) + connectConnsMu sync.Mutex ) -// AddForwarding adds ForwardConn to with it's ID -func AddForwarding(fwd *ForwardConn) { - forwardConnsMu.Lock() - defer forwardConnsMu.Unlock() - forwardConns[fwd.ID] = fwd +// AddConnect adds ConnectConn to with it's ID +func AddConnect(fwd *ConnectConn) { + connectConnsMu.Lock() + defer connectConnsMu.Unlock() + connectConns[fwd.ID] = fwd } -// GetForwardConn get's a ForwardConn by ID -func GetForwardConn(id uuid.UUID) *ForwardConn { - forwardConnsMu.Lock() - defer forwardConnsMu.Unlock() +// GetConnectConn get's a ConnectConn by ID +func GetConnectConn(id uuid.UUID) *ConnectConn { + connectConnsMu.Lock() + defer connectConnsMu.Unlock() - return forwardConns[id] + return connectConns[id] } -// GetAllForwardConns gets all ForwardConns -func GetAllForwardConns() map[uuid.UUID]*ForwardConn { - forwardConnsMu.Lock() - defer forwardConnsMu.Unlock() +// GetAllConnectConns gets all ConnectConns +func GetAllConnectConns() map[uuid.UUID]*ConnectConn { + connectConnsMu.Lock() + defer connectConnsMu.Unlock() - return forwardConns + return connectConns } -// RemoveForwardConn removes a ForwardConn by ID -func RemoveForwardConn(id uuid.UUID) { - forwardConnsMu.Lock() - defer forwardConnsMu.Unlock() - delete(forwardConns, id) +// RemoveConnectConn removes a ConnectConn by ID +func RemoveConnectConn(id uuid.UUID) { + connectConnsMu.Lock() + defer connectConnsMu.Unlock() + delete(connectConns, id) } -// ForwardConn ... -type ForwardConn struct { +// ConnectConn represents a connection that is published on the skywire network +type ConnectConn struct { ID uuid.UUID LocalPort int RemotePort int @@ -63,8 +63,8 @@ type ForwardConn struct { log *logging.Logger } -// NewForwardConn creates a new forwarding conn -func NewForwardConn(log *logging.Logger, remoteConn net.Conn, remotePort, localPort int) *ForwardConn { +// NewConnectConn creates a new ConnectConn +func NewConnectConn(log *logging.Logger, remoteConn net.Conn, remotePort, localPort int) *ConnectConn { closeChan := make(chan struct{}) var once sync.Once handler := http.NewServeMux() @@ -78,7 +78,7 @@ func NewForwardConn(log *logging.Logger, remoteConn net.Conn, remotePort, localP WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } - fwdConn := &ForwardConn{ + fwdConn := &ConnectConn{ ID: uuid.New(), remoteConn: remoteConn, srv: srv, @@ -87,12 +87,12 @@ func NewForwardConn(log *logging.Logger, remoteConn net.Conn, remotePort, localP closeChan: closeChan, log: log, } - AddForwarding(fwdConn) + AddConnect(fwdConn) return fwdConn } // Serve serves a HTTP forward conn that accepts all requests and forwards them directly to the remote server over the specified net.Conn. -func (f *ForwardConn) Serve() { +func (f *ConnectConn) Serve() { go func() { err := f.srv.ListenAndServe() if err != nil { @@ -113,11 +113,11 @@ func (f *ForwardConn) Serve() { } // Close closes the server and remote connection. -func (f *ForwardConn) Close() (err error) { +func (f *ConnectConn) Close() (err error) { f.closeOnce.Do(func() { err = f.srv.Close() err = f.remoteConn.Close() - RemoveForwardConn(f.ID) + RemoveConnectConn(f.ID) }) return err } diff --git a/pkg/visor/api.go b/pkg/visor/api.go index 8e63206da8..b57dfeaa02 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -121,7 +121,7 @@ type API interface { ListHTTPPorts() ([]int, error) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error) Disconnect(id uuid.UUID) error - List() (map[uuid.UUID]*appnet.ForwardConn, error) + List() (map[uuid.UUID]*appnet.ConnectConn, error) DialPing(config PingConfig) error Ping(config PingConfig) ([]time.Duration, error) StopPing(pk cipher.PubKey) error @@ -1631,20 +1631,20 @@ func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid v.log.WithError(fmt.Errorf(*sErr)).Error("Server closed with error") return uuid.UUID{}, fmt.Errorf(*sErr) } - forwardConn := appnet.NewForwardConn(v.log, remoteConn, remotePort, localPort) - forwardConn.Serve() - return forwardConn.ID, nil + connectConn := appnet.NewConnectConn(v.log, remoteConn, remotePort, localPort) + connectConn.Serve() + return connectConn.ID, nil } // Disconnect implements API. func (v *Visor) Disconnect(id uuid.UUID) error { - forwardConn := appnet.GetForwardConn(id) - return forwardConn.Close() + connectConn := appnet.GetConnectConn(id) + return connectConn.Close() } // List implements API. -func (v *Visor) List() (map[uuid.UUID]*appnet.ForwardConn, error) { - return appnet.GetAllForwardConns(), nil +func (v *Visor) List() (map[uuid.UUID]*appnet.ConnectConn, error) { + return appnet.GetAllConnectConns(), nil } func isPortAvailable(log *logging.Logger, port int) bool { diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index 2b713bf8a6..8d03a9f899 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -771,7 +771,7 @@ func (r *RPC) Disconnect(id *uuid.UUID, _ *struct{}) (err error) { } // List returns all the ongoing skyforwarding connections -func (r *RPC) List(_ *struct{}, out *map[uuid.UUID]*appnet.ForwardConn) (err error) { +func (r *RPC) List(_ *struct{}, out *map[uuid.UUID]*appnet.ConnectConn) (err error) { defer rpcutil.LogCall(r.log, "List", nil)(out, &err) proxies, err := r.visor.List() *out = proxies diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index ab8381a6b2..4b9dffda8b 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -573,8 +573,8 @@ func (rc *rpcClient) Disconnect(id uuid.UUID) error { } // List calls List. -func (rc *rpcClient) List() (map[uuid.UUID]*appnet.ForwardConn, error) { - var out map[uuid.UUID]*appnet.ForwardConn +func (rc *rpcClient) List() (map[uuid.UUID]*appnet.ConnectConn, error) { + var out map[uuid.UUID]*appnet.ConnectConn err := rc.Call("List", &struct{}{}, &out) return out, err } @@ -1321,7 +1321,7 @@ func (mc *mockRPCClient) Disconnect(id uuid.UUID) error { //nolint:all } // List implements API. -func (mc *mockRPCClient) List() (map[uuid.UUID]*appnet.ForwardConn, error) { +func (mc *mockRPCClient) List() (map[uuid.UUID]*appnet.ConnectConn, error) { return nil, nil } diff --git a/pkg/visor/visor.go b/pkg/visor/visor.go index 4fb66884a6..63c21fc6a7 100644 --- a/pkg/visor/visor.go +++ b/pkg/visor/visor.go @@ -407,7 +407,7 @@ func (v *Visor) Close() error { log.Info("Begin shutdown.") // Cleanly close ongoing forward conns - for _, forwardConn := range appnet.GetAllForwardConns() { + for _, forwardConn := range appnet.GetAllConnectConns() { err := forwardConn.Close() if err != nil { log.WithError(err).Warn("Forward conn stopped with unexpected result.") From 8ee38faa134bc9f3f2caed7f3cb2f33cf5e4632b Mon Sep 17 00:00:00 2001 From: ersonp Date: Mon, 17 Jun 2024 11:43:16 +0530 Subject: [PATCH 03/25] feat: Add `publish` and update `connect` --- cmd/skywire-cli/commands/net/connect.go | 3 +- cmd/skywire-cli/commands/net/publish.go | 8 +- pkg/app/appnet/connect.go | 117 ++++++------ pkg/app/appnet/publish.go | 226 ++++++++++++++++++++++++ pkg/visor/api.go | 64 +++---- pkg/visor/rpc.go | 16 ++ pkg/visor/rpc_client.go | 23 +++ 7 files changed, 356 insertions(+), 101 deletions(-) create mode 100644 pkg/app/appnet/publish.go diff --git a/cmd/skywire-cli/commands/net/connect.go b/cmd/skywire-cli/commands/net/connect.go index 6031496f57..bfb7f85174 100644 --- a/cmd/skywire-cli/commands/net/connect.go +++ b/cmd/skywire-cli/commands/net/connect.go @@ -56,7 +56,7 @@ var conCmd = &cobra.Command{ internal.Catch(cmd.Flags(), err) for _, connectConn := range connectConns { - _, err = fmt.Fprintf(w, "%s\t%s\t%s\n", connectConn.ID, strconv.Itoa(int(connectConn.LocalPort)), + _, err = fmt.Fprintf(w, "%s\t%s\t%s\n", connectConn.ID, strconv.Itoa(int(connectConn.WebPort)), strconv.Itoa(int(connectConn.RemotePort))) internal.Catch(cmd.Flags(), err) } @@ -97,7 +97,6 @@ var conCmd = &cobra.Command{ if 65536 < remotePort || 65536 < localPort { internal.PrintFatalError(cmd.Flags(), fmt.Errorf("port cannot be greater than 65535")) } - id, err := rpcClient.Connect(remotePK, remotePort, localPort) internal.Catch(cmd.Flags(), err) internal.PrintOutput(cmd.Flags(), id, fmt.Sprintln(id)) diff --git a/cmd/skywire-cli/commands/net/publish.go b/cmd/skywire-cli/commands/net/publish.go index 624b04e08f..ab574fe25e 100644 --- a/cmd/skywire-cli/commands/net/publish.go +++ b/cmd/skywire-cli/commands/net/publish.go @@ -78,11 +78,15 @@ var pubCmd = &cobra.Command{ if deregister { err = rpcClient.DeregisterHTTPPort(portNo) + internal.Catch(cmd.Flags(), err) + } else { err = rpcClient.RegisterHTTPPort(portNo) + internal.Catch(cmd.Flags(), err) + id, err := rpcClient.Publish(portNo) + internal.Catch(cmd.Flags(), err) + internal.PrintOutput(cmd.Flags(), "id: %v\n", fmt.Sprintln(id)) } - internal.Catch(cmd.Flags(), err) - internal.PrintOutput(cmd.Flags(), "OK", "OK\n") }, } diff --git a/pkg/app/appnet/connect.go b/pkg/app/appnet/connect.go index da28e4f8b4..67c9ae1890 100644 --- a/pkg/app/appnet/connect.go +++ b/pkg/app/appnet/connect.go @@ -8,10 +8,11 @@ import ( "net" "net/http" "sync" - "time" + "github.com/gin-gonic/gin" "github.com/google/uuid" + "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/logging" ) @@ -54,39 +55,37 @@ func RemoveConnectConn(id uuid.UUID) { // ConnectConn represents a connection that is published on the skywire network type ConnectConn struct { ID uuid.UUID - LocalPort int + WebPort int RemotePort int remoteConn net.Conn + r *gin.Engine closeOnce sync.Once - srv *http.Server closeChan chan struct{} log *logging.Logger } // NewConnectConn creates a new ConnectConn -func NewConnectConn(log *logging.Logger, remoteConn net.Conn, remotePort, localPort int) *ConnectConn { - closeChan := make(chan struct{}) - var once sync.Once - handler := http.NewServeMux() - var lock sync.Mutex - handler.HandleFunc("/", handleFunc(remoteConn, log, closeChan, once, &lock)) - - srv := &http.Server{ - Addr: fmt.Sprintf(":%v", localPort), - Handler: handler, - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, - MaxHeaderBytes: 1 << 20, - } +func NewConnectConn(log *logging.Logger, remoteConn net.Conn, remotePK cipher.PubKey, remotePort, webPort int) *ConnectConn { + + httpC := &http.Client{Transport: MakeHTTPTransport(remoteConn, log)} + + r := gin.New() + + r.Use(gin.Recovery()) + + r.Use(loggingMiddleware()) + + r.Any("/*path", handleConnectFunc(httpC, remotePK, remotePort)) + fwdConn := &ConnectConn{ ID: uuid.New(), remoteConn: remoteConn, - srv: srv, - LocalPort: localPort, + WebPort: webPort, RemotePort: remotePort, - closeChan: closeChan, log: log, + r: r, } + AddConnect(fwdConn) return fwdConn } @@ -94,7 +93,7 @@ func NewConnectConn(log *logging.Logger, remoteConn net.Conn, remotePort, localP // Serve serves a HTTP forward conn that accepts all requests and forwards them directly to the remote server over the specified net.Conn. func (f *ConnectConn) Serve() { go func() { - err := f.srv.ListenAndServe() + err := f.r.Run(":" + fmt.Sprintf("%v", f.WebPort)) //nolint if err != nil { // don't print error if local server is closed if !errors.Is(err, http.ErrServerClosed) { @@ -109,71 +108,57 @@ func (f *ConnectConn) Serve() { f.log.Error(err) } }() - f.log.Debugf("Serving on localhost:%v", f.LocalPort) + f.log.Debugf("Serving on localhost:%v", f.WebPort) } // Close closes the server and remote connection. func (f *ConnectConn) Close() (err error) { f.closeOnce.Do(func() { - err = f.srv.Close() err = f.remoteConn.Close() RemoveConnectConn(f.ID) }) return err } -func isClosed(c chan struct{}) bool { - select { - case <-c: - return true - default: - return false - } -} - -func handleFunc(remoteConn net.Conn, log *logging.Logger, closeChan chan struct{}, once sync.Once, lock *sync.Mutex) func(w http.ResponseWriter, req *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - lock.Lock() - defer lock.Unlock() +func handleConnectFunc(httpC *http.Client, remotePK cipher.PubKey, remotePort int) func(c *gin.Context) { + return func(c *gin.Context) { + var urlStr string + urlStr = fmt.Sprintf("sky://%s:%v%s", remotePK, remotePort, c.Param("path")) + if c.Request.URL.RawQuery != "" { + urlStr = fmt.Sprintf("%s?%s", urlStr, c.Request.URL.RawQuery) + } - if isClosed(closeChan) { + fmt.Printf("Proxying request: %s %s\n", c.Request.Method, urlStr) + req, err := http.NewRequest(c.Request.Method, urlStr, c.Request.Body) + if err != nil { + c.String(http.StatusInternalServerError, "Failed to create HTTP request") return } - client := http.Client{Transport: MakeHTTPTransport(remoteConn, log)} - // Forward request to remote server - resp, err := client.Transport.RoundTrip(r) + + for header, values := range c.Request.Header { + for _, value := range values { + req.Header.Add(header, value) + } + } + + resp, err := httpC.Do(req) if err != nil { - http.Error(w, "Could not reach remote server", 500) - log.WithError(err).Errorf("Could not reach remote server %v", resp) - once.Do(func() { - close(closeChan) - }) + c.String(http.StatusInternalServerError, "Failed to connect to HTTP server") + fmt.Printf("Error: %v\n", err) return } + defer resp.Body.Close() //nolint - defer func() { - if err := resp.Body.Close(); err != nil { - log.WithError(err).Errorln("Failed to close forwarding response body") - } - }() - for key, value := range resp.Header { - for _, v := range value { - w.Header().Set(key, v) + for header, values := range resp.Header { + for _, value := range values { + c.Writer.Header().Add(header, value) } } - w.WriteHeader(resp.StatusCode) - // Transfer response from remote server -> client - if resp.ContentLength > 0 { - if _, err := io.CopyN(w, resp.Body, resp.ContentLength); err != nil { - log.Warn(err) - } - } else if resp.Close { - // Copy until EOF or some other error occurs - for { - if _, err := io.Copy(w, resp.Body); err != nil { - break - } - } + + c.Status(resp.StatusCode) + if _, err := io.Copy(c.Writer, resp.Body); err != nil { + c.String(http.StatusInternalServerError, "Failed to copy response body") + fmt.Printf("Error copying response body: %v\n", err) } } } diff --git a/pkg/app/appnet/publish.go b/pkg/app/appnet/publish.go new file mode 100644 index 0000000000..802609fa75 --- /dev/null +++ b/pkg/app/appnet/publish.go @@ -0,0 +1,226 @@ +// Package appnet pkg/app/appnet/forwarding.go +package appnet + +import ( + "errors" + "fmt" + "net" + "net/http" + "net/http/httputil" + "net/url" + "sync" + "time" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + + "github.com/skycoin/skywire-utilities/pkg/logging" +) + +// nolint: gochecknoglobals +var ( + publishListenertners = make(map[uuid.UUID]*publishListener) + publishListenerMu sync.Mutex +) + +// AddPublish adds publishListener to with it's ID +func AddPublish(fwd *publishListener) { + publishListenerMu.Lock() + defer publishListenerMu.Unlock() + publishListenertners[fwd.ID] = fwd +} + +// GetpublishListenertner get's a publishListener by ID +func GetpublishListenertner(id uuid.UUID) *publishListener { + publishListenerMu.Lock() + defer publishListenerMu.Unlock() + + return publishListenertners[id] +} + +// GetAllpublishListenertners gets all publishListeners +func GetAllpublishListenertners() map[uuid.UUID]*publishListener { + publishListenerMu.Lock() + defer publishListenerMu.Unlock() + + return publishListenertners +} + +// RemovepublishListener removes a publishListener by ID +func RemovepublishListener(id uuid.UUID) { + publishListenerMu.Lock() + defer publishListenerMu.Unlock() + delete(publishListenertners, id) +} + +// publishListener represents a publishion that is published on the skywire network +type publishListener struct { + ID uuid.UUID + LocalPort int + lis net.Listener + closeOnce sync.Once + srv *http.Server + closeChan chan struct{} + log *logging.Logger +} + +type ginHandler struct { + Router *gin.Engine +} + +func (h *ginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + h.Router.ServeHTTP(w, r) +} + +// NewPublishListener creates a new publishListener +func NewPublishListener(log *logging.Logger, lis net.Listener, localPort int) *publishListener { + closeChan := make(chan struct{}) + r1 := gin.New() + r1.Use(gin.Recovery()) + r1.Use(loggingMiddleware()) + authRoute := r1.Group("/") + authRoute.Any("/*path", func(c *gin.Context) { + log.Error("Request received") + targetURL, _ := url.Parse(fmt.Sprintf("http://127.0.0.1:%v%s?%s", localPort, c.Request.URL.Path, c.Request.URL.RawQuery)) //nolint + proxy := httputil.ReverseProxy{ + Director: func(req *http.Request) { + req.URL = targetURL + req.Host = targetURL.Host + req.Method = c.Request.Method + }, + Transport: &http.Transport{}, + } + proxy.ServeHTTP(c.Writer, c.Request) + }) + srv := &http.Server{ + Handler: &ginHandler{Router: r1}, + ReadHeaderTimeout: 5 * time.Second, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + pubLis := &publishListener{ + ID: uuid.New(), + srv: srv, + lis: lis, + LocalPort: localPort, + closeChan: closeChan, + log: log, + } + AddPublish(pubLis) + return pubLis +} + +// Serve serves a HTTP forward Lis that accepts all requests and forwards them directly to the remote server over the specified net.Lis. +func (f *publishListener) Listen() { + go func() { + err := f.srv.Serve(f.lis) + if err != nil { + // don't print error if local server is closed + if !errors.Is(err, http.ErrServerClosed) { + f.log.WithError(err).Error("Error listening and serving app forwarding.") + } + } + }() + go func() { + <-f.closeChan + err := f.Close() + if err != nil { + f.log.Error(err) + } + }() + f.log.Debugf("Serving HTTP on dmsg port %v with DMSG listener %s", f.LocalPort, f.lis.Addr().String()) +} + +// Close closes the server and remote publishion. +func (f *publishListener) Close() (err error) { + f.closeOnce.Do(func() { + f.log.Error("Closing publishListener") + err = f.srv.Close() + err = f.lis.Close() + RemovepublishListener(f.ID) + }) + return err +} + +func loggingMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + start := time.Now() + c.Next() + latency := time.Since(start) + if latency > time.Minute { + latency = latency.Truncate(time.Second) + } + statusCode := c.Writer.Status() + method := c.Request.Method + path := c.Request.URL.Path + // Get the background color based on the status code + statusCodeBackgroundColor := getBackgroundColor(statusCode) + // Get the method color + methodColor := getMethodColor(method) + // Print the logging in a custom format which includes the publickeyfrom c.Request.RemoteAddr ex.: + // [DMSGHTTP] 2023/05/18 - 19:43:15 | 200 | 10.80885ms | | 02b5ee5333aa6b7f5fc623b7d5f35f505cb7f974e98a70751cf41962f84c8c4637:49153 | GET /node-info.json + fmt.Printf("[DMSGWEB] %s |%s %3d %s| %13v | %15s | %72s |%s %-7s %s %s\n", + time.Now().Format("2006/01/02 - 15:04:05"), + statusCodeBackgroundColor, + statusCode, + resetColor(), + latency, + c.ClientIP(), + c.Request.RemoteAddr, + methodColor, + method, + resetColor(), + path, + ) + } +} + +func getBackgroundColor(statusCode int) string { + switch { + case statusCode >= http.StatusOK && statusCode < http.StatusMultipleChoices: + return green + case statusCode >= http.StatusMultipleChoices && statusCode < http.StatusBadRequest: + return white + case statusCode >= http.StatusBadRequest && statusCode < http.StatusInternalServerError: + return yellow + default: + return red + } +} + +func getMethodColor(method string) string { + switch method { + case http.MethodGet: + return blue + case http.MethodPost: + return cyan + case http.MethodPut: + return yellow + case http.MethodDelete: + return red + case http.MethodPatch: + return green + case http.MethodHead: + return magenta + case http.MethodOptions: + return white + default: + return reset + } +} + +func resetColor() string { + return reset +} + +const ( + green = "\033[97;42m" + white = "\033[90;47m" + yellow = "\033[90;43m" + red = "\033[97;41m" + blue = "\033[97;44m" + magenta = "\033[97;45m" + cyan = "\033[97;46m" + reset = "\033[0m" +) diff --git a/pkg/visor/api.go b/pkg/visor/api.go index b57dfeaa02..744a681657 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -119,6 +119,8 @@ type API interface { RegisterHTTPPort(localPort int) error DeregisterHTTPPort(localPort int) error ListHTTPPorts() ([]int, error) + Publish(localPort int) (uuid.UUID, error) + Depublish(id uuid.UUID) error Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error) Disconnect(id uuid.UUID) error List() (map[uuid.UUID]*appnet.ConnectConn, error) @@ -1582,6 +1584,7 @@ func (v *Visor) ListHTTPPorts() ([]int, error) { // Connect implements API. func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error) { + v.log.Errorf("Connecting to %v:%v via %v", remotePK, remotePort, localPort) ok := isPortAvailable(v.log, localPort) if !ok { return uuid.UUID{}, fmt.Errorf(":%v local port already in use", localPort) @@ -1589,7 +1592,7 @@ func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid connApp := appnet.Addr{ Net: appnet.TypeSkynet, PubKey: remotePK, - Port: routing.Port(skyenv.SkyForwardingServerPort), + Port: routing.Port(remotePort), } conn, err := appnet.Dial(connApp) if err != nil { @@ -1600,46 +1603,45 @@ func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid return uuid.UUID{}, err } - cMsg := clientMsg{ - Port: remotePort, - } + connectConn := appnet.NewConnectConn(v.log, remoteConn, remotePK, remotePort, localPort) + connectConn.Serve() + return connectConn.ID, nil +} - clientMsg, err := json.Marshal(cMsg) - if err != nil { - return uuid.UUID{}, err +// Disconnect implements API. +func (v *Visor) Disconnect(id uuid.UUID) error { + connectConn := appnet.GetConnectConn(id) + return connectConn.Close() +} + +// Publish implements API. +func (v *Visor) Publish(localPort int) (uuid.UUID, error) { + v.log.Errorf("Publishing on %v:%v", v.conf.PK, localPort) + ok := isPortAvailable(v.log, localPort) + if ok { + return uuid.UUID{}, fmt.Errorf(":%v local port not in use", localPort) } - _, err = remoteConn.Write([]byte(clientMsg)) - if err != nil { - return uuid.UUID{}, err + connApp := appnet.Addr{ + Net: appnet.TypeSkynet, + PubKey: v.conf.PK, + Port: routing.Port(localPort), } - v.log.Debugf("Msg sent %s", clientMsg) - buf := make([]byte, 32*1024) - n, err := remoteConn.Read(buf) + lis, err := appnet.Listen(connApp) if err != nil { return uuid.UUID{}, err } - var sReply serverReply - err = json.Unmarshal(buf[:n], &sReply) - if err != nil { - return uuid.UUID{}, err - } - v.log.Debugf("Received: %v", sReply) - if sReply.Error != nil { - sErr := sReply.Error - v.log.WithError(fmt.Errorf(*sErr)).Error("Server closed with error") - return uuid.UUID{}, fmt.Errorf(*sErr) - } - connectConn := appnet.NewConnectConn(v.log, remoteConn, remotePort, localPort) - connectConn.Serve() - return connectConn.ID, nil + publishLis := appnet.NewPublishListener(v.log, lis, localPort) + publishLis.Listen() + + return publishLis.ID, nil } -// Disconnect implements API. -func (v *Visor) Disconnect(id uuid.UUID) error { - connectConn := appnet.GetConnectConn(id) - return connectConn.Close() +// Depublish implements API. +func (v *Visor) Depublish(id uuid.UUID) error { + forwardConn := appnet.GetConnectConn(id) + return forwardConn.Close() } // List implements API. diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index 8d03a9f899..58ecc8bdc8 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -770,6 +770,22 @@ func (r *RPC) Disconnect(id *uuid.UUID, _ *struct{}) (err error) { return err } +// Connect creates a connection with the remote visor to listen on the remote port and serve that on the local port +func (r *RPC) Publish(localPort *int, out *uuid.UUID) (err error) { + defer rpcutil.LogCall(r.log, "Publish", localPort)(out, &err) + + id, err := r.visor.Publish(*localPort) + *out = id + return err +} + +// Disconnect breaks the connection with the given id +func (r *RPC) Depublish(id *uuid.UUID, _ *struct{}) (err error) { + defer rpcutil.LogCall(r.log, "Depublish", id)(nil, &err) + err = r.visor.Depublish(*id) + return err +} + // List returns all the ongoing skyforwarding connections func (r *RPC) List(_ *struct{}, out *map[uuid.UUID]*appnet.ConnectConn) (err error) { defer rpcutil.LogCall(r.log, "List", nil)(out, &err) diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index 4b9dffda8b..a26c444fec 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -572,6 +572,19 @@ func (rc *rpcClient) Disconnect(id uuid.UUID) error { return err } +// Publish calls Publish. +func (rc *rpcClient) Publish(localPort int) (uuid.UUID, error) { + var out uuid.UUID + err := rc.Call("Publish", &localPort, &out) + return out, err +} + +// Depublish calls Depublish. +func (rc *rpcClient) Depublish(id uuid.UUID) error { + err := rc.Call("Depublish", &id, &struct{}{}) + return err +} + // List calls List. func (rc *rpcClient) List() (map[uuid.UUID]*appnet.ConnectConn, error) { var out map[uuid.UUID]*appnet.ConnectConn @@ -1320,6 +1333,16 @@ func (mc *mockRPCClient) Disconnect(id uuid.UUID) error { //nolint:all return nil } +// Publish implements API. +func (mc *mockRPCClient) Publish(localPort int) (uuid.UUID, error) { //nolint:all + return uuid.UUID{}, nil +} + +// Depublish implements API. +func (mc *mockRPCClient) Depublish(id uuid.UUID) error { //nolint:all + return nil +} + // List implements API. func (mc *mockRPCClient) List() (map[uuid.UUID]*appnet.ConnectConn, error) { return nil, nil From 102802f3dad0f3ce3b51ae7bc4f297c9ed5a94a1 Mon Sep 17 00:00:00 2001 From: ersonp Date: Sat, 22 Jun 2024 16:04:01 +0530 Subject: [PATCH 04/25] fix(publish): Remove ReadTimeout --- pkg/app/appnet/publish.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/app/appnet/publish.go b/pkg/app/appnet/publish.go index 802609fa75..314038250c 100644 --- a/pkg/app/appnet/publish.go +++ b/pkg/app/appnet/publish.go @@ -95,7 +95,6 @@ func NewPublishListener(log *logging.Logger, lis net.Listener, localPort int) *p srv := &http.Server{ Handler: &ginHandler{Router: r1}, ReadHeaderTimeout: 5 * time.Second, - ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, } From 47672e056d0591fcb6c2db542e7019fba73d3e3a Mon Sep 17 00:00:00 2001 From: ersonp Date: Sat, 22 Jun 2024 16:06:12 +0530 Subject: [PATCH 05/25] fix(connect): Add sync.Mutex to handleConnectFunc --- pkg/app/appnet/connect.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/app/appnet/connect.go b/pkg/app/appnet/connect.go index 67c9ae1890..d5a6a48c4e 100644 --- a/pkg/app/appnet/connect.go +++ b/pkg/app/appnet/connect.go @@ -68,6 +68,7 @@ type ConnectConn struct { func NewConnectConn(log *logging.Logger, remoteConn net.Conn, remotePK cipher.PubKey, remotePort, webPort int) *ConnectConn { httpC := &http.Client{Transport: MakeHTTPTransport(remoteConn, log)} + mu := new(sync.Mutex) r := gin.New() @@ -75,7 +76,7 @@ func NewConnectConn(log *logging.Logger, remoteConn net.Conn, remotePK cipher.Pu r.Use(loggingMiddleware()) - r.Any("/*path", handleConnectFunc(httpC, remotePK, remotePort)) + r.Any("/*path", handleConnectFunc(httpC, remotePK, remotePort, mu)) fwdConn := &ConnectConn{ ID: uuid.New(), @@ -120,8 +121,11 @@ func (f *ConnectConn) Close() (err error) { return err } -func handleConnectFunc(httpC *http.Client, remotePK cipher.PubKey, remotePort int) func(c *gin.Context) { +func handleConnectFunc(httpC *http.Client, remotePK cipher.PubKey, remotePort int, mu *sync.Mutex) func(c *gin.Context) { return func(c *gin.Context) { + mu.Lock() + defer mu.Unlock() + var urlStr string urlStr = fmt.Sprintf("sky://%s:%v%s", remotePK, remotePort, c.Param("path")) if c.Request.URL.RawQuery != "" { From c5681da2873e9b0bc2b5c92c3726f0121fb1acec Mon Sep 17 00:00:00 2001 From: ersonp Date: Sat, 22 Jun 2024 16:57:43 +0530 Subject: [PATCH 06/25] refactor: Remove `RegisterHTTPPort` and `DeregisterHTTPPort` from API and RPC --- cmd/skywire-cli/commands/net/publish.go | 14 ++++--- docs/skywire_forwarding.md | 4 +- example/http-server/server.go | 4 +- pkg/visor/api.go | 52 ++++++------------------- pkg/visor/rpc.go | 12 ------ pkg/visor/rpc_client.go | 21 ---------- 6 files changed, 24 insertions(+), 83 deletions(-) diff --git a/cmd/skywire-cli/commands/net/publish.go b/cmd/skywire-cli/commands/net/publish.go index ab574fe25e..b05b577db2 100644 --- a/cmd/skywire-cli/commands/net/publish.go +++ b/cmd/skywire-cli/commands/net/publish.go @@ -8,6 +8,7 @@ import ( "strconv" "text/tabwriter" + "github.com/google/uuid" "github.com/spf13/cobra" clirpc "github.com/skycoin/skywire/cmd/skywire-cli/commands/rpc" @@ -15,13 +16,13 @@ import ( ) var ( - portNo int - deregister bool + portNo int + depublish string ) func init() { pubCmd.PersistentFlags().IntVarP(&portNo, "port", "p", 0, "local port of the external (http) app") - pubCmd.PersistentFlags().BoolVarP(&deregister, "deregister", "d", false, "deregister local port of the external (http) app") + pubCmd.PersistentFlags().StringVarP(&depublish, "depublish", "d", "", "deregister local port of the external (http) app with id") pubCmd.PersistentFlags().BoolVarP(&lsPorts, "ls", "l", false, "list published local ports") RootCmd.AddCommand(pubCmd) } @@ -76,12 +77,13 @@ var pubCmd = &cobra.Command{ internal.PrintFatalError(cmd.Flags(), fmt.Errorf("port cannot be greater than 65535")) } - if deregister { - err = rpcClient.DeregisterHTTPPort(portNo) + if depublish != "" { + id, err := uuid.Parse(disconnect) + internal.Catch(cmd.Flags(), err) + err = rpcClient.Depublish(id) internal.Catch(cmd.Flags(), err) } else { - err = rpcClient.RegisterHTTPPort(portNo) internal.Catch(cmd.Flags(), err) id, err := rpcClient.Publish(portNo) internal.Catch(cmd.Flags(), err) diff --git a/docs/skywire_forwarding.md b/docs/skywire_forwarding.md index 5d6abfeb52..618bd576e3 100644 --- a/docs/skywire_forwarding.md +++ b/docs/skywire_forwarding.md @@ -36,8 +36,8 @@ func client() (visor.API, error) { ``` And then use the created RPC conn to register and deregister the server ``` -err = rpcClient.RegisterHTTPPort(port) -err = rpcClient.DeregisterHTTPPort(port) +id, err := rpcClient.Publish(port) +err = rpcClient.Depublish(id) ``` [Example](../example/http-server/README.md) diff --git a/example/http-server/server.go b/example/http-server/server.go index 4e231a39ed..3bd6062cc4 100644 --- a/example/http-server/server.go +++ b/example/http-server/server.go @@ -60,7 +60,7 @@ func main() { fmt.Printf("error serving: %v\n", err) } - err = rpcClient.RegisterHTTPPort(port) + id, err := rpcClient.Publish(port) if err != nil { log.Errorf("error closing server: %v", err) } @@ -70,7 +70,7 @@ func main() { if err != nil { log.Errorf("error closing server: %v", err) } - err = rpcClient.DeregisterHTTPPort(port) + err = rpcClient.Depublish(id) if err != nil { log.Errorf("error closing server: %v", err) } diff --git a/pkg/visor/api.go b/pkg/visor/api.go index 744a681657..7d076ae94a 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -116,11 +116,9 @@ type API interface { RouteGroups() ([]RouteGroupInfo, error) SetMinHops(uint16) error - RegisterHTTPPort(localPort int) error - DeregisterHTTPPort(localPort int) error - ListHTTPPorts() ([]int, error) Publish(localPort int) (uuid.UUID, error) Depublish(id uuid.UUID) error + ListHTTPPorts() ([]int, error) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error) Disconnect(id uuid.UUID) error List() (map[uuid.UUID]*appnet.ConnectConn, error) @@ -1545,43 +1543,6 @@ func (v *Visor) IsDMSGClientReady() (bool, error) { return false, errors.New("dmsg client is not ready") } -// RegisterHTTPPort implements API. -func (v *Visor) RegisterHTTPPort(localPort int) error { - v.allowedMX.Lock() - defer v.allowedMX.Unlock() - ok := isPortAvailable(v.log, localPort) - if ok { - return fmt.Errorf("No connection on local port :%v", localPort) - } - if v.allowedPorts[localPort] { - return fmt.Errorf("Port :%v already registered", localPort) - } - v.allowedPorts[localPort] = true - return nil -} - -// DeregisterHTTPPort implements API. -func (v *Visor) DeregisterHTTPPort(localPort int) error { - v.allowedMX.Lock() - defer v.allowedMX.Unlock() - if !v.allowedPorts[localPort] { - return fmt.Errorf("Port :%v not registered", localPort) - } - delete(v.allowedPorts, localPort) - return nil -} - -// ListHTTPPorts implements API. -func (v *Visor) ListHTTPPorts() ([]int, error) { - v.allowedMX.Lock() - defer v.allowedMX.Unlock() - keys := make([]int, 0, len(v.allowedPorts)) - for k := range v.allowedPorts { - keys = append(keys, k) - } - return keys, nil -} - // Connect implements API. func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error) { v.log.Errorf("Connecting to %v:%v via %v", remotePK, remotePort, localPort) @@ -1614,6 +1575,17 @@ func (v *Visor) Disconnect(id uuid.UUID) error { return connectConn.Close() } +// ListHTTPPorts implements API. +func (v *Visor) ListHTTPPorts() ([]int, error) { + v.allowedMX.Lock() + defer v.allowedMX.Unlock() + keys := make([]int, 0, len(v.allowedPorts)) + for k := range v.allowedPorts { + keys = append(keys, k) + } + return keys, nil +} + // Publish implements API. func (v *Visor) Publish(localPort int) (uuid.UUID, error) { v.log.Errorf("Publishing on %v:%v", v.conf.PK, localPort) diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index 58ecc8bdc8..8b5a2aa3fe 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -727,18 +727,6 @@ func (r *RPC) IsDMSGClientReady(_ *struct{}, out *bool) (err error) { return err } -// RegisterHTTPPort registers the local port to be accessed by remote visors -func (r *RPC) RegisterHTTPPort(port *int, _ *struct{}) (err error) { - defer rpcutil.LogCall(r.log, "RegisterHTTPPort", port)(nil, &err) - return r.visor.RegisterHTTPPort(*port) -} - -// DeregisterHTTPPort deregisters the local port that can be accessed by remote visors -func (r *RPC) DeregisterHTTPPort(port *int, _ *struct{}) (err error) { - defer rpcutil.LogCall(r.log, "DeregisterHTTPPort", port)(nil, &err) - return r.visor.DeregisterHTTPPort(*port) -} - // ListHTTPPorts lists all the local por that can be accessed by remote visors func (r *RPC) ListHTTPPorts(_ *struct{}, out *[]int) (err error) { defer rpcutil.LogCall(r.log, "ListHTTPPorts", nil)(out, &err) diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index a26c444fec..0aa5de9f28 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -592,17 +592,6 @@ func (rc *rpcClient) List() (map[uuid.UUID]*appnet.ConnectConn, error) { return out, err } -// RegisterHTTPPort calls RegisterHTTPPort. -func (rc *rpcClient) RegisterHTTPPort(localPort int) error { - return rc.Call("RegisterHTTPPort", &localPort, &struct{}{}) -} - -// DeregisterHTTPPort calls DeregisterHTTPPort. -func (rc *rpcClient) DeregisterHTTPPort(localPort int) error { - err := rc.Call("DeregisterHTTPPort", &localPort, &struct{}{}) - return err -} - // ListHTTPPorts calls ListHTTPPorts. func (rc *rpcClient) ListHTTPPorts() ([]int, error) { var out []int @@ -1348,16 +1337,6 @@ func (mc *mockRPCClient) List() (map[uuid.UUID]*appnet.ConnectConn, error) { return nil, nil } -// RegisterHTTPPort implements API. -func (mc *mockRPCClient) RegisterHTTPPort(localPort int) error { //nolint:all - return nil -} - -// DeregisterHTTPPort implements API. -func (mc *mockRPCClient) DeregisterHTTPPort(localPort int) error { //nolint:all - return nil -} - // ListHTTPPorts implements API. func (mc *mockRPCClient) ListHTTPPorts() ([]int, error) { return nil, nil From 09b153244bdfe1dab21cf9bd2e7a7346dea08371 Mon Sep 17 00:00:00 2001 From: ersonp Date: Sat, 22 Jun 2024 17:23:26 +0530 Subject: [PATCH 07/25] chore(visor/api): Fix go-staticcheck warnings --- pkg/visor/api.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/visor/api.go b/pkg/visor/api.go index 7d076ae94a..468aca6fd3 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -400,7 +400,7 @@ func (v *Visor) DeleteRewardAddress() error { path := v.conf.LocalPath + "/" + visorconfig.RewardFile err := os.Remove(path) if err != nil { - return fmt.Errorf("Error deleting file. err=%v", err) + return fmt.Errorf("error deleting file. err=%v", err) } return nil } @@ -585,7 +585,7 @@ func (v *Visor) FetchUptimeTrackerData(pk string) ([]byte, error) { if pk != "" { err := pubkey.Set(pk) if err != nil { - return body, fmt.Errorf("Invalid or missing public key") + return body, fmt.Errorf("invalid or missing public key") } } if v.uptimeTracker == nil { @@ -611,7 +611,7 @@ func (v *Visor) StartSkysocksClient(serverKey string) error { for index, app := range v.conf.Launcher.Apps { if app.Name == visorconfig.SkysocksClientName { if v.GetSkysocksClientAddress() == "" && serverKey == "" { - return errors.New("Skysocks server pub key is missing") + return errors.New("skysocks server pub key is missing") } if serverKey != "" { @@ -1279,7 +1279,7 @@ func (v *Visor) DialPing(conf PingConfig) error { skywireConn, isSkywireConn := conn.(*appnet.SkywireConn) if !isSkywireConn { - return fmt.Errorf("Can't get such info from this conn") + return fmt.Errorf("can't get such info from this conn") } v.pingConnMx.Lock() v.pingConns[conf.PK] = ping{ From a5752242e13e199c9cbd776da16707963b61366d Mon Sep 17 00:00:00 2001 From: ersonp Date: Sat, 22 Jun 2024 17:39:33 +0530 Subject: [PATCH 08/25] chore: Update .gitignore to ignore skywire-config.json and local directories --- .gitignore | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 0618ccef16..d1d861a777 100644 --- a/.gitignore +++ b/.gitignore @@ -7,17 +7,18 @@ *.out .DS_Store *.pem +*.db .idea/ .vscode/ +./skywire-config.json ./skywire.json ./build/ build/ ./apps/ ./skywire/ -./local/* -./local*/* +local*/ ./transport_logs ./dmsgpty ./rewards @@ -26,16 +27,16 @@ rewards ./pkg/visor/apps/ ./pkg/visor/bar/ ./pkg/visor/foo/ +./skywire-config.json ./bin ./node -./users.db ./hypervisor ./*-node ./*-visor ./*-cli ./*-server -./*.json +*.json !/dmsghttp-config.json !/services-config.json ./*.sh From 5eecb1a2f0a093e2de0bca81e8a191e18e14be24 Mon Sep 17 00:00:00 2001 From: ersonp Date: Sat, 22 Jun 2024 21:11:29 +0530 Subject: [PATCH 09/25] refactor: Convert remaning skyfwd code to `net` --- pkg/app/appnet/connect.go | 59 ++-------- pkg/app/appnet/publish.go | 144 ++---------------------- pkg/app/appnet/skynet.go | 193 ++++++++++++++++++++++++++++++++ pkg/skyenv/skyenv.go | 16 ++- pkg/visor/api.go | 55 +++------ pkg/visor/init.go | 180 ++--------------------------- pkg/visor/visor.go | 10 +- pkg/visor/visorconfig/values.go | 14 +-- 8 files changed, 252 insertions(+), 419 deletions(-) create mode 100644 pkg/app/appnet/skynet.go diff --git a/pkg/app/appnet/connect.go b/pkg/app/appnet/connect.go index d5a6a48c4e..78c3b852c1 100644 --- a/pkg/app/appnet/connect.go +++ b/pkg/app/appnet/connect.go @@ -1,4 +1,4 @@ -// Package appnet pkg/app/appnet/forwarding.go +// Package appnet pkg/app/appnet/connect.go package appnet import ( @@ -16,42 +16,6 @@ import ( "github.com/skycoin/skywire-utilities/pkg/logging" ) -// nolint: gochecknoglobals -var ( - connectConns = make(map[uuid.UUID]*ConnectConn) - connectConnsMu sync.Mutex -) - -// AddConnect adds ConnectConn to with it's ID -func AddConnect(fwd *ConnectConn) { - connectConnsMu.Lock() - defer connectConnsMu.Unlock() - connectConns[fwd.ID] = fwd -} - -// GetConnectConn get's a ConnectConn by ID -func GetConnectConn(id uuid.UUID) *ConnectConn { - connectConnsMu.Lock() - defer connectConnsMu.Unlock() - - return connectConns[id] -} - -// GetAllConnectConns gets all ConnectConns -func GetAllConnectConns() map[uuid.UUID]*ConnectConn { - connectConnsMu.Lock() - defer connectConnsMu.Unlock() - - return connectConns -} - -// RemoveConnectConn removes a ConnectConn by ID -func RemoveConnectConn(id uuid.UUID) { - connectConnsMu.Lock() - defer connectConnsMu.Unlock() - delete(connectConns, id) -} - // ConnectConn represents a connection that is published on the skywire network type ConnectConn struct { ID uuid.UUID @@ -60,12 +24,12 @@ type ConnectConn struct { remoteConn net.Conn r *gin.Engine closeOnce sync.Once - closeChan chan struct{} log *logging.Logger + nm *NetManager } // NewConnectConn creates a new ConnectConn -func NewConnectConn(log *logging.Logger, remoteConn net.Conn, remotePK cipher.PubKey, remotePort, webPort int) *ConnectConn { +func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, remotePK cipher.PubKey, remotePort, webPort int) *ConnectConn { httpC := &http.Client{Transport: MakeHTTPTransport(remoteConn, log)} mu := new(sync.Mutex) @@ -78,6 +42,11 @@ func NewConnectConn(log *logging.Logger, remoteConn net.Conn, remotePK cipher.Pu r.Any("/*path", handleConnectFunc(httpC, remotePK, remotePort, mu)) + // srv := &http.Server{ + // Addr: ":8080", + // Handler: r, + // } + fwdConn := &ConnectConn{ ID: uuid.New(), remoteConn: remoteConn, @@ -85,9 +54,10 @@ func NewConnectConn(log *logging.Logger, remoteConn net.Conn, remotePK cipher.Pu RemotePort: remotePort, log: log, r: r, + nm: nm, } - AddConnect(fwdConn) + nm.AddConnect(fwdConn) return fwdConn } @@ -102,13 +72,6 @@ func (f *ConnectConn) Serve() { } } }() - go func() { - <-f.closeChan - err := f.Close() - if err != nil { - f.log.Error(err) - } - }() f.log.Debugf("Serving on localhost:%v", f.WebPort) } @@ -116,7 +79,7 @@ func (f *ConnectConn) Serve() { func (f *ConnectConn) Close() (err error) { f.closeOnce.Do(func() { err = f.remoteConn.Close() - RemoveConnectConn(f.ID) + f.nm.RemoveConnectConn(f.ID) }) return err } diff --git a/pkg/app/appnet/publish.go b/pkg/app/appnet/publish.go index 314038250c..eb7c23f275 100644 --- a/pkg/app/appnet/publish.go +++ b/pkg/app/appnet/publish.go @@ -1,4 +1,4 @@ -// Package appnet pkg/app/appnet/forwarding.go +// Package appnet pkg/app/appnet/publish.go package appnet import ( @@ -17,42 +17,6 @@ import ( "github.com/skycoin/skywire-utilities/pkg/logging" ) -// nolint: gochecknoglobals -var ( - publishListenertners = make(map[uuid.UUID]*publishListener) - publishListenerMu sync.Mutex -) - -// AddPublish adds publishListener to with it's ID -func AddPublish(fwd *publishListener) { - publishListenerMu.Lock() - defer publishListenerMu.Unlock() - publishListenertners[fwd.ID] = fwd -} - -// GetpublishListenertner get's a publishListener by ID -func GetpublishListenertner(id uuid.UUID) *publishListener { - publishListenerMu.Lock() - defer publishListenerMu.Unlock() - - return publishListenertners[id] -} - -// GetAllpublishListenertners gets all publishListeners -func GetAllpublishListenertners() map[uuid.UUID]*publishListener { - publishListenerMu.Lock() - defer publishListenerMu.Unlock() - - return publishListenertners -} - -// RemovepublishListener removes a publishListener by ID -func RemovepublishListener(id uuid.UUID) { - publishListenerMu.Lock() - defer publishListenerMu.Unlock() - delete(publishListenertners, id) -} - // publishListener represents a publishion that is published on the skywire network type publishListener struct { ID uuid.UUID @@ -60,8 +24,8 @@ type publishListener struct { lis net.Listener closeOnce sync.Once srv *http.Server - closeChan chan struct{} log *logging.Logger + nm *NetManager } type ginHandler struct { @@ -73,14 +37,13 @@ func (h *ginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // NewPublishListener creates a new publishListener -func NewPublishListener(log *logging.Logger, lis net.Listener, localPort int) *publishListener { - closeChan := make(chan struct{}) +func NewPublishListener(log *logging.Logger, nm *NetManager, lis net.Listener, localPort int) (*publishListener, error) { + r1 := gin.New() r1.Use(gin.Recovery()) r1.Use(loggingMiddleware()) authRoute := r1.Group("/") authRoute.Any("/*path", func(c *gin.Context) { - log.Error("Request received") targetURL, _ := url.Parse(fmt.Sprintf("http://127.0.0.1:%v%s?%s", localPort, c.Request.URL.Path, c.Request.URL.RawQuery)) //nolint proxy := httputil.ReverseProxy{ Director: func(req *http.Request) { @@ -92,6 +55,7 @@ func NewPublishListener(log *logging.Logger, lis net.Listener, localPort int) *p } proxy.ServeHTTP(c.Writer, c.Request) }) + srv := &http.Server{ Handler: &ginHandler{Router: r1}, ReadHeaderTimeout: 5 * time.Second, @@ -103,11 +67,11 @@ func NewPublishListener(log *logging.Logger, lis net.Listener, localPort int) *p srv: srv, lis: lis, LocalPort: localPort, - closeChan: closeChan, log: log, + nm: nm, } - AddPublish(pubLis) - return pubLis + nm.AddPublish(pubLis) + return pubLis, nil } // Serve serves a HTTP forward Lis that accepts all requests and forwards them directly to the remote server over the specified net.Lis. @@ -121,105 +85,15 @@ func (f *publishListener) Listen() { } } }() - go func() { - <-f.closeChan - err := f.Close() - if err != nil { - f.log.Error(err) - } - }() f.log.Debugf("Serving HTTP on dmsg port %v with DMSG listener %s", f.LocalPort, f.lis.Addr().String()) } // Close closes the server and remote publishion. func (f *publishListener) Close() (err error) { f.closeOnce.Do(func() { - f.log.Error("Closing publishListener") err = f.srv.Close() err = f.lis.Close() - RemovepublishListener(f.ID) + f.nm.RemovePublishListener(f.ID) }) return err } - -func loggingMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - start := time.Now() - c.Next() - latency := time.Since(start) - if latency > time.Minute { - latency = latency.Truncate(time.Second) - } - statusCode := c.Writer.Status() - method := c.Request.Method - path := c.Request.URL.Path - // Get the background color based on the status code - statusCodeBackgroundColor := getBackgroundColor(statusCode) - // Get the method color - methodColor := getMethodColor(method) - // Print the logging in a custom format which includes the publickeyfrom c.Request.RemoteAddr ex.: - // [DMSGHTTP] 2023/05/18 - 19:43:15 | 200 | 10.80885ms | | 02b5ee5333aa6b7f5fc623b7d5f35f505cb7f974e98a70751cf41962f84c8c4637:49153 | GET /node-info.json - fmt.Printf("[DMSGWEB] %s |%s %3d %s| %13v | %15s | %72s |%s %-7s %s %s\n", - time.Now().Format("2006/01/02 - 15:04:05"), - statusCodeBackgroundColor, - statusCode, - resetColor(), - latency, - c.ClientIP(), - c.Request.RemoteAddr, - methodColor, - method, - resetColor(), - path, - ) - } -} - -func getBackgroundColor(statusCode int) string { - switch { - case statusCode >= http.StatusOK && statusCode < http.StatusMultipleChoices: - return green - case statusCode >= http.StatusMultipleChoices && statusCode < http.StatusBadRequest: - return white - case statusCode >= http.StatusBadRequest && statusCode < http.StatusInternalServerError: - return yellow - default: - return red - } -} - -func getMethodColor(method string) string { - switch method { - case http.MethodGet: - return blue - case http.MethodPost: - return cyan - case http.MethodPut: - return yellow - case http.MethodDelete: - return red - case http.MethodPatch: - return green - case http.MethodHead: - return magenta - case http.MethodOptions: - return white - default: - return reset - } -} - -func resetColor() string { - return reset -} - -const ( - green = "\033[97;42m" - white = "\033[90;47m" - yellow = "\033[90;43m" - red = "\033[97;41m" - blue = "\033[97;44m" - magenta = "\033[97;45m" - cyan = "\033[97;46m" - reset = "\033[0m" -) diff --git a/pkg/app/appnet/skynet.go b/pkg/app/appnet/skynet.go new file mode 100644 index 0000000000..533a5c9654 --- /dev/null +++ b/pkg/app/appnet/skynet.go @@ -0,0 +1,193 @@ +// Package appnet pkg/app/appnet/skynet.go +package appnet + +import ( + "fmt" + "net/http" + "sync" + "time" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +type NetManager struct { + listeners map[uuid.UUID]*publishListener + conns map[uuid.UUID]*ConnectConn + mu sync.Mutex +} + +func NewNetManager() *NetManager { + return &NetManager{ + listeners: make(map[uuid.UUID]*publishListener), + conns: make(map[uuid.UUID]*ConnectConn), + } +} + +// AddPublish adds publishListener to with it's ID +func (nm *NetManager) AddPublish(lis *publishListener) { + nm.mu.Lock() + defer nm.mu.Unlock() + + nm.listeners[lis.ID] = lis +} + +// GetpublishListenertner get's a publishListener by ID +func (nm *NetManager) GetPublishListener(id uuid.UUID) *publishListener { + nm.mu.Lock() + defer nm.mu.Unlock() + + return nm.listeners[id] +} + +// GetAllpublishListenertners gets all publishListeners +func (nm *NetManager) GetAllPublishListeners() map[uuid.UUID]*publishListener { + nm.mu.Lock() + defer nm.mu.Unlock() + + return nm.listeners +} + +// RemovepublishListener removes a publishListener by ID +func (nm *NetManager) RemovePublishListener(id uuid.UUID) { + nm.mu.Lock() + defer nm.mu.Unlock() + + delete(nm.listeners, id) +} + +// AddConnect adds ConnectConn to with it's ID +func (nm *NetManager) AddConnect(conn *ConnectConn) { + nm.mu.Lock() + defer nm.mu.Unlock() + + nm.conns[conn.ID] = conn +} + +// GetConnectConn get's a ConnectConn by ID +func (nm *NetManager) GetConnectConn(id uuid.UUID) *ConnectConn { + nm.mu.Lock() + defer nm.mu.Unlock() + + return nm.conns[id] +} + +// GetAllConnectConns gets all ConnectConns +func (nm *NetManager) GetAllConnectConns() map[uuid.UUID]*ConnectConn { + nm.mu.Lock() + defer nm.mu.Unlock() + + return nm.conns +} + +// RemoveConnectConn removes a ConnectConn by ID +func (nm *NetManager) RemoveConnectConn(id uuid.UUID) { + nm.mu.Lock() + defer nm.mu.Unlock() + + delete(nm.conns, id) +} + +// RemoveConnectConn removes a ConnectConn by ID +func (nm *NetManager) Close() error { + nm.mu.Lock() + defer nm.mu.Unlock() + + for _, conn := range nm.conns { + err := conn.Close() + if err != nil { + return err + } + } + + for _, lis := range nm.listeners { + err := lis.Close() + if err != nil { + return err + } + } + + return nil +} + +func loggingMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + start := time.Now() + c.Next() + latency := time.Since(start) + if latency > time.Minute { + latency = latency.Truncate(time.Second) + } + statusCode := c.Writer.Status() + method := c.Request.Method + path := c.Request.URL.Path + // Get the background color based on the status code + statusCodeBackgroundColor := getBackgroundColor(statusCode) + // Get the method color + methodColor := getMethodColor(method) + // Print the logging in a custom format which includes the publickeyfrom c.Request.RemoteAddr ex.: + // [DMSGHTTP] 2023/05/18 - 19:43:15 | 200 | 10.80885ms | | 02b5ee5333aa6b7f5fc623b7d5f35f505cb7f974e98a70751cf41962f84c8c4637:49153 | GET /node-info.json + fmt.Printf("[DMSGWEB] %s |%s %3d %s| %13v | %15s | %72s |%s %-7s %s %s\n", + time.Now().Format("2006/01/02 - 15:04:05"), + statusCodeBackgroundColor, + statusCode, + resetColor(), + latency, + c.ClientIP(), + c.Request.RemoteAddr, + methodColor, + method, + resetColor(), + path, + ) + } +} + +func getBackgroundColor(statusCode int) string { + switch { + case statusCode >= http.StatusOK && statusCode < http.StatusMultipleChoices: + return green + case statusCode >= http.StatusMultipleChoices && statusCode < http.StatusBadRequest: + return white + case statusCode >= http.StatusBadRequest && statusCode < http.StatusInternalServerError: + return yellow + default: + return red + } +} + +func getMethodColor(method string) string { + switch method { + case http.MethodGet: + return blue + case http.MethodPost: + return cyan + case http.MethodPut: + return yellow + case http.MethodDelete: + return red + case http.MethodPatch: + return green + case http.MethodHead: + return magenta + case http.MethodOptions: + return white + default: + return reset + } +} + +func resetColor() string { + return reset +} + +const ( + green = "\033[97;42m" + white = "\033[90;47m" + yellow = "\033[90;43m" + red = "\033[97;41m" + blue = "\033[97;44m" + magenta = "\033[97;45m" + cyan = "\033[97;46m" + reset = "\033[0m" +) diff --git a/pkg/skyenv/skyenv.go b/pkg/skyenv/skyenv.go index 518a592a5c..59a1e47a73 100644 --- a/pkg/skyenv/skyenv.go +++ b/pkg/skyenv/skyenv.go @@ -59,15 +59,13 @@ const ( // TODO(darkrengarius): this one's not needed for the app to run but lack of it causes errors - VPNClientPort uint16 = 43 // VPNClientPort ... - ExampleServerName = "example-server-app" // ExampleServerName ... - ExampleServerPort uint16 = 45 // ExampleServerPort ... - ExampleClientName = "example-client-app" // ExampleClientName ... - ExampleClientPort uint16 = 46 // ExampleClientPort ... - SkyForwardingServerName = "sky-forwarding" // SkyForwardingServerName ... - SkyForwardingServerPort uint16 = 47 // SkyForwardingServerPort ... - SkyPingName = "sky-ping" // SkyPingName ... - SkyPingPort uint16 = 48 // SkyPingPort ... + VPNClientPort uint16 = 43 // VPNClientPort ... + ExampleServerName = "example-server-app" // ExampleServerName ... + ExampleServerPort uint16 = 45 // ExampleServerPort ... + ExampleClientName = "example-client-app" // ExampleClientName ... + ExampleClientPort uint16 = 46 // ExampleClientPort ... + SkyPingName = "sky-ping" // SkyPingName ... + SkyPingPort uint16 = 48 // SkyPingPort ... // RPC constants. diff --git a/pkg/visor/api.go b/pkg/visor/api.go index 468aca6fd3..8627adf5b2 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -1545,11 +1545,7 @@ func (v *Visor) IsDMSGClientReady() (bool, error) { // Connect implements API. func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error) { - v.log.Errorf("Connecting to %v:%v via %v", remotePK, remotePort, localPort) - ok := isPortAvailable(v.log, localPort) - if !ok { - return uuid.UUID{}, fmt.Errorf(":%v local port already in use", localPort) - } + connApp := appnet.Addr{ Net: appnet.TypeSkynet, PubKey: remotePK, @@ -1564,14 +1560,15 @@ func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid return uuid.UUID{}, err } - connectConn := appnet.NewConnectConn(v.log, remoteConn, remotePK, remotePort, localPort) + connectConn := appnet.NewConnectConn(v.log, v.nM, remoteConn, remotePK, remotePort, localPort) connectConn.Serve() + return connectConn.ID, nil } // Disconnect implements API. func (v *Visor) Disconnect(id uuid.UUID) error { - connectConn := appnet.GetConnectConn(id) + connectConn := v.nM.GetConnectConn(id) return connectConn.Close() } @@ -1588,11 +1585,7 @@ func (v *Visor) ListHTTPPorts() ([]int, error) { // Publish implements API. func (v *Visor) Publish(localPort int) (uuid.UUID, error) { - v.log.Errorf("Publishing on %v:%v", v.conf.PK, localPort) - ok := isPortAvailable(v.log, localPort) - if ok { - return uuid.UUID{}, fmt.Errorf(":%v local port not in use", localPort) - } + connApp := appnet.Addr{ Net: appnet.TypeSkynet, PubKey: v.conf.PK, @@ -1604,7 +1597,11 @@ func (v *Visor) Publish(localPort int) (uuid.UUID, error) { return uuid.UUID{}, err } - publishLis := appnet.NewPublishListener(v.log, lis, localPort) + publishLis, err := appnet.NewPublishListener(v.log, v.nM, lis, localPort) + if err != nil { + return uuid.UUID{}, err + } + publishLis.Listen() return publishLis.ID, nil @@ -1612,37 +1609,11 @@ func (v *Visor) Publish(localPort int) (uuid.UUID, error) { // Depublish implements API. func (v *Visor) Depublish(id uuid.UUID) error { - forwardConn := appnet.GetConnectConn(id) - return forwardConn.Close() + publishConn := v.nM.GetPublishListener(id) + return publishConn.Close() } // List implements API. func (v *Visor) List() (map[uuid.UUID]*appnet.ConnectConn, error) { - return appnet.GetAllConnectConns(), nil -} - -func isPortAvailable(log *logging.Logger, port int) bool { - timeout := time.Second - conn, err := net.DialTimeout("tcp", fmt.Sprintf(":%v", port), timeout) - if err != nil { - return true - } - if conn != nil { - defer closeConn(log, conn) - return false - } - return true -} - -func isPortRegistered(port int, v *Visor) bool { - ports, err := v.ListHTTPPorts() - if err != nil { - return false - } - for _, p := range ports { - if p == port { - return true - } - } - return false + return v.nM.GetAllConnectConns(), nil } diff --git a/pkg/visor/init.go b/pkg/visor/init.go index 4f341c1b88..3759f8adfe 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -2,8 +2,6 @@ package visor import ( - "bufio" - "bytes" "context" "encoding/json" "errors" @@ -130,8 +128,8 @@ var ( dmsgHTTP vinit.Module // Dmsg trackers module dmsgTrackers vinit.Module - // Skywire Forwarding conn module - skyFwd vinit.Module + // Skywire Net module + skyNet vinit.Module // Ping module pi vinit.Module // visor that groups all modules together @@ -175,12 +173,12 @@ func registerModules(logger *logging.MasterLogger) { trs = maker("transport_setup", initTransportSetup, &dmsgC, &tr) tm = vinit.MakeModule("transports", vinit.DoNothing, logger, &sc, &sudphC, &dmsgCtrl, &dmsgHTTPLogServer, &dmsgTrackers, &launch) pvs = maker("public_visor", initPublicVisor, &tr, &ar, &disc, &stcprC) - skyFwd = maker("sky_forward_conn", initSkywireForwardConn, &dmsgC, &dmsgCtrl, &tr, &launch) + skyNet = maker("sky_net", initSkywireNet, &dmsgC, &dmsgCtrl, &tr, &launch) pi = maker("ping", initPing, &dmsgC, &tm) tc = maker("transportable", initEnsureVisorIsTransportable, &dmsgC, &tm) tpdco = maker("tpd_concurrency", initEnsureTPDConcurrency, &dmsgC, &tm) vis = vinit.MakeModule("visor", vinit.DoNothing, logger, &ebc, &ar, &disc, &pty, - &tr, &rt, &launch, &cli, &hvs, &ut, &pv, &pvs, &trs, &stcpC, &stcprC, &skyFwd, &pi, &systemSurvey, &tc, &tpdco) + &tr, &rt, &launch, &cli, &hvs, &ut, &pv, &pvs, &trs, &stcpC, &stcprC, &skyNet, &pi, &systemSurvey, &tc, &tpdco) hv = maker("hypervisor", initHypervisor, &vis) } @@ -612,182 +610,28 @@ func initTransportSetup(ctx context.Context, v *Visor, log *logging.Logger) erro return nil } -func initSkywireForwardConn(ctx context.Context, v *Visor, log *logging.Logger) error { +func initSkywireNet(ctx context.Context, v *Visor, log *logging.Logger) error { ctx, cancel := context.WithCancel(ctx) // waiting for at least one transport to initialize <-v.tpM.Ready() - connApp := appnet.Addr{ - Net: appnet.TypeSkynet, - PubKey: v.conf.PK, - Port: routing.Port(skyenv.SkyForwardingServerPort), - } - l, err := appnet.ListenContext(ctx, connApp) - if err != nil { - cancel() - return err - } - v.pushCloseStack("sky_forwarding", func() error { + nM := appnet.NewNetManager() + + v.pushCloseStack("sky_net", func() error { cancel() - if cErr := l.Close(); cErr != nil { + if cErr := nM.Close(); cErr != nil { log.WithError(cErr).Error("Error closing listener.") } return nil }) - go func() { - for { - log.Debug("Accepting sky forwarding conn...") - conn, err := l.Accept() - if err != nil { - if !errors.Is(appnet.ErrClosedConn, err) { - log.WithError(err).Error("Failed to accept conn") - } - return - } - log.Debug("Accepted sky forwarding conn") - - v.pushCloseStack("sky_forwarding", func() error { - cancel() - if cErr := conn.Close(); cErr != nil { - log.WithError(cErr).Error("Error closing conn.") - } - return nil - }) - - log.Debug("Wrapping conn...") - wrappedConn, err := appnet.WrapConn(conn) - if err != nil { - log.WithError(err).Error("Failed to wrap conn") - return - } - - rAddr := wrappedConn.RemoteAddr().(appnet.Addr) - log.Debugf("Accepted sky forwarding conn on %s from %s", wrappedConn.LocalAddr(), rAddr.PubKey) - go handleServerConn(log, wrappedConn, v) - } - }() + v.initLock.Lock() + v.nM = nM + v.initLock.Unlock() return nil } -func handleServerConn(log *logging.Logger, remoteConn net.Conn, v *Visor) { - buf := make([]byte, 32*1024) - n, err := remoteConn.Read(buf) - if err != nil { - log.WithError(err).Error("Failed to read packet") - return - } - - var cMsg clientMsg - err = json.Unmarshal(buf[:n], &cMsg) - if err != nil { - log.WithError(err).Error("Failed to marshal json") - sendError(log, remoteConn, err) - return - } - log.Debugf("Received: %v", cMsg) - - lHost := fmt.Sprintf("localhost:%v", cMsg.Port) - ok := isPortRegistered(cMsg.Port, v) - if !ok { - log.Errorf("Port :%v not registered", cMsg.Port) - sendError(log, remoteConn, fmt.Errorf("Port :%v not registered", cMsg.Port)) - return - } - - ok = isPortAvailable(log, cMsg.Port) - if ok { - log.Errorf("Failed to dial port %v", cMsg.Port) - sendError(log, remoteConn, fmt.Errorf("Failed to dial port %v", cMsg.Port)) - return - } - - log.Debugf("Forwarding %s", lHost) - - // send nil error to indicate to the remote connection that everything is ok - sendError(log, remoteConn, nil) - - go forward(log, remoteConn, lHost) -} - -// forward reads a http.Request from the remote conn of the requesting visor forwards that request -// to the requested local server and forwards the http.Response from the local server to the requesting -// visor via the remote conn -func forward(log *logging.Logger, remoteConn net.Conn, lHost string) { - for { - buf := make([]byte, 32*1024) - n, err := remoteConn.Read(buf) - if err != nil { - log.WithError(err).Error("Failed to read packet") - closeConn(log, remoteConn) - return - } - req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(buf[:n]))) - if err != nil { - log.WithError(err).Error("Failed to ReadRequest") - closeConn(log, remoteConn) - return - } - req.RequestURI = "" - req.URL.Scheme = "http" - req.URL.Host = lHost - client := http.Client{} - resp, err := client.Do(req) - if err != nil { - log.WithError(err).Error("Failed to Do req") - closeConn(log, remoteConn) - return - } - err = resp.Write(remoteConn) - if err != nil { - log.WithError(err).Error("Failed to Write") - closeConn(log, remoteConn) - return - } - } -} - -func sendError(log *logging.Logger, remoteConn net.Conn, sendErr error) { - var sReply serverReply - if sendErr != nil { - sErr := sendErr.Error() - sReply = serverReply{ - Error: &sErr, - } - } - - srvReply, err := json.Marshal(sReply) - if err != nil { - log.WithError(err).Error("Failed to unmarshal json") - } - - _, err = remoteConn.Write([]byte(srvReply)) - if err != nil { - log.WithError(err).Error("Failed write server msg") - } - - log.Debugf("Server reply sent %s", srvReply) - // close conn if we send an error - if sendErr != nil { - closeConn(log, remoteConn) - } -} - -func closeConn(log *logging.Logger, conn net.Conn) { - if err := conn.Close(); err != nil { - log.WithError(err).Errorf("Error closing client %s connection", conn.RemoteAddr()) - } -} - -type clientMsg struct { - Port int `json:"port"` -} - -type serverReply struct { - Error *string `json:"error,omitempty"` -} - func initPing(ctx context.Context, v *Visor, log *logging.Logger) error { ctx, cancel := context.WithCancel(ctx) // waiting for at least one transport to initialize diff --git a/pkg/visor/visor.go b/pkg/visor/visor.go index 63c21fc6a7..208244072a 100644 --- a/pkg/visor/visor.go +++ b/pkg/visor/visor.go @@ -93,6 +93,7 @@ type Visor struct { arClient addrresolver.APIClient router router.Router rfClient rfclient.Client + nM *appnet.NetManager procM appserver.ProcManager // proc manager appL *launcher.AppLauncher // app launcher @@ -406,15 +407,6 @@ func (v *Visor) Close() error { log := v.MasterLogger().PackageLogger("visor:shutdown") log.Info("Begin shutdown.") - // Cleanly close ongoing forward conns - for _, forwardConn := range appnet.GetAllConnectConns() { - err := forwardConn.Close() - if err != nil { - log.WithError(err).Warn("Forward conn stopped with unexpected result.") - continue - } - } - for i := len(v.closeStack) - 1; i >= 0; i-- { cl := v.closeStack[i] diff --git a/pkg/visor/visorconfig/values.go b/pkg/visor/visorconfig/values.go index df50d143fd..8db28f0193 100644 --- a/pkg/visor/visorconfig/values.go +++ b/pkg/visor/visorconfig/values.go @@ -87,14 +87,12 @@ var ( VPNClientPort = skyenv.VPNClientPort // VPNClientPort ... - ExampleServerName = skyenv.ExampleServerName // ExampleServerName ... - ExampleServerPort = skyenv.ExampleServerPort // ExampleServerPort ... - ExampleClientName = skyenv.ExampleClientName // ExampleClientName ... - ExampleClientPort = skyenv.ExampleClientPort // ExampleClientPort ... - SkyForwardingServerName = skyenv.SkyForwardingServerName // SkyForwardingServerName ... - SkyForwardingServerPort = skyenv.SkyForwardingServerPort // SkyForwardingServerPort ... - SkyPingName = skyenv.SkyPingName // SkyPingName ... - SkyPingPort = skyenv.SkyPingPort // SkyPingPort ... + ExampleServerName = skyenv.ExampleServerName // ExampleServerName ... + ExampleServerPort = skyenv.ExampleServerPort // ExampleServerPort ... + ExampleClientName = skyenv.ExampleClientName // ExampleClientName ... + ExampleClientPort = skyenv.ExampleClientPort // ExampleClientPort ... + SkyPingName = skyenv.SkyPingName // SkyPingName ... + SkyPingPort = skyenv.SkyPingPort // SkyPingPort ... // RPC constants. From 27b864e80fe3ca61d98c88e467f06979231b3009 Mon Sep 17 00:00:00 2001 From: ersonp Date: Sun, 23 Jun 2024 00:52:18 +0530 Subject: [PATCH 10/25] refactor: Rename and change `ListHTTPPorts` and `ListConnected` --- cmd/skywire-cli/commands/net/connect.go | 8 +++--- cmd/skywire-cli/commands/net/publish.go | 33 ++++++++++----------- pkg/app/appnet/addr.go | 5 ++++ pkg/app/appnet/connect.go | 11 +++---- pkg/app/appnet/publish.go | 12 ++++---- pkg/app/appnet/skynet.go | 10 +++---- pkg/visor/api.go | 38 ++++++++++++------------- pkg/visor/rpc.go | 26 ++++++++--------- pkg/visor/rpc_client.go | 20 ++++++------- 9 files changed, 85 insertions(+), 78 deletions(-) diff --git a/cmd/skywire-cli/commands/net/connect.go b/cmd/skywire-cli/commands/net/connect.go index bfb7f85174..8810bb4b77 100644 --- a/cmd/skywire-cli/commands/net/connect.go +++ b/cmd/skywire-cli/commands/net/connect.go @@ -47,17 +47,17 @@ var conCmd = &cobra.Command{ } if lsPorts { - connectConns, err := rpcClient.List() + connectConns, err := rpcClient.ListConnected() internal.Catch(cmd.Flags(), err) var b bytes.Buffer w := tabwriter.NewWriter(&b, 0, 0, 3, ' ', tabwriter.TabIndent) - _, err = fmt.Fprintln(w, "id\tlocal_port\tremote_port") + _, err = fmt.Fprintln(w, "id\taddr\tweb_port") internal.Catch(cmd.Flags(), err) for _, connectConn := range connectConns { - _, err = fmt.Fprintf(w, "%s\t%s\t%s\n", connectConn.ID, strconv.Itoa(int(connectConn.WebPort)), - strconv.Itoa(int(connectConn.RemotePort))) + _, err = fmt.Fprintf(w, "%s\t%s\t%s\n", connectConn.ID, connectConn.Addr.String(), + strconv.Itoa(int(connectConn.WebPort))) internal.Catch(cmd.Flags(), err) } internal.Catch(cmd.Flags(), w.Flush()) diff --git a/cmd/skywire-cli/commands/net/publish.go b/cmd/skywire-cli/commands/net/publish.go index b05b577db2..0db82088de 100644 --- a/cmd/skywire-cli/commands/net/publish.go +++ b/cmd/skywire-cli/commands/net/publish.go @@ -40,19 +40,28 @@ var pubCmd = &cobra.Command{ os.Exit(1) } + if depublish != "" { + id, err := uuid.Parse(depublish) + internal.Catch(cmd.Flags(), err) + err = rpcClient.Depublish(id) + internal.Catch(cmd.Flags(), err) + internal.PrintOutput(cmd.Flags(), "OK", "OK\n") + os.Exit(0) + } + if lsPorts { - ports, err := rpcClient.ListHTTPPorts() + liss, err := rpcClient.ListPublished() internal.Catch(cmd.Flags(), err) var b bytes.Buffer w := tabwriter.NewWriter(&b, 0, 0, 2, ' ', tabwriter.TabIndent) _, err = fmt.Fprintln(w, "id\tlocal_port") internal.Catch(cmd.Flags(), err) - for id, port := range ports { - _, err = fmt.Fprintf(w, "%v\t%v\n", id, port) + for id, lis := range liss { + _, err = fmt.Fprintf(w, "%v\t%v\n", id, lis.LocalPort) internal.Catch(cmd.Flags(), err) } internal.Catch(cmd.Flags(), w.Flush()) - internal.PrintOutput(cmd.Flags(), ports, b.String()) + internal.PrintOutput(cmd.Flags(), liss, b.String()) os.Exit(0) } @@ -77,18 +86,10 @@ var pubCmd = &cobra.Command{ internal.PrintFatalError(cmd.Flags(), fmt.Errorf("port cannot be greater than 65535")) } - if depublish != "" { - id, err := uuid.Parse(disconnect) - internal.Catch(cmd.Flags(), err) - err = rpcClient.Depublish(id) - internal.Catch(cmd.Flags(), err) - - } else { - internal.Catch(cmd.Flags(), err) - id, err := rpcClient.Publish(portNo) - internal.Catch(cmd.Flags(), err) - internal.PrintOutput(cmd.Flags(), "id: %v\n", fmt.Sprintln(id)) - } + internal.Catch(cmd.Flags(), err) + id, err := rpcClient.Publish(portNo) + internal.Catch(cmd.Flags(), err) + internal.PrintOutput(cmd.Flags(), "id: %v\n", fmt.Sprintln(id)) }, } diff --git a/pkg/app/appnet/addr.go b/pkg/app/appnet/addr.go index 37d512bcf9..eab9bdfa5e 100644 --- a/pkg/app/appnet/addr.go +++ b/pkg/app/appnet/addr.go @@ -44,6 +44,11 @@ func (a Addr) PK() cipher.PubKey { return a.PubKey } +// GetPort returns port in the Addr. +func (a Addr) GetPort() routing.Port { + return a.Port +} + // ConvertAddr asserts type of the passed `net.Addr` and converts it // to `Addr` if possible. func ConvertAddr(addr net.Addr) (Addr, error) { diff --git a/pkg/app/appnet/connect.go b/pkg/app/appnet/connect.go index 78c3b852c1..2a89df5e95 100644 --- a/pkg/app/appnet/connect.go +++ b/pkg/app/appnet/connect.go @@ -14,13 +14,14 @@ import ( "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/logging" + "github.com/skycoin/skywire/pkg/routing" ) // ConnectConn represents a connection that is published on the skywire network type ConnectConn struct { ID uuid.UUID WebPort int - RemotePort int + Addr Addr remoteConn net.Conn r *gin.Engine closeOnce sync.Once @@ -29,7 +30,7 @@ type ConnectConn struct { } // NewConnectConn creates a new ConnectConn -func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, remotePK cipher.PubKey, remotePort, webPort int) *ConnectConn { +func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, addr Addr, webPort int) *ConnectConn { httpC := &http.Client{Transport: MakeHTTPTransport(remoteConn, log)} mu := new(sync.Mutex) @@ -40,7 +41,7 @@ func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, re r.Use(loggingMiddleware()) - r.Any("/*path", handleConnectFunc(httpC, remotePK, remotePort, mu)) + r.Any("/*path", handleConnectFunc(httpC, addr.PK(), addr.GetPort(), mu)) // srv := &http.Server{ // Addr: ":8080", @@ -51,7 +52,7 @@ func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, re ID: uuid.New(), remoteConn: remoteConn, WebPort: webPort, - RemotePort: remotePort, + Addr: addr, log: log, r: r, nm: nm, @@ -84,7 +85,7 @@ func (f *ConnectConn) Close() (err error) { return err } -func handleConnectFunc(httpC *http.Client, remotePK cipher.PubKey, remotePort int, mu *sync.Mutex) func(c *gin.Context) { +func handleConnectFunc(httpC *http.Client, remotePK cipher.PubKey, remotePort routing.Port, mu *sync.Mutex) func(c *gin.Context) { return func(c *gin.Context) { mu.Lock() defer mu.Unlock() diff --git a/pkg/app/appnet/publish.go b/pkg/app/appnet/publish.go index eb7c23f275..cc31527c8f 100644 --- a/pkg/app/appnet/publish.go +++ b/pkg/app/appnet/publish.go @@ -17,8 +17,8 @@ import ( "github.com/skycoin/skywire-utilities/pkg/logging" ) -// publishListener represents a publishion that is published on the skywire network -type publishListener struct { +// PublishLis represents a publishion that is published on the skywire network +type PublishLis struct { ID uuid.UUID LocalPort int lis net.Listener @@ -37,7 +37,7 @@ func (h *ginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // NewPublishListener creates a new publishListener -func NewPublishListener(log *logging.Logger, nm *NetManager, lis net.Listener, localPort int) (*publishListener, error) { +func NewPublishListener(log *logging.Logger, nm *NetManager, lis net.Listener, localPort int) (*PublishLis, error) { r1 := gin.New() r1.Use(gin.Recovery()) @@ -62,7 +62,7 @@ func NewPublishListener(log *logging.Logger, nm *NetManager, lis net.Listener, l WriteTimeout: 10 * time.Second, } - pubLis := &publishListener{ + pubLis := &PublishLis{ ID: uuid.New(), srv: srv, lis: lis, @@ -75,7 +75,7 @@ func NewPublishListener(log *logging.Logger, nm *NetManager, lis net.Listener, l } // Serve serves a HTTP forward Lis that accepts all requests and forwards them directly to the remote server over the specified net.Lis. -func (f *publishListener) Listen() { +func (f *PublishLis) Listen() { go func() { err := f.srv.Serve(f.lis) if err != nil { @@ -89,7 +89,7 @@ func (f *publishListener) Listen() { } // Close closes the server and remote publishion. -func (f *publishListener) Close() (err error) { +func (f *PublishLis) Close() (err error) { f.closeOnce.Do(func() { err = f.srv.Close() err = f.lis.Close() diff --git a/pkg/app/appnet/skynet.go b/pkg/app/appnet/skynet.go index 533a5c9654..c8536c8ea9 100644 --- a/pkg/app/appnet/skynet.go +++ b/pkg/app/appnet/skynet.go @@ -12,20 +12,20 @@ import ( ) type NetManager struct { - listeners map[uuid.UUID]*publishListener + listeners map[uuid.UUID]*PublishLis conns map[uuid.UUID]*ConnectConn mu sync.Mutex } func NewNetManager() *NetManager { return &NetManager{ - listeners: make(map[uuid.UUID]*publishListener), + listeners: make(map[uuid.UUID]*PublishLis), conns: make(map[uuid.UUID]*ConnectConn), } } // AddPublish adds publishListener to with it's ID -func (nm *NetManager) AddPublish(lis *publishListener) { +func (nm *NetManager) AddPublish(lis *PublishLis) { nm.mu.Lock() defer nm.mu.Unlock() @@ -33,7 +33,7 @@ func (nm *NetManager) AddPublish(lis *publishListener) { } // GetpublishListenertner get's a publishListener by ID -func (nm *NetManager) GetPublishListener(id uuid.UUID) *publishListener { +func (nm *NetManager) GetPublishListener(id uuid.UUID) *PublishLis { nm.mu.Lock() defer nm.mu.Unlock() @@ -41,7 +41,7 @@ func (nm *NetManager) GetPublishListener(id uuid.UUID) *publishListener { } // GetAllpublishListenertners gets all publishListeners -func (nm *NetManager) GetAllPublishListeners() map[uuid.UUID]*publishListener { +func (nm *NetManager) GetAllPublishListeners() map[uuid.UUID]*PublishLis { nm.mu.Lock() defer nm.mu.Unlock() diff --git a/pkg/visor/api.go b/pkg/visor/api.go index 8627adf5b2..ec40d2a88f 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -118,10 +118,10 @@ type API interface { Publish(localPort int) (uuid.UUID, error) Depublish(id uuid.UUID) error - ListHTTPPorts() ([]int, error) + ListPublished() (map[uuid.UUID]*appnet.PublishLis, error) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error) Disconnect(id uuid.UUID) error - List() (map[uuid.UUID]*appnet.ConnectConn, error) + ListConnected() (map[uuid.UUID]*appnet.ConnectConn, error) DialPing(config PingConfig) error Ping(config PingConfig) ([]time.Duration, error) StopPing(pk cipher.PubKey) error @@ -1544,14 +1544,14 @@ func (v *Visor) IsDMSGClientReady() (bool, error) { } // Connect implements API. -func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error) { +func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, webPort int) (uuid.UUID, error) { - connApp := appnet.Addr{ + addr := appnet.Addr{ Net: appnet.TypeSkynet, PubKey: remotePK, Port: routing.Port(remotePort), } - conn, err := appnet.Dial(connApp) + conn, err := appnet.Dial(addr) if err != nil { return uuid.UUID{}, err } @@ -1560,7 +1560,7 @@ func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid return uuid.UUID{}, err } - connectConn := appnet.NewConnectConn(v.log, v.nM, remoteConn, remotePK, remotePort, localPort) + connectConn := appnet.NewConnectConn(v.log, v.nM, remoteConn, addr, webPort) connectConn.Serve() return connectConn.ID, nil @@ -1569,30 +1569,27 @@ func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid // Disconnect implements API. func (v *Visor) Disconnect(id uuid.UUID) error { connectConn := v.nM.GetConnectConn(id) + if connectConn == nil { + return ErrNotFound + } return connectConn.Close() } // ListHTTPPorts implements API. -func (v *Visor) ListHTTPPorts() ([]int, error) { - v.allowedMX.Lock() - defer v.allowedMX.Unlock() - keys := make([]int, 0, len(v.allowedPorts)) - for k := range v.allowedPorts { - keys = append(keys, k) - } - return keys, nil +func (v *Visor) ListConnected() (map[uuid.UUID]*appnet.ConnectConn, error) { + return v.nM.GetAllConnectConns(), nil } // Publish implements API. func (v *Visor) Publish(localPort int) (uuid.UUID, error) { - connApp := appnet.Addr{ + addr := appnet.Addr{ Net: appnet.TypeSkynet, PubKey: v.conf.PK, Port: routing.Port(localPort), } - lis, err := appnet.Listen(connApp) + lis, err := appnet.Listen(addr) if err != nil { return uuid.UUID{}, err } @@ -1610,10 +1607,13 @@ func (v *Visor) Publish(localPort int) (uuid.UUID, error) { // Depublish implements API. func (v *Visor) Depublish(id uuid.UUID) error { publishConn := v.nM.GetPublishListener(id) + if publishConn == nil { + return ErrNotFound + } return publishConn.Close() } -// List implements API. -func (v *Visor) List() (map[uuid.UUID]*appnet.ConnectConn, error) { - return v.nM.GetAllConnectConns(), nil +// ListPublished implements API. +func (v *Visor) ListPublished() (map[uuid.UUID]*appnet.PublishLis, error) { + return v.nM.GetAllPublishListeners(), nil } diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index 8b5a2aa3fe..0400a45f90 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -727,14 +727,6 @@ func (r *RPC) IsDMSGClientReady(_ *struct{}, out *bool) (err error) { return err } -// ListHTTPPorts lists all the local por that can be accessed by remote visors -func (r *RPC) ListHTTPPorts(_ *struct{}, out *[]int) (err error) { - defer rpcutil.LogCall(r.log, "ListHTTPPorts", nil)(out, &err) - ports, err := r.visor.ListHTTPPorts() - *out = ports - return err -} - // ConnectIn is input for Connect. type ConnectIn struct { RemotePK cipher.PubKey @@ -758,6 +750,14 @@ func (r *RPC) Disconnect(id *uuid.UUID, _ *struct{}) (err error) { return err } +// ListConnected lists all the sky connections that are connected +func (r *RPC) ListConnected(_ *struct{}, out *map[uuid.UUID]*appnet.ConnectConn) (err error) { + defer rpcutil.LogCall(r.log, "ListConnected", nil)(out, &err) + conns, err := r.visor.ListConnected() + *out = conns + return err +} + // Connect creates a connection with the remote visor to listen on the remote port and serve that on the local port func (r *RPC) Publish(localPort *int, out *uuid.UUID) (err error) { defer rpcutil.LogCall(r.log, "Publish", localPort)(out, &err) @@ -774,11 +774,11 @@ func (r *RPC) Depublish(id *uuid.UUID, _ *struct{}) (err error) { return err } -// List returns all the ongoing skyforwarding connections -func (r *RPC) List(_ *struct{}, out *map[uuid.UUID]*appnet.ConnectConn) (err error) { - defer rpcutil.LogCall(r.log, "List", nil)(out, &err) - proxies, err := r.visor.List() - *out = proxies +// ListPublished lists all the local ports that are being published +func (r *RPC) ListPublished(_ *struct{}, out *map[uuid.UUID]*appnet.PublishLis) (err error) { + defer rpcutil.LogCall(r.log, "ListPublished", nil)(out, &err) + liss, err := r.visor.ListPublished() + *out = liss return err } diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index 0aa5de9f28..60691096c8 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -585,17 +585,17 @@ func (rc *rpcClient) Depublish(id uuid.UUID) error { return err } -// List calls List. -func (rc *rpcClient) List() (map[uuid.UUID]*appnet.ConnectConn, error) { - var out map[uuid.UUID]*appnet.ConnectConn - err := rc.Call("List", &struct{}{}, &out) +// ListPublished calls ListPublished. +func (rc *rpcClient) ListPublished() (map[uuid.UUID]*appnet.PublishLis, error) { + var out map[uuid.UUID]*appnet.PublishLis + err := rc.Call("ListPublished", &struct{}{}, &out) return out, err } -// ListHTTPPorts calls ListHTTPPorts. -func (rc *rpcClient) ListHTTPPorts() ([]int, error) { - var out []int - err := rc.Call("ListHTTPPorts", &struct{}{}, &out) +// ListConnected calls ListConnected. +func (rc *rpcClient) ListConnected() (map[uuid.UUID]*appnet.ConnectConn, error) { + var out map[uuid.UUID]*appnet.ConnectConn + err := rc.Call("ListConnected", &struct{}{}, &out) return out, err } @@ -1333,12 +1333,12 @@ func (mc *mockRPCClient) Depublish(id uuid.UUID) error { //nolint:all } // List implements API. -func (mc *mockRPCClient) List() (map[uuid.UUID]*appnet.ConnectConn, error) { +func (mc *mockRPCClient) ListConnected() (map[uuid.UUID]*appnet.ConnectConn, error) { return nil, nil } // ListHTTPPorts implements API. -func (mc *mockRPCClient) ListHTTPPorts() ([]int, error) { +func (mc *mockRPCClient) ListPublished() (map[uuid.UUID]*appnet.PublishLis, error) { return nil, nil } From 2a8cee5b498330365317f7055ba7701f99e49ca2 Mon Sep 17 00:00:00 2001 From: ersonp Date: Sun, 23 Jun 2024 01:00:37 +0530 Subject: [PATCH 11/25] refactor: Update ConnectConn to use http.Server for serving --- pkg/app/appnet/connect.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/app/appnet/connect.go b/pkg/app/appnet/connect.go index 2a89df5e95..455c1ce4b7 100644 --- a/pkg/app/appnet/connect.go +++ b/pkg/app/appnet/connect.go @@ -23,7 +23,7 @@ type ConnectConn struct { WebPort int Addr Addr remoteConn net.Conn - r *gin.Engine + srv *http.Server closeOnce sync.Once log *logging.Logger nm *NetManager @@ -43,10 +43,10 @@ func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, ad r.Any("/*path", handleConnectFunc(httpC, addr.PK(), addr.GetPort(), mu)) - // srv := &http.Server{ - // Addr: ":8080", - // Handler: r, - // } + srv := &http.Server{ + Addr: fmt.Sprint(":", webPort), + Handler: r, + } fwdConn := &ConnectConn{ ID: uuid.New(), @@ -54,7 +54,7 @@ func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, ad WebPort: webPort, Addr: addr, log: log, - r: r, + srv: srv, nm: nm, } @@ -65,7 +65,7 @@ func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, ad // Serve serves a HTTP forward conn that accepts all requests and forwards them directly to the remote server over the specified net.Conn. func (f *ConnectConn) Serve() { go func() { - err := f.r.Run(":" + fmt.Sprintf("%v", f.WebPort)) //nolint + err := f.srv.ListenAndServe() //nolint if err != nil { // don't print error if local server is closed if !errors.Is(err, http.ErrServerClosed) { @@ -80,6 +80,7 @@ func (f *ConnectConn) Serve() { func (f *ConnectConn) Close() (err error) { f.closeOnce.Do(func() { err = f.remoteConn.Close() + err = f.srv.Close() f.nm.RemoveConnectConn(f.ID) }) return err From 87e67482fe528b89faa1cc8c08a83c64f42de422 Mon Sep 17 00:00:00 2001 From: ersonp Date: Sun, 23 Jun 2024 01:02:45 +0530 Subject: [PATCH 12/25] chore: Update comment --- pkg/app/appnet/publish.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/app/appnet/publish.go b/pkg/app/appnet/publish.go index cc31527c8f..d133ec9f80 100644 --- a/pkg/app/appnet/publish.go +++ b/pkg/app/appnet/publish.go @@ -88,7 +88,7 @@ func (f *PublishLis) Listen() { f.log.Debugf("Serving HTTP on dmsg port %v with DMSG listener %s", f.LocalPort, f.lis.Addr().String()) } -// Close closes the server and remote publishion. +// Close closes the server and publish listner. func (f *PublishLis) Close() (err error) { f.closeOnce.Do(func() { err = f.srv.Close() From d73b4004d4d4a5a0475df0b9ae3cd61fb4f52a38 Mon Sep 17 00:00:00 2001 From: ersonp Date: Sun, 23 Jun 2024 01:22:06 +0530 Subject: [PATCH 13/25] refactor: Update `connect` command to print connection details in JSON format --- cmd/skywire-cli/commands/net/connect.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd/skywire-cli/commands/net/connect.go b/cmd/skywire-cli/commands/net/connect.go index 8810bb4b77..0287b610ad 100644 --- a/cmd/skywire-cli/commands/net/connect.go +++ b/cmd/skywire-cli/commands/net/connect.go @@ -99,6 +99,14 @@ var conCmd = &cobra.Command{ } id, err := rpcClient.Connect(remotePK, remotePort, localPort) internal.Catch(cmd.Flags(), err) - internal.PrintOutput(cmd.Flags(), id, fmt.Sprintln(id)) + + jsonOptout := struct { + ID string `json:"id"` + Addr string `json:"addr"` + }{ + ID: id.String(), + Addr: fmt.Sprintf(remotePk + ":" + strconv.Itoa(remotePort)), + } + internal.PrintOutput(cmd.Flags(), jsonOptout, fmt.Sprintf("Connected to %s:%d with ID: %s\n", remotePK, remotePort, id.String())) }, } From dcd7b588b36deed4dfa39aa47f65836ce9f0b1c8 Mon Sep 17 00:00:00 2001 From: ersonp Date: Mon, 24 Jun 2024 19:06:16 +0530 Subject: [PATCH 14/25] feat: add app type support Add support for HTTP, TCP, UDP in the API, RPC and CMD --- cmd/skywire-cli/commands/net/connect.go | 28 +++- cmd/skywire-cli/commands/net/publish.go | 51 ++++--- example/http-server/server.go | 5 +- pkg/app/appnet/connect.go | 174 ++++++++++++++++++------ pkg/app/appnet/publish.go | 156 +++++++++++++++------ pkg/app/appnet/skynet.go | 14 ++ pkg/visor/api.go | 32 +++-- pkg/visor/rpc.go | 20 ++- pkg/visor/rpc_client.go | 15 +- 9 files changed, 362 insertions(+), 133 deletions(-) diff --git a/cmd/skywire-cli/commands/net/connect.go b/cmd/skywire-cli/commands/net/connect.go index 0287b610ad..9ebaa17b62 100644 --- a/cmd/skywire-cli/commands/net/connect.go +++ b/cmd/skywire-cli/commands/net/connect.go @@ -14,6 +14,7 @@ import ( "github.com/skycoin/skywire-utilities/pkg/cipher" clirpc "github.com/skycoin/skywire/cmd/skywire-cli/commands/rpc" "github.com/skycoin/skywire/cmd/skywire-cli/internal" + "github.com/skycoin/skywire/pkg/app/appnet" ) var ( @@ -29,6 +30,7 @@ func init() { conCmd.Flags().StringVarP(&remotePk, "pk", "k", "", "remote public key to connect to") conCmd.Flags().IntVarP(&localPort, "port", "p", 0, "local port to reverse proxy") conCmd.Flags().BoolVarP(&lsPorts, "ls", "l", false, "list configured connections") + conCmd.Flags().StringVarP(&netType, "type", "t", "http", "type of the remote app connection (http, tcp, udp)") conCmd.Flags().StringVarP(&disconnect, "stop", "d", "", "disconnect from specified ") RootCmd.AddCommand(conCmd) } @@ -56,7 +58,7 @@ var conCmd = &cobra.Command{ internal.Catch(cmd.Flags(), err) for _, connectConn := range connectConns { - _, err = fmt.Fprintf(w, "%s\t%s\t%s\n", connectConn.ID, connectConn.Addr.String(), + _, err = fmt.Fprintf(w, "%s\t%s\t%s\n", connectConn.ID, connectConn.RemoteAddr.String(), strconv.Itoa(int(connectConn.WebPort))) internal.Catch(cmd.Flags(), err) } @@ -97,15 +99,29 @@ var conCmd = &cobra.Command{ if 65536 < remotePort || 65536 < localPort { internal.PrintFatalError(cmd.Flags(), fmt.Errorf("port cannot be greater than 65535")) } - id, err := rpcClient.Connect(remotePK, remotePort, localPort) + + var appType appnet.AppType + + switch netType { + case "http": + appType = appnet.HTTP + case "tcp": + appType = appnet.TCP + case "udp": + appType = appnet.UDP + default: + internal.PrintFatalError(cmd.Flags(), fmt.Errorf("invalid type")) + } + + id, err := rpcClient.Connect(remotePK, remotePort, localPort, appType) internal.Catch(cmd.Flags(), err) jsonOptout := struct { - ID string `json:"id"` - Addr string `json:"addr"` + ID string `json:"id"` + RemoteAddr string `json:"addr"` }{ - ID: id.String(), - Addr: fmt.Sprintf(remotePk + ":" + strconv.Itoa(remotePort)), + ID: id.String(), + RemoteAddr: fmt.Sprintf(remotePk + ":" + strconv.Itoa(remotePort)), } internal.PrintOutput(cmd.Flags(), jsonOptout, fmt.Sprintf("Connected to %s:%d with ID: %s\n", remotePK, remotePort, id.String())) }, diff --git a/cmd/skywire-cli/commands/net/publish.go b/cmd/skywire-cli/commands/net/publish.go index 0db82088de..81871ee266 100644 --- a/cmd/skywire-cli/commands/net/publish.go +++ b/cmd/skywire-cli/commands/net/publish.go @@ -5,7 +5,6 @@ import ( "bytes" "fmt" "os" - "strconv" "text/tabwriter" "github.com/google/uuid" @@ -13,16 +12,20 @@ import ( clirpc "github.com/skycoin/skywire/cmd/skywire-cli/commands/rpc" "github.com/skycoin/skywire/cmd/skywire-cli/internal" + "github.com/skycoin/skywire/pkg/app/appnet" ) var ( - portNo int depublish string + netType string + skyPort int ) func init() { - pubCmd.PersistentFlags().IntVarP(&portNo, "port", "p", 0, "local port of the external (http) app") - pubCmd.PersistentFlags().StringVarP(&depublish, "depublish", "d", "", "deregister local port of the external (http) app with id") + pubCmd.PersistentFlags().IntVarP(&localPort, "port", "p", 0, "local port of the external (http, tcp, udp) app") + pubCmd.PersistentFlags().IntVarP(&skyPort, "skyport", "s", localPort, "skywire port for the external (http, tcp, udp) app") + pubCmd.PersistentFlags().StringVarP(&depublish, "depublish", "d", "", "deregister local port of the external (http, tcp, udp) app with id") + pubCmd.PersistentFlags().StringVarP(&netType, "type", "t", "http", "type of the external app connection (http, tcp, udp)") pubCmd.PersistentFlags().BoolVarP(&lsPorts, "ls", "l", false, "list published local ports") RootCmd.AddCommand(pubCmd) } @@ -33,7 +36,7 @@ var pubCmd = &cobra.Command{ Short: "Publish over skywire network", Long: "Publish over skywire network\nPublish a local port over the skywire network. This will allow other nodes to access the local port via the skywire network.", Args: cobra.MinimumNArgs(0), - Run: func(cmd *cobra.Command, args []string) { + Run: func(cmd *cobra.Command, _ []string) { rpcClient, err := clirpc.Client(cmd.Flags()) if err != nil { @@ -57,7 +60,7 @@ var pubCmd = &cobra.Command{ _, err = fmt.Fprintln(w, "id\tlocal_port") internal.Catch(cmd.Flags(), err) for id, lis := range liss { - _, err = fmt.Fprintf(w, "%v\t%v\n", id, lis.LocalPort) + _, err = fmt.Fprintf(w, "%v\t%v\n", id, lis.LocalAddr.GetPort()) internal.Catch(cmd.Flags(), err) } internal.Catch(cmd.Flags(), w.Flush()) @@ -65,29 +68,45 @@ var pubCmd = &cobra.Command{ os.Exit(0) } - if len(args) == 0 && portNo == 0 { - cmd.Help() //nolint - os.Exit(0) + if skyPort == 0 { + skyPort = localPort } - //if port is specified via flag, argument will override - if len(args) > 0 { - portNo, err = strconv.Atoi(args[0]) - internal.Catch(cmd.Flags(), err) + if localPort == 0 && skyPort == 0 { + cmd.Help() //nolint + os.Exit(0) } //port 0 is reserved / not usable - if portNo == 0 { + if localPort == 0 { internal.PrintFatalError(cmd.Flags(), fmt.Errorf("port cannot be 0")) } + //skyPort 0 is reserved / not usable + if skyPort == 0 { + internal.PrintFatalError(cmd.Flags(), fmt.Errorf("skyPort cannot be 0")) + } + //65535 is the highest TCP port number - if 65536 < portNo { + if 65536 < localPort { internal.PrintFatalError(cmd.Flags(), fmt.Errorf("port cannot be greater than 65535")) } + var appType appnet.AppType + + switch netType { + case "http": + appType = appnet.HTTP + case "tcp": + appType = appnet.TCP + case "udp": + appType = appnet.UDP + default: + internal.PrintFatalError(cmd.Flags(), fmt.Errorf("invalid type")) + } + internal.Catch(cmd.Flags(), err) - id, err := rpcClient.Publish(portNo) + id, err := rpcClient.Publish(localPort, skyPort, appType) internal.Catch(cmd.Flags(), err) internal.PrintOutput(cmd.Flags(), "id: %v\n", fmt.Sprintln(id)) diff --git a/example/http-server/server.go b/example/http-server/server.go index 3bd6062cc4..428d337e99 100644 --- a/example/http-server/server.go +++ b/example/http-server/server.go @@ -12,6 +12,7 @@ import ( "github.com/skycoin/skywire-utilities/pkg/logging" "github.com/skycoin/skywire/example/http-server/html" + "github.com/skycoin/skywire/pkg/app/appnet" "github.com/skycoin/skywire/pkg/visor" ) @@ -60,7 +61,9 @@ func main() { fmt.Printf("error serving: %v\n", err) } - id, err := rpcClient.Publish(port) + skyPort := port + + id, err := rpcClient.Publish(port, skyPort, appnet.HTTP) if err != nil { log.Errorf("error closing server: %v", err) } diff --git a/pkg/app/appnet/connect.go b/pkg/app/appnet/connect.go index 455c1ce4b7..14096d8239 100644 --- a/pkg/app/appnet/connect.go +++ b/pkg/app/appnet/connect.go @@ -17,70 +17,96 @@ import ( "github.com/skycoin/skywire/pkg/routing" ) +type ConnectInfo struct { + ID uuid.UUID `json:"id"` + WebPort int `json:"web_port"` + RemoteAddr Addr `json:"remote_addr"` + AppType AppType `json:"app_type"` +} + // ConnectConn represents a connection that is published on the skywire network type ConnectConn struct { - ID uuid.UUID - WebPort int - Addr Addr - remoteConn net.Conn - srv *http.Server - closeOnce sync.Once - log *logging.Logger - nm *NetManager + ConnectInfo + skyConn net.Conn + srv *http.Server + lis net.Listener + closeOnce sync.Once + log *logging.Logger + nm *NetManager } // NewConnectConn creates a new ConnectConn -func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, addr Addr, webPort int) *ConnectConn { - - httpC := &http.Client{Transport: MakeHTTPTransport(remoteConn, log)} - mu := new(sync.Mutex) - - r := gin.New() - - r.Use(gin.Recovery()) - - r.Use(loggingMiddleware()) - - r.Any("/*path", handleConnectFunc(httpC, addr.PK(), addr.GetPort(), mu)) - - srv := &http.Server{ - Addr: fmt.Sprint(":", webPort), - Handler: r, +func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, remoteAddr Addr, webPort int, appType AppType) (*ConnectConn, error) { + var srv *http.Server + var lis net.Listener + + switch appType { + case HTTP: + srv = newHTTPConnectServer(log, remoteConn, remoteAddr, webPort) + case TCP: + // lis = newTCPConnectListner(log, webPort) + return nil, errors.New("app type TCP is not supported yet") + case UDP: + return nil, errors.New("app type UDP is not supported yet") } - fwdConn := &ConnectConn{ - ID: uuid.New(), - remoteConn: remoteConn, - WebPort: webPort, - Addr: addr, - log: log, - srv: srv, - nm: nm, + conn := &ConnectConn{ + ConnectInfo: ConnectInfo{ + ID: uuid.New(), + WebPort: webPort, + RemoteAddr: remoteAddr, + AppType: appType, + }, + skyConn: remoteConn, + log: log, + srv: srv, + lis: lis, + nm: nm, } - nm.AddConnect(fwdConn) - return fwdConn + nm.AddConnect(conn) + return conn, nil } // Serve serves a HTTP forward conn that accepts all requests and forwards them directly to the remote server over the specified net.Conn. -func (f *ConnectConn) Serve() { - go func() { - err := f.srv.ListenAndServe() //nolint - if err != nil { - // don't print error if local server is closed - if !errors.Is(err, http.ErrServerClosed) { - f.log.WithError(err).Error("Error listening and serving app forwarding.") +func (f *ConnectConn) Serve() error { + switch f.AppType { + case HTTP: + go func() { + err := f.srv.ListenAndServe() //nolint + if err != nil { + // don't print error if local server is closed + if !errors.Is(err, http.ErrServerClosed) { + f.log.WithError(err).Error("Error listening and serving app forwarding.") + } } - } - }() + }() + case TCP: + // go func() { + // handleConnectTCPConnection(f.lis, f.remoteConn, f.log) + // }() + return errors.New("app type TCP is not supported yet") + case UDP: + return errors.New("app type UDP is not supported yet") + } f.log.Debugf("Serving on localhost:%v", f.WebPort) + return nil } // Close closes the server and remote connection. func (f *ConnectConn) Close() (err error) { f.closeOnce.Do(func() { - err = f.remoteConn.Close() - err = f.srv.Close() + + switch f.AppType { + case HTTP: + err = f.srv.Close() + case TCP: + // err = f.lis.Close() + return + case UDP: + return + } + err = f.skyConn.Close() f.nm.RemoveConnectConn(f.ID) }) return err @@ -131,3 +157,61 @@ func handleConnectFunc(httpC *http.Client, remotePK cipher.PubKey, remotePort ro } } } + +func newHTTPConnectServer(log *logging.Logger, remoteConn net.Conn, remoteAddr Addr, webPort int) *http.Server { + + httpC := &http.Client{Transport: MakeHTTPTransport(remoteConn, log)} + mu := new(sync.Mutex) + + r := gin.New() + + r.Use(gin.Recovery()) + + r.Use(loggingMiddleware()) + + r.Any("/*path", handleConnectFunc(httpC, remoteAddr.PK(), remoteAddr.GetPort(), mu)) + + srv := &http.Server{ + Addr: fmt.Sprint(":", webPort), + Handler: r, + } + return srv +} + +func newTCPConnectListner(log *logging.Logger, webPort int) net.Listener { + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", webPort)) + if err != nil { + log.Errorf("Failed to start TCP listener on port %d: %v", webPort, err) + } + return listener +} + +func handleConnectTCPConnection(listener net.Listener, remoteConn net.Conn, log *logging.Logger) { + for { + conn, err := listener.Accept() + if err != nil { + log.Printf("Failed to accept connection: %v", err) + continue + } + + go func(conn net.Conn) { + defer conn.Close() //nolint + + go func() { + _, err := io.Copy(remoteConn, conn) + if err != nil { + log.Printf("Error copying data to dmsg server: %v", err) + } + remoteConn.Close() //nolint + }() + + go func() { + _, err := io.Copy(conn, remoteConn) + if err != nil { + log.Printf("Error copying data from dmsg server: %v", err) + } + conn.Close() //nolint + }() + }(conn) + } +} diff --git a/pkg/app/appnet/publish.go b/pkg/app/appnet/publish.go index d133ec9f80..d2504a7a62 100644 --- a/pkg/app/appnet/publish.go +++ b/pkg/app/appnet/publish.go @@ -4,6 +4,7 @@ package appnet import ( "errors" "fmt" + "io" "net" "net/http" "net/http/httputil" @@ -17,32 +18,109 @@ import ( "github.com/skycoin/skywire-utilities/pkg/logging" ) -// PublishLis represents a publishion that is published on the skywire network +type PublishInfo struct { + ID uuid.UUID `json:"id"` + LocalAddr Addr `json:"local_addr"` + AppType AppType `json:"app_type"` +} + +// PublishLis represents a listner that is published on the skywire network type PublishLis struct { - ID uuid.UUID - LocalPort int - lis net.Listener + PublishInfo + skyLis net.Listener closeOnce sync.Once srv *http.Server + conn net.Conn log *logging.Logger nm *NetManager } -type ginHandler struct { - Router *gin.Engine +// NewPublishListener creates a new publishListener +func NewPublishListener(log *logging.Logger, nm *NetManager, skyLis net.Listener, addr Addr, appType AppType) (*PublishLis, error) { + var srv *http.Server + var conn net.Conn + switch appType { + case HTTP: + srv = newHTTPPublishServer(int(addr.GetPort())) + case TCP: + // conn = newTCPPublishConn(log, webPort) + return nil, errors.New("app type TCP is not supported yet") + case UDP: + return nil, errors.New("app type UDP is not supported yet") + } + pubLis := &PublishLis{ + PublishInfo: PublishInfo{ + ID: uuid.New(), + LocalAddr: addr, + AppType: appType, + }, + skyLis: skyLis, + srv: srv, + conn: conn, + log: log, + nm: nm, + } + nm.AddPublish(pubLis) + return pubLis, nil } -func (h *ginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - h.Router.ServeHTTP(w, r) +// Serve serves a HTTP forward Lis that accepts all requests and forwards them directly to the remote server over the specified net.Lis. +func (f *PublishLis) Listen() error { + switch f.AppType { + case HTTP: + go func() { + err := f.srv.Serve(f.skyLis) + if err != nil { + // don't print error if local server is closed + if !errors.Is(err, http.ErrServerClosed) { + f.log.WithError(err).Error("error listening and serving app forwarding.") + } + } + }() + case TCP: + // go func() { + // for { + // conn, err := f.skyLis.Accept() + // if err != nil { + // f.log.Errorf("error accepting connection: %v", err) + // return + // } + + // go f.handlePublishTCPConnection(conn) + // } + // }() + return errors.New("app type TCP is not supported yet") + case UDP: + return errors.New("app type UDP is not supported yet") + } + + f.log.Debugf("Serving HTTP on sky port %v with SKY listener %s", f.LocalAddr.GetPort(), f.skyLis.Addr().String()) + return nil } -// NewPublishListener creates a new publishListener -func NewPublishListener(log *logging.Logger, nm *NetManager, lis net.Listener, localPort int) (*PublishLis, error) { +// Close closes the server and publish listner. +func (f *PublishLis) Close() (err error) { + f.closeOnce.Do(func() { + switch f.AppType { + case HTTP: + err = f.srv.Close() + case TCP: + // err = f.conn.Close() + return + case UDP: + return + } + err = f.skyLis.Close() + f.nm.RemovePublishListener(f.ID) + }) + return err +} - r1 := gin.New() - r1.Use(gin.Recovery()) - r1.Use(loggingMiddleware()) - authRoute := r1.Group("/") +func newHTTPPublishServer(localPort int) *http.Server { + r := gin.New() + r.Use(gin.Recovery()) + r.Use(loggingMiddleware()) + authRoute := r.Group("/") authRoute.Any("/*path", func(c *gin.Context) { targetURL, _ := url.Parse(fmt.Sprintf("http://127.0.0.1:%v%s?%s", localPort, c.Request.URL.Path, c.Request.URL.RawQuery)) //nolint proxy := httputil.ReverseProxy{ @@ -57,43 +135,35 @@ func NewPublishListener(log *logging.Logger, nm *NetManager, lis net.Listener, l }) srv := &http.Server{ - Handler: &ginHandler{Router: r1}, + Handler: r, ReadHeaderTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } - pubLis := &PublishLis{ - ID: uuid.New(), - srv: srv, - lis: lis, - LocalPort: localPort, - log: log, - nm: nm, + return srv +} + +func newTCPPublishConn(log *logging.Logger, localPort int) net.Conn { + + conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort)) + if err != nil { + log.Printf("Error connecting to local port %d: %v", localPort, err) + return nil } - nm.AddPublish(pubLis) - return pubLis, nil + + return conn } -// Serve serves a HTTP forward Lis that accepts all requests and forwards them directly to the remote server over the specified net.Lis. -func (f *PublishLis) Listen() { - go func() { - err := f.srv.Serve(f.lis) +func (f *PublishLis) handlePublishTCPConnection(conn net.Conn) { + defer conn.Close() //nolint + + copyConn := func(dst net.Conn, src net.Conn) { + _, err := io.Copy(dst, src) if err != nil { - // don't print error if local server is closed - if !errors.Is(err, http.ErrServerClosed) { - f.log.WithError(err).Error("Error listening and serving app forwarding.") - } + f.log.Printf("Error during copy: %v", err) } - }() - f.log.Debugf("Serving HTTP on dmsg port %v with DMSG listener %s", f.LocalPort, f.lis.Addr().String()) -} + } -// Close closes the server and publish listner. -func (f *PublishLis) Close() (err error) { - f.closeOnce.Do(func() { - err = f.srv.Close() - err = f.lis.Close() - f.nm.RemovePublishListener(f.ID) - }) - return err + go copyConn(conn, f.conn) + go copyConn(f.conn, conn) } diff --git a/pkg/app/appnet/skynet.go b/pkg/app/appnet/skynet.go index c8536c8ea9..0d9763a446 100644 --- a/pkg/app/appnet/skynet.go +++ b/pkg/app/appnet/skynet.go @@ -11,12 +11,26 @@ import ( "github.com/google/uuid" ) +// AppType is the type of the network of the external app that is being published or connected to +type AppType string + +const ( + // TCP is the type of network of the external app that is being published or connected to + TCP AppType = "TCP" + // UDP is the type of network of the external app that is being published or connected to + UDP AppType = "UDP" + // HTTP is the type of network of the external app that is being published or connected to + HTTP AppType = "HTTP" +) + +// NetManager manages all the connections and listeners type NetManager struct { listeners map[uuid.UUID]*PublishLis conns map[uuid.UUID]*ConnectConn mu sync.Mutex } +// NewNetManager creates a new NetManager func NewNetManager() *NetManager { return &NetManager{ listeners: make(map[uuid.UUID]*PublishLis), diff --git a/pkg/visor/api.go b/pkg/visor/api.go index ec40d2a88f..5b378e7943 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -116,10 +116,10 @@ type API interface { RouteGroups() ([]RouteGroupInfo, error) SetMinHops(uint16) error - Publish(localPort int) (uuid.UUID, error) + Publish(localPort, skyPort int, appType appnet.AppType) (uuid.UUID, error) Depublish(id uuid.UUID) error ListPublished() (map[uuid.UUID]*appnet.PublishLis, error) - Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error) + Connect(remotePK cipher.PubKey, remotePort, localPort int, appType appnet.AppType) (uuid.UUID, error) Disconnect(id uuid.UUID) error ListConnected() (map[uuid.UUID]*appnet.ConnectConn, error) DialPing(config PingConfig) error @@ -1544,14 +1544,15 @@ func (v *Visor) IsDMSGClientReady() (bool, error) { } // Connect implements API. -func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, webPort int) (uuid.UUID, error) { +func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, webPort int, appType appnet.AppType) (uuid.UUID, error) { - addr := appnet.Addr{ + remoteAddr := appnet.Addr{ Net: appnet.TypeSkynet, PubKey: remotePK, Port: routing.Port(remotePort), } - conn, err := appnet.Dial(addr) + + conn, err := appnet.Dial(remoteAddr) if err != nil { return uuid.UUID{}, err } @@ -1560,8 +1561,14 @@ func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, webPort int) (uuid.U return uuid.UUID{}, err } - connectConn := appnet.NewConnectConn(v.log, v.nM, remoteConn, addr, webPort) - connectConn.Serve() + connectConn, err := appnet.NewConnectConn(v.log, v.nM, remoteConn, remoteAddr, webPort, appType) + if err != nil { + return uuid.UUID{}, err + } + err = connectConn.Serve() + if err != nil { + return uuid.UUID{}, err + } return connectConn.ID, nil } @@ -1581,12 +1588,12 @@ func (v *Visor) ListConnected() (map[uuid.UUID]*appnet.ConnectConn, error) { } // Publish implements API. -func (v *Visor) Publish(localPort int) (uuid.UUID, error) { +func (v *Visor) Publish(localPort, skyPort int, appType appnet.AppType) (uuid.UUID, error) { addr := appnet.Addr{ Net: appnet.TypeSkynet, PubKey: v.conf.PK, - Port: routing.Port(localPort), + Port: routing.Port(skyPort), } lis, err := appnet.Listen(addr) @@ -1594,12 +1601,15 @@ func (v *Visor) Publish(localPort int) (uuid.UUID, error) { return uuid.UUID{}, err } - publishLis, err := appnet.NewPublishListener(v.log, v.nM, lis, localPort) + publishLis, err := appnet.NewPublishListener(v.log, v.nM, lis, addr, appType) if err != nil { return uuid.UUID{}, err } - publishLis.Listen() + err = publishLis.Listen() + if err != nil { + return uuid.UUID{}, err + } return publishLis.ID, nil } diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index 0400a45f90..6daead8074 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -732,13 +732,14 @@ type ConnectIn struct { RemotePK cipher.PubKey RemotePort int LocalPort int + AppType appnet.AppType } // Connect creates a connection with the remote visor to listen on the remote port and serve that on the local port func (r *RPC) Connect(in *ConnectIn, out *uuid.UUID) (err error) { defer rpcutil.LogCall(r.log, "Connect", in)(out, &err) - id, err := r.visor.Connect(in.RemotePK, in.RemotePort, in.LocalPort) + id, err := r.visor.Connect(in.RemotePK, in.RemotePort, in.LocalPort, in.AppType) *out = id return err } @@ -758,16 +759,23 @@ func (r *RPC) ListConnected(_ *struct{}, out *map[uuid.UUID]*appnet.ConnectConn) return err } -// Connect creates a connection with the remote visor to listen on the remote port and serve that on the local port -func (r *RPC) Publish(localPort *int, out *uuid.UUID) (err error) { - defer rpcutil.LogCall(r.log, "Publish", localPort)(out, &err) +// PublishIn is input for Publish. +type PublishIn struct { + LocalPort int + SkyPort int + AppType appnet.AppType +} - id, err := r.visor.Publish(*localPort) +// Publish publishes a listner for the local port to the skyport +func (r *RPC) Publish(in *PublishIn, out *uuid.UUID) (err error) { + defer rpcutil.LogCall(r.log, "Publish", in)(out, &err) + + id, err := r.visor.Publish(in.LocalPort, in.SkyPort, in.AppType) *out = id return err } -// Disconnect breaks the connection with the given id +// Depublish removes the published port with the given id func (r *RPC) Depublish(id *uuid.UUID, _ *struct{}) (err error) { defer rpcutil.LogCall(r.log, "Depublish", id)(nil, &err) err = r.visor.Depublish(*id) diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index 60691096c8..175a6936b7 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -556,12 +556,13 @@ func (rc *rpcClient) IsDMSGClientReady() (bool, error) { } // Connect calls Connect. -func (rc *rpcClient) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error) { +func (rc *rpcClient) Connect(remotePK cipher.PubKey, remotePort, localPort int, appType appnet.AppType) (uuid.UUID, error) { var out uuid.UUID err := rc.Call("Connect", &ConnectIn{ RemotePK: remotePK, RemotePort: remotePort, LocalPort: localPort, + AppType: appType, }, &out) return out, err } @@ -573,9 +574,13 @@ func (rc *rpcClient) Disconnect(id uuid.UUID) error { } // Publish calls Publish. -func (rc *rpcClient) Publish(localPort int) (uuid.UUID, error) { +func (rc *rpcClient) Publish(localPort int, skyPort int, appType appnet.AppType) (uuid.UUID, error) { var out uuid.UUID - err := rc.Call("Publish", &localPort, &out) + err := rc.Call("Publish", &PublishIn{ + LocalPort: localPort, + SkyPort: skyPort, + AppType: appType, + }, &out) return out, err } @@ -1313,7 +1318,7 @@ func (mc *mockRPCClient) IsDMSGClientReady() (bool, error) { } // Connect implements API. -func (mc *mockRPCClient) Connect(remotePK cipher.PubKey, remotePort, localPort int) (uuid.UUID, error) { //nolint:all +func (mc *mockRPCClient) Connect(remotePK cipher.PubKey, remotePort, localPort int, appType appnet.AppType) (uuid.UUID, error) { //nolint:all return uuid.UUID{}, nil } @@ -1323,7 +1328,7 @@ func (mc *mockRPCClient) Disconnect(id uuid.UUID) error { //nolint:all } // Publish implements API. -func (mc *mockRPCClient) Publish(localPort int) (uuid.UUID, error) { //nolint:all +func (mc *mockRPCClient) Publish(localPort int, skyPort int, appType appnet.AppType) (uuid.UUID, error) { //nolint:all return uuid.UUID{}, nil } From 870c6eed2b098ec4de173e548074535f932f43cd Mon Sep 17 00:00:00 2001 From: ersonp Date: Tue, 25 Jun 2024 15:25:29 +0530 Subject: [PATCH 15/25] feat: ensure port availability Ensure port availability before adding ConnectConn and PublishListener --- pkg/app/appnet/connect.go | 6 ++++- pkg/app/appnet/publish.go | 8 ++++++- pkg/app/appnet/skynet.go | 50 +++++++++++++++++++++++++++++++++++++-- pkg/visor/api.go | 8 +++++++ 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/pkg/app/appnet/connect.go b/pkg/app/appnet/connect.go index 14096d8239..7406181285 100644 --- a/pkg/app/appnet/connect.go +++ b/pkg/app/appnet/connect.go @@ -37,6 +37,7 @@ type ConnectConn struct { // NewConnectConn creates a new ConnectConn func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, remoteAddr Addr, webPort int, appType AppType) (*ConnectConn, error) { + var srv *http.Server var lis net.Listener @@ -64,7 +65,10 @@ func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, re nm: nm, } - nm.AddConnect(conn) + if err := nm.AddConnect(conn); err != nil { + return nil, err + } + return conn, nil } diff --git a/pkg/app/appnet/publish.go b/pkg/app/appnet/publish.go index d2504a7a62..b00e242ad5 100644 --- a/pkg/app/appnet/publish.go +++ b/pkg/app/appnet/publish.go @@ -37,6 +37,7 @@ type PublishLis struct { // NewPublishListener creates a new publishListener func NewPublishListener(log *logging.Logger, nm *NetManager, skyLis net.Listener, addr Addr, appType AppType) (*PublishLis, error) { + var srv *http.Server var conn net.Conn switch appType { @@ -48,6 +49,7 @@ func NewPublishListener(log *logging.Logger, nm *NetManager, skyLis net.Listener case UDP: return nil, errors.New("app type UDP is not supported yet") } + pubLis := &PublishLis{ PublishInfo: PublishInfo{ ID: uuid.New(), @@ -60,7 +62,11 @@ func NewPublishListener(log *logging.Logger, nm *NetManager, skyLis net.Listener log: log, nm: nm, } - nm.AddPublish(pubLis) + + if err := nm.AddPublish(pubLis); err != nil { + return nil, err + } + return pubLis, nil } diff --git a/pkg/app/appnet/skynet.go b/pkg/app/appnet/skynet.go index 0d9763a446..2883b45913 100644 --- a/pkg/app/appnet/skynet.go +++ b/pkg/app/appnet/skynet.go @@ -38,12 +38,35 @@ func NewNetManager() *NetManager { } } +func (nm *NetManager) isPublishPortAvailable(addr Addr, appType AppType) error { + + for _, l := range nm.listeners { + if l.LocalAddr.GetPort() == addr.GetPort() { + return fmt.Errorf("port %d is already in use for app type %v", addr.GetPort(), appType) + } + } + return nil +} + +// isPublishPortAvailable checks if a port and apptype is available for publishing +func (nm *NetManager) IsPublishPortAvailable(addr Addr, appType AppType) error { + nm.mu.Lock() + defer nm.mu.Unlock() + + return nm.isPublishPortAvailable(addr, appType) +} + // AddPublish adds publishListener to with it's ID -func (nm *NetManager) AddPublish(lis *PublishLis) { +func (nm *NetManager) AddPublish(lis *PublishLis) error { nm.mu.Lock() defer nm.mu.Unlock() + if err := nm.isPublishPortAvailable(lis.LocalAddr, lis.AppType); err != nil { + return err + } + nm.listeners[lis.ID] = lis + return nil } // GetpublishListenertner get's a publishListener by ID @@ -70,12 +93,35 @@ func (nm *NetManager) RemovePublishListener(id uuid.UUID) { delete(nm.listeners, id) } +func (nm *NetManager) isConnectPortAvailable(webPort int) error { + + for _, c := range nm.conns { + if c.WebPort == webPort { + return fmt.Errorf("web port %d is already in use", webPort) + } + } + return nil +} + +// IsConnectPortAvailable checks if a web port is available +func (nm *NetManager) IsConnectPortAvailable(webPort int) error { + nm.mu.Lock() + defer nm.mu.Unlock() + + return nm.isConnectPortAvailable(webPort) +} + // AddConnect adds ConnectConn to with it's ID -func (nm *NetManager) AddConnect(conn *ConnectConn) { +func (nm *NetManager) AddConnect(conn *ConnectConn) error { nm.mu.Lock() defer nm.mu.Unlock() + if err := nm.isConnectPortAvailable(conn.WebPort); err != nil { + return err + } + nm.conns[conn.ID] = conn + return nil } // GetConnectConn get's a ConnectConn by ID diff --git a/pkg/visor/api.go b/pkg/visor/api.go index 5b378e7943..d3a6ae8fba 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -1546,6 +1546,10 @@ func (v *Visor) IsDMSGClientReady() (bool, error) { // Connect implements API. func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, webPort int, appType appnet.AppType) (uuid.UUID, error) { + if err := v.nM.IsConnectPortAvailable(webPort); err != nil { + return uuid.UUID{}, err + } + remoteAddr := appnet.Addr{ Net: appnet.TypeSkynet, PubKey: remotePK, @@ -1596,6 +1600,10 @@ func (v *Visor) Publish(localPort, skyPort int, appType appnet.AppType) (uuid.UU Port: routing.Port(skyPort), } + if err := v.nM.IsPublishPortAvailable(addr, appType); err != nil { + return uuid.UUID{}, err + } + lis, err := appnet.Listen(addr) if err != nil { return uuid.UUID{}, err From cbbc403a079d6800afbd8a0e7f731887b48b515a Mon Sep 17 00:00:00 2001 From: ersonp Date: Tue, 25 Jun 2024 16:20:12 +0530 Subject: [PATCH 16/25] refactor: update APIs Update `Connect` and `Publish` API to return ConnectInfo and PublishInfo as well as for `ListPublished` and `ListConnected` to return []PublishInfo and []ConnectInfo --- cmd/skywire-cli/commands/net/connect.go | 8 ++-- cmd/skywire-cli/commands/net/publish.go | 12 +++++- example/http-server/server.go | 4 +- pkg/visor/api.go | 52 +++++++++++++++---------- pkg/visor/rpc.go | 8 ++-- pkg/visor/rpc_client.go | 28 ++++++------- 6 files changed, 65 insertions(+), 47 deletions(-) diff --git a/cmd/skywire-cli/commands/net/connect.go b/cmd/skywire-cli/commands/net/connect.go index 9ebaa17b62..c5b9ca3fc9 100644 --- a/cmd/skywire-cli/commands/net/connect.go +++ b/cmd/skywire-cli/commands/net/connect.go @@ -113,16 +113,16 @@ var conCmd = &cobra.Command{ internal.PrintFatalError(cmd.Flags(), fmt.Errorf("invalid type")) } - id, err := rpcClient.Connect(remotePK, remotePort, localPort, appType) + connInfo, err := rpcClient.Connect(remotePK, remotePort, localPort, appType) internal.Catch(cmd.Flags(), err) jsonOptout := struct { ID string `json:"id"` RemoteAddr string `json:"addr"` }{ - ID: id.String(), - RemoteAddr: fmt.Sprintf(remotePk + ":" + strconv.Itoa(remotePort)), + ID: connInfo.ID.String(), + RemoteAddr: connInfo.RemoteAddr.String(), } - internal.PrintOutput(cmd.Flags(), jsonOptout, fmt.Sprintf("Connected to %s:%d with ID: %s\n", remotePK, remotePort, id.String())) + internal.PrintOutput(cmd.Flags(), jsonOptout, fmt.Sprintf("Connected to %s with ID: %s\n", connInfo.RemoteAddr.String(), connInfo.ID.String())) }, } diff --git a/cmd/skywire-cli/commands/net/publish.go b/cmd/skywire-cli/commands/net/publish.go index 81871ee266..158ead5d8a 100644 --- a/cmd/skywire-cli/commands/net/publish.go +++ b/cmd/skywire-cli/commands/net/publish.go @@ -106,9 +106,17 @@ var pubCmd = &cobra.Command{ } internal.Catch(cmd.Flags(), err) - id, err := rpcClient.Publish(localPort, skyPort, appType) + pubInfo, err := rpcClient.Publish(localPort, skyPort, appType) internal.Catch(cmd.Flags(), err) - internal.PrintOutput(cmd.Flags(), "id: %v\n", fmt.Sprintln(id)) + + jsonOptout := struct { + ID string `json:"id"` + LocalAddr string `json:"addr"` + }{ + ID: pubInfo.ID.String(), + LocalAddr: pubInfo.LocalAddr.String(), + } + internal.PrintOutput(cmd.Flags(), jsonOptout, fmt.Sprintf("Published on %s with ID: %s\n", pubInfo.LocalAddr.String(), pubInfo.ID.String())) }, } diff --git a/example/http-server/server.go b/example/http-server/server.go index 428d337e99..cb2d762a86 100644 --- a/example/http-server/server.go +++ b/example/http-server/server.go @@ -63,7 +63,7 @@ func main() { skyPort := port - id, err := rpcClient.Publish(port, skyPort, appnet.HTTP) + pubInfo, err := rpcClient.Publish(port, skyPort, appnet.HTTP) if err != nil { log.Errorf("error closing server: %v", err) } @@ -73,7 +73,7 @@ func main() { if err != nil { log.Errorf("error closing server: %v", err) } - err = rpcClient.Depublish(id) + err = rpcClient.Depublish(pubInfo.ID) if err != nil { log.Errorf("error closing server: %v", err) } diff --git a/pkg/visor/api.go b/pkg/visor/api.go index d3a6ae8fba..af3ed56bd1 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -116,12 +116,12 @@ type API interface { RouteGroups() ([]RouteGroupInfo, error) SetMinHops(uint16) error - Publish(localPort, skyPort int, appType appnet.AppType) (uuid.UUID, error) + Publish(localPort, skyPort int, appType appnet.AppType) (appnet.PublishInfo, error) Depublish(id uuid.UUID) error - ListPublished() (map[uuid.UUID]*appnet.PublishLis, error) - Connect(remotePK cipher.PubKey, remotePort, localPort int, appType appnet.AppType) (uuid.UUID, error) + ListPublished() ([]appnet.PublishInfo, error) + Connect(remotePK cipher.PubKey, remotePort, localPort int, appType appnet.AppType) (appnet.ConnectInfo, error) Disconnect(id uuid.UUID) error - ListConnected() (map[uuid.UUID]*appnet.ConnectConn, error) + ListConnected() ([]appnet.ConnectInfo, error) DialPing(config PingConfig) error Ping(config PingConfig) ([]time.Duration, error) StopPing(pk cipher.PubKey) error @@ -1544,10 +1544,10 @@ func (v *Visor) IsDMSGClientReady() (bool, error) { } // Connect implements API. -func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, webPort int, appType appnet.AppType) (uuid.UUID, error) { +func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, webPort int, appType appnet.AppType) (appnet.ConnectInfo, error) { if err := v.nM.IsConnectPortAvailable(webPort); err != nil { - return uuid.UUID{}, err + return appnet.ConnectInfo{}, err } remoteAddr := appnet.Addr{ @@ -1558,23 +1558,23 @@ func (v *Visor) Connect(remotePK cipher.PubKey, remotePort, webPort int, appType conn, err := appnet.Dial(remoteAddr) if err != nil { - return uuid.UUID{}, err + return appnet.ConnectInfo{}, err } remoteConn, err := appnet.WrapConn(conn) if err != nil { - return uuid.UUID{}, err + return appnet.ConnectInfo{}, err } connectConn, err := appnet.NewConnectConn(v.log, v.nM, remoteConn, remoteAddr, webPort, appType) if err != nil { - return uuid.UUID{}, err + return appnet.ConnectInfo{}, err } err = connectConn.Serve() if err != nil { - return uuid.UUID{}, err + return appnet.ConnectInfo{}, err } - return connectConn.ID, nil + return connectConn.ConnectInfo, nil } // Disconnect implements API. @@ -1587,12 +1587,17 @@ func (v *Visor) Disconnect(id uuid.UUID) error { } // ListHTTPPorts implements API. -func (v *Visor) ListConnected() (map[uuid.UUID]*appnet.ConnectConn, error) { - return v.nM.GetAllConnectConns(), nil +func (v *Visor) ListConnected() ([]appnet.ConnectInfo, error) { + cons := v.nM.GetAllConnectConns() + var connectInfos []appnet.ConnectInfo + for _, con := range cons { + connectInfos = append(connectInfos, con.ConnectInfo) + } + return connectInfos, nil } // Publish implements API. -func (v *Visor) Publish(localPort, skyPort int, appType appnet.AppType) (uuid.UUID, error) { +func (v *Visor) Publish(localPort, skyPort int, appType appnet.AppType) (appnet.PublishInfo, error) { addr := appnet.Addr{ Net: appnet.TypeSkynet, @@ -1601,25 +1606,25 @@ func (v *Visor) Publish(localPort, skyPort int, appType appnet.AppType) (uuid.UU } if err := v.nM.IsPublishPortAvailable(addr, appType); err != nil { - return uuid.UUID{}, err + return appnet.PublishInfo{}, err } lis, err := appnet.Listen(addr) if err != nil { - return uuid.UUID{}, err + return appnet.PublishInfo{}, err } publishLis, err := appnet.NewPublishListener(v.log, v.nM, lis, addr, appType) if err != nil { - return uuid.UUID{}, err + return appnet.PublishInfo{}, err } err = publishLis.Listen() if err != nil { - return uuid.UUID{}, err + return appnet.PublishInfo{}, err } - return publishLis.ID, nil + return publishLis.PublishInfo, nil } // Depublish implements API. @@ -1632,6 +1637,11 @@ func (v *Visor) Depublish(id uuid.UUID) error { } // ListPublished implements API. -func (v *Visor) ListPublished() (map[uuid.UUID]*appnet.PublishLis, error) { - return v.nM.GetAllPublishListeners(), nil +func (v *Visor) ListPublished() ([]appnet.PublishInfo, error) { + liss := v.nM.GetAllPublishListeners() + var publishInfos []appnet.PublishInfo + for _, lis := range liss { + publishInfos = append(publishInfos, lis.PublishInfo) + } + return publishInfos, nil } diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index 6daead8074..e106727f47 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -736,7 +736,7 @@ type ConnectIn struct { } // Connect creates a connection with the remote visor to listen on the remote port and serve that on the local port -func (r *RPC) Connect(in *ConnectIn, out *uuid.UUID) (err error) { +func (r *RPC) Connect(in *ConnectIn, out *appnet.ConnectInfo) (err error) { defer rpcutil.LogCall(r.log, "Connect", in)(out, &err) id, err := r.visor.Connect(in.RemotePK, in.RemotePort, in.LocalPort, in.AppType) @@ -752,7 +752,7 @@ func (r *RPC) Disconnect(id *uuid.UUID, _ *struct{}) (err error) { } // ListConnected lists all the sky connections that are connected -func (r *RPC) ListConnected(_ *struct{}, out *map[uuid.UUID]*appnet.ConnectConn) (err error) { +func (r *RPC) ListConnected(_ *struct{}, out *[]appnet.ConnectInfo) (err error) { defer rpcutil.LogCall(r.log, "ListConnected", nil)(out, &err) conns, err := r.visor.ListConnected() *out = conns @@ -767,7 +767,7 @@ type PublishIn struct { } // Publish publishes a listner for the local port to the skyport -func (r *RPC) Publish(in *PublishIn, out *uuid.UUID) (err error) { +func (r *RPC) Publish(in *PublishIn, out *appnet.PublishInfo) (err error) { defer rpcutil.LogCall(r.log, "Publish", in)(out, &err) id, err := r.visor.Publish(in.LocalPort, in.SkyPort, in.AppType) @@ -783,7 +783,7 @@ func (r *RPC) Depublish(id *uuid.UUID, _ *struct{}) (err error) { } // ListPublished lists all the local ports that are being published -func (r *RPC) ListPublished(_ *struct{}, out *map[uuid.UUID]*appnet.PublishLis) (err error) { +func (r *RPC) ListPublished(_ *struct{}, out *[]appnet.PublishInfo) (err error) { defer rpcutil.LogCall(r.log, "ListPublished", nil)(out, &err) liss, err := r.visor.ListPublished() *out = liss diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index 175a6936b7..a89fe06682 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -556,8 +556,8 @@ func (rc *rpcClient) IsDMSGClientReady() (bool, error) { } // Connect calls Connect. -func (rc *rpcClient) Connect(remotePK cipher.PubKey, remotePort, localPort int, appType appnet.AppType) (uuid.UUID, error) { - var out uuid.UUID +func (rc *rpcClient) Connect(remotePK cipher.PubKey, remotePort, localPort int, appType appnet.AppType) (appnet.ConnectInfo, error) { + var out appnet.ConnectInfo err := rc.Call("Connect", &ConnectIn{ RemotePK: remotePK, RemotePort: remotePort, @@ -574,8 +574,8 @@ func (rc *rpcClient) Disconnect(id uuid.UUID) error { } // Publish calls Publish. -func (rc *rpcClient) Publish(localPort int, skyPort int, appType appnet.AppType) (uuid.UUID, error) { - var out uuid.UUID +func (rc *rpcClient) Publish(localPort int, skyPort int, appType appnet.AppType) (appnet.PublishInfo, error) { + var out appnet.PublishInfo err := rc.Call("Publish", &PublishIn{ LocalPort: localPort, SkyPort: skyPort, @@ -591,15 +591,15 @@ func (rc *rpcClient) Depublish(id uuid.UUID) error { } // ListPublished calls ListPublished. -func (rc *rpcClient) ListPublished() (map[uuid.UUID]*appnet.PublishLis, error) { - var out map[uuid.UUID]*appnet.PublishLis +func (rc *rpcClient) ListPublished() ([]appnet.PublishInfo, error) { + var out []appnet.PublishInfo err := rc.Call("ListPublished", &struct{}{}, &out) return out, err } // ListConnected calls ListConnected. -func (rc *rpcClient) ListConnected() (map[uuid.UUID]*appnet.ConnectConn, error) { - var out map[uuid.UUID]*appnet.ConnectConn +func (rc *rpcClient) ListConnected() ([]appnet.ConnectInfo, error) { + var out []appnet.ConnectInfo err := rc.Call("ListConnected", &struct{}{}, &out) return out, err } @@ -1318,8 +1318,8 @@ func (mc *mockRPCClient) IsDMSGClientReady() (bool, error) { } // Connect implements API. -func (mc *mockRPCClient) Connect(remotePK cipher.PubKey, remotePort, localPort int, appType appnet.AppType) (uuid.UUID, error) { //nolint:all - return uuid.UUID{}, nil +func (mc *mockRPCClient) Connect(remotePK cipher.PubKey, remotePort, localPort int, appType appnet.AppType) (appnet.ConnectInfo, error) { //nolint:all + return appnet.ConnectInfo{}, nil } // Disconnect implements API. @@ -1328,8 +1328,8 @@ func (mc *mockRPCClient) Disconnect(id uuid.UUID) error { //nolint:all } // Publish implements API. -func (mc *mockRPCClient) Publish(localPort int, skyPort int, appType appnet.AppType) (uuid.UUID, error) { //nolint:all - return uuid.UUID{}, nil +func (mc *mockRPCClient) Publish(localPort int, skyPort int, appType appnet.AppType) (appnet.PublishInfo, error) { //nolint:all + return appnet.PublishInfo{}, nil } // Depublish implements API. @@ -1338,12 +1338,12 @@ func (mc *mockRPCClient) Depublish(id uuid.UUID) error { //nolint:all } // List implements API. -func (mc *mockRPCClient) ListConnected() (map[uuid.UUID]*appnet.ConnectConn, error) { +func (mc *mockRPCClient) ListConnected() ([]appnet.ConnectInfo, error) { return nil, nil } // ListHTTPPorts implements API. -func (mc *mockRPCClient) ListPublished() (map[uuid.UUID]*appnet.PublishLis, error) { +func (mc *mockRPCClient) ListPublished() ([]appnet.PublishInfo, error) { return nil, nil } From 4e695129d3955d0e768cfd35ef53fc7813b2576f Mon Sep 17 00:00:00 2001 From: ersonp Date: Tue, 25 Jun 2024 23:50:30 +0530 Subject: [PATCH 17/25] refactor: add subcommands to con and pub --- cmd/skywire-cli/commands/net/connect.go | 131 +++++++++++++++--------- cmd/skywire-cli/commands/net/publish.go | 96 +++++++++++------ 2 files changed, 148 insertions(+), 79 deletions(-) diff --git a/cmd/skywire-cli/commands/net/connect.go b/cmd/skywire-cli/commands/net/connect.go index c5b9ca3fc9..890bc175e7 100644 --- a/cmd/skywire-cli/commands/net/connect.go +++ b/cmd/skywire-cli/commands/net/connect.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "strconv" + "strings" "text/tabwriter" "github.com/google/uuid" @@ -18,29 +19,24 @@ import ( ) var ( - remotePort int - remotePk string - localPort int - lsPorts bool - disconnect string + localPort int ) func init() { - conCmd.Flags().IntVarP(&remotePort, "remote", "r", 0, "remote port to read from") - conCmd.Flags().StringVarP(&remotePk, "pk", "k", "", "remote public key to connect to") - conCmd.Flags().IntVarP(&localPort, "port", "p", 0, "local port to reverse proxy") - conCmd.Flags().BoolVarP(&lsPorts, "ls", "l", false, "list configured connections") + conCmd.Flags().IntVarP(&localPort, "port", "p", 0, "local port to serve the remote app on") conCmd.Flags().StringVarP(&netType, "type", "t", "http", "type of the remote app connection (http, tcp, udp)") - conCmd.Flags().StringVarP(&disconnect, "stop", "d", "", "disconnect from specified ") + + conCmd.AddCommand(lsCmd) + conCmd.AddCommand(stopCmd) RootCmd.AddCommand(conCmd) } -// conCmd contains commands to connect to a published port on the skywire network +// conCmd contains commands to connect to a published app on the skywire network var conCmd = &cobra.Command{ - Use: "con", - Short: "Connect to a published port on the skywire network", - Long: "Connect to a published port on the skywire network\nConnect to a remote port on the skywire network. This will allow you to access the remote port via the skywire network.", - Args: cobra.MinimumNArgs(0), + Use: "con [flags]", + Short: "Connect to a published app on the skywire network", + Long: "Connect to a published app on the skywire network.\n This will allow you to access the remote app via the skywire network.", + Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { rpcClient, err := clirpc.Client(cmd.Flags()) @@ -48,48 +44,25 @@ var conCmd = &cobra.Command{ os.Exit(1) } - if lsPorts { - connectConns, err := rpcClient.ListConnected() - internal.Catch(cmd.Flags(), err) - - var b bytes.Buffer - w := tabwriter.NewWriter(&b, 0, 0, 3, ' ', tabwriter.TabIndent) - _, err = fmt.Fprintln(w, "id\taddr\tweb_port") - internal.Catch(cmd.Flags(), err) - - for _, connectConn := range connectConns { - _, err = fmt.Fprintf(w, "%s\t%s\t%s\n", connectConn.ID, connectConn.RemoteAddr.String(), - strconv.Itoa(int(connectConn.WebPort))) - internal.Catch(cmd.Flags(), err) - } - internal.Catch(cmd.Flags(), w.Flush()) - internal.PrintOutput(cmd.Flags(), connectConns, b.String()) + if len(args) == 0 { + cmd.Help() //nolint os.Exit(0) } - if disconnect != "" { - id, err := uuid.Parse(disconnect) - internal.Catch(cmd.Flags(), err) - err = rpcClient.Disconnect(id) - internal.Catch(cmd.Flags(), err) - internal.PrintOutput(cmd.Flags(), "OK", "OK\n") - os.Exit(0) - } + var remotePK cipher.PubKey + var remotePort int + + parts := strings.Split(args[0], ":") - if len(args) == 0 && remotePk == "" { + if len(parts) != 2 { cmd.Help() //nolint os.Exit(0) } - var remotePK cipher.PubKey + internal.Catch(cmd.Flags(), remotePK.Set(parts[0])) - //if pk is specified via flag, argument will override - if len(args) > 0 { - internal.Catch(cmd.Flags(), remotePK.Set(args[0])) - } else { - if remotePk != "" { - internal.Catch(cmd.Flags(), remotePK.Set(remotePk)) - } + if remotePort, err = strconv.Atoi(parts[1]); err != nil { + internal.PrintFatalError(cmd.Flags(), fmt.Errorf("invalid port: %s", parts[1])) } if remotePort == 0 || localPort == 0 { @@ -126,3 +99,65 @@ var conCmd = &cobra.Command{ internal.PrintOutput(cmd.Flags(), jsonOptout, fmt.Sprintf("Connected to %s with ID: %s\n", connInfo.RemoteAddr.String(), connInfo.ID.String())) }, } + +// lsCmd contains commands to list connected apps on the skywire network +var lsCmd = &cobra.Command{ + Use: "ls", + Short: "List connected apps on the skywire network", + Long: "List connected apps on the skywire network.\nThis will show you the ID, address, and web port of the connected apps.", + Args: cobra.MinimumNArgs(0), + Run: func(cmd *cobra.Command, args []string) { + + if len(args) != 0 { + cmd.Help() //nolint + os.Exit(0) + } + + rpcClient, err := clirpc.Client(cmd.Flags()) + if err != nil { + os.Exit(1) + } + + connectConns, err := rpcClient.ListConnected() + internal.Catch(cmd.Flags(), err) + + var b bytes.Buffer + w := tabwriter.NewWriter(&b, 0, 0, 3, ' ', tabwriter.TabIndent) + _, err = fmt.Fprintln(w, "id\taddr\tweb_port\tapp_type") + internal.Catch(cmd.Flags(), err) + + for _, connectConn := range connectConns { + _, err = fmt.Fprintf(w, "%v\t%v\t%v\t%v\n", connectConn.ID, connectConn.RemoteAddr, + connectConn.WebPort, connectConn.AppType) + internal.Catch(cmd.Flags(), err) + } + internal.Catch(cmd.Flags(), w.Flush()) + internal.PrintOutput(cmd.Flags(), connectConns, b.String()) + }, +} + +// stopCmd contains commands to stop a connection to a published app on the skywire network +var stopCmd = &cobra.Command{ + Use: "stop ", + Short: "Stop a connection to a published app on the skywire network", + Long: "Stop a connection to a published app on the skywire network.\nThis will disconnect you from the remote app.", + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + + if len(args) == 0 { + cmd.Help() //nolint + os.Exit(0) + } + + rpcClient, err := clirpc.Client(cmd.Flags()) + if err != nil { + os.Exit(1) + } + + id, err := uuid.Parse(args[0]) + internal.Catch(cmd.Flags(), err) + err = rpcClient.Disconnect(id) + internal.Catch(cmd.Flags(), err) + internal.PrintOutput(cmd.Flags(), "OK", "OK\n") + }, +} diff --git a/cmd/skywire-cli/commands/net/publish.go b/cmd/skywire-cli/commands/net/publish.go index 158ead5d8a..6ebfdca65d 100644 --- a/cmd/skywire-cli/commands/net/publish.go +++ b/cmd/skywire-cli/commands/net/publish.go @@ -16,23 +16,23 @@ import ( ) var ( - depublish string - netType string - skyPort int + netType string + skyPort int ) func init() { pubCmd.PersistentFlags().IntVarP(&localPort, "port", "p", 0, "local port of the external (http, tcp, udp) app") pubCmd.PersistentFlags().IntVarP(&skyPort, "skyport", "s", localPort, "skywire port for the external (http, tcp, udp) app") - pubCmd.PersistentFlags().StringVarP(&depublish, "depublish", "d", "", "deregister local port of the external (http, tcp, udp) app with id") pubCmd.PersistentFlags().StringVarP(&netType, "type", "t", "http", "type of the external app connection (http, tcp, udp)") - pubCmd.PersistentFlags().BoolVarP(&lsPorts, "ls", "l", false, "list published local ports") + + pubCmd.AddCommand(lsPubCmd) + pubCmd.AddCommand(stopPubCmd) RootCmd.AddCommand(pubCmd) } // pubCmd contains commands to publish over the skywire network var pubCmd = &cobra.Command{ - Use: "pub", + Use: "pub [flags]", Short: "Publish over skywire network", Long: "Publish over skywire network\nPublish a local port over the skywire network. This will allow other nodes to access the local port via the skywire network.", Args: cobra.MinimumNArgs(0), @@ -43,31 +43,6 @@ var pubCmd = &cobra.Command{ os.Exit(1) } - if depublish != "" { - id, err := uuid.Parse(depublish) - internal.Catch(cmd.Flags(), err) - err = rpcClient.Depublish(id) - internal.Catch(cmd.Flags(), err) - internal.PrintOutput(cmd.Flags(), "OK", "OK\n") - os.Exit(0) - } - - if lsPorts { - liss, err := rpcClient.ListPublished() - internal.Catch(cmd.Flags(), err) - var b bytes.Buffer - w := tabwriter.NewWriter(&b, 0, 0, 2, ' ', tabwriter.TabIndent) - _, err = fmt.Fprintln(w, "id\tlocal_port") - internal.Catch(cmd.Flags(), err) - for id, lis := range liss { - _, err = fmt.Fprintf(w, "%v\t%v\n", id, lis.LocalAddr.GetPort()) - internal.Catch(cmd.Flags(), err) - } - internal.Catch(cmd.Flags(), w.Flush()) - internal.PrintOutput(cmd.Flags(), liss, b.String()) - os.Exit(0) - } - if skyPort == 0 { skyPort = localPort } @@ -120,3 +95,62 @@ var pubCmd = &cobra.Command{ }, } + +// lsPubCmd lists all the publised apps on the skywire network by the visor +var lsPubCmd = &cobra.Command{ + Use: "ls", + Short: "List published apps on the skywire network by the visor", + Long: "List published apps on the skywire network by the visor\nThe list contains the id and the local port of the published app.", + Args: cobra.MinimumNArgs(0), + Run: func(cmd *cobra.Command, args []string) { + + if len(args) != 0 { + cmd.Help() //nolint + os.Exit(0) + } + + rpcClient, err := clirpc.Client(cmd.Flags()) + if err != nil { + os.Exit(1) + } + + liss, err := rpcClient.ListPublished() + internal.Catch(cmd.Flags(), err) + var b bytes.Buffer + w := tabwriter.NewWriter(&b, 0, 0, 2, ' ', tabwriter.TabIndent) + _, err = fmt.Fprintln(w, "id\tlocal_port\tapp_type") + internal.Catch(cmd.Flags(), err) + for _, lis := range liss { + _, err = fmt.Fprintf(w, "%v\t%v\t%v\n", lis.ID, lis.LocalAddr.GetPort(), lis.AppType) + internal.Catch(cmd.Flags(), err) + } + internal.Catch(cmd.Flags(), w.Flush()) + internal.PrintOutput(cmd.Flags(), liss, b.String()) + }, +} + +// stopPubCmd stops a published app on the skywire network published by the visor +var stopPubCmd = &cobra.Command{ + Use: "stop ", + Short: "Stop a published app on the skywire network by the visor", + Long: "Stop a published app on the skywire network by the visor.\nThis will stop the published app and remove it from the skywire network.", + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + + if len(args) == 0 { + cmd.Help() //nolint + os.Exit(0) + } + + rpcClient, err := clirpc.Client(cmd.Flags()) + if err != nil { + os.Exit(1) + } + + id, err := uuid.Parse(args[0]) + internal.Catch(cmd.Flags(), err) + err = rpcClient.Depublish(id) + internal.Catch(cmd.Flags(), err) + internal.PrintOutput(cmd.Flags(), "OK", "OK\n") + }, +} From b9b97c5564dcce02af93b08bad4beeadaece731b Mon Sep 17 00:00:00 2001 From: ersonp Date: Tue, 25 Jun 2024 23:58:03 +0530 Subject: [PATCH 18/25] fix(publish): remove http.Server headers Remove ReadHeaderTimeout and WriteTimeout that were causing the publisher to close the incoming connection if nothing was connectead to it withing 5 seconds. --- pkg/app/appnet/publish.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/app/appnet/publish.go b/pkg/app/appnet/publish.go index b00e242ad5..f14d9ec642 100644 --- a/pkg/app/appnet/publish.go +++ b/pkg/app/appnet/publish.go @@ -10,7 +10,6 @@ import ( "net/http/httputil" "net/url" "sync" - "time" "github.com/gin-gonic/gin" "github.com/google/uuid" @@ -141,9 +140,7 @@ func newHTTPPublishServer(localPort int) *http.Server { }) srv := &http.Server{ - Handler: r, - ReadHeaderTimeout: 5 * time.Second, - WriteTimeout: 10 * time.Second, + Handler: r, } return srv From 7205554f5498286238b1b0e4f9b8115df0d1f031 Mon Sep 17 00:00:00 2001 From: ersonp Date: Wed, 26 Jun 2024 12:12:19 +0530 Subject: [PATCH 19/25] chore: fix linting issues --- pkg/app/appnet/connect.go | 14 ++++++++------ pkg/app/appnet/publish.go | 7 +++++-- pkg/app/appnet/skynet.go | 18 +++++++++--------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/pkg/app/appnet/connect.go b/pkg/app/appnet/connect.go index 7406181285..c1d9030962 100644 --- a/pkg/app/appnet/connect.go +++ b/pkg/app/appnet/connect.go @@ -8,6 +8,7 @@ import ( "net" "net/http" "sync" + "time" "github.com/gin-gonic/gin" "github.com/google/uuid" @@ -17,6 +18,7 @@ import ( "github.com/skycoin/skywire/pkg/routing" ) +// ConnectInfo represents the information of a connected connection type ConnectInfo struct { ID uuid.UUID `json:"id"` WebPort int `json:"web_port"` @@ -24,7 +26,7 @@ type ConnectInfo struct { AppType AppType `json:"app_type"` } -// ConnectConn represents a connection that is published on the skywire network +// ConnectConn represents a connection that is connected to a published app type ConnectConn struct { ConnectInfo skyConn net.Conn @@ -37,7 +39,6 @@ type ConnectConn struct { // NewConnectConn creates a new ConnectConn func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, remoteAddr Addr, webPort int, appType AppType) (*ConnectConn, error) { - var srv *http.Server var lis net.Listener @@ -72,7 +73,7 @@ func NewConnectConn(log *logging.Logger, nm *NetManager, remoteConn net.Conn, re return conn, nil } -// Serve serves a HTTP forward conn that accepts all requests and forwards them directly to the remote server over the specified net.Conn. +// Serve starts the server based on the AppType of the ConnectConn. func (f *ConnectConn) Serve() error { switch f.AppType { case HTTP: @@ -97,7 +98,7 @@ func (f *ConnectConn) Serve() error { return nil } -// Close closes the server and remote connection. +// Close closes the server, listener and remote connection. func (f *ConnectConn) Close() (err error) { f.closeOnce.Do(func() { @@ -176,8 +177,9 @@ func newHTTPConnectServer(log *logging.Logger, remoteConn net.Conn, remoteAddr A r.Any("/*path", handleConnectFunc(httpC, remoteAddr.PK(), remoteAddr.GetPort(), mu)) srv := &http.Server{ - Addr: fmt.Sprint(":", webPort), - Handler: r, + Addr: fmt.Sprint(":", webPort), + ReadHeaderTimeout: 5 * time.Second, + Handler: r, } return srv } diff --git a/pkg/app/appnet/publish.go b/pkg/app/appnet/publish.go index f14d9ec642..263ad57146 100644 --- a/pkg/app/appnet/publish.go +++ b/pkg/app/appnet/publish.go @@ -10,6 +10,7 @@ import ( "net/http/httputil" "net/url" "sync" + "time" "github.com/gin-gonic/gin" "github.com/google/uuid" @@ -17,6 +18,7 @@ import ( "github.com/skycoin/skywire-utilities/pkg/logging" ) +// PublishInfo represents the information of a published listener type PublishInfo struct { ID uuid.UUID `json:"id"` LocalAddr Addr `json:"local_addr"` @@ -69,7 +71,7 @@ func NewPublishListener(log *logging.Logger, nm *NetManager, skyLis net.Listener return pubLis, nil } -// Serve serves a HTTP forward Lis that accepts all requests and forwards them directly to the remote server over the specified net.Lis. +// Listen initializes the server based on AppType of the PublishLis. func (f *PublishLis) Listen() error { switch f.AppType { case HTTP: @@ -140,7 +142,8 @@ func newHTTPPublishServer(localPort int) *http.Server { }) srv := &http.Server{ - Handler: r, + Handler: r, + ReadHeaderTimeout: 5 * time.Second, } return srv diff --git a/pkg/app/appnet/skynet.go b/pkg/app/appnet/skynet.go index 2883b45913..6f70ef4945 100644 --- a/pkg/app/appnet/skynet.go +++ b/pkg/app/appnet/skynet.go @@ -48,7 +48,7 @@ func (nm *NetManager) isPublishPortAvailable(addr Addr, appType AppType) error { return nil } -// isPublishPortAvailable checks if a port and apptype is available for publishing +// IsPublishPortAvailable checks if a port and apptype is available for publishing func (nm *NetManager) IsPublishPortAvailable(addr Addr, appType AppType) error { nm.mu.Lock() defer nm.mu.Unlock() @@ -56,7 +56,7 @@ func (nm *NetManager) IsPublishPortAvailable(addr Addr, appType AppType) error { return nm.isPublishPortAvailable(addr, appType) } -// AddPublish adds publishListener to with it's ID +// AddPublish adds publishListener to the NetManager func (nm *NetManager) AddPublish(lis *PublishLis) error { nm.mu.Lock() defer nm.mu.Unlock() @@ -69,7 +69,7 @@ func (nm *NetManager) AddPublish(lis *PublishLis) error { return nil } -// GetpublishListenertner get's a publishListener by ID +// GetPublishListener get's a publishListener by ID func (nm *NetManager) GetPublishListener(id uuid.UUID) *PublishLis { nm.mu.Lock() defer nm.mu.Unlock() @@ -77,7 +77,7 @@ func (nm *NetManager) GetPublishListener(id uuid.UUID) *PublishLis { return nm.listeners[id] } -// GetAllpublishListenertners gets all publishListeners +// GetAllPublishListeners gets all publishListeners func (nm *NetManager) GetAllPublishListeners() map[uuid.UUID]*PublishLis { nm.mu.Lock() defer nm.mu.Unlock() @@ -85,7 +85,7 @@ func (nm *NetManager) GetAllPublishListeners() map[uuid.UUID]*PublishLis { return nm.listeners } -// RemovepublishListener removes a publishListener by ID +// RemovePublishListener removes a publishListener by ID func (nm *NetManager) RemovePublishListener(id uuid.UUID) { nm.mu.Lock() defer nm.mu.Unlock() @@ -111,7 +111,7 @@ func (nm *NetManager) IsConnectPortAvailable(webPort int) error { return nm.isConnectPortAvailable(webPort) } -// AddConnect adds ConnectConn to with it's ID +// AddConnect adds ConnectConn to the NetManager func (nm *NetManager) AddConnect(conn *ConnectConn) error { nm.mu.Lock() defer nm.mu.Unlock() @@ -148,7 +148,7 @@ func (nm *NetManager) RemoveConnectConn(id uuid.UUID) { delete(nm.conns, id) } -// RemoveConnectConn removes a ConnectConn by ID +// Close closes all the connections and listeners func (nm *NetManager) Close() error { nm.mu.Lock() defer nm.mu.Unlock() @@ -186,8 +186,8 @@ func loggingMiddleware() gin.HandlerFunc { // Get the method color methodColor := getMethodColor(method) // Print the logging in a custom format which includes the publickeyfrom c.Request.RemoteAddr ex.: - // [DMSGHTTP] 2023/05/18 - 19:43:15 | 200 | 10.80885ms | | 02b5ee5333aa6b7f5fc623b7d5f35f505cb7f974e98a70751cf41962f84c8c4637:49153 | GET /node-info.json - fmt.Printf("[DMSGWEB] %s |%s %3d %s| %13v | %15s | %72s |%s %-7s %s %s\n", + // [SKYNET] 2023/05/18 - 19:43:15 | 200 | 10.80885ms | | 02b5ee5333aa6b7f5fc623b7d5f35f505cb7f974e98a70751cf41962f84c8c4637:49153 | GET /node-info.json + fmt.Printf("[SKYNET] %s |%s %3d %s| %13v | %15s | %72s |%s %-7s %s %s\n", time.Now().Format("2006/01/02 - 15:04:05"), statusCodeBackgroundColor, statusCode, From b2f97a90bf86a3064ae22f6f22b15fb0aac43280 Mon Sep 17 00:00:00 2001 From: ersonp Date: Wed, 26 Jun 2024 15:52:05 +0530 Subject: [PATCH 20/25] chore: fix linting --- pkg/visor/init.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/visor/init.go b/pkg/visor/init.go index 3759f8adfe..a4f5c8a9fe 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -611,14 +611,12 @@ func initTransportSetup(ctx context.Context, v *Visor, log *logging.Logger) erro } func initSkywireNet(ctx context.Context, v *Visor, log *logging.Logger) error { - ctx, cancel := context.WithCancel(ctx) // waiting for at least one transport to initialize <-v.tpM.Ready() nM := appnet.NewNetManager() v.pushCloseStack("sky_net", func() error { - cancel() if cErr := nM.Close(); cErr != nil { log.WithError(cErr).Error("Error closing listener.") } From 1daefdd3e12fff75fa7c848f6014aa56cb5b5cac Mon Sep 17 00:00:00 2001 From: ersonp Date: Wed, 26 Jun 2024 16:20:10 +0530 Subject: [PATCH 21/25] fix(publish): impliment proper usage of LocalPort --- pkg/app/appnet/publish.go | 14 ++++++++------ pkg/app/appnet/skynet.go | 15 +++++++++------ pkg/visor/api.go | 4 ++-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/pkg/app/appnet/publish.go b/pkg/app/appnet/publish.go index 263ad57146..bd29a07d13 100644 --- a/pkg/app/appnet/publish.go +++ b/pkg/app/appnet/publish.go @@ -21,7 +21,8 @@ import ( // PublishInfo represents the information of a published listener type PublishInfo struct { ID uuid.UUID `json:"id"` - LocalAddr Addr `json:"local_addr"` + SkyAddr Addr `json:"sky_addr"` + LocalPort int `json:"local_port"` AppType AppType `json:"app_type"` } @@ -37,15 +38,15 @@ type PublishLis struct { } // NewPublishListener creates a new publishListener -func NewPublishListener(log *logging.Logger, nm *NetManager, skyLis net.Listener, addr Addr, appType AppType) (*PublishLis, error) { +func NewPublishListener(log *logging.Logger, nm *NetManager, skyLis net.Listener, localPort int, skyAddr Addr, appType AppType) (*PublishLis, error) { var srv *http.Server var conn net.Conn switch appType { case HTTP: - srv = newHTTPPublishServer(int(addr.GetPort())) + srv = newHTTPPublishServer(localPort) case TCP: - // conn = newTCPPublishConn(log, webPort) + // conn = newTCPPublishConn(log, localPort) return nil, errors.New("app type TCP is not supported yet") case UDP: return nil, errors.New("app type UDP is not supported yet") @@ -54,7 +55,8 @@ func NewPublishListener(log *logging.Logger, nm *NetManager, skyLis net.Listener pubLis := &PublishLis{ PublishInfo: PublishInfo{ ID: uuid.New(), - LocalAddr: addr, + SkyAddr: skyAddr, + LocalPort: localPort, AppType: appType, }, skyLis: skyLis, @@ -101,7 +103,7 @@ func (f *PublishLis) Listen() error { return errors.New("app type UDP is not supported yet") } - f.log.Debugf("Serving HTTP on sky port %v with SKY listener %s", f.LocalAddr.GetPort(), f.skyLis.Addr().String()) + f.log.Debugf("Serving local HTTP port: %v on SKY Addr %s", f.LocalPort, f.skyLis.Addr().String()) return nil } diff --git a/pkg/app/appnet/skynet.go b/pkg/app/appnet/skynet.go index 6f70ef4945..1609fd920b 100644 --- a/pkg/app/appnet/skynet.go +++ b/pkg/app/appnet/skynet.go @@ -38,22 +38,25 @@ func NewNetManager() *NetManager { } } -func (nm *NetManager) isPublishPortAvailable(addr Addr, appType AppType) error { +func (nm *NetManager) isPublishPortAvailable(addr Addr, localPort int, appType AppType) error { for _, l := range nm.listeners { - if l.LocalAddr.GetPort() == addr.GetPort() { - return fmt.Errorf("port %d is already in use for app type %v", addr.GetPort(), appType) + if l.SkyAddr.GetPort() == addr.GetPort() { + return fmt.Errorf("skyport %d is already in use for app type %v", addr.GetPort(), appType) + } + if l.LocalPort == localPort { + return fmt.Errorf("local port %d is already in use for app type %v", localPort, appType) } } return nil } // IsPublishPortAvailable checks if a port and apptype is available for publishing -func (nm *NetManager) IsPublishPortAvailable(addr Addr, appType AppType) error { +func (nm *NetManager) IsPublishPortAvailable(addr Addr, localPort int, appType AppType) error { nm.mu.Lock() defer nm.mu.Unlock() - return nm.isPublishPortAvailable(addr, appType) + return nm.isPublishPortAvailable(addr, localPort, appType) } // AddPublish adds publishListener to the NetManager @@ -61,7 +64,7 @@ func (nm *NetManager) AddPublish(lis *PublishLis) error { nm.mu.Lock() defer nm.mu.Unlock() - if err := nm.isPublishPortAvailable(lis.LocalAddr, lis.AppType); err != nil { + if err := nm.isPublishPortAvailable(lis.SkyAddr, lis.LocalPort, lis.AppType); err != nil { return err } diff --git a/pkg/visor/api.go b/pkg/visor/api.go index af3ed56bd1..ec3840b539 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -1605,7 +1605,7 @@ func (v *Visor) Publish(localPort, skyPort int, appType appnet.AppType) (appnet. Port: routing.Port(skyPort), } - if err := v.nM.IsPublishPortAvailable(addr, appType); err != nil { + if err := v.nM.IsPublishPortAvailable(addr, localPort, appType); err != nil { return appnet.PublishInfo{}, err } @@ -1614,7 +1614,7 @@ func (v *Visor) Publish(localPort, skyPort int, appType appnet.AppType) (appnet. return appnet.PublishInfo{}, err } - publishLis, err := appnet.NewPublishListener(v.log, v.nM, lis, addr, appType) + publishLis, err := appnet.NewPublishListener(v.log, v.nM, lis, localPort, addr, appType) if err != nil { return appnet.PublishInfo{}, err } From 02e630ce866f4da603d087b301dc81272cb6d6cb Mon Sep 17 00:00:00 2001 From: ersonp Date: Wed, 26 Jun 2024 16:20:37 +0530 Subject: [PATCH 22/25] refactor: Update `connect` and `publish` commands --- cmd/skywire-cli/commands/net/connect.go | 10 ++-------- cmd/skywire-cli/commands/net/publish.go | 13 +++---------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/cmd/skywire-cli/commands/net/connect.go b/cmd/skywire-cli/commands/net/connect.go index 890bc175e7..cbdd9731f9 100644 --- a/cmd/skywire-cli/commands/net/connect.go +++ b/cmd/skywire-cli/commands/net/connect.go @@ -89,14 +89,8 @@ var conCmd = &cobra.Command{ connInfo, err := rpcClient.Connect(remotePK, remotePort, localPort, appType) internal.Catch(cmd.Flags(), err) - jsonOptout := struct { - ID string `json:"id"` - RemoteAddr string `json:"addr"` - }{ - ID: connInfo.ID.String(), - RemoteAddr: connInfo.RemoteAddr.String(), - } - internal.PrintOutput(cmd.Flags(), jsonOptout, fmt.Sprintf("Connected to %s with ID: %s\n", connInfo.RemoteAddr.String(), connInfo.ID.String())) + internal.PrintOutput(cmd.Flags(), connInfo, fmt.Sprintf("Connected to %s with ID: %s\n", connInfo.RemoteAddr.String(), connInfo.ID.String())) + internal.PrintOutput(cmd.Flags(), connInfo, fmt.Sprintf("%v avaialble on localhost:%d\n", connInfo.AppType, connInfo.WebPort)) }, } diff --git a/cmd/skywire-cli/commands/net/publish.go b/cmd/skywire-cli/commands/net/publish.go index 6ebfdca65d..c7ec85ec06 100644 --- a/cmd/skywire-cli/commands/net/publish.go +++ b/cmd/skywire-cli/commands/net/publish.go @@ -84,14 +84,7 @@ var pubCmd = &cobra.Command{ pubInfo, err := rpcClient.Publish(localPort, skyPort, appType) internal.Catch(cmd.Flags(), err) - jsonOptout := struct { - ID string `json:"id"` - LocalAddr string `json:"addr"` - }{ - ID: pubInfo.ID.String(), - LocalAddr: pubInfo.LocalAddr.String(), - } - internal.PrintOutput(cmd.Flags(), jsonOptout, fmt.Sprintf("Published on %s with ID: %s\n", pubInfo.LocalAddr.String(), pubInfo.ID.String())) + internal.PrintOutput(cmd.Flags(), pubInfo, fmt.Sprintf("Published on %s with ID: %s\n", pubInfo.SkyAddr.String(), pubInfo.ID.String())) }, } @@ -118,10 +111,10 @@ var lsPubCmd = &cobra.Command{ internal.Catch(cmd.Flags(), err) var b bytes.Buffer w := tabwriter.NewWriter(&b, 0, 0, 2, ' ', tabwriter.TabIndent) - _, err = fmt.Fprintln(w, "id\tlocal_port\tapp_type") + _, err = fmt.Fprintln(w, "id\tsky_port\tlocal_port\tapp_type") internal.Catch(cmd.Flags(), err) for _, lis := range liss { - _, err = fmt.Fprintf(w, "%v\t%v\t%v\n", lis.ID, lis.LocalAddr.GetPort(), lis.AppType) + _, err = fmt.Fprintf(w, "%v\t%v\t%v\t%v\n", lis.ID, lis.SkyAddr.GetPort(), lis.LocalPort, lis.AppType) internal.Catch(cmd.Flags(), err) } internal.Catch(cmd.Flags(), w.Flush()) From 42e44ad8417b0974301c94189a4178715d5d27b6 Mon Sep 17 00:00:00 2001 From: ersonp Date: Wed, 26 Jun 2024 16:28:15 +0530 Subject: [PATCH 23/25] refactor: Update `publish.go` to address Slowloris attack vector --- pkg/app/appnet/publish.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/app/appnet/publish.go b/pkg/app/appnet/publish.go index bd29a07d13..a1c7decb6a 100644 --- a/pkg/app/appnet/publish.go +++ b/pkg/app/appnet/publish.go @@ -10,7 +10,6 @@ import ( "net/http/httputil" "net/url" "sync" - "time" "github.com/gin-gonic/gin" "github.com/google/uuid" @@ -142,10 +141,10 @@ func newHTTPPublishServer(localPort int) *http.Server { } proxy.ServeHTTP(c.Writer, c.Request) }) - + // #nosec G112 -- Ignoring potential Slowloris attacks as it the connection to close if the skynet connect is too slow to send the request srv := &http.Server{ - Handler: r, - ReadHeaderTimeout: 5 * time.Second, + Handler: r, + // todo(ersonp): Consider setting ReadHeaderTimeout to a reasonable value to address the Slowloris attack vector } return srv From 18b96db317d6f33e9a9f9e4a53e3aa4be445cff4 Mon Sep 17 00:00:00 2001 From: ersonp Date: Wed, 26 Jun 2024 16:43:18 +0530 Subject: [PATCH 24/25] chore: fix linting --- pkg/visor/api.go | 2 +- pkg/visor/init.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/visor/api.go b/pkg/visor/api.go index ec3840b539..c66c7f1c65 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -1586,7 +1586,7 @@ func (v *Visor) Disconnect(id uuid.UUID) error { return connectConn.Close() } -// ListHTTPPorts implements API. +// ListConnected implements API. func (v *Visor) ListConnected() ([]appnet.ConnectInfo, error) { cons := v.nM.GetAllConnectConns() var connectInfos []appnet.ConnectInfo diff --git a/pkg/visor/init.go b/pkg/visor/init.go index a4f5c8a9fe..2c4c074617 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -610,7 +610,7 @@ func initTransportSetup(ctx context.Context, v *Visor, log *logging.Logger) erro return nil } -func initSkywireNet(ctx context.Context, v *Visor, log *logging.Logger) error { +func initSkywireNet(_ context.Context, v *Visor, log *logging.Logger) error { // waiting for at least one transport to initialize <-v.tpM.Ready() From 3ab96d9d137bda52277798a14984b8ec9ac608b6 Mon Sep 17 00:00:00 2001 From: ersonp Date: Fri, 28 Jun 2024 12:27:08 +0530 Subject: [PATCH 25/25] chore: fix linting --- cmd/skywire-cli/commands/net/connect.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/skywire-cli/commands/net/connect.go b/cmd/skywire-cli/commands/net/connect.go index cbdd9731f9..c34e386bfd 100644 --- a/cmd/skywire-cli/commands/net/connect.go +++ b/cmd/skywire-cli/commands/net/connect.go @@ -90,7 +90,7 @@ var conCmd = &cobra.Command{ internal.Catch(cmd.Flags(), err) internal.PrintOutput(cmd.Flags(), connInfo, fmt.Sprintf("Connected to %s with ID: %s\n", connInfo.RemoteAddr.String(), connInfo.ID.String())) - internal.PrintOutput(cmd.Flags(), connInfo, fmt.Sprintf("%v avaialble on localhost:%d\n", connInfo.AppType, connInfo.WebPort)) + internal.PrintOutput(cmd.Flags(), connInfo, fmt.Sprintf("%v available on localhost:%d\n", connInfo.AppType, connInfo.WebPort)) }, }