Skip to content

Commit c358614

Browse files
author
tanq
committed
configurable keep alive t/o; randomize user agents
1 parent ff2368f commit c358614

File tree

4 files changed

+60
-17
lines changed

4 files changed

+60
-17
lines changed

README.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,31 @@ go build .
5353
### Command Options
5454

5555
```
56+
Danzo is a fast CLI download manager
57+
5658
Usage:
5759
danzo [flags]
5860
danzo [command]
5961
6062
Available Commands:
6163
clean Clean up temporary files
64+
completion Generate the autocompletion script for the specified shell
6265
help Help about any command
6366
6467
Flags:
65-
-c, --connections int Number of connections per download (default: # CPU cores)
66-
--debug Enable debug logging
67-
-h, --help help for danzo
68-
-o, --output string Output file path (required with --url/-u)
69-
-p, --proxy string HTTP/HTTPS proxy URL (e.g., proxy.example.com:8080)
70-
-t, --timeout duration Connection timeout (default 3m0s)
71-
-u, --url string URL to download
72-
-a, --user-agent string User agent (default "Danzo/1337")
73-
-l, --urllist string Path to YAML file containing URLs and output paths
74-
-w, --workers int Number of links to download in parallel (default 1)
68+
-c, --connections int Number of connections per download (default: CPU cores) (default 16)
69+
--debug Enable debug logging
70+
-h, --help help for danzo
71+
-k, --keep-alive-timeout duration Keep-alive timeout for client (eg./ 10s, 1m, 80s; default: 90s) (default 1m30s)
72+
-o, --output string Output file path (required with --url/-u)
73+
-p, --proxy string HTTP/HTTPS proxy URL (e.g., proxy.example.com:8080)
74+
-t, --timeout duration Connection timeout (eg., 5s, 10m; default: 3m) (default 3m0s)
75+
-u, --url string URL to download
76+
-l, --urllist string Path to YAML file containing URLs and output paths
77+
-a, --user-agent string User agent (default "Danzo/1337")
78+
-w, --workers int Number of links to download in parallel (default: 1) (default 1)
79+
80+
Use "danzo [command] --help" for more information about a command.
7581
```
7682

