Skip to content

Commit 0f23bd3

Browse files
authored
Add no-proxy flags for downloading files (#283)
1 parent 0f6cfe2 commit 0f23bd3

File tree

9 files changed

+391
-72
lines changed

9 files changed

+391
-72
lines changed

cmd/get.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/linuxsuren/http-downloader/pkg"
1414
"github.com/linuxsuren/http-downloader/pkg/installer"
15+
"github.com/linuxsuren/http-downloader/pkg/net"
1516
"github.com/spf13/cobra"
1617
"github.com/spf13/viper"
1718
"gopkg.in/yaml.v3"
@@ -43,6 +44,7 @@ func newGetCmd(ctx context.Context) (cmd *cobra.Command) {
4344
`The default timeout in seconds with the HTTP request`)
4445
flags.IntVarP(&opt.MaxAttempts, "max-attempts", "", 10,
4546
`Max times to attempt to download, zero means there's no retry action'`)
47+
flags.BoolVarP(&opt.NoProxy, "no-proxy", "", false, "Indicate no HTTP proxy taken")
4648
flags.BoolVarP(&opt.ShowProgress, "show-progress", "", true, "If show the progress of download")
4749
flags.Int64VarP(&opt.ContinueAt, "continue-at", "", -1, "ContinueAt")
4850
flags.IntVarP(&opt.Thread, "thread", "t", viper.GetInt("thread"),
@@ -74,6 +76,7 @@ type downloadOption struct {
7476
Output string
7577
ShowProgress bool
7678
Timeout int
79+
NoProxy bool
7780
MaxAttempts int
7881
AcceptPreRelease bool
7982
RoundTripper http.RoundTripper
@@ -216,9 +219,17 @@ func (o *downloadOption) runE(cmd *cobra.Command, args []string) (err error) {
216219

217220
cmd.Printf("start to download from %s\n", o.URL)
218221
if o.Thread <= 1 {
219-
err = pkg.DownloadWithContinue(o.URL, o.Output, o.ContinueAt, -1, 0, o.ShowProgress)
222+
downloader := &net.ContinueDownloader{}
223+
downloader.WithoutProxy(o.NoProxy).
224+
WithRoundTripper(o.RoundTripper)
225+
err = downloader.DownloadWithContinue(o.URL, o.Output, o.ContinueAt, -1, 0, o.ShowProgress)
220226
} else {
221-
err = pkg.DownloadFileWithMultipleThreadKeepParts(o.URL, o.Output, o.Thread, o.KeepPart, o.ShowProgress)
227+
downloader := &net.MultiThreadDownloader{}
228+
downloader.WithKeepParts(o.KeepPart).
229+
WithShowProgress(o.ShowProgress).
230+
WithoutProxy(o.NoProxy).
231+
WithRoundTripper(o.RoundTripper)
232+
err = downloader.Download(o.URL, o.Output, o.Thread)
222233
}
223234
return
224235
}

cmd/get_test.go

Lines changed: 134 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
package cmd
22

33
import (
4+
"bytes"
45
"context"
56
"errors"
7+
"fmt"
8+
"io/ioutil"
9+
"net/http"
10+
"os"
11+
"path"
612
"testing"
13+
"time"
714

15+
"github.com/golang/mock/gomock"
16+
"github.com/linuxsuren/http-downloader/mock/mhttp"
817
"github.com/linuxsuren/http-downloader/pkg/installer"
918
"github.com/spf13/cobra"
1019
"github.com/stretchr/testify/assert"
@@ -26,6 +35,8 @@ func Test_newGetCmd(t *testing.T) {
2635
name: "time",
2736
}, {
2837
name: "max-attempts",
38+
}, {
39+
name: "no-proxy",
2940
}, {
3041
name: "show-progress",
3142
}, {
@@ -97,12 +108,127 @@ func TestPreRunE(t *testing.T) {
97108
}
98109

99110
func TestRunE(t *testing.T) {
100-
fakeCmd := &cobra.Command{}
101-
102-
opt := &downloadOption{}
103-
opt.fetcher = &installer.FakeFetcher{}
104-
105-
// print schema
106-
opt.PrintSchema = true
107-
assert.Nil(t, opt.runE(fakeCmd, nil))
111+
tests := []struct {
112+
name string
113+
opt *downloadOption
114+
args []string
115+
prepare func(t *testing.T, do *downloadOption)
116+
wantErr bool
117+
}{{
118+
name: "print shcema only",
119+
opt: &downloadOption{
120+
fetcher: &installer.FakeFetcher{},
121+
PrintSchema: true,
122+
},
123+
wantErr: false,
124+
}, {
125+
name: "download from an URL with one thread",
126+
opt: &downloadOption{
127+
fetcher: &installer.FakeFetcher{},
128+
NoProxy: true,
129+
},
130+
prepare: func(t *testing.T, do *downloadOption) {
131+
do.Output = path.Join(os.TempDir(), fmt.Sprintf("fake-%d", time.Now().Nanosecond()))
132+
do.URL = "https://foo.com"
133+
134+
ctrl := gomock.NewController(t)
135+
roundTripper := mhttp.NewMockRoundTripper(ctrl)
136+
137+
mockRequest, _ := http.NewRequest(http.MethodGet, do.URL, nil)
138+
mockResponse := &http.Response{
139+
StatusCode: http.StatusPartialContent,
140+
Proto: "HTTP/1.1",
141+
Request: mockRequest,
142+
Header: map[string][]string{
143+
"Content-Length": {"100"},
144+
},
145+
Body: ioutil.NopCloser(bytes.NewBufferString("responseBody")),
146+
}
147+
roundTripper.EXPECT().
148+
RoundTrip(mockRequest).Return(mockResponse, nil)
149+
do.RoundTripper = roundTripper
150+
},
151+
wantErr: false,
152+
}, {
153+
name: "download from an URL with multi-threads",
154+
opt: &downloadOption{
155+
fetcher: &installer.FakeFetcher{},
156+
NoProxy: true,
157+
Thread: 2,
158+
},
159+
prepare: func(t *testing.T, do *downloadOption) {
160+
do.Output = path.Join(os.TempDir(), fmt.Sprintf("fake-%d", time.Now().Nanosecond()))
161+
do.URL = "https://foo.com"
162+
163+
ctrl := gomock.NewController(t)
164+
roundTripper := mhttp.NewMockRoundTripper(ctrl)
165+
166+
// for size detecting
167+
mockRequest, _ := http.NewRequest(http.MethodGet, do.URL, nil)
168+
mockRequest.Header.Set("Range", "bytes=2-")
169+
mockResponse := &http.Response{
170+
StatusCode: http.StatusPartialContent,
171+
Proto: "HTTP/1.1",
172+
Request: mockRequest,
173+
Header: map[string][]string{
174+
"Content-Length": {"100"},
175+
},
176+
Body: ioutil.NopCloser(bytes.NewBufferString("responseBody")),
177+
}
178+
roundTripper.EXPECT().
179+
RoundTrip(mockRequest).Return(mockResponse, nil)
180+
181+
// for group-1
182+
mockRequest1, _ := http.NewRequest(http.MethodGet, do.URL, nil)
183+
mockRequest1.Header.Set("Range", "bytes=0-50")
184+
mockResponse1 := &http.Response{
185+
StatusCode: http.StatusPartialContent,
186+
Proto: "HTTP/1.1",
187+
Request: mockRequest1,
188+
Header: map[string][]string{
189+
"Content-Length": {"100"},
190+
},
191+
Body: ioutil.NopCloser(bytes.NewBufferString("responseBody")),
192+
}
193+
roundTripper.EXPECT().
194+
RoundTrip(mockRequest1).Return(mockResponse1, nil)
195+
196+
// for group-2
197+
mockRequest2, _ := http.NewRequest(http.MethodGet, do.URL, nil)
198+
mockRequest2.Header.Set("Range", "bytes=51-101")
199+
mockResponse2 := &http.Response{
200+
StatusCode: http.StatusPartialContent,
201+
Proto: "HTTP/1.1",
202+
Request: mockRequest2,
203+
Header: map[string][]string{
204+
"Content-Length": {"100"},
205+
},
206+
Body: ioutil.NopCloser(bytes.NewBufferString("responseBody")),
207+
}
208+
roundTripper.EXPECT().
209+
RoundTrip(mockRequest2).Return(mockResponse2, nil)
210+
do.RoundTripper = roundTripper
211+
},
212+
wantErr: false,
213+
}}
214+
for i, tt := range tests {
215+
t.Run(tt.name, func(t *testing.T) {
216+
fakeCmd := &cobra.Command{}
217+
fakeCmd.SetOut(new(bytes.Buffer))
218+
if tt.prepare != nil {
219+
tt.prepare(t, tt.opt)
220+
}
221+
if tt.opt.Output != "" {
222+
defer func() {
223+
_ = os.RemoveAll(tt.opt.Output)
224+
}()
225+
}
226+
err := tt.opt.runE(fakeCmd, tt.args)
227+
if tt.wantErr {
228+
assert.NotNil(t, err, "should error in [%d][%s]", i, tt.name)
229+
} else {
230+
assert.Nil(t, err, "should not error in [%d][%s]", i, tt.name)
231+
}
232+
})
233+
}
108234
}

cmd/install.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Cannot find your desired package? Please run command: hd fetch --reset, then try
6060
"Clean the package if the installation is success")
6161
flags.IntVarP(&opt.Thread, "thread", "t", viper.GetInt("thread"),
6262
`Download file with multi-threads. It only works when its value is bigger than 1`)
63+
flags.BoolVarP(&opt.NoProxy, "no-proxy", "", false, "Indicate no HTTP proxy taken")
6364
flags.BoolVarP(&opt.KeepPart, "keep-part", "", false,
6465
"If you want to keep the part files instead of deleting them")
6566
flags.StringVarP(&opt.OS, "os", "", runtime.GOOS, "The OS of target binary file")

cmd/install_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ func Test_newInstallCmd(t *testing.T) {
5151
Name: "fetch",
5252
}, {
5353
Name: "provider",
54+
}, {
55+
Name: "no-proxy",
5456
}}
5557
test.Valid(t, cmd.Flags())
5658
}

pkg/common/util.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ func ParseVersionNum(release string) string {
8282
}
8383

8484
// GetEnvironment retrieves the value of the environment variable named by the key.
85-
// If the environment dosen't exist, we will lookup for alternative environment variables
86-
// unti we find an environment. Return empty environment value while no environment variables found.
85+
// If the environment doesn't exist, we will lookup for alternative environment variables
86+
// until we find an environment. Return empty environment value while no environment variables found.
8787
func GetEnvironment(key string, alternativeKeys ...string) string {
8888
if value, exists := os.LookupEnv(key); exists {
8989
return value

pkg/compress/types_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,4 @@ func Test_extraFile(t *testing.T) {
163163
tt.wantErr(t, extraFile(tt.args.name, tt.args.targetName, tt.args.tarFile, tt.args.header, tt.args.tarReader), fmt.Sprintf("extraFile(%v, %v, %v, %v, %v)", tt.args.name, tt.args.targetName, tt.args.tarFile, tt.args.header, tt.args.tarReader))
164164
})
165165
}
166-
}
166+
}

0 commit comments

Comments
 (0)