Skip to content

Commit ba52e6c

Browse files
authored
feat: support to skip tls when download file (#348)
Co-authored-by: rick <[email protected]>
1 parent b80f4f5 commit ba52e6c

File tree

6 files changed

+246
-155
lines changed

6 files changed

+246
-155
lines changed

cmd/get.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func newGetCmd(ctx context.Context) (cmd *cobra.Command) {
5050
"Same with option --accept-preRelease")
5151
flags.BoolVarP(&opt.Force, "force", "f", false, "Overwrite the exist file if this is true")
5252
flags.IntVarP(&opt.Mod, "mod", "", -1, "The file permission, -1 means using the system default")
53+
flags.BoolVarP(&opt.SkipTLS, "skip-tls", "k", false, "Skip the TLS")
5354

5455
flags.IntVarP(&opt.Timeout, "time", "", 10,
5556
`The default timeout in seconds with the HTTP request`)
@@ -106,6 +107,7 @@ type downloadOption struct {
106107
Magnet bool
107108
Force bool
108109
Mod int
110+
SkipTLS bool
109111

110112
ContinueAt int64
111113

@@ -297,24 +299,44 @@ func (o *downloadOption) runE(cmd *cobra.Command, args []string) (err error) {
297299
targetURL = strings.Replace(targetURL, "raw.githubusercontent.com", fmt.Sprintf("%s/https://raw.githubusercontent.com", o.ProxyGitHub), 1)
298300
}
299301
logger.Printf("start to download from %s\n", targetURL)
302+
var suggestedFilenameAware net.SuggestedFilenameAware
300303
if o.Thread <= 1 {
301304
downloader := &net.ContinueDownloader{}
305+
suggestedFilenameAware = downloader
302306
downloader.WithoutProxy(o.NoProxy).
303-
WithRoundTripper(o.RoundTripper)
307+
WithRoundTripper(o.RoundTripper).
308+
WithInsecureSkipVerify(o.SkipTLS)
304309
err = downloader.DownloadWithContinue(targetURL, o.Output, o.ContinueAt, -1, 0, o.ShowProgress)
305310
} else {
306311
downloader := &net.MultiThreadDownloader{}
312+
suggestedFilenameAware = downloader
307313
downloader.WithKeepParts(o.KeepPart).
308314
WithShowProgress(o.ShowProgress).
309315
WithoutProxy(o.NoProxy).
310-
WithRoundTripper(o.RoundTripper)
316+
WithRoundTripper(o.RoundTripper).
317+
WithInsecureSkipVerify(o.SkipTLS)
311318
err = downloader.Download(targetURL, o.Output, o.Thread)
312319
}
313320

314321
// set file permission
315322
if o.Mod != -1 {
316323
err = sysos.Chmod(o.Output, fs.FileMode(o.Mod))
317324
}
325+
326+
if err == nil {
327+
logger.Printf("downloaded: %s\n", o.Output)
328+
}
329+
330+
if suggested := suggestedFilenameAware.GetSuggestedFilename(); suggested != "" {
331+
confirm := &survey.Confirm{
332+
Message: fmt.Sprintf("Do you want to rename filename from '%s' to '%s'?", o.Output, suggested),
333+
}
334+
var yes bool
335+
if confirmErr := survey.AskOne(confirm, &yes); confirmErr == nil && yes {
336+
fmt.Println("rename")
337+
err = sysos.Rename(o.Output, suggested)
338+
}
339+
}
318340
return
319341
}
320342

pkg/net/http.go

Lines changed: 47 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"os"
1111
"path"
1212
"strconv"
13-
"sync"
13+
"strings"
1414
"time"
1515

1616
"github.com/linuxsuren/http-downloader/pkg/common"
@@ -50,6 +50,7 @@ type HTTPDownloader struct {
5050
Debug bool
5151
RoundTripper http.RoundTripper
5252
progressIndicator *ProgressIndicator
53+
suggestedFilename string
5354
}
5455

5556
// SetProxy set the proxy for a http
@@ -150,6 +151,14 @@ func (h *HTTPDownloader) DownloadFile() error {
150151
}
151152
}
152153

