Add --port

This commit is contained in:
2026-09-12 23:48:03 +02:00
parent a180fe4b52
commit da36645aaf
14 changed files with 677 additions and 40 deletions
+21
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"net"
"net/url"
"strconv"
"strings"
"time"
)
@@ -13,6 +14,7 @@ import (
// Config is the fully-resolved server configuration.
type Config struct {
Listen string
Port int // overrides the port in Listen when set
DataDir string
BasePath string // normalised: always "/" or "/prefix/"
PublicURL string // absolute origin+path for generated links; "" => relative
@@ -40,6 +42,8 @@ const EnvPrefix = "SEND_"
func (c *Config) Register(s *Set) {
s.String(&c.Listen, "listen", "l", "127.0.0.1:8080", "ADDR",
"address to listen on; keep it on loopback behind a reverse proxy")
s.Int(&c.Port, "port", "p", 0,
"port to listen on, replacing the one in --listen")
s.String(&c.DataDir, "data", "d", "./data", "DIR",
"directory holding uploaded objects and their metadata")
s.String(&c.BasePath, "base-url", "b", "/", "PATH",
@@ -78,6 +82,23 @@ func (c *Config) Register(s *Set) {
func (c *Config) Normalise() error {
c.BasePath = NormalisePath(c.BasePath)
// --port is a convenience over --listen: it replaces only the port, so the
// host stays wherever --listen (or its default) put it.
if c.Port != 0 {
if c.Port < 1 || c.Port > 65535 {
return fmt.Errorf("--port: %d is not a port number", c.Port)
}
host, _, err := net.SplitHostPort(c.Listen)
if err != nil {
// --listen held a bare host, which is fine once a port is supplied.
host = strings.TrimSpace(c.Listen)
}
c.Listen = net.JoinHostPort(host, strconv.Itoa(c.Port))
}
if _, _, err := net.SplitHostPort(c.Listen); err != nil {
return fmt.Errorf("--listen: %q is not an address:port (use --port to set just the port)", c.Listen)
}
if c.PublicURL != "" {
u, err := url.Parse(c.PublicURL)
if err != nil {
+40
View File
@@ -165,3 +165,43 @@ func TestDefaultExpiryMustFitWithinMax(t *testing.T) {
t.Error("an unlimited default under a finite maximum was accepted")
}
}
func TestPortOverridesListen(t *testing.T) {
cases := []struct {
listen string
port int
want string
}{
{"127.0.0.1:8080", 0, "127.0.0.1:8080"},
{"127.0.0.1:8080", 9000, "127.0.0.1:9000"},
{"0.0.0.0:8080", 3000, "0.0.0.0:3000"},
{"localhost", 3000, "localhost:3000"}, // a bare host is fine with --port
{"[::1]:8080", 3000, "[::1]:3000"},
}
for _, c := range cases {
cfg := &Config{
Listen: c.listen, Port: c.port, BasePath: "/", DataDir: "d",
SweepInterval: time.Minute, MaxConcurrent: 1,
}
if err := cfg.Normalise(); err != nil {
t.Errorf("listen %q port %d: %v", c.listen, c.port, err)
continue
}
if cfg.Listen != c.want {
t.Errorf("listen %q port %d => %q, want %q", c.listen, c.port, cfg.Listen, c.want)
}
}
for _, c := range []struct {
listen string
port int
}{{"127.0.0.1:8080", 70000}, {"127.0.0.1:8080", -1}, {"localhost", 0}} {
cfg := &Config{
Listen: c.listen, Port: c.port, BasePath: "/", DataDir: "d",
SweepInterval: time.Minute, MaxConcurrent: 1,
}
if err := cfg.Normalise(); err == nil {
t.Errorf("listen %q port %d was accepted", c.listen, c.port)
}
}
}
+3 -1
View File
@@ -222,7 +222,9 @@ func (s *Set) PrintUsage(w io.Writer, header string) {
}
fmt.Fprintf(w, "%s\n", name)
fmt.Fprintf(w, " %s", sp.usage)
if sp.def != "" && sp.def != "false" {
// A zero default means "unset" for the options that have one, and
// printing it would read as a real value.
if sp.def != "" && sp.def != "false" && sp.def != "0" {
fmt.Fprintf(w, " (default %s)", sp.def)
}
fmt.Fprintf(w, "\n [%s]\n", s.envName(sp.long))