commit b2267537c51b4fde2ff36cda3e99972a5a2c176b Author: Ruben Jenster Date: Fri Jul 31 13:12:54 2020 +0200 Initial implementation. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9c6c786 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +BUILD_HASH := $(shell git describe --always --tags --long) +LDFLAGS := "-X main.version=${BUILD_HASH} -s -w" + +.PHONY: install +install: + go fmt ./... + go install -ldflags ${LDFLAGS} ./... + +.PHONY: build +build: + go fmt ./... + go build -ldflags ${LDFLAGS} ./... diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6c45d80 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module gopkg.intern.drachenfels.de/goget + +go 1.14 diff --git a/goget.go b/goget.go new file mode 100644 index 0000000..d7c5ce0 --- /dev/null +++ b/goget.go @@ -0,0 +1,118 @@ +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 +} + +func main() { + c := client{} + var filename string + var showVersion bool + + flag.StringVar(&c.User, "http-user", "", "HTTP auth username") + flag.StringVar(&c.Password, "http-password", "", "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.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 { + os.Exit(1) + } +} + +func (c *client) DownloadFile(filename string, url string) (err error) { + println("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, + }, + } + + client := &http.Client{Transport: tr} + + // Get the data + resp, err := client.Get(url) + 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 +}