1// +build go1.13,integration,perftest
2
3package main
4
5import (
6	"flag"
7	"fmt"
8	"net/http"
9	"strings"
10	"time"
11
12	"github.com/aws/aws-sdk-go/service/s3/s3manager"
13)
14
15type Config struct {
16	Bucket         string
17	Size           int64
18	Key            string
19	LogVerbose     bool
20	UploadPartSize int64
21
22	SDK      SDKConfig
23	Client   ClientConfig
24	Profiler Profiler
25}
26
27func (c *Config) SetupFlags(prefix string, flagset *flag.FlagSet) {
28	flagset.StringVar(&c.Bucket, "bucket", "",
29		"The S3 bucket `name` to download the object from.")
30	flagset.Int64Var(&c.Size, "size", 0,
31		"The S3 object size in bytes to be first uploaded then downloaded")
32	flagset.StringVar(&c.Key, "key", "", "The S3 object key to download")
33	flagset.BoolVar(&c.LogVerbose, "verbose", false,
34		"The output log will include verbose request information")
35	flagset.Int64Var(&c.UploadPartSize, "upload-part-size", 0, "the upload part size when uploading a file to s3")
36
37	c.SDK.SetupFlags(prefix, flagset)
38	c.Client.SetupFlags(prefix, flagset)
39	c.Profiler.SetupFlags(prefix, flagset)
40}
41
42func (c *Config) Validate() error {
43	var errs Errors
44
45	if len(c.Bucket) == 0 || (c.Size <= 0 && len(c.Key) == 0) {
46		errs = append(errs, fmt.Errorf("bucket and filename/size are required"))
47	}
48
49	if err := c.SDK.Validate(); err != nil {
50		errs = append(errs, err)
51	}
52	if err := c.Client.Validate(); err != nil {
53		errs = append(errs, err)
54	}
55
56	if len(errs) != 0 {
57		return errs
58	}
59
60	return nil
61}
62
63type SDKConfig struct {
64	PartSize       int64
65	Concurrency    int
66	BufferProvider s3manager.WriterReadFromProvider
67}
68
69func (c *SDKConfig) SetupFlags(prefix string, flagset *flag.FlagSet) {
70	prefix += "sdk."
71
72	flagset.Int64Var(&c.PartSize, prefix+"part-size", s3manager.DefaultDownloadPartSize,
73		"Specifies the `size` of parts of the object to download.")
74	flagset.IntVar(&c.Concurrency, prefix+"concurrency", s3manager.DefaultDownloadConcurrency,
75		"Specifies the number of parts to download `at once`.")
76}
77
78func (c *SDKConfig) Validate() error {
79	return nil
80}
81
82type ClientConfig struct {
83	KeepAlive bool
84	Timeouts  Timeouts
85
86	MaxIdleConns        int
87	MaxIdleConnsPerHost int
88
89	// Go 1.13
90	ReadBufferSize  int
91	WriteBufferSize int
92}
93
94func (c *ClientConfig) SetupFlags(prefix string, flagset *flag.FlagSet) {
95	prefix += "client."
96
97	flagset.BoolVar(&c.KeepAlive, prefix+"http-keep-alive", true,
98		"Specifies if HTTP keep alive is enabled.")
99
100	defTR := http.DefaultTransport.(*http.Transport)
101
102	flagset.IntVar(&c.MaxIdleConns, prefix+"idle-conns", defTR.MaxIdleConns,
103		"Specifies max idle connection pool size.")
104
105	flagset.IntVar(&c.MaxIdleConnsPerHost, prefix+"idle-conns-host", http.DefaultMaxIdleConnsPerHost,
106		"Specifies max idle connection pool per host, will be truncated by idle-conns.")
107
108	flagset.IntVar(&c.ReadBufferSize, prefix+"read-buffer", defTR.ReadBufferSize, "size of the transport read buffer used")
109	flagset.IntVar(&c.WriteBufferSize, prefix+"writer-buffer", defTR.WriteBufferSize, "size of the transport write buffer used")
110
111	c.Timeouts.SetupFlags(prefix, flagset)
112}
113
114func (c *ClientConfig) Validate() error {
115	var errs Errors
116
117	if err := c.Timeouts.Validate(); err != nil {
118		errs = append(errs, err)
119	}
120
121	if len(errs) != 0 {
122		return errs
123	}
124	return nil
125}
126
127type Timeouts struct {
128	Connect        time.Duration
129	TLSHandshake   time.Duration
130	ExpectContinue time.Duration
131	ResponseHeader time.Duration
132}
133
134func (c *Timeouts) SetupFlags(prefix string, flagset *flag.FlagSet) {
135	prefix += "timeout."
136
137	flagset.DurationVar(&c.Connect, prefix+"connect", 30*time.Second,
138		"The `timeout` connecting to the remote host.")
139
140	defTR := http.DefaultTransport.(*http.Transport)
141
142	flagset.DurationVar(&c.TLSHandshake, prefix+"tls", defTR.TLSHandshakeTimeout,
143		"The `timeout` waiting for the TLS handshake to complete.")
144
145	flagset.DurationVar(&c.ExpectContinue, prefix+"expect-continue", defTR.ExpectContinueTimeout,
146		"The `timeout` waiting for the TLS handshake to complete.")
147
148	flagset.DurationVar(&c.ResponseHeader, prefix+"response-header", defTR.ResponseHeaderTimeout,
149		"The `timeout` waiting for the TLS handshake to complete.")
150}
151
152func (c *Timeouts) Validate() error {
153	return nil
154}
155
156type Errors []error
157
158func (es Errors) Error() string {
159	var buf strings.Builder
160	for _, e := range es {
161		buf.WriteString(e.Error())
162	}
163
164	return buf.String()
165}
166