├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build.yml │ ├── build_docs.yml │ └── release.yml ├── .gitignore ├── .goreleaser.yml ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── cmd ├── fastget │ └── main.go └── gen-docs │ └── docs.go ├── commands └── root.go ├── docs ├── README.md ├── _config.yml ├── fastget.md └── index.md ├── examples └── fastget_demo.gif ├── fastget.go ├── go.mod ├── go.sum └── scripts └── install.sh /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | 13 | build: 14 | name: Build 15 | runs-on: ubuntu-latest 16 | steps: 17 | 18 | - name: Set up Go 1.x 19 | uses: actions/setup-go@v2 20 | with: 21 | go-version: ^1.13 22 | id: go 23 | 24 | - name: Check out code into the Go module directory 25 | uses: actions/checkout@v2 26 | 27 | - name: Get dependencies 28 | run: | 29 | go get -v -t -d ./... 30 | if [ -f Gopkg.toml ]; then 31 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 32 | dep ensure 33 | fi 34 | - name: Build 35 | run: go build -v . 36 | 37 | - name: Test 38 | run: go test -v . 39 | -------------------------------------------------------------------------------- /.github/workflows/build_docs.yml: -------------------------------------------------------------------------------- 1 | name: build_docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - run: | 13 | go run ./cmd/gen-docs/docs.go 14 | cp ./docs/fastget.md ./docs/index.md 15 | git config user.name github-actions 16 | git config user.email github-actions@github.com 17 | git add docs 18 | git commit -m "docs update: latest" || echo "No document(s) updates found!" 19 | git push 20 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Checkout 14 | uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | - 18 | name: Set up Go 19 | uses: actions/setup-go@v2 20 | with: 21 | go-version: 1.14 22 | - 23 | name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v2 25 | with: 26 | version: latest 27 | args: release --rm-dist 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.FASTGET_RELEASER }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | dist -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example goreleaser.yaml file with some sane defaults. 2 | # Make sure to check the documentation at http://goreleaser.com 3 | before: 4 | hooks: 5 | # You may remove this if you don't use go modules. 6 | - go mod download 7 | # you may remove this if you don't need go generate 8 | - go generate ./... 9 | 10 | builds: 11 | - main: ./cmd/fastget 12 | env: 13 | - CGO_ENABLED=0 14 | ldflags: 15 | - -s -w -X main.version={{.Version}} -X main.build={{time "01/02/2006"}} 16 | goos: 17 | - linux 18 | - windows 19 | # - darwin 20 | - <<: &build_defaults 21 | binary: bin/fastget 22 | main: ./cmd/fastget 23 | hooks: 24 | post: 25 | - upx "{{ .Path }}" 26 | id: macos 27 | goos: [darwin] 28 | goarch: [amd64] 29 | 30 | - <<: *build_defaults 31 | id: linux 32 | goos: [linux] 33 | goarch: [386, amd64] 34 | 35 | - <<: *build_defaults 36 | id: windows 37 | goos: [windows] 38 | goarch: [386, amd64] 39 | archives: 40 | - replacements: 41 | darwin: macOS 42 | 386: i386 43 | amd64: x86_64 44 | format_overrides: 45 | - goos: windows 46 | format: zip 47 | - id: nix 48 | builds: [macos, linux] 49 | <<: &archive_defaults 50 | name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" 51 | wrap_in_directory: false 52 | replacements: 53 | darwin: macOS 54 | linux: linux 55 | format: tar.gz 56 | scoop: 57 | url_template: "http://github.com/pgollangi/fastget/releases/download/{{ .Tag }}/{{ .ArtifactName }}" 58 | bucket: 59 | owner: pgollangi 60 | name: scoop-bucket 61 | commit_author: 62 | name: Prasanna Kumar Gollangi 63 | email: prassu158@gmail.com 64 | commit_msg_template: "Scoop update for {{ .ProjectName }} version {{ .Tag }}" 65 | homepage: "https://github.com/pgollangi/fastget" 66 | description: "A open source cli tool to ultra fast download files over HTTP(S)." 67 | license: MIT 68 | 69 | brews: 70 | - name: fastget 71 | ids: 72 | - nix 73 | description: "A open source cli tool to ultra fast download files over HTTP(S)." 74 | commit_author: 75 | name: Prasanna Kumar Gollangi 76 | email: prassu158@gmail.com 77 | folder: Formula 78 | goarm: 6 79 | tap: 80 | owner: pgollangi 81 | name: homebrew-tap 82 | url_template: "http://github.com/pgollangi/fastget/releases/download/{{ .Tag }}/{{ .ArtifactName }}" 83 | # Packages your package depends on. 84 | homepage: "https://github.com/pgollangi/fastget" 85 | install: bin.install "bin/fastget" 86 | test: | 87 | system "#{bin}/fastget version" 88 | 89 | nfpms: 90 | - 91 | id: foo 92 | package_name: fastget 93 | replacements: 94 | amd64: x86_64 95 | 386: i386 96 | darwin: macOS 97 | vendor: Prasana Kumar Gollangi 98 | homepage: https://github.com/pgollangi/fastget 99 | maintainer: Prasanna Kumar Gollangi 100 | description: A open source cli tool to ultra fast download files over HTTP(S). 101 | license: MIT 102 | formats: 103 | - deb 104 | - rpm 105 | checksum: 106 | name_template: 'checksums.txt' 107 | snapshot: 108 | name_template: "{{ .Tag }}-next" 109 | changelog: 110 | sort: asc 111 | filters: 112 | exclude: 113 | - '^docs' 114 | - '^test:' 115 | - '^Merge' 116 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${workspaceFolder}/cmd/fastget", 13 | "env": {}, 14 | "args": [" http://speedtest.tele2.net/10MB.zip"] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Prasanna Kumar Gollangi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![build](https://github.com/pgollangi/fastget/workflows/build/badge.svg) 2 | [![Go Report Card](https://goreportcard.com/badge/github.com/pgollangi/fastget)](https://goreportcard.com/report/github.com/pgollangi/fastget) 3 | ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/pgollangi/fastget) 4 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/pgollangi/fastget) 5 | [![PkgGoDev](https://pkg.go.dev/badge/github.com/pgollangi/netselect)](https://pkg.go.dev/github.com/pgollangi/fastget) 6 | [![Maintainability](https://api.codeclimate.com/v1/badges/76a0d1d3903c6f0683c7/maintainability)](https://codeclimate.com/github/pgollangi/fastget/maintainability) 7 | [![Code Climate technical debt](https://img.shields.io/codeclimate/tech-debt/pgollangi/fastget)](https://codeclimate.com/github/pgollangi/fastget/trends/technical_debt) 8 | ![License: MIT](https://img.shields.io/github/license/pgollangi/fastget) 9 | 10 | # FastGet 11 | 12 | A CLI tool as well as go library to ultrafast download files over HTTP(s). 13 | 14 | > DISCLAIMER: FastGet performance heavily reliant on the network and CPU performance of the client machine. More importantly HTTP(s) endpoint must allow partial requests presenting `Accept-Ranges` and accepting `Range` headers. 15 | 16 | 17 | ## Demo 18 | ![fastget Demo](examples/fastget_demo.gif) 19 | 20 | ## Usage 21 | `fastget` available as Commnad-Line tool and Go library. 22 | ### Commnad-Line 23 | 24 | ```sh 25 | fastget [options] 26 | ``` 27 | #### Examples 28 | ```sh 29 | $ fastget http://speedtest.tele2.net/10MB.zip 30 | Download started.. 31 | 3.50 MB / 3.50 MB 100 % [===========================================================================| 0s ] 267.59 KB/s 32 | 3.50 MB / 3.50 MB 100 % [===========================================================================| 0s ] 165.65 KB/s 33 | 3.50 MB / 3.50 MB 100 % [===========================================================================| 0s ] 116.10 KB/s 34 | Download finished in 3s. File: E:\10MB.zip 35 | ``` 36 | 37 | Read the [documentation](https://pgollangi.github.io/fastget) for more information on the CLI usage. 38 | 39 | ### Go Library 40 | 41 | Here is a simple example that finds fastest hosts: 42 | 43 | ```go 44 | fg, err := fastget.NewFastGetter("http://speedtest.tele2.net/10MB.zip") 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | result, err := fg.Get() 50 | if err != nil { 51 | panic(err) 52 | } 53 | file := result.OutputFile 54 | elapsedTime := result.ElapsedTime 55 | 56 | ``` 57 | Read the [API documentation](https://pkg.go.dev/github.com/pgollangi/fastget) for more information on the library usage. 58 | 59 | ## Installation 60 | 61 | Download suitable binary for your OS at the [releases page](https://github.com/pgollangi/fastget/releases/latest). 62 | 63 | ### Bash Install 64 | You can install or update `fastget` with: 65 | 66 | ``` 67 | curl -s https://raw.githubusercontent.com/pgollangi/fastget/master/scripts/install.sh | sudo bash 68 | ``` 69 | Installs into `/usr/local/bin` 70 | 71 | ### Scoop 72 | ```sh 73 | scoop bucket add pgollangi-bucket https://github.com/pgollangi/scoop-bucket.git 74 | scoop install fastget 75 | ``` 76 | Updating: 77 | ``` 78 | scoop update fastget 79 | ``` 80 | ### Homebrew 81 | ```sh 82 | brew install pgollangi/tap/fastget 83 | ``` 84 | Updating: 85 | ``` 86 | brew upgrade fastget 87 | ``` 88 | ### Go 89 | ```sh 90 | $ go get github.com/pgollangi/fastget/cmd/fastget 91 | $ fastget 92 | ``` 93 | 94 | ### Manual 95 | 1. Download and install binary from the [latest release](https://github.com/pgollangi/fastget/releases/latest). 96 | 2. Recommended: add `fastget` executable to your $PATH. 97 | 98 | ## Building from source 99 | 100 | `fastget` CLI is written in the [Go programming language](https://golang.org/), so to build the CLI yourself, you first need to have Go installed and configured on your machine. 101 | 102 | ### Install Go 103 | 104 | To download and install `Go`, please refer to the [Go documentation](https://golang.org/doc/install). Please download `Go 1.14.x` or above. 105 | 106 | ### Clone this repository 107 | ```sh 108 | $ git clone https://gitlab.com/pgollangi/fastget.git 109 | $ cd fastget 110 | ``` 111 | ### Build 112 | 113 | ```sh 114 | $ go build cmd/fastget/main.go 115 | $ fastget 116 | ``` 117 | 118 | ## Contributing 119 | Thanks for considering contributing to this project! 120 | 121 | Please read the [Contributions](.github/CONTRIBUTING.md) and [Code of conduct](.github/CODE_OF_CONDUCT.md). 122 | 123 | Feel free to open an issue or submit a pull request! 124 | 125 | ## License 126 | Copyright © [Prasanna Kumar](https://pgollangi.github.io/) 127 | 128 | `fastget` is open-sourced software licensed under the [MIT](LICENSE) license. 129 | 130 | ## Author 131 | Built with ❤ by [Prasanna Kumar](https://pgollangi.github.io/tabs/about/) 132 | -------------------------------------------------------------------------------- /cmd/fastget/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/pgollangi/fastget/commands" 8 | ) 9 | 10 | // Version is set at build 11 | var version string 12 | 13 | // build is set at build 14 | var build string 15 | 16 | func main() { 17 | commands.Version = version 18 | commands.Build = build 19 | if err := commands.RootCmd.Execute(); err != nil { 20 | fmt.Println(err) 21 | os.Exit(1) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cmd/gen-docs/docs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/pgollangi/fastget/commands" 7 | "github.com/spf13/cobra/doc" 8 | ) 9 | 10 | func main() { 11 | err := doc.GenMarkdownTree(commands.RootCmd, "./docs") 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /commands/root.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | "time" 9 | 10 | "github.com/MakeNowJust/heredoc" 11 | "github.com/pgollangi/fastget" 12 | "github.com/spf13/cobra" 13 | 14 | "github.com/vbauerster/mpb/v5" 15 | "github.com/vbauerster/mpb/v5/decor" 16 | 17 | "github.com/inhies/go-bytesize" 18 | ) 19 | 20 | // Version is the version for netselect 21 | var Version string 22 | 23 | // Build holds the date bin was released 24 | var Build string 25 | 26 | // RootCmd is the main root/parent command 27 | var RootCmd = &cobra.Command{ 28 | Use: "fastget $fileURL", 29 | Short: "A fastget CLI Tool", 30 | Long: `fastget is an open source CLI tool to ultrafast download files over HTTP(s).`, 31 | SilenceErrors: true, 32 | SilenceUsage: true, 33 | Example: heredoc.Doc(` 34 | $ fastget https://file.example.com // Basic Usage 35 | $ fastget http://speedtest.tele2.net/10MB.zip -H "Authorization: Basic ASYFASUF" // Custom Header 36 | $ fastget http://speedtest.tele2.net/10MB.zip -w 6 // Increased no. of workers 37 | $ fastget -v 38 | `), 39 | RunE: runCommand, 40 | } 41 | 42 | type barStatus struct { 43 | iT time.Time 44 | bar *mpb.Bar 45 | } 46 | 47 | func runCommand(cmd *cobra.Command, args []string) error { 48 | if ok, _ := cmd.Flags().GetBool("version"); ok { 49 | executeVersionCmd() 50 | return nil 51 | } else if len(args) != 1 { 52 | cmd.Usage() 53 | return nil 54 | } 55 | 56 | threads, _ := cmd.Flags().GetInt("workers") 57 | 58 | headers, _ := cmd.Flags().GetStringArray("header") 59 | 60 | url := args[0] 61 | 62 | fg, err := fastget.NewFastGetter(url) 63 | 64 | if err != nil { 65 | return err 66 | } 67 | fg.Workers = threads 68 | 69 | for _, header := range headers { 70 | split := strings.Split(header, ":") 71 | fg.Headers[split[0]] = split[1] 72 | fmt.Println(header) 73 | } 74 | 75 | fmt.Println("Initializing download..") 76 | 77 | mpbars := make(map[int]*barStatus) 78 | 79 | mp := mpb.New( 80 | mpb.WithWidth(100), 81 | mpb.WithRefreshRate(240*time.Millisecond), 82 | ) 83 | 84 | fg.OnBeforeStart = func(filesize int64, chunckLen int64) { 85 | fmt.Printf("File size : %s\n", bytesize.New(float64(filesize))) 86 | } 87 | 88 | fg.OnStart = func(worker int, totalSize int64) { 89 | mpbar := mp.AddBar(totalSize, mpb.BarStyle("[=>-|"), 90 | mpb.PrependDecorators( 91 | decor.CountersKiloByte("% 6.2f / % .2f"), 92 | decor.Percentage(decor.WCSyncSpace), 93 | ), 94 | mpb.AppendDecorators( 95 | decor.EwmaETA(decor.ET_STYLE_GO, 90), 96 | decor.Name(" ] "), 97 | decor.EwmaSpeed(decor.UnitKB, "% .2f", 60), 98 | )) 99 | 100 | mpbars[worker] = &barStatus{ 101 | bar: mpbar, 102 | iT: time.Now(), 103 | } 104 | 105 | } 106 | 107 | fg.OnProgress = func(worker int, download int64) { 108 | barStatus := mpbars[worker] 109 | 110 | dur := time.Since(barStatus.iT) 111 | 112 | barStatus.bar.SetCurrent(download) 113 | barStatus.bar.DecoratorEwmaUpdate(dur) 114 | barStatus.iT = time.Now() 115 | } 116 | 117 | result, err := fg.Get() 118 | if err != nil { 119 | return err 120 | } 121 | 122 | mp.Wait() 123 | 124 | pwd, err := os.Getwd() 125 | 126 | oFile := filepath.Join(pwd, result.OutputFile.Name()) 127 | 128 | fmt.Printf("Download finished in %s. File: %s", result.ElapsedTime.Round(time.Second), oFile) 129 | 130 | return nil 131 | } 132 | 133 | // Execute performs fastget command execution 134 | func Execute() error { 135 | return RootCmd.Execute() 136 | } 137 | 138 | func init() { 139 | RootCmd.Flags().BoolP("version", "v", false, "show fastget version information") 140 | RootCmd.Flags().BoolP("debug", "d", false, "show debug information") 141 | RootCmd.Flags().IntP("workers", "w", 3, "use parellel threads") 142 | RootCmd.Flags().StringP("output", "o", ".", "output file to be written") 143 | RootCmd.Flags().StringArrayP("header", "H", []string{}, "output file to be written") 144 | } 145 | 146 | func executeVersionCmd() { 147 | fmt.Printf("fast version %s (%s)\n", Version, Build) 148 | fmt.Println("More info: pgollangi.com/fastget") 149 | } 150 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT! 2 | 3 | The docs for the command(s) including new changes are automatically generated. Do not change any file in this folder. 4 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /docs/fastget.md: -------------------------------------------------------------------------------- 1 | ## fastget 2 | 3 | A fastget CLI Tool 4 | 5 | ### Synopsis 6 | 7 | fastget is an open source CLI tool to ultrafast download files over HTTP(s). 8 | 9 | ``` 10 | fastget $fileURL [flags] 11 | ``` 12 | 13 | ### Examples 14 | 15 | ``` 16 | $ fastget https://file.example.com // Basic Usage 17 | $ fastget http://speedtest.tele2.net/10MB.zip -H "Authorization: Basic ASYFASUF" // Custom Header 18 | $ fastget http://speedtest.tele2.net/10MB.zip -w 6 // Increased no. of workers 19 | $ fastget -v 20 | 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | -d, --debug show debug information 27 | -H, --header stringArray output file to be written 28 | -h, --help help for fastget 29 | -o, --output string output file to be written (default ".") 30 | -v, --version show fastget version information 31 | -w, --workers int use parellel threads (default 3) 32 | ``` 33 | 34 | ###### Auto generated by spf13/cobra on 23-Jan-2024 35 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ## fastget 2 | 3 | A fastget CLI Tool 4 | 5 | ### Synopsis 6 | 7 | fastget is an open source CLI tool to ultrafast download files over HTTP(s). 8 | 9 | ``` 10 | fastget $fileURL [flags] 11 | ``` 12 | 13 | ### Examples 14 | 15 | ``` 16 | $ fastget https://file.example.com // Basic Usage 17 | $ fastget http://speedtest.tele2.net/10MB.zip -H "Authorization: Basic ASYFASUF" // Custom Header 18 | $ fastget http://speedtest.tele2.net/10MB.zip -w 6 // Increased no. of workers 19 | $ fastget -v 20 | 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | -d, --debug show debug information 27 | -H, --header stringArray output file to be written 28 | -h, --help help for fastget 29 | -o, --output string output file to be written (default ".") 30 | -v, --version show fastget version information 31 | -w, --workers int use parellel threads (default 3) 32 | ``` 33 | 34 | ###### Auto generated by spf13/cobra on 23-Jan-2024 35 | -------------------------------------------------------------------------------- /examples/fastget_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgollangi/fastget/8e3bb86629fc20a4d090e06457c107703093e64a/examples/fastget_demo.gif -------------------------------------------------------------------------------- /fastget.go: -------------------------------------------------------------------------------- 1 | package fastget 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net/http" 9 | "os" 10 | "path" 11 | "time" 12 | 13 | "golang.org/x/net/context/ctxhttp" 14 | "golang.org/x/sync/errgroup" 15 | ) 16 | 17 | // FastGetter Represents the information required to fastget a file url 18 | type FastGetter struct { 19 | // URL of the file to be downloaded 20 | URL string 21 | // Number of concurrent worked should be used to download file 22 | Workers int 23 | // Path of output file to write download 24 | OutputFile string 25 | // Headers to be included to while making requests 26 | Headers map[string]string 27 | // OnBeforeStart to be called before even download start 28 | OnBeforeStart func(int64, int64) 29 | 30 | // OnStart to be called on started downloading a chunk / a part 31 | OnStart func(int, int64) 32 | // OnProgress to be called on change in progress of downloading a chunk / a part 33 | OnProgress func(int, int64) 34 | // OnFinish to be called on finished downloading a chunk / a part 35 | OnFinish func(int) 36 | } 37 | 38 | // chunkInfo holds the information about a chunk to be downloaded 39 | type chunkInfo struct { 40 | ctx context.Context 41 | client *http.Client 42 | output io.WriterAt 43 | url string 44 | off, lim int64 45 | wid int 46 | headers map[string]string 47 | } 48 | 49 | // Result represents the result of fastget 50 | type Result struct { 51 | URL string 52 | Size int64 53 | OutputFile *os.File 54 | ElapsedTime time.Duration 55 | } 56 | 57 | // NewFastGetter creates and returns an instance of FastGetter 58 | func NewFastGetter(url string) (*FastGetter, error) { 59 | fg := &FastGetter{ 60 | URL: url, 61 | Workers: 3, 62 | OutputFile: path.Base(url), 63 | Headers: make(map[string]string), 64 | } 65 | return fg, nil 66 | } 67 | 68 | // Get ultrafast downloads the file 69 | func (fg *FastGetter) Get() (*Result, error) { 70 | return fg.get() 71 | } 72 | 73 | func (fg *FastGetter) get() (*Result, error) { 74 | canFastGet, length, err := fg.checkEligibility() 75 | if err != nil { 76 | return nil, err 77 | } 78 | if !canFastGet { 79 | log.Println("WARN: FileURL doesn't support parellel download.") 80 | fg.Workers = 1 81 | } 82 | 83 | chunkLen := int64(length / int64(fg.Workers)) 84 | 85 | if fg.OnBeforeStart != nil { 86 | fg.OnBeforeStart(length, chunkLen) 87 | } 88 | 89 | ctx := context.Background() 90 | client := http.DefaultClient 91 | 92 | output, err := os.OpenFile(fg.OutputFile, os.O_CREATE|os.O_RDWR, 0666) 93 | if err != nil { 94 | return nil, err 95 | } 96 | 97 | wg, ctx := errgroup.WithContext(ctx) 98 | startTime := time.Now() 99 | 100 | var start, end int64 101 | for wid := 1; wid <= fg.Workers; wid++ { 102 | 103 | if wid == fg.Workers { 104 | end = length // last part 105 | } else { 106 | end = start + chunkLen 107 | } 108 | 109 | wid := wid 110 | off := start 111 | lim := end 112 | 113 | wg.Go(func() error { 114 | return fg.getChunk(&chunkInfo{ 115 | ctx: ctx, client: client, output: output, url: fg.URL, 116 | off: off, lim: lim, wid: wid, headers: fg.Headers, 117 | }) 118 | }) 119 | 120 | start = end 121 | } 122 | 123 | err = wg.Wait() 124 | if err != nil { 125 | return nil, err 126 | } 127 | elapsed := time.Since(startTime) 128 | 129 | result := &Result{ 130 | URL: fg.URL, 131 | Size: length, 132 | OutputFile: output, 133 | ElapsedTime: elapsed, 134 | } 135 | return result, nil 136 | } 137 | 138 | func (fg FastGetter) checkEligibility() (bool, int64, error) { 139 | req, err := http.NewRequest("HEAD", fg.URL, nil) 140 | if err != nil { 141 | return false, 0, err 142 | } 143 | for key, value := range fg.Headers { 144 | req.Header.Add(key, value) 145 | } 146 | res, err := http.DefaultClient.Do(req) 147 | if err != nil { 148 | return false, 0, err 149 | } 150 | defer res.Body.Close() 151 | acceptRanges := res.Header.Get("Accept-Ranges") == "bytes" 152 | length := res.ContentLength 153 | 154 | return acceptRanges, length, nil 155 | } 156 | 157 | // makeRequest performs the HTTP request and returns the response object 158 | func (cInfo chunkInfo) makeRequest() (*http.Response, error) { 159 | req, err := http.NewRequest("GET", cInfo.url, nil) 160 | if err != nil { 161 | return nil, err 162 | } 163 | req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", cInfo.off, cInfo.lim)) 164 | // Add custom headers 165 | for key, value := range cInfo.headers { 166 | req.Header.Add(key, value) 167 | } 168 | 169 | resp, err := ctxhttp.Do(cInfo.ctx, cInfo.client, req) 170 | if err != nil { 171 | return nil, err 172 | } 173 | return resp, nil 174 | } 175 | 176 | // getChunk fetches the part of file to download 177 | func (fg FastGetter) getChunk(cInfo *chunkInfo) error { 178 | if fg.OnStart != nil { 179 | fg.OnStart(cInfo.wid, cInfo.lim-cInfo.off) 180 | } 181 | resp, err := cInfo.makeRequest() 182 | if err != nil { 183 | return err 184 | } 185 | defer resp.Body.Close() 186 | if resp.StatusCode != http.StatusPartialContent { 187 | return fmt.Errorf("server responded with %d status code, expected %d", resp.StatusCode, http.StatusPartialContent) 188 | } 189 | 190 | var written int64 191 | contentLen := resp.ContentLength 192 | 193 | buf := make([]byte, 1*1024*1024) 194 | for { 195 | nr, er := resp.Body.Read(buf) 196 | 197 | if nr > 0 { 198 | nw, err := cInfo.output.WriteAt(buf[0:nr], cInfo.off) 199 | if err != nil { 200 | return fmt.Errorf("error writing chunk. %s", err.Error()) 201 | } 202 | if nr != nw { 203 | return fmt.Errorf("error writing chunk. written %d, but expected %d", nw, nr) 204 | } 205 | 206 | cInfo.off = int64(nw) + cInfo.off 207 | if nw > 0 { 208 | written += int64(nw) 209 | } 210 | if fg.OnProgress != nil { 211 | fg.OnProgress(cInfo.wid, written) 212 | } 213 | } 214 | 215 | if er != nil { 216 | if er.Error() == "EOF" { 217 | if contentLen == written { 218 | // Download successfully 219 | } else { 220 | return fmt.Errorf("error reading response. %s", er.Error()) 221 | } 222 | break 223 | } 224 | return er 225 | } 226 | } 227 | if fg.OnFinish != nil { 228 | fg.OnFinish(cInfo.wid) 229 | } 230 | return nil 231 | } 232 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pgollangi/fastget 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/MakeNowJust/heredoc v1.0.0 7 | github.com/inhies/go-bytesize v0.0.0-20200716184324-4fe85e9b81b2 8 | github.com/spf13/cobra v1.0.0 9 | github.com/spf13/pflag v1.0.5 // indirect 10 | github.com/vbauerster/mpb/v5 v5.3.0 11 | golang.org/x/net v0.0.0-20200822124328-c89045814202 12 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 13 | ) 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= 4 | github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= 5 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 6 | github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= 7 | github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= 8 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= 9 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= 10 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 11 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 12 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 13 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 14 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 15 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 16 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 17 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 18 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 19 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 20 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 21 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 22 | github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= 23 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 24 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 26 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 27 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 28 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 29 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 30 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 31 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 32 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 33 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 34 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 35 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 36 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 37 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 38 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 39 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 40 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 41 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 42 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 43 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 44 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 45 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 46 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 47 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 48 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 49 | github.com/inhies/go-bytesize v0.0.0-20200716184324-4fe85e9b81b2 h1:cdDdMBSC31fswuzvGrd05fGdb5EyCF++DVr8Q1JOhdU= 50 | github.com/inhies/go-bytesize v0.0.0-20200716184324-4fe85e9b81b2/go.mod h1:KrtyD5PFj++GKkFS/7/RRrfnRhAMGQwy75GLCHWrCNs= 51 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 52 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 53 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 54 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 55 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 56 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 57 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 58 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 59 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 60 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 61 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 62 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 63 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 64 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 65 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 66 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 67 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 68 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 69 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 70 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 71 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 72 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 73 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 74 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 75 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 76 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 77 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 78 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 79 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 80 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 81 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 82 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 83 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 84 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 85 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 86 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 87 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 88 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 89 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 90 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 91 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 92 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 93 | github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= 94 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 95 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 96 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 97 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 98 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 99 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 100 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 101 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 102 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 103 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 104 | github.com/vbauerster/mpb/v5 v5.3.0 h1:vgrEJjUzHaSZKDRRxul5Oh4C72Yy/5VEMb0em+9M0mQ= 105 | github.com/vbauerster/mpb/v5 v5.3.0/go.mod h1:4yTkvAb8Cm4eylAp6t0JRq6pXDkFJ4krUlDqWYkakAs= 106 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 107 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 108 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 109 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 110 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 111 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 112 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 113 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 114 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 115 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 116 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 117 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 118 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 119 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 120 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 121 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 122 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 123 | golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= 124 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 125 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 126 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 127 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 128 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 129 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= 130 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 131 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 132 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 133 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 134 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 135 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 136 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 137 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 138 | golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed h1:WBkVNH1zd9jg/dK4HCM4lNANnmd12EHC9z+LmcCG4ns= 139 | golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 140 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 141 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 142 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 143 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 144 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 145 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 146 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 147 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 148 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 149 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 150 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 151 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 152 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 153 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 154 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 155 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 156 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 157 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 158 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 159 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | 5 | if [[ ! -z ${DEBUG-} ]]; then 6 | set -x 7 | fi 8 | 9 | : ${PREFIX:=/usr/local} 10 | BINDIR="$PREFIX/bin" 11 | 12 | if [[ $# -gt 0 ]]; then 13 | BINDIR=$1 14 | fi 15 | 16 | _can_install() { 17 | if [[ ! -d "$BINDIR" ]]; then 18 | mkdir -p "$BINDIR" 2> /dev/null 19 | fi 20 | [[ -d "$BINDIR" && -w "$BINDIR" ]] 21 | } 22 | 23 | if ! _can_install && [[ $EUID != 0 ]]; then 24 | echo "Run script as sudo" 25 | exit 1 26 | fi 27 | 28 | if ! _can_install; then 29 | echo "Can't install to $BINDIR" 30 | exit 1 31 | fi 32 | 33 | machine=$(uname -m) 34 | 35 | case $(uname -s) in 36 | Linux) 37 | os="linux" 38 | ;; 39 | Darwin) 40 | os="darwin" 41 | ;; 42 | *) 43 | echo "OS not supported" 44 | exit 1 45 | ;; 46 | esac 47 | 48 | latest="$(curl -sL 'https://api.github.com/repos/pgollangi/fastget/releases/latest' | grep 'tag_name' | grep --only 'v[0-9\.]\+' | cut -c 2-)" 49 | curl -sL "https://github.com/pgollangi/fastget/releases/download/v${latest}/fastget_${latest}_${os}_${machine}.tar.gz" | tar -C /tmp/ -xzf - 50 | install -m755 /tmp/fastget $BINDIR/fastget 51 | echo "Successfully installed fastget into $BINDIR/" 52 | --------------------------------------------------------------------------------