Files
goget/goget.go

135 lines
3.1 KiB
Go

package main
import (
"crypto/tls"
"flag"
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
"path"
"time"
)
var version string
type client struct {
User string
Password string
Insecure bool // disable certificate check
ConnectTimeout time.Duration
ReadTimeout time.Duration
Retries int
RetryWait time.Duration
NoProxy bool
}
func main() {
c := client{}
var filename string
var showVersion bool
flag.StringVar(&c.User, "http-user", "", "HTTP auth username")
flag.StringVar(&c.Password, "http-passwd", "", "HTTP auth password")
flag.IntVar(&c.Retries, "retries", 1, "Number of retries in case of download errors.")
flag.DurationVar(&c.RetryWait, "retry-wait", 2*time.Second, "Time to wait before retrying download.")
flag.DurationVar(&c.ConnectTimeout, "connect-timeout", 5*time.Second, "TCP connections that take longer to establish will be aborted")
flag.DurationVar(&c.ReadTimeout, "read-timeout", 10*time.Second, "TCP connections that that are longer idle will be aborted")
flag.BoolVar(&c.Insecure, "insecure", false, "Disable TLS certificate verification.")
flag.BoolVar(&showVersion, "version", false, "Show version and exit.")
flag.BoolVar(&c.NoProxy, "no-proxy", false, "Ignore *_proxy environment variables.")
flag.StringVar(&filename, "filename", "", "Filename / path where to write downloaded file to. Defaults to filename of the URL.")
flag.Parse()
if showVersion {
println("version:", version)
os.Exit(1)
}
downloadURI := flag.Args()[0]
if filename == "" {
uri, err := url.Parse(downloadURI)
if err != nil {
panic(err)
}
filename = path.Base(uri.Path)
}
var err error
for i := 0; i < c.Retries; i++ {
err = c.DownloadFile(filename, downloadURI)
if err == nil {
break
}
println(err.Error())
if c.RetryWait > 0 {
time.Sleep(c.RetryWait)
}
}
if err != nil {
if err := os.Remove(filename); err != nil {
println(err)
}
os.Exit(1)
}
}
func (c *client) DownloadFile(filename string, url string) (err error) {
fmt.Fprintf(os.Stderr, "downloading %s to file %s\n", url, filename)
// Create the target file
out, err := os.Create(filename)
if err != nil {
return err
}
defer out.Close()
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: c.ReadTimeout,
DisableCompression: true,
DialContext: (&net.Dialer{
Timeout: c.ConnectTimeout, // connect timeout
}).DialContext,
// TLSHandshakeTimeout: 5 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: c.Insecure,
},
}
if c.NoProxy {
tr.Proxy = nil
} else {
tr.Proxy = http.ProxyFromEnvironment
}
client := &http.Client{Transport: tr}
// Get the data
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return err
}
req.SetBasicAuth(c.User, c.Password)
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// Check server response
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bad status: %s", resp.Status)
}
// Writer the body to file
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
return nil
}