├── .travis.yml
├── screenshot-1.png
├── screenshot-2.png
├── main.go
├── .gitignore
├── LICENSE
├── configuration.go
├── binary_file_system.go
├── README.md
└── http_server.go
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 |
3 | go:
4 | - tip
5 |
--------------------------------------------------------------------------------
/screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mtojek/bigfiles/HEAD/screenshot-1.png
--------------------------------------------------------------------------------
/screenshot-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mtojek/bigfiles/HEAD/screenshot-2.png
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | createHttpServer(readConfiguration(), createBinaryFileSystem()).listenAndServe()
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.dll
4 | *.so
5 | *.dylib
6 |
7 | # Test binary, build with `go test -c`
8 | *.test
9 |
10 | # Output of the go coverage tool, specifically when used with LiteIDE
11 | *.out
12 |
13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
14 | .glide/
15 |
16 | # IDEA
17 | .idea/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Marcin Tojek
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 |
--------------------------------------------------------------------------------
/configuration.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "flag"
6 | "fmt"
7 | "log"
8 | "math"
9 |
10 | "github.com/c2h5oh/datasize"
11 | )
12 |
13 | type configuration struct {
14 | hostPort string
15 | maxFileSize int64
16 | }
17 |
18 | func readConfiguration() *configuration {
19 | var hostPort string
20 | var maxFileSize string
21 |
22 | flag.StringVar(&hostPort, "hostPort", ":5000", "host:port of the HTTP server")
23 | flag.StringVar(&maxFileSize, "maxFileSize", "9223372036854775807B", "maximum size of file that can be downloaded")
24 | flag.Parse()
25 |
26 | var c *configuration
27 | var err error
28 | if c, err = validate(hostPort, maxFileSize); err != nil {
29 | log.Fatal(err)
30 | }
31 | return c
32 | }
33 |
34 | func validate(hostPort, maxFileSize string) (*configuration, error) {
35 | if hostPort == "" {
36 | return nil, errors.New("host:post must not be empty")
37 | }
38 |
39 | if maxFileSize == "" {
40 | return nil, errors.New("maxFileSize must no be empty")
41 | }
42 |
43 | var uSize datasize.ByteSize
44 | if err := uSize.UnmarshalText([]byte(maxFileSize)); err != nil {
45 | return nil, err
46 | }
47 |
48 | if uSize > math.MaxInt64 {
49 | return nil, fmt.Errorf("file size cannot be greater than %v B", math.MaxInt64)
50 | }
51 |
52 | return &configuration{hostPort: hostPort, maxFileSize: int64(uSize)}, nil
53 | }
54 |
--------------------------------------------------------------------------------
/binary_file_system.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "log"
6 | "math/rand"
7 | "net/http"
8 | "os"
9 | "regexp"
10 | "time"
11 |
12 | "github.com/c2h5oh/datasize"
13 | )
14 |
15 | var (
16 | errDirListingUnsupported = errors.New("not supported")
17 | kindNameRegexp = regexp.MustCompile("/files/([^/]+)/([^/]+)")
18 | )
19 |
20 | type binaryFileInfo struct {
21 | fileSize int64
22 | }
23 |
24 | func (bfi *binaryFileInfo) Name() string {
25 | return ""
26 | }
27 |
28 | func (bfi *binaryFileInfo) Size() int64 {
29 | return bfi.fileSize
30 | }
31 |
32 | func (bfi *binaryFileInfo) Mode() os.FileMode {
33 | return os.FileMode(os.ModePerm)
34 | }
35 |
36 | func (bfi *binaryFileInfo) ModTime() time.Time {
37 | return time.Now()
38 | }
39 |
40 | func (bfi *binaryFileInfo) IsDir() bool {
41 | return false
42 | }
43 |
44 | func (bfi *binaryFileInfo) Sys() interface{} {
45 | return nil
46 | }
47 |
48 | type binaryFile struct {
49 | fileType string
50 | fileSize int64
51 | }
52 |
53 | func (bf *binaryFile) Close() error {
54 | return nil
55 | }
56 |
57 | func (bf *binaryFile) Read(p []byte) (n int, err error) {
58 | for i := 0; i < len(p); i++ {
59 | if bf.fileType == "sparse" {
60 | p[i] = 0
61 | } else if bf.fileType == "random" {
62 | p[i] = byte(rand.Int())
63 | } else {
64 | log.Fatal("unsupported file type selected")
65 | }
66 | }
67 | return len(p), nil
68 | }
69 |
70 | func (bf *binaryFile) Readdir(count int) ([]os.FileInfo, error) {
71 | return nil, errDirListingUnsupported
72 | }
73 |
74 | func (bf *binaryFile) Stat() (os.FileInfo, error) {
75 | return &binaryFileInfo{fileSize: bf.fileSize}, nil
76 | }
77 |
78 | func (bf *binaryFile) Seek(offset int64, whence int) (int64, error) {
79 | return -1, nil
80 | }
81 |
82 | type binaryFileSystemImpl struct{}
83 |
84 | func (bfsi *binaryFileSystemImpl) Open(name string) (http.File, error) {
85 | m := kindNameRegexp.FindStringSubmatch(name)
86 | fileType := m[1]
87 | fileSize := m[2]
88 |
89 | var fileSizeValue datasize.ByteSize
90 | fileSizeValue.UnmarshalText([]byte(fileSize))
91 | return &binaryFile{fileType, int64(fileSizeValue.Bytes())}, nil
92 | }
93 |
94 | func createBinaryFileSystem() http.FileSystem {
95 | return &binaryFileSystemImpl{}
96 | }
97 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BigFiles
2 |
3 | Status: **Done** (waiting for feedback)
4 |
5 | [](https://travis-ci.org/mtojek/bigfiles)
6 |
7 | ## Description
8 |
9 | Are you bored with overloaded speed test services? You don't have to use them at all, because you can run own webserver, serving **large test files** (custom size: **100 MB**, **100 GB**, **1 TB**..., upto **8192 PB**).
10 |
11 | There is no more need to use publicly hosted storage services to download ordinary **100MB.bin**. With a single command spawn **own speed test** instance to verify your Internet provider.
12 |
13 | ### Screenshots
14 |
15 | #### Index view
16 |
17 |
18 |
19 | #### Chrome "Downloads" page
20 |
21 |
22 |
23 | ## Features
24 |
25 | * download huge files < **8192 PB**
26 | * choose between **zeros** file or random content
27 | * **easy to use** HTTP GET endpoints (Chrome, curl, etc.)
28 | * user-defined **file size limit**
29 |
30 | ## Quickstart
31 |
32 | Download and install BigFiles:
33 | ```bash
34 | go get github.com/mtojek/bigfiles
35 | ```
36 | Run the application:
37 | ```bash
38 | bigfiles
39 | ```
40 |
41 | Use wget to download a sparse file:
42 | ```bash
43 | wget http://localhost:5000/files/sparse/100MB
44 | --2017-10-01 00:34:18-- http://localhost:5000/files/sparse/100MB
45 | Resolving localhost... ::1, fe80::1, 127.0.0.1
46 | Connecting to localhost|::1|:5000... connected.
47 | HTTP request sent, awaiting response... 200 OK
48 | Length: 104857600 (100M) [application/octet-stream]
49 | Saving to: '100MB'
50 |
51 | 100MB 100%[===================================================>] 100.00M 218MB/s in 0.5s
52 |
53 | 2017-10-01 00:34:19 (218 MB/s) - '100MB' saved [104857600/104857600]
54 |
55 | ```
56 |
57 | or go to web panel to download files:
58 |
59 | [http://localhost:5000](http://localhost:5000)
60 |
61 | ## Contact
62 |
63 | Please feel free to leave any comment or feedback by opening a new issue or contacting me directly via [email](mailto:marcin@tojek.pl). Thank you.
64 |
65 | ## License
66 |
67 | MIT License, see [LICENSE](https://github.com/mtojek/bigfiles/blob/master/LICENSE) file.
68 |
--------------------------------------------------------------------------------
/http_server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "net/http"
7 |
8 | "github.com/c2h5oh/datasize"
9 | "github.com/julienschmidt/httprouter"
10 | )
11 |
12 | var (
13 | indexMessage = []byte(`
14 |
15 |