diff --git a/internal/cli/tunnel.go b/internal/cli/tunnel.go index bd19051..6d88995 100644 --- a/internal/cli/tunnel.go +++ b/internal/cli/tunnel.go @@ -1,13 +1,16 @@ package cli import ( + "context" "fmt" - "net/http" - "net/http/httputil" - "net/url" + "os" + "os/signal" + "syscall" - "github.com/kernelshard/expose/internal/config" "github.com/spf13/cobra" + + "github.com/kernelshard/expose/internal/config" + "github.com/kernelshard/expose/internal/tunnel" ) // tunnelCmd represents the 'tunnel' command in the CLI application. @@ -40,14 +43,39 @@ func newTunnelCmd() *cobra.Command { // runTunnel sets up a reverse proxy to expose the local server // on the specified port. func runTunnel(port int) error { - fmt.Printf("Exposing localhost:%d\n", port) - fmt.Printf("Local URL: http://localhost:8080\n") - fmt.Println("\nPress Ctrl+C to stop") - // Create reverse proxy to forward requests - target, _ := url.Parse(fmt.Sprintf("http://localhost:%d", port)) - proxy := httputil.NewSingleHostReverseProxy(target) + // Create manager + mgr := tunnel.NewManager(port) + + // context with signal handling + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // handle Ctrl+C, kill pid etc + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + + // waiting to read from channel is blocking ops, so wait in bg. + go func() { + <-sigChan + fmt.Println("\n\nShutting down...") + cancel() + }() + + // start in background + errChan := make(chan error, 1) + go func() { + errChan <- mgr.Start(ctx) + }() + + // wait for ready + <-mgr.Ready() + + // Show info + fmt.Printf("🚀 Starting tunnel for localhost:%d\n\n", port) + fmt.Printf("✓ Public URL: %s\n", mgr.PublicURL()) + fmt.Printf("✓ Forwarding to: http://localhost:%d\n\n", port) + fmt.Println("Press Ctrl+C to stop") - // Start local server - return http.ListenAndServe(":8080", proxy) + return <-errChan } diff --git a/internal/cli/tunnel_test.go b/internal/cli/tunnel_test.go new file mode 100644 index 0000000..646e39b --- /dev/null +++ b/internal/cli/tunnel_test.go @@ -0,0 +1,27 @@ +package cli + +import ( + "testing" +) + +func TestTunnelCmd(t *testing.T) { + cmd := newTunnelCmd() + + if cmd == nil { + t.Fatal("newTunnelCmd returned nil") + } + + if cmd.Use != "tunnel" { + t.Errorf("expected Use 'tunnel', got '%s'", cmd.Use) + } + + // Check flag parsing + flag := cmd.Flags().Lookup("port") + if flag == nil { + t.Error("port flag not defined") + } + + if flag.Shorthand != "p" { + t.Errorf("expected shorthand 'p' got %s", flag.Shorthand) + } +}