154+
if disposition, ok := resp.Header["Content-Disposition"]; ok && len(disposition) >= 1 {
155+
h.suggestedFilename = strings.TrimPrefix(disposition[0], `filename="`)
156+
h.suggestedFilename = strings.TrimSuffix(h.suggestedFilename, `"`)
157+
if h.suggestedFilename == filepath {
158+
h.suggestedFilename = ""
159+
}
160+
}
161+
153162
// pre-hook before get started to download file
154163
if h.PreStart != nil && !h.PreStart(resp) {
155164
return nil
@@ -192,127 +201,15 @@ func (h *HTTPDownloader) DownloadFile() error {
192201
return err
193202
}
194203

195-
// DownloadFileWithMultipleThread downloads the files with multiple threads
196-
func DownloadFileWithMultipleThread(targetURL, targetFilePath string, thread int, showProgress bool) (err error) {
197-
return DownloadFileWithMultipleThreadKeepParts(targetURL, targetFilePath, thread, false, showProgress)
204+
// GetSuggestedFilename returns the suggested filename which comes from the HTTP response header.
205+
// Returns empty string if the filename is same with the given name.
206+
func (h *HTTPDownloader) GetSuggestedFilename() string {
207+
return h.suggestedFilename
198208
}
199209

200-
// MultiThreadDownloader is a download with multi-thread
201-
type MultiThreadDownloader struct {
202-
noProxy bool
203-
keepParts, showProgress bool
204-
205-
roundTripper http.RoundTripper
206-
}
207-
208-
// WithoutProxy indicates not use HTTP proxy
209-
func (d *MultiThreadDownloader) WithoutProxy(noProxy bool) *MultiThreadDownloader {
210-
d.noProxy = noProxy
211-
return d
212-
}
213-
214-
// WithShowProgress indicate if show the download progress
215-
func (d *MultiThreadDownloader) WithShowProgress(showProgress bool) *MultiThreadDownloader {
216-
d.showProgress = showProgress
217-
return d
218-
}
219-
220-
// WithKeepParts indicates if keeping the part files
221-
func (d *MultiThreadDownloader) WithKeepParts(keepParts bool) *MultiThreadDownloader {
222-
d.keepParts = keepParts
223-
return d
224-
}
225-
226-
// WithRoundTripper sets RoundTripper
227-
func (d *MultiThreadDownloader) WithRoundTripper(roundTripper http.RoundTripper) *MultiThreadDownloader {
228-
d.roundTripper = roundTripper
229-
return d
230-
}
231-
232-
// Download starts to download the target URL
233-
func (d *MultiThreadDownloader) Download(targetURL, targetFilePath string, thread int) (err error) {
234-
// get the total size of the target file
235-
var total int64
236-
var rangeSupport bool
237-
if total, rangeSupport, err = DetectSizeWithRoundTripper(targetURL, targetFilePath, true, d.noProxy, d.roundTripper); err != nil {
238-
return
239-
}
240-
241-
if rangeSupport {
242-
unit := total / int64(thread)
243-
offset := total - unit*int64(thread)
244-
var wg sync.WaitGroup
245-
var partItems []string
246-
var m sync.Mutex
247-
248-
defer func() {
249-
// remove all partial files
250-
for _, part := range partItems {
251-
_ = os.RemoveAll(part)
252-
}
253-
}()
254-
255-
fmt.Printf("start to download with %d threads, size: %d, unit: %d\n", thread, total, unit)
256-
for i := 0; i < thread; i++ {
257-
wg.Add(1)
258-
go func(index int, wg *sync.WaitGroup) {
259-
defer wg.Done()
260-
output := fmt.Sprintf("%s-%d", targetFilePath, index)
261-
262-
m.Lock()
263-
partItems = append(partItems, output)
264-
m.Unlock()
265-
266-
end := unit*int64(index+1) - 1
267-
if index == thread-1 {
268-
// this is the last part
269-
end += offset
270-
}
271-
start := unit * int64(index)
272-
273-
downloader := &ContinueDownloader{}
274-
downloader.WithoutProxy(d.noProxy).
275-
WithRoundTripper(d.roundTripper)
276-
if downloadErr := downloader.DownloadWithContinue(targetURL, output,
277-
int64(index), start, end, d.showProgress); downloadErr != nil {
278-
fmt.Println(downloadErr)
279-
}
280-
}(i, &wg)
281-
}
282-
283-
wg.Wait()
284-
ProgressIndicator{}.Close()
285-
286-
// concat all these partial files
287-
var f *os.File
288-
if f, err = os.OpenFile(targetFilePath, os.O_CREATE|os.O_WRONLY, 0600); err == nil {
289-
defer func() {
290-
_ = f.Close()
291-
}()
292-
293-
for i := 0; i < thread; i++ {
294-
partFile := fmt.Sprintf("%s-%d", targetFilePath, i)
295-
if data, ferr := os.ReadFile(partFile); ferr == nil {
296-
if _, err = f.Write(data); err != nil {
297-
err = fmt.Errorf("failed to write file: '%s'", partFile)
298-
break
299-
} else if !d.keepParts {
300-
_ = os.RemoveAll(partFile)
301-
}
302-
} else {
303-
err = fmt.Errorf("failed to read file: '%s'", partFile)
304-
break
305-
}
306-
}
307-
}
308-
} else {
309-
fmt.Println("cannot download it using multiple threads, failed to one")
310-
downloader := &ContinueDownloader{}
311-
downloader.WithoutProxy(d.noProxy)
312-
downloader.WithRoundTripper(d.roundTripper)
313-
err = downloader.DownloadWithContinue(targetURL, targetFilePath, -1, 0, 0, true)
314-
}
315-
return
210+
// SuggestedFilenameAware is the interface for getting suggested filename
211+
type SuggestedFilenameAware interface {
212+
GetSuggestedFilename() string
316213
}
317214

318215
// DownloadFileWithMultipleThreadKeepParts downloads the files with multiple threads
@@ -326,8 +223,14 @@ func DownloadFileWithMultipleThreadKeepParts(targetURL, targetFilePath string, t
326223
type ContinueDownloader struct {
327224
downloader *HTTPDownloader
328225

329-
roundTripper http.RoundTripper
330-
noProxy bool
226+
roundTripper http.RoundTripper
227+
noProxy bool
228+
insecureSkipVerify bool
229+
}
230+
231+
// GetSuggestedFilename returns the suggested filename
232+
func (c *ContinueDownloader) GetSuggestedFilename() string {
233+
return c.downloader.GetSuggestedFilename()
331234
}
332235

333236
// WithRoundTripper set WithRoundTripper
@@ -342,14 +245,21 @@ func (c *ContinueDownloader) WithoutProxy(noProxy bool) *ContinueDownloader {
342245
return c
343246
}
344247

248+
// WithInsecureSkipVerify set if skip the insecure verify
249+
func (c *ContinueDownloader) WithInsecureSkipVerify(insecureSkipVerify bool) *ContinueDownloader {
250+
c.insecureSkipVerify = insecureSkipVerify
251+
return c
252+
}
253+
345254
// DownloadWithContinue downloads the files continuously
346255
func (c *ContinueDownloader) DownloadWithContinue(targetURL, output string, index, continueAt, end int64, showProgress bool) (err error) {
347256
c.downloader = &HTTPDownloader{
348-
TargetFilePath: output,
349-
URL: targetURL,
350-
ShowProgress: showProgress,
351-
NoProxy: c.noProxy,
352-
RoundTripper: c.roundTripper,
257+
TargetFilePath: output,
258+
URL: targetURL,
259+
ShowProgress: showProgress,
260+
NoProxy: c.noProxy,
261+
RoundTripper: c.roundTripper,
262+
InsecureSkipVerify: c.insecureSkipVerify,
353263
}
354264
if index >= 0 {
355265
c.downloader.Title = fmt.Sprintf("Downloading part %d", index)
@@ -371,21 +281,16 @@ func (c *ContinueDownloader) DownloadWithContinue(targetURL, output string, inde
371281
return
372282
}
373283

374-
// DetectSize returns the size of target resource
375-
//
376-
// Deprecated, use DetectSizeWithRoundTripper instead
377-
func DetectSize(targetURL, output string, showProgress bool) (int64, bool, error) {
378-
return DetectSizeWithRoundTripper(targetURL, output, showProgress, false, nil)
379-
}
380-
381284
// DetectSizeWithRoundTripper returns the size of target resource
382-
func DetectSizeWithRoundTripper(targetURL, output string, showProgress bool, noProxy bool, roundTripper http.RoundTripper) (total int64, rangeSupport bool, err error) {
285+
func DetectSizeWithRoundTripper(targetURL, output string, showProgress, noProxy, insecureSkipVerify bool,
286+
roundTripper http.RoundTripper) (total int64, rangeSupport bool, err error) {
383287
downloader := HTTPDownloader{
384-
TargetFilePath: output,
385-
URL: targetURL,
386-
ShowProgress: showProgress,
387-
RoundTripper: roundTripper,
388-
NoProxy: false, // below HTTP request does not need proxy
288+
TargetFilePath: output,
289+
URL: targetURL,
290+
ShowProgress: showProgress,
291+
RoundTripper: roundTripper,
292+
NoProxy: false, // below HTTP request does not need proxy
293+
InsecureSkipVerify: insecureSkipVerify,
389294
}
390295

391296
var detectOffset int64
@@ -400,6 +305,8 @@ func DetectSizeWithRoundTripper(targetURL, output string, showProgress bool, noP
400305
contentLen := resp.Header.Get("Content-Length")
401306
if total, lenErr = strconv.ParseInt(contentLen, 10, 0); lenErr == nil {
402307
total += detectOffset
308+
} else {
309+
rangeSupport = false
403310
}
404311
// always return false because we just want to get the header from response
405312
return false

0 commit comments

Comments
 (0)