summaryrefslogtreecommitdiffhomepage
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/tailscale/cli/serve.go1
-rw-r--r--cmd/tailscale/cli/serve_dev.go85
2 files changed, 74 insertions, 12 deletions
diff --git a/cmd/tailscale/cli/serve.go b/cmd/tailscale/cli/serve.go
index 39c0e106e..ab1a16913 100644
--- a/cmd/tailscale/cli/serve.go
+++ b/cmd/tailscale/cli/serve.go
@@ -149,7 +149,6 @@ type localServeClient interface {
QueryFeature(ctx context.Context, feature string) (*tailcfg.QueryFeatureResponse, error)
WatchIPNBus(ctx context.Context, mask ipn.NotifyWatchOpt) (*tailscale.IPNBusWatcher, error)
IncrementCounter(ctx context.Context, name string, delta int) error
- StreamServe(ctx context.Context, req ipn.ServeStreamRequest) (io.ReadCloser, error) // TODO: testing :)
}
// serveEnv is the environment the serve command runs within. All I/O should be
diff --git a/cmd/tailscale/cli/serve_dev.go b/cmd/tailscale/cli/serve_dev.go
index c2c94cc4a..30aa43119 100644
--- a/cmd/tailscale/cli/serve_dev.go
+++ b/cmd/tailscale/cli/serve_dev.go
@@ -5,9 +5,10 @@ package cli
import (
"context"
+ "encoding/json"
+ "errors"
"flag"
"fmt"
- "io"
"log"
"os"
"os/signal"
@@ -29,15 +30,15 @@ var infoMap = map[string]commandInfo{
"serve": {
ShortHelp: "Serve content and local servers on your tailnet",
LongHelp: strings.Join([]string{
- "Serve lets you share a local server securely within your tailnet.",
- "To share a local server on the internet, use \"tailscale funnel\"",
+ `Serve lets you share a local server securely within your tailnet.`,
+ `To share a local server on the internet, use "tailscale funnel"`,
}, "\n"),
},
"funnel": {
ShortHelp: "Serve content and local servers on the internet",
LongHelp: strings.Join([]string{
- "Funnel lets you share a local server on the internet using Tailscale.",
- "To share only within your tailnet, use \"tailscale serve\"",
+ `Funnel lets you share a local server on the internet using Tailscale.`,
+ `To share only within your tailnet, use "tailscale serve"`,
}, "\n"),
},
}
@@ -134,14 +135,76 @@ func (e *serveEnv) runServeDev(funnel bool) execFunc {
}
func (e *serveEnv) streamServe(ctx context.Context, req ipn.ServeStreamRequest) error {
- stream, err := e.lc.StreamServe(ctx, req)
+ watcher, err := e.lc.WatchIPNBus(ctx, ipn.NotifyInitialState|ipn.NotifyServeRequest)
if err != nil {
return err
}
- defer stream.Close()
+ defer watcher.Close()
+ n, err := watcher.Next()
+ if err != nil {
+ return err
+ }
+ if n.SessionID == "" {
+ return errors.New("missing session id")
+ }
+ sc, err := e.lc.GetServeConfig(ctx)
+ if err != nil {
+ return fmt.Errorf("error getting serve config: %w", err)
+ }
+ if sc == nil {
+ sc = &ipn.ServeConfig{}
+ }
+ setHandler(sc, req, n.SessionID)
+ err = e.lc.SetServeConfig(ctx, sc)
+ if err != nil {
+ return fmt.Errorf("error setting serve config: %w", err)
+ }
+
+ fmt.Fprintf(os.Stderr, "Funnel started on \"https://%s\".\n", strings.TrimSuffix(string(req.HostPort), ":443"))
+ fmt.Fprintf(os.Stderr, "Press Ctrl-C to stop Funnel.\n\n")
- fmt.Fprintf(os.Stderr, "Serve started on \"https://%s\".\n", strings.TrimSuffix(string(req.HostPort), ":443"))
- fmt.Fprintf(os.Stderr, "Press Ctrl-C to stop.\n\n")
- _, err = io.Copy(os.Stdout, stream)
- return err
+ for {
+ n, err := watcher.Next()
+ if err != nil {
+ return fmt.Errorf("error calling next: %w", err)
+ }
+ if n.FunnelRequestLog == nil {
+ continue
+ }
+ bts, _ := json.Marshal(n.FunnelRequestLog)
+ fmt.Printf("%s\n", bts)
+ }
+}
+
+func setHandler(sc *ipn.ServeConfig, req ipn.ServeStreamRequest, sessionID string) {
+ if sc.Foreground == nil {
+ sc.Foreground = make(map[string]*ipn.ServeConfig)
+ }
+ if sc.Foreground[sessionID] == nil {
+ sc.Foreground[sessionID] = &ipn.ServeConfig{}
+ }
+ if sc.Foreground[sessionID].TCP == nil {
+ sc.Foreground[sessionID].TCP = make(map[uint16]*ipn.TCPPortHandler)
+ }
+ if _, ok := sc.Foreground[sessionID].TCP[443]; !ok {
+ sc.Foreground[sessionID].TCP[443] = &ipn.TCPPortHandler{HTTPS: true}
+ }
+ if sc.Foreground[sessionID].Web == nil {
+ sc.Foreground[sessionID].Web = make(map[ipn.HostPort]*ipn.WebServerConfig)
+ }
+ wsc, ok := sc.Foreground[sessionID].Web[req.HostPort]
+ if !ok {
+ wsc = &ipn.WebServerConfig{}
+ sc.Foreground[sessionID].Web[req.HostPort] = wsc
+ }
+ if wsc.Handlers == nil {
+ wsc.Handlers = make(map[string]*ipn.HTTPHandler)
+ }
+ wsc.Handlers[req.MountPoint] = &ipn.HTTPHandler{
+ Proxy: req.Source,
+ }
+ if sc.AllowFunnel == nil {
+ sc.AllowFunnel = make(map[ipn.HostPort]bool)
+ }
+ sc.AllowFunnel[req.HostPort] = true
}