all: cut bloated HTTP-client and tuning options
This is a personal torrent client, not a web client or daemon, so the rarely-used HTTP-client knobs and redundant tuning flags are gone — flag, config field, implementation, and tests removed for each. The option surface drops from 54 to 28. Web-client cluster removed: --all-proxy (HTTP_PROXY env still works), load/save-cookies, --referer, -U/--user-agent, --http-user/--http-passwd, --conditional-get, --remote-time, --check-certificate, --ca-certificate, --lowest-speed-limit. Pass --header for a one-off auth/cookie/UA header. Redundant knobs removed: -x and -k (folded into -s), --connect-timeout, --retry-wait, per-item --max-download-limit/-u, --bt-max-peers, --bt-stop-timeout, --disable-ipv6, --auto-save-interval, --human-readable, --stop, -T (a positional .torrent works). follow-torrent and human-readable are now unconditional defaults.
This commit is contained in:
@@ -45,7 +45,7 @@ func (t Tag) String() string {
|
||||
|
||||
// Opt describes one option: its names, value kind, default and help text.
|
||||
type Opt struct {
|
||||
Long string // long name without dashes, e.g. "max-connection-per-server"
|
||||
Long string // long name without dashes, e.g. "max-concurrent-downloads"
|
||||
Short byte // short flag byte, e.g. 'x'; 0 if none
|
||||
Kind Kind
|
||||
Default string
|
||||
@@ -82,52 +82,26 @@ var options = []Opt{
|
||||
{Long: "quiet", Short: 'q', Kind: Bool, Default: "false", Help: "suppress the progress readout", Tag: Basic},
|
||||
|
||||
// --- http ---
|
||||
{Long: "max-connection-per-server", Short: 'x', Kind: Int, Default: "1", Min: 1, Max: 16, Help: "cap connections to any single server (1-16); limits --split per host, mainly for mirrors", Tag: HTTP},
|
||||
{Long: "min-split-size", Short: 'k', Kind: Size, Default: "20M", Min: 1 << 20, Max: 1 << 30, Help: "do not split a piece smaller than SIZE (1M-1024M)", Tag: HTTP},
|
||||
{Long: "max-download-limit", Kind: Size, Default: "0", Help: "per-download speed limit (0 = unlimited)", Tag: HTTP},
|
||||
{Long: "force-sequential", Short: 'Z', Kind: Bool, Default: "false", Help: "download each command-line URI as its own file instead of mirroring them", Tag: HTTP},
|
||||
{Long: "max-tries", Short: 'm', Kind: Int, Default: "5", Min: 0, Help: "max retries per segment (0 = unlimited)", Tag: HTTP},
|
||||
{Long: "timeout", Short: 't', Kind: Int, Default: "60", Min: 1, Help: "connection timeout in seconds", Tag: HTTP},
|
||||
{Long: "connect-timeout", Kind: Int, Default: "60", Min: 1, Help: "connection establishment timeout in seconds", Tag: HTTP},
|
||||
{Long: "retry-wait", Kind: Int, Default: "0", Min: 0, Max: 600, Help: "seconds to wait between retries (0 = no wait)", Tag: HTTP},
|
||||
{Long: "header", Kind: List, Help: "append an extra HTTP header (repeatable)", Tag: HTTP},
|
||||
{Long: "user-agent", Short: 'U', Kind: Str, Default: "got/1.0", Help: "HTTP User-Agent", Tag: HTTP},
|
||||
{Long: "referer", Kind: Str, Help: "HTTP Referer header", Tag: HTTP},
|
||||
{Long: "all-proxy", Kind: Str, Help: "proxy for all protocols (host:port)", Tag: HTTP},
|
||||
{Long: "check-certificate", Kind: Bool, Default: "true", Help: "verify the server's TLS certificate", Tag: HTTP},
|
||||
{Long: "ca-certificate", Kind: Str, Help: "verify HTTPS servers against the CA certificates in FILE (PEM)", Tag: HTTP},
|
||||
{Long: "load-cookies", Kind: Str, Help: "load Cookies from FILE (Netscape/Mozilla format)", Tag: HTTP},
|
||||
{Long: "save-cookies", Kind: Str, Help: "save Cookies to FILE on exit", Tag: HTTP},
|
||||
{Long: "conditional-get", Kind: Bool, Default: "false", Help: "download only if the remote file is newer than the local one", Tag: HTTP},
|
||||
{Long: "remote-time", Kind: Bool, Default: "false", Help: "set the local file's mtime from the server", Tag: HTTP},
|
||||
{Long: "http-user", Kind: Str, Help: "HTTP basic-auth user", Tag: HTTP},
|
||||
{Long: "http-passwd", Kind: Str, Help: "HTTP basic-auth password", Tag: HTTP},
|
||||
{Long: "lowest-speed-limit", Kind: Size, Default: "0", Help: "abort if speed stays below SIZE bytes/sec (0 = off)", Tag: HTTP},
|
||||
{Long: "timeout", Short: 't', Kind: Int, Default: "60", Min: 1, Help: "connection/idle timeout in seconds", Tag: HTTP},
|
||||
{Long: "header", Kind: List, Help: "append an extra HTTP header (repeatable), e.g. 'Authorization: Bearer …'", Tag: HTTP},
|
||||
|
||||
// --- bittorrent ---
|
||||
{Long: "torrent-file", Short: 'T', Kind: Str, Help: "path to a .torrent file", Tag: BitTorrent},
|
||||
{Long: "check-integrity", Short: 'V', Kind: Bool, Default: "false", Help: "re-verify torrent data against piece hashes", Tag: BitTorrent},
|
||||
{Long: "seed-time", Kind: Float, Metavar: "MIN", Help: "minutes to seed after completion (0 = no seeding)", Tag: BitTorrent},
|
||||
{Long: "seed-ratio", Kind: Float, Default: "1.0", Help: "stop seeding at this share ratio (0 = unlimited)", Tag: BitTorrent},
|
||||
{Long: "bt-stop-timeout", Kind: Int, Default: "0", Min: 0, Help: "stop a torrent with no download progress for N seconds (0 = off)", Tag: BitTorrent},
|
||||
{Long: "listen-port", Kind: Str, Default: "6881-6999", Help: "TCP port (range) for incoming peers", Tag: BitTorrent},
|
||||
{Long: "enable-dht", Kind: Bool, Default: "true", Help: "enable the BitTorrent DHT", Tag: BitTorrent},
|
||||
{Long: "bt-max-peers", Kind: Int, Default: "55", Min: 0, Help: "max peers per torrent (0 = unlimited)", Tag: BitTorrent},
|
||||
{Long: "enable-dht", Kind: Bool, Default: "true", Help: "enable the BitTorrent DHT (turn off for private torrents)", Tag: BitTorrent},
|
||||
{Long: "max-overall-upload-limit", Kind: Size, Default: "0", Help: "global upload speed limit (0 = unlimited)", Tag: BitTorrent},
|
||||
{Long: "max-upload-limit", Short: 'u', Kind: Size, Default: "0", Help: "per-torrent upload speed limit", Tag: BitTorrent},
|
||||
{Long: "select-file", Kind: Str, Help: "download only these file indexes, e.g. 1,3-5", Tag: BitTorrent},
|
||||
{Long: "follow-torrent", Kind: Bool, Default: "true", Help: "after fetching a .torrent over HTTP, download its content", Tag: BitTorrent},
|
||||
|
||||
// --- advanced ---
|
||||
{Long: "allow-overwrite", Kind: Bool, Default: "false", Help: "overwrite an existing file", Tag: Advanced},
|
||||
{Long: "allow-overwrite", Kind: Bool, Default: "false", Help: "overwrite an existing file instead of renaming the new one", Tag: Advanced},
|
||||
{Long: "auto-file-renaming", Kind: Bool, Default: "true", Help: "rename file (.1, .2, ...) if it already exists", Tag: Advanced},
|
||||
{Long: "human-readable", Kind: Bool, Default: "true", Help: "show sizes as Ki/Mi/Gi", Tag: Advanced},
|
||||
{Long: "file-allocation", Short: 'a', Kind: Enum, Default: "prealloc", Enum: []string{"none", "prealloc", "trunc", "falloc"}, Help: "how to reserve disk space (prealloc and falloc are identical here)", Tag: Advanced},
|
||||
{Long: "dry-run", Kind: Bool, Default: "false", Help: "check that the file is available but do not download it", Tag: Advanced},
|
||||
{Long: "stop", Kind: Int, Default: "0", Min: 0, Help: "stop the program after N seconds (0 = off)", Tag: Advanced},
|
||||
{Long: "disable-ipv6", Kind: Bool, Default: "false", Help: "disable IPv6 (force IPv4-only connections)", Tag: Advanced},
|
||||
{Long: "save-session", Kind: Str, Help: "on exit, write unfinished downloads to FILE (reload with -i)", Tag: Advanced},
|
||||
{Long: "auto-save-interval", Kind: Int, Default: "60", Min: 0, Max: 600, Help: "save the session file every N seconds (0 = only on exit)", Tag: Advanced},
|
||||
{Long: "conf-path", Kind: Str, Help: "path to the config file", Tag: Advanced},
|
||||
{Long: "no-conf", Kind: Bool, Default: "false", Help: "do not read a config file", Tag: Advanced},
|
||||
}
|
||||
|
||||
19
cli/parse.go
19
cli/parse.go
@@ -27,8 +27,8 @@ type Result struct {
|
||||
}
|
||||
|
||||
// Parse resolves args into options and URIs, applying the layered override
|
||||
// order: built-in defaults, then the config file, then proxy
|
||||
// environment variables, then the command line (last wins).
|
||||
// order: built-in defaults, then the config file, then the command line
|
||||
// (last wins).
|
||||
func Parse(args []string) (*Result, error) {
|
||||
cmdVals, uris, action, helpTag, err := parseArgs(args)
|
||||
if err != nil {
|
||||
@@ -48,7 +48,6 @@ func Parse(args []string) (*Result, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
applyEnv(opts)
|
||||
|
||||
for name, val := range cmdVals {
|
||||
if err := set(opts, name, val); err != nil {
|
||||
@@ -225,20 +224,6 @@ func applyConfig(o *Options, path string, explicit bool) error {
|
||||
return sc.Err()
|
||||
}
|
||||
|
||||
// applyEnv folds the conventional proxy environment variables into all-proxy
|
||||
// unless it was already set explicitly.
|
||||
func applyEnv(o *Options) {
|
||||
if o.set["all-proxy"] {
|
||||
return
|
||||
}
|
||||
for _, key := range []string{"all_proxy", "https_proxy", "http_proxy"} {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
o.vals["all-proxy"] = v
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set stores a raw value, marking the option as explicitly set. Validation and
|
||||
// conversion happen later, once, in finalize(); set only records the string.
|
||||
func set(o *Options, name, val string) error {
|
||||
|
||||
@@ -13,7 +13,7 @@ func TestParseArgs(t *testing.T) {
|
||||
wantVal map[string]string
|
||||
uris []string
|
||||
}{
|
||||
{"short attached", []string{"-x16", "https://e.com"}, Run, map[string]string{"max-connection-per-server": "16"}, []string{"https://e.com"}},
|
||||
{"short attached", []string{"-m3", "https://e.com"}, Run, map[string]string{"max-tries": "3"}, []string{"https://e.com"}},
|
||||
{"long equals + bool", []string{"--split=8", "-c", "u"}, Run, map[string]string{"split": "8", "continue": "true"}, []string{"u"}},
|
||||
{"long separate value", []string{"--out", "f.bin", "u"}, Run, map[string]string{"out": "f.bin"}, []string{"u"}},
|
||||
{"bool bundle", []string{"-cq", "u"}, Run, map[string]string{"continue": "true", "quiet": "true"}, []string{"u"}},
|
||||
@@ -22,7 +22,7 @@ func TestParseArgs(t *testing.T) {
|
||||
{"permute flag after uri", []string{"u", "--split=8"}, Run, map[string]string{"split": "8"}, []string{"u"}},
|
||||
{"permute interleaved", []string{"a", "-c", "b", "--quiet"}, Run, map[string]string{"continue": "true", "quiet": "true"}, []string{"a", "b"}},
|
||||
{"dash dash terminator", []string{"--split=8", "--", "--not-a-flag", "u"}, Run, map[string]string{"split": "8"}, []string{"--not-a-flag", "u"}},
|
||||
{"long int + magnet uri", []string{"--bt-stop-timeout=120", "magnet:x"}, Run, map[string]string{"bt-stop-timeout": "120"}, []string{"magnet:x"}},
|
||||
{"long int + magnet uri", []string{"--timeout=120", "magnet:x"}, Run, map[string]string{"timeout": "120"}, []string{"magnet:x"}},
|
||||
{"help", []string{"--help"}, ShowHelp, nil, nil},
|
||||
{"help short", []string{"-h"}, ShowHelp, nil, nil},
|
||||
{"version", []string{"-v"}, ShowVersion, nil, nil},
|
||||
@@ -95,13 +95,18 @@ func TestParseSize(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseValueRange(t *testing.T) {
|
||||
x := byLong["max-connection-per-server"]
|
||||
if _, err := parseValue(x, "16"); err != nil {
|
||||
// A synthetic bounded Int exercises the Min/Max range check independent of
|
||||
// which options happen to carry bounds.
|
||||
o := &Opt{Long: "n", Kind: Int, Min: 1, Max: 16}
|
||||
if _, err := parseValue(o, "16"); err != nil {
|
||||
t.Errorf("parseValue 16: %v", err)
|
||||
}
|
||||
if _, err := parseValue(x, "99"); err == nil {
|
||||
if _, err := parseValue(o, "99"); err == nil {
|
||||
t.Errorf("parseValue 99: want out-of-range error")
|
||||
}
|
||||
if _, err := parseValue(o, "0"); err == nil {
|
||||
t.Errorf("parseValue 0: want below-minimum error")
|
||||
}
|
||||
a := byLong["file-allocation"]
|
||||
if _, err := parseValue(a, "bogus"); err == nil {
|
||||
t.Errorf("parseValue enum bogus: want error")
|
||||
@@ -156,17 +161,17 @@ func TestParseValueBool(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseValueSizeBounds(t *testing.T) {
|
||||
k := byLong["min-split-size"] // 1M..1024M (no G unit)
|
||||
k := &Opt{Long: "sz", Kind: Size, Min: 1 << 20, Max: 1 << 30} // 1M..1024M
|
||||
if _, err := parseValue(k, "20M"); err != nil {
|
||||
t.Errorf("parseValue min-split-size 20M: %v", err)
|
||||
t.Errorf("parseValue 20M: %v", err)
|
||||
}
|
||||
if _, err := parseValue(k, "512K"); err == nil {
|
||||
t.Errorf("parseValue min-split-size 512K: want below-minimum error")
|
||||
t.Errorf("parseValue 512K: want below-minimum error")
|
||||
}
|
||||
// 1025M exceeds the 1<<30 cap and still parses, so it tests the bounds path
|
||||
// (where "2G" would have failed earlier as an unknown unit).
|
||||
if _, err := parseValue(k, "1025M"); err == nil {
|
||||
t.Errorf("parseValue min-split-size 1025M: want above-maximum error")
|
||||
t.Errorf("parseValue 1025M: want above-maximum error")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +209,7 @@ func TestExplicitConfMissingFatal(t *testing.T) {
|
||||
func TestOptionsGetters(t *testing.T) {
|
||||
o := newOptions()
|
||||
o.vals["split"] = "8"
|
||||
o.vals["min-split-size"] = "20M"
|
||||
o.vals["max-overall-download-limit"] = "20M"
|
||||
o.vals["continue"] = "true"
|
||||
o.vals["seed-ratio"] = "1.5"
|
||||
if err := o.finalize(); err != nil { // parse the raw values once, as Parse does
|
||||
@@ -213,8 +218,8 @@ func TestOptionsGetters(t *testing.T) {
|
||||
if o.Int("split") != 8 {
|
||||
t.Errorf("Int split = %d", o.Int("split"))
|
||||
}
|
||||
if o.Size("min-split-size") != 20<<20 {
|
||||
t.Errorf("Size min-split-size = %d", o.Size("min-split-size"))
|
||||
if o.Size("max-overall-download-limit") != 20<<20 {
|
||||
t.Errorf("Size max-overall-download-limit = %d", o.Size("max-overall-download-limit"))
|
||||
}
|
||||
if !o.Bool("continue") {
|
||||
t.Errorf("Bool continue = false")
|
||||
|
||||
Reference in New Issue
Block a user