7783
### Batch Download
@@ -118,3 +124,4 @@ Danzo stores partial downloads on disk in the `.danzo-temp` directory (situated
118124
- For servers with rate limiting, reducing the number of connections might help
119125
- Debug mode (`--debug`) provides detailed information about the download process
120126
- Temporary files are stored in a .danzo-temp directory and automatically cleaned up after download
127+
- Use `-a randomize` to randomize the user agent for every HTTP client

cmd/root.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ var (
1616
output string
1717
connections int
1818
timeout time.Duration
19+
kaTimeout time.Duration
1920
userAgent string
2021
proxyURL string
2122
debug bool
@@ -45,7 +46,7 @@ var rootCmd = &cobra.Command{
4546
log.Fatal().Msg("Output file path is required with --url")
4647
}
4748
entries := []internal.DownloadEntry{{URL: url, OutputPath: output}}
48-
err := internal.BatchDownload(entries, 1, connections, timeout, userAgent, proxyURL)
49+
err := internal.BatchDownload(entries, 1, connections, timeout, kaTimeout, userAgent, proxyURL)
4950
if err != nil {
5051
log.Fatal().Err(err).Msg("Download failed")
5152
}
@@ -63,7 +64,7 @@ var rootCmd = &cobra.Command{
6364
connectionsPerLink = max(maxConnections/numLinks, 1)
6465
log.Warn().Int("connections", connectionsPerLink).Int("numLinks", numLinks).Msg("adjusted connections to below max limit")
6566
}
66-
err = internal.BatchDownload(entries, numLinks, connectionsPerLink, timeout, userAgent, proxyURL)
67+
err = internal.BatchDownload(entries, numLinks, connectionsPerLink, timeout, kaTimeout, userAgent, proxyURL)
6768
if err != nil {
6869
log.Fatal().Err(err).Msg("Batch download completed with errors")
6970
}
@@ -96,6 +97,7 @@ func init() {
9697
rootCmd.Flags().IntVarP(&numLinks, "workers", "w", 1, "Number of links to download in parallel (default: 1)")
9798
rootCmd.Flags().IntVarP(&connections, "connections", "c", min(runtime.NumCPU(), 32), "Number of connections per download (default: CPU cores)")
9899
rootCmd.Flags().DurationVarP(&timeout, "timeout", "t", 3*time.Minute, "Connection timeout (eg., 5s, 10m; default: 3m)")
100+
rootCmd.Flags().DurationVarP(&kaTimeout, "keep-alive-timeout", "k", 90*time.Second, "Keep-alive timeout for client (eg./ 10s, 1m, 80s; default: 90s)")
99101
rootCmd.Flags().StringVarP(&userAgent, "user-agent", "a", "Danzo/1337", "User agent")
100102
rootCmd.Flags().StringVarP(&proxyURL, "proxy", "p", "", "HTTP/HTTPS proxy URL (e.g., proxy.example.com:8080)")
101103
rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "Enable debug logging")

internal/downloader.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"time"
99
)
1010

11-
func BatchDownload(entries []DownloadEntry, numLinks int, connectionsPerLink int, timeout time.Duration, userAgent string, proxyURL string) error {
11+
func BatchDownload(entries []DownloadEntry, numLinks int, connectionsPerLink int, timeout time.Duration, kaTimeout time.Duration, userAgent string, proxyURL string) error {
1212
log := GetLogger("downloader")
1313
log.Info().Int("totalFiles", len(entries)).Int("numLinks", numLinks).Int("connections", connectionsPerLink).Msg("Starting batch download")
1414

@@ -38,17 +38,21 @@ func BatchDownload(entries []DownloadEntry, numLinks int, connectionsPerLink int
3838
logger := log.With().Int("workerID", workerID).Logger()
3939
for entry := range entriesCh {
4040
logger.Debug().Str("output", entry.OutputPath).Msg("Worker starting download")
41+
if userAgent == "randomize" {
42+
userAgent = getRandomUserAgent()
43+
}
4144
config := DownloadConfig{
4245
URL: entry.URL,
4346
OutputPath: entry.OutputPath,
4447
Connections: connectionsPerLink,
4548
Timeout: timeout,
49+
KATimeout: kaTimeout,
4650
UserAgent: userAgent,
4751
ProxyURL: proxyURL,
4852
}
4953
progressCh := make(chan int64)
5054
doneCh := make(chan struct{})
51-
client := createHTTPClient(config.Timeout, config.ProxyURL)
55+
client := createHTTPClient(config.Timeout, config.KATimeout, config.ProxyURL)
5256
fileSize, err := getFileSize(config.URL, config.UserAgent, client)
5357
if err != nil {
5458
logger.Error().Err(err).Str("output", entry.OutputPath).Msg("Failed to get file size")
@@ -108,7 +112,7 @@ func BatchDownload(entries []DownloadEntry, numLinks int, connectionsPerLink int
108112

109113
func downloadWithProgress(config DownloadConfig, fileSize int64, progressCh chan<- int64) error {
110114
log := GetLogger("download-worker")
111-
client := createHTTPClient(config.Timeout, config.ProxyURL)
115+
client := createHTTPClient(config.Timeout, config.KATimeout, config.ProxyURL)
112116
log.Debug().Str("size", formatBytes(uint64(fileSize))).Msg("File size determined")
113117
job := DownloadJob{
114118
Config: config,

internal/helpers.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type DownloadConfig struct {
2323
OutputPath string
2424
Connections int
2525
Timeout time.Duration
26+
KATimeout time.Duration
2627
ProxyURL string
2728
UserAgent string
2829
}
@@ -53,6 +54,35 @@ type DownloadEntry struct {
5354
URL string `yaml:"link"`
5455
}
5556

57+
func getRandomUserAgent() string {
58+
userAgents := []string{
59+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
60+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
61+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0",
62+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
63+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
64+
"Mozilla/5.0 (X11; Linux x86_64; rv:135.0) Gecko/20100101 Firefox/135.0",
65+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15",
66+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:135.0) Gecko/20100101 Firefox/135.0",
67+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0",
68+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
69+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
70+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
71+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
72+
"Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0",
73+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.2 Safari/605.1.15",
74+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0",
75+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
76+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Safari/605.1.15",
77+
"Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0",
78+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 YaBrowser/27.7.7.7 Yowser/2.5 Safari/537.36",
79+
"Mozilla/5.0 (Windows NT 10.0; rv:109.0) Gecko/20100101 Firefox/115.0",
80+
"curl/7.88.1",
81+
"Wget/1.21.4",
82+
}
83+
return userAgents[time.Now().UnixNano()%int64(len(userAgents))]
84+
}
85+
5686
func ReadDownloadList(filePath string) ([]DownloadEntry, error) {
5787
log := GetLogger("config")
5888
data, err := os.ReadFile(filePath)
@@ -76,11 +106,11 @@ func ReadDownloadList(filePath string) ([]DownloadEntry, error) {
76106
return entries, nil
77107
}
78108

79-
func createHTTPClient(timeout time.Duration, proxyURL string) *http.Client {
109+
func createHTTPClient(timeout time.Duration, keepAliveTO time.Duration, proxyURL string) *http.Client {
80110
transport := &http.Transport{
81111
MaxIdleConns: 100,
82112
MaxIdleConnsPerHost: 100, // for connection reuse
83-
IdleConnTimeout: 90 * time.Second,
113+
IdleConnTimeout: keepAliveTO,
84114
DisableCompression: true,
85115
MaxConnsPerHost: 0,
86116
// These two seem to reduce performance drastically with custom dial context

0 commit comments

Comments
 (0)