{cmd/tailscale/cli,ipn}: add http support to tailscale serve (#8358)

Updates #8357

Signed-off-by: Shayne Sweeney <shayne@tailscale.com>
pull/8120/head
shayne 12 months ago committed by GitHub
parent a2153afeeb
commit 6697690b55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -30,10 +30,10 @@ func newFunnelCommand(e *serveEnv) *ffcli.Command {
return &ffcli.Command{ return &ffcli.Command{
Name: "funnel", Name: "funnel",
ShortHelp: "Turn on/off Funnel service", ShortHelp: "Turn on/off Funnel service",
ShortUsage: strings.TrimSpace(` ShortUsage: strings.Join([]string{
funnel <serve-port> {on|off} "funnel <serve-port> {on|off}",
funnel status [--json] "funnel status [--json]",
`), }, "\n "),
LongHelp: strings.Join([]string{ LongHelp: strings.Join([]string{
"Funnel allows you to publish a 'tailscale serve'", "Funnel allows you to publish a 'tailscale serve'",
"server publicly, open to the entire internet.", "server publicly, open to the entire internet.",

@ -35,13 +35,14 @@ func newServeCommand(e *serveEnv) *ffcli.Command {
return &ffcli.Command{ return &ffcli.Command{
Name: "serve", Name: "serve",
ShortHelp: "Serve content and local servers", ShortHelp: "Serve content and local servers",
ShortUsage: strings.TrimSpace(` ShortUsage: strings.Join([]string{
serve https:<port> <mount-point> <source> [off] "serve http:<port> <mount-point> <source> [off]",
serve tcp:<port> tcp://localhost:<local-port> [off] "serve https:<port> <mount-point> <source> [off]",
serve tls-terminated-tcp:<port> tcp://localhost:<local-port> [off] "serve tcp:<port> tcp://localhost:<local-port> [off]",
serve status [--json] "serve tls-terminated-tcp:<port> tcp://localhost:<local-port> [off]",
serve reset "serve status [--json]",
`), "serve reset",
}, "\n "),
LongHelp: strings.TrimSpace(` LongHelp: strings.TrimSpace(`
*** BETA; all of this is subject to change *** *** BETA; all of this is subject to change ***
@ -58,8 +59,8 @@ EXAMPLES
- To proxy requests to a web server at 127.0.0.1:3000: - To proxy requests to a web server at 127.0.0.1:3000:
$ tailscale serve https:443 / http://127.0.0.1:3000 $ tailscale serve https:443 / http://127.0.0.1:3000
Or, using the default port: Or, using the default port (443):
$ tailscale serve https / http://127.0.0.1:3000 $ tailscale serve https / http://127.0.0.1:3000
- To serve a single file or a directory of files: - To serve a single file or a directory of files:
$ tailscale serve https / /home/alice/blog/index.html $ tailscale serve https / /home/alice/blog/index.html
@ -68,6 +69,12 @@ EXAMPLES
- To serve simple static text: - To serve simple static text:
$ tailscale serve https:8080 / text:"Hello, world!" $ tailscale serve https:8080 / text:"Hello, world!"
- To serve over HTTP (tailnet only):
$ tailscale serve http:80 / http://127.0.0.1:3000
Or, using the default port (80):
$ tailscale serve http / http://127.0.0.1:3000
- To forward incoming TCP connections on port 2222 to a local TCP server on - To forward incoming TCP connections on port 2222 to a local TCP server on
port 22 (e.g. to run OpenSSH in parallel with Tailscale SSH): port 22 (e.g. to run OpenSSH in parallel with Tailscale SSH):
$ tailscale serve tcp:2222 tcp://localhost:22 $ tailscale serve tcp:2222 tcp://localhost:22
@ -175,6 +182,7 @@ func (e *serveEnv) getLocalClientStatus(ctx context.Context) (*ipnstate.Status,
// serve config types like proxy, path, and text. // serve config types like proxy, path, and text.
// //
// Examples: // Examples:
// - tailscale serve http / http://localhost:3000
// - tailscale serve https / http://localhost:3000 // - tailscale serve https / http://localhost:3000
// - tailscale serve https /images/ /var/www/images/ // - tailscale serve https /images/ /var/www/images/
// - tailscale serve https:10000 /motd.txt text:"Hello, world!" // - tailscale serve https:10000 /motd.txt text:"Hello, world!"
@ -199,19 +207,14 @@ func (e *serveEnv) runServe(ctx context.Context, args []string) error {
return e.lc.SetServeConfig(ctx, sc) return e.lc.SetServeConfig(ctx, sc)
} }
parsePort := func(portStr string) (uint16, error) {
port64, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return 0, err
}
return uint16(port64), nil
}
srcType, srcPortStr, found := strings.Cut(args[0], ":") srcType, srcPortStr, found := strings.Cut(args[0], ":")
if !found { if !found {
if srcType == "https" && srcPortStr == "" { if srcType == "https" && srcPortStr == "" {
// Default https port to 443. // Default https port to 443.
srcPortStr = "443" srcPortStr = "443"
} else if srcType == "http" && srcPortStr == "" {
// Default http port to 80.
srcPortStr = "80"
} else { } else {
return flag.ErrHelp return flag.ErrHelp
} }
@ -219,18 +222,18 @@ func (e *serveEnv) runServe(ctx context.Context, args []string) error {
turnOff := "off" == args[len(args)-1] turnOff := "off" == args[len(args)-1]
if len(args) < 2 || (srcType == "https" && !turnOff && len(args) < 3) { if len(args) < 2 || ((srcType == "https" || srcType == "http") && !turnOff && len(args) < 3) {
fmt.Fprintf(os.Stderr, "error: invalid number of arguments\n\n") fmt.Fprintf(os.Stderr, "error: invalid number of arguments\n\n")
return flag.ErrHelp return flag.ErrHelp
} }
srcPort, err := parsePort(srcPortStr) srcPort, err := parseServePort(srcPortStr)
if err != nil { if err != nil {
return err return fmt.Errorf("invalid port %q: %w", srcPortStr, err)
} }
switch srcType { switch srcType {
case "https": case "https", "http":
mount, err := cleanMountPoint(args[1]) mount, err := cleanMountPoint(args[1])
if err != nil { if err != nil {
return err return err
@ -238,7 +241,8 @@ func (e *serveEnv) runServe(ctx context.Context, args []string) error {
if turnOff { if turnOff {
return e.handleWebServeRemove(ctx, srcPort, mount) return e.handleWebServeRemove(ctx, srcPort, mount)
} }
return e.handleWebServe(ctx, srcPort, mount, args[2]) useTLS := srcType == "https"
return e.handleWebServe(ctx, srcPort, useTLS, mount, args[2])
case "tcp", "tls-terminated-tcp": case "tcp", "tls-terminated-tcp":
if turnOff { if turnOff {
return e.handleTCPServeRemove(ctx, srcPort) return e.handleTCPServeRemove(ctx, srcPort)
@ -246,20 +250,20 @@ func (e *serveEnv) runServe(ctx context.Context, args []string) error {
return e.handleTCPServe(ctx, srcType, srcPort, args[1]) return e.handleTCPServe(ctx, srcType, srcPort, args[1])
default: default:
fmt.Fprintf(os.Stderr, "error: invalid serve type %q\n", srcType) fmt.Fprintf(os.Stderr, "error: invalid serve type %q\n", srcType)
fmt.Fprint(os.Stderr, "must be one of: https:<port>, tcp:<port> or tls-terminated-tcp:<port>\n\n", srcType) fmt.Fprint(os.Stderr, "must be one of: http:<port>, https:<port>, tcp:<port> or tls-terminated-tcp:<port>\n\n", srcType)
return flag.ErrHelp return flag.ErrHelp
} }
} }
// handleWebServe handles the "tailscale serve https:..." subcommand. // handleWebServe handles the "tailscale serve (http/https):..." subcommand. It
// It configures the serve config to forward HTTPS connections to the // configures the serve config to forward HTTPS connections to the given source.
// given source.
// //
// Examples: // Examples:
// - tailscale serve http / http://localhost:3000
// - tailscale serve https / http://localhost:3000 // - tailscale serve https / http://localhost:3000
// - tailscale serve https:8443 /files/ /home/alice/shared-files/ // - tailscale serve https:8443 /files/ /home/alice/shared-files/
// - tailscale serve https:10000 /motd.txt text:"Hello, world!" // - tailscale serve https:10000 /motd.txt text:"Hello, world!"
func (e *serveEnv) handleWebServe(ctx context.Context, srvPort uint16, mount, source string) error { func (e *serveEnv) handleWebServe(ctx context.Context, srvPort uint16, useTLS bool, mount, source string) error {
h := new(ipn.HTTPHandler) h := new(ipn.HTTPHandler)
ts, _, _ := strings.Cut(source, ":") ts, _, _ := strings.Cut(source, ":")
@ -318,7 +322,7 @@ func (e *serveEnv) handleWebServe(ctx context.Context, srvPort uint16, mount, so
return flag.ErrHelp return flag.ErrHelp
} }
mak.Set(&sc.TCP, srvPort, &ipn.TCPPortHandler{HTTPS: true}) mak.Set(&sc.TCP, srvPort, &ipn.TCPPortHandler{HTTPS: useTLS, HTTP: !useTLS})
if _, ok := sc.Web[hp]; !ok { if _, ok := sc.Web[hp]; !ok {
mak.Set(&sc.Web, hp, new(ipn.WebServerConfig)) mak.Set(&sc.Web, hp, new(ipn.WebServerConfig))
@ -626,7 +630,10 @@ func (e *serveEnv) runServeStatus(ctx context.Context, args []string) error {
printf("\n") printf("\n")
} }
for hp := range sc.Web { for hp := range sc.Web {
printWebStatusTree(sc, hp) err := e.printWebStatusTree(sc, hp)
if err != nil {
return err
}
printf("\n") printf("\n")
} }
printFunnelWarning(sc) printFunnelWarning(sc)
@ -665,20 +672,37 @@ func printTCPStatusTree(ctx context.Context, sc *ipn.ServeConfig, st *ipnstate.S
return nil return nil
} }
func printWebStatusTree(sc *ipn.ServeConfig, hp ipn.HostPort) { func (e *serveEnv) printWebStatusTree(sc *ipn.ServeConfig, hp ipn.HostPort) error {
// No-op if no serve config
if sc == nil { if sc == nil {
return return nil
} }
fStatus := "tailnet only" fStatus := "tailnet only"
if sc.AllowFunnel[hp] { if sc.AllowFunnel[hp] {
fStatus = "Funnel on" fStatus = "Funnel on"
} }
host, portStr, _ := net.SplitHostPort(string(hp)) host, portStr, _ := net.SplitHostPort(string(hp))
if portStr == "443" {
printf("https://%s (%s)\n", host, fStatus) port, err := parseServePort(portStr)
} else { if err != nil {
printf("https://%s:%s (%s)\n", host, portStr, fStatus) return fmt.Errorf("invalid port %q: %w", portStr, err)
}
scheme := "https"
if sc.IsServingHTTP(port) {
scheme = "http"
} }
portPart := ":" + portStr
if scheme == "http" && portStr == "80" ||
scheme == "https" && portStr == "443" {
portPart = ""
}
if scheme == "http" {
hostname, _, _ := strings.Cut("host", ".")
printf("%s://%s%s (%s)\n", scheme, hostname, portPart, fStatus)
}
printf("%s://%s%s (%s)\n", scheme, host, portPart, fStatus)
srvTypeAndDesc := func(h *ipn.HTTPHandler) (string, string) { srvTypeAndDesc := func(h *ipn.HTTPHandler) (string, string) {
switch { switch {
case h.Path != "": case h.Path != "":
@ -705,6 +729,8 @@ func printWebStatusTree(sc *ipn.ServeConfig, hp ipn.HostPort) {
t, d := srvTypeAndDesc(h) t, d := srvTypeAndDesc(h)
printf("%s %s%s %-5s %s\n", "|--", m, strings.Repeat(" ", maxLen-len(m)), t, d) printf("%s %s%s %-5s %s\n", "|--", m, strings.Repeat(" ", maxLen-len(m)), t, d)
} }
return nil
} }
func elipticallyTruncate(s string, max int) string { func elipticallyTruncate(s string, max int) string {
@ -725,3 +751,16 @@ func (e *serveEnv) runServeReset(ctx context.Context, args []string) error {
sc := new(ipn.ServeConfig) sc := new(ipn.ServeConfig)
return e.lc.SetServeConfig(ctx, sc) return e.lc.SetServeConfig(ctx, sc)
} }
// parseServePort parses a port number from a string and returns it as a
// uint16. It returns an error if the port number is invalid or zero.
func parseServePort(s string) (uint16, error) {
p, err := strconv.ParseUint(s, 10, 16)
if err != nil {
return 0, err
}
if p == 0 {
return 0, errors.New("port number must be non-zero")
}
return uint16(p), nil
}

@ -89,6 +89,59 @@ func TestServeConfigMutations(t *testing.T) {
wantErr: exactErr(flag.ErrHelp, "flag.ErrHelp"), wantErr: exactErr(flag.ErrHelp, "flag.ErrHelp"),
}) })
// https
add(step{reset: true})
add(step{ // allow omitting port (default to 80)
command: cmd("http / http://localhost:3000"),
want: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{80: {HTTP: true}},
Web: map[ipn.HostPort]*ipn.WebServerConfig{
"foo.test.ts.net:80": {Handlers: map[string]*ipn.HTTPHandler{
"/": {Proxy: "http://127.0.0.1:3000"},
}},
},
},
})
add(step{ // support non Funnel port
command: cmd("http:9999 /abc http://localhost:3001"),
want: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{80: {HTTP: true}, 9999: {HTTP: true}},
Web: map[ipn.HostPort]*ipn.WebServerConfig{
"foo.test.ts.net:80": {Handlers: map[string]*ipn.HTTPHandler{
"/": {Proxy: "http://127.0.0.1:3000"},
}},
"foo.test.ts.net:9999": {Handlers: map[string]*ipn.HTTPHandler{
"/abc": {Proxy: "http://127.0.0.1:3001"},
}},
},
},
})
add(step{
command: cmd("http:9999 /abc off"),
want: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{80: {HTTP: true}},
Web: map[ipn.HostPort]*ipn.WebServerConfig{
"foo.test.ts.net:80": {Handlers: map[string]*ipn.HTTPHandler{
"/": {Proxy: "http://127.0.0.1:3000"},
}},
},
},
})
add(step{
command: cmd("http:8080 /abc http://127.0.0.1:3001"),
want: &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{80: {HTTP: true}, 8080: {HTTP: true}},
Web: map[ipn.HostPort]*ipn.WebServerConfig{
"foo.test.ts.net:80": {Handlers: map[string]*ipn.HTTPHandler{
"/": {Proxy: "http://127.0.0.1:3000"},
}},
"foo.test.ts.net:8080": {Handlers: map[string]*ipn.HTTPHandler{
"/abc": {Proxy: "http://127.0.0.1:3001"},
}},
},
},
})
// https // https
add(step{reset: true}) add(step{reset: true})
add(step{ add(step{

@ -103,6 +103,7 @@ func (src *TCPPortHandler) Clone() *TCPPortHandler {
// A compilation failure here means this code must be regenerated, with the command at the top of this file. // A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _TCPPortHandlerCloneNeedsRegeneration = TCPPortHandler(struct { var _TCPPortHandlerCloneNeedsRegeneration = TCPPortHandler(struct {
HTTPS bool HTTPS bool
HTTP bool
TCPForward string TCPForward string
TerminateTLS string TerminateTLS string
}{}) }{})

@ -228,12 +228,14 @@ func (v *TCPPortHandlerView) UnmarshalJSON(b []byte) error {
} }
func (v TCPPortHandlerView) HTTPS() bool { return v.ж.HTTPS } func (v TCPPortHandlerView) HTTPS() bool { return v.ж.HTTPS }
func (v TCPPortHandlerView) HTTP() bool { return v.ж.HTTP }
func (v TCPPortHandlerView) TCPForward() string { return v.ж.TCPForward } func (v TCPPortHandlerView) TCPForward() string { return v.ж.TCPForward }
func (v TCPPortHandlerView) TerminateTLS() string { return v.ж.TerminateTLS } func (v TCPPortHandlerView) TerminateTLS() string { return v.ж.TerminateTLS }
// A compilation failure here means this code must be regenerated, with the command at the top of this file. // A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _TCPPortHandlerViewNeedsRegeneration = TCPPortHandler(struct { var _TCPPortHandlerViewNeedsRegeneration = TCPPortHandler(struct {
HTTPS bool HTTPS bool
HTTP bool
TCPForward string TCPForward string
TerminateTLS string TerminateTLS string
}{}) }{})

@ -4129,6 +4129,10 @@ func (b *LocalBackend) setServeProxyHandlersLocked() {
b.serveConfig.Web().Range(func(_ ipn.HostPort, conf ipn.WebServerConfigView) (cont bool) { b.serveConfig.Web().Range(func(_ ipn.HostPort, conf ipn.WebServerConfigView) (cont bool) {
conf.Handlers().Range(func(_ string, h ipn.HTTPHandlerView) (cont bool) { conf.Handlers().Range(func(_ string, h ipn.HTTPHandlerView) (cont bool) {
backend := h.Proxy() backend := h.Proxy()
if backend == "" {
// Only create proxy handlers for servers with a proxy backend.
return true
}
mak.Set(&backends, backend, true) mak.Set(&backends, backend, true)
if _, ok := b.serveProxyHandlers.Load(backend); ok { if _, ok := b.serveProxyHandlers.Load(backend); ok {
return true return true

@ -332,11 +332,8 @@ func (b *LocalBackend) tcpHandlerForServe(dport uint16, srcAddr netip.AddrPort)
return nil return nil
} }
if tcph.HTTPS() { if tcph.HTTPS() || tcph.HTTP() {
hs := &http.Server{ hs := &http.Server{
TLSConfig: &tls.Config{
GetCertificate: b.getTLSServeCertForPort(dport),
},
Handler: http.HandlerFunc(b.serveWebHandler), Handler: http.HandlerFunc(b.serveWebHandler),
BaseContext: func(_ net.Listener) context.Context { BaseContext: func(_ net.Listener) context.Context {
return context.WithValue(context.Background(), serveHTTPContextKey{}, &serveHTTPContext{ return context.WithValue(context.Background(), serveHTTPContextKey{}, &serveHTTPContext{
@ -345,8 +342,17 @@ func (b *LocalBackend) tcpHandlerForServe(dport uint16, srcAddr netip.AddrPort)
}) })
}, },
} }
if tcph.HTTPS() {
hs.TLSConfig = &tls.Config{
GetCertificate: b.getTLSServeCertForPort(dport),
}
return func(c net.Conn) error {
return hs.ServeTLS(netutil.NewOneConnListener(c, nil), "", "")
}
}
return func(c net.Conn) error { return func(c net.Conn) error {
return hs.ServeTLS(netutil.NewOneConnListener(c, nil), "", "") return hs.Serve(netutil.NewOneConnListener(c, nil))
} }
} }
@ -406,8 +412,14 @@ func getServeHTTPContext(r *http.Request) (c *serveHTTPContext, ok bool) {
func (b *LocalBackend) getServeHandler(r *http.Request) (_ ipn.HTTPHandlerView, at string, ok bool) { func (b *LocalBackend) getServeHandler(r *http.Request) (_ ipn.HTTPHandlerView, at string, ok bool) {
var z ipn.HTTPHandlerView // zero value var z ipn.HTTPHandlerView // zero value
hostname := r.Host
if r.TLS == nil { if r.TLS == nil {
return z, "", false tcd := "." + b.Status().CurrentTailnet.MagicDNSSuffix
if !strings.HasSuffix(hostname, tcd) {
hostname += tcd
}
} else {
hostname = r.TLS.ServerName
} }
sctx, ok := getServeHTTPContext(r) sctx, ok := getServeHTTPContext(r)
@ -415,7 +427,7 @@ func (b *LocalBackend) getServeHandler(r *http.Request) (_ ipn.HTTPHandlerView,
b.logf("[unexpected] localbackend: no serveHTTPContext in request") b.logf("[unexpected] localbackend: no serveHTTPContext in request")
return z, "", false return z, "", false
} }
wsc, ok := b.webServerConfig(r.TLS.ServerName, sctx.DestPort) wsc, ok := b.webServerConfig(hostname, sctx.DestPort)
if !ok { if !ok {
return z, "", false return z, "", false
} }
@ -472,7 +484,9 @@ func (b *LocalBackend) proxyHandlerForBackend(backend string) (*httputil.Reverse
func addProxyForwardedHeaders(r *httputil.ProxyRequest) { func addProxyForwardedHeaders(r *httputil.ProxyRequest) {
r.Out.Header.Set("X-Forwarded-Host", r.In.Host) r.Out.Header.Set("X-Forwarded-Host", r.In.Host)
r.Out.Header.Set("X-Forwarded-Proto", "https") if r.In.TLS != nil {
r.Out.Header.Set("X-Forwarded-Proto", "https")
}
if c, ok := getServeHTTPContext(r.Out); ok { if c, ok := getServeHTTPContext(r.Out); ok {
r.Out.Header.Set("X-Forwarded-For", c.SrcAddr.Addr().String()) r.Out.Header.Set("X-Forwarded-For", c.SrcAddr.Addr().String())
} }
@ -634,8 +648,8 @@ func allNumeric(s string) bool {
return s != "" return s != ""
} }
func (b *LocalBackend) webServerConfig(sniName string, port uint16) (c ipn.WebServerConfigView, ok bool) { func (b *LocalBackend) webServerConfig(hostname string, port uint16) (c ipn.WebServerConfigView, ok bool) {
key := ipn.HostPort(fmt.Sprintf("%s:%v", sniName, port)) key := ipn.HostPort(fmt.Sprintf("%s:%v", hostname, port))
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()

@ -76,6 +76,12 @@ type TCPPortHandler struct {
// It is mutually exclusive with TCPForward. // It is mutually exclusive with TCPForward.
HTTPS bool `json:",omitempty"` HTTPS bool `json:",omitempty"`
// HTTP, if true, means that tailscaled should handle this connection as an
// HTTP request as configured by ServeConfig.Web.
//
// It is mutually exclusive with TCPForward.
HTTP bool `json:",omitempty"`
// TCPForward is the IP:port to forward TCP connections to. // TCPForward is the IP:port to forward TCP connections to.
// Whether or not TLS is terminated by tailscaled depends on // Whether or not TLS is terminated by tailscaled depends on
// TerminateTLS. // TerminateTLS.
@ -103,7 +109,7 @@ type HTTPHandler struct {
// temporary ones? Error codes? Redirects? // temporary ones? Error codes? Redirects?
} }
// WebHandlerExists checks if the ServeConfig Web handler exists for // WebHandlerExists reports whether if the ServeConfig Web handler exists for
// the given host:port and mount point. // the given host:port and mount point.
func (sc *ServeConfig) WebHandlerExists(hp HostPort, mount string) bool { func (sc *ServeConfig) WebHandlerExists(hp HostPort, mount string) bool {
h := sc.GetWebHandler(hp, mount) h := sc.GetWebHandler(hp, mount)
@ -128,9 +134,8 @@ func (sc *ServeConfig) GetTCPPortHandler(port uint16) *TCPPortHandler {
return sc.TCP[port] return sc.TCP[port]
} }
// IsTCPForwardingAny checks if ServeConfig is currently forwarding // IsTCPForwardingAny reports whether ServeConfig is currently forwarding in
// in TCPForward mode on any port. // TCPForward mode on any port. This is exclusive of Web/HTTPS serving.
// This is exclusive of Web/HTTPS serving.
func (sc *ServeConfig) IsTCPForwardingAny() bool { func (sc *ServeConfig) IsTCPForwardingAny() bool {
if sc == nil || len(sc.TCP) == 0 { if sc == nil || len(sc.TCP) == 0 {
return false return false
@ -143,34 +148,47 @@ func (sc *ServeConfig) IsTCPForwardingAny() bool {
return false return false
} }
// IsTCPForwardingOnPort checks if ServeConfig is currently forwarding // IsTCPForwardingOnPort reports whether if ServeConfig is currently forwarding
// in TCPForward mode on the given port. // in TCPForward mode on the given port. This is exclusive of Web/HTTPS serving.
// This is exclusive of Web/HTTPS serving.
func (sc *ServeConfig) IsTCPForwardingOnPort(port uint16) bool { func (sc *ServeConfig) IsTCPForwardingOnPort(port uint16) bool {
if sc == nil || sc.TCP[port] == nil { if sc == nil || sc.TCP[port] == nil {
return false return false
} }
return !sc.TCP[port].HTTPS return !sc.IsServingWeb(port)
} }
// IsServingWeb checks if ServeConfig is currently serving // IsServingWeb reports whether if ServeConfig is currently serving Web
// Web/HTTPS on the given port. // (HTTP/HTTPS) on the given port. This is exclusive of TCPForwarding.
// This is exclusive of TCPForwarding.
func (sc *ServeConfig) IsServingWeb(port uint16) bool { func (sc *ServeConfig) IsServingWeb(port uint16) bool {
return sc.IsServingHTTP(port) || sc.IsServingHTTPS(port)
}
// IsServingHTTPS reports whether if ServeConfig is currently serving HTTPS on
// the given port. This is exclusive of HTTP and TCPForwarding.
func (sc *ServeConfig) IsServingHTTPS(port uint16) bool {
if sc == nil || sc.TCP[port] == nil { if sc == nil || sc.TCP[port] == nil {
return false return false
} }
return sc.TCP[port].HTTPS return sc.TCP[port].HTTPS
} }
// IsFunnelOn checks if ServeConfig is currently allowing // IsServingHTTP reports whether if ServeConfig is currently serving HTTP on the
// funnel traffic for any host:port. // given port. This is exclusive of HTTPS and TCPForwarding.
func (sc *ServeConfig) IsServingHTTP(port uint16) bool {
if sc == nil || sc.TCP[port] == nil {
return false
}
return sc.TCP[port].HTTP
}
// IsFunnelOn reports whether if ServeConfig is currently allowing funnel
// traffic for any host:port.
// //
// View version of ServeConfig.IsFunnelOn. // View version of ServeConfig.IsFunnelOn.
func (v ServeConfigView) IsFunnelOn() bool { return v.ж.IsFunnelOn() } func (v ServeConfigView) IsFunnelOn() bool { return v.ж.IsFunnelOn() }
// IsFunnelOn checks if ServeConfig is currently allowing // IsFunnelOn reports whether if ServeConfig is currently allowing funnel
// funnel traffic for any host:port. // traffic for any host:port.
func (sc *ServeConfig) IsFunnelOn() bool { func (sc *ServeConfig) IsFunnelOn() bool {
if sc == nil { if sc == nil {
return false return false

Loading…
Cancel
Save