github.com/laof/lite-speed-test@v0.0.0-20230930011949-1f39b7037845/download/download.go (about)

     1  package download
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"net"
     8  	"net/http"
     9  	"strings"
    10  	"time"
    11  
    12  	"errors"
    13  
    14  	"github.com/laof/lite-speed-test/common/pool"
    15  	"github.com/laof/lite-speed-test/outbound"
    16  	"github.com/laof/lite-speed-test/proxy"
    17  	"github.com/laof/lite-speed-test/stats"
    18  	"github.com/laof/lite-speed-test/utils"
    19  )
    20  
    21  const (
    22  	downloadLink      = "https://download.microsoft.com/download/2/0/E/20E90413-712F-438C-988E-FDAA79A8AC3D/dotnetfx35.exe"
    23  	cloudflareLink100 = "https://speed.cloudflare.com/__down?bytes=100000000"
    24  	cachefly10        = "http://cachefly.cachefly.net/10mb.test"
    25  	cachefly100       = "http://cachefly.cachefly.net/100mb.test"
    26  )
    27  
    28  type DownloadOption struct {
    29  	URL              string
    30  	DownloadTimeout  time.Duration
    31  	HandshakeTimeout time.Duration
    32  	Ranges           []Range
    33  }
    34  
    35  type Discard struct {
    36  	total stats.Counter
    37  }
    38  
    39  func (e *Discard) Write(p []byte) (n int, err error) {
    40  	n = len(p)
    41  	pool.Put(p)
    42  	e.total.Add(int64(n))
    43  	// fmt.Printf("==%s\n", ByteCountIEC(int64(n)))
    44  	return n, nil
    45  }
    46  
    47  func (e *Discard) Size() int64 {
    48  	return e.total.Set(0)
    49  }
    50  
    51  func ByteCountIEC(b int64) string {
    52  	const unit = 1024
    53  	if b < unit {
    54  		return fmt.Sprintf("%d B/s", b)
    55  	}
    56  	div, exp := int64(unit), 0
    57  	for n := b / unit; n >= unit; n /= unit {
    58  		div *= unit
    59  		exp++
    60  	}
    61  	return fmt.Sprintf("%.1f%cB/s",
    62  		float64(b)/float64(div), "KMGTPE"[exp])
    63  }
    64  
    65  func ByteCountIECTrim(b int64) string {
    66  	result := ByteCountIEC(b)
    67  	return strings.TrimSuffix(result, "/s")
    68  }
    69  
    70  func createClient(ctx context.Context, link string) (*proxy.Client, error) {
    71  	var d outbound.Dialer
    72  	matches, err := utils.CheckLink(link)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	creator, err := outbound.GetDialerCreator(matches[1])
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	d, err = creator(link)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	if d != nil {
    85  		return proxy.NewClient(ctx, d), nil
    86  	}
    87  
    88  	return nil, errors.New("not supported link")
    89  }
    90  
    91  func Download(link string, timeout time.Duration, handshakeTimeout time.Duration, resultChan chan<- int64, startChan chan<- time.Time) (int64, error) {
    92  	ctx := context.Background()
    93  	client, err := createClient(ctx, link)
    94  	if err != nil {
    95  		return 0, err
    96  	}
    97  	option := DownloadOption{
    98  		DownloadTimeout:  timeout,
    99  		HandshakeTimeout: handshakeTimeout,
   100  		URL:              downloadLink,
   101  	}
   102  	return downloadInternal(ctx, option, resultChan, startChan, client.Dial)
   103  }
   104  
   105  func downloadInternal(ctx context.Context, option DownloadOption, resultChan chan<- int64, startOuterChan chan<- time.Time, dial func(network, addr string) (net.Conn, error)) (int64, error) {
   106  	var max int64 = 0
   107  	httpTransport := &http.Transport{}
   108  	httpClient := &http.Client{Transport: httpTransport, Timeout: option.HandshakeTimeout}
   109  	if dial != nil {
   110  		httpTransport.Dial = dial
   111  	}
   112  	req, err := http.NewRequest("GET", option.URL, nil)
   113  	if err != nil {
   114  		return max, err
   115  	}
   116  	response, err := httpClient.Do(req)
   117  	if err != nil {
   118  		return max, err
   119  	}
   120  	defer response.Body.Close()
   121  	prev := time.Now()
   122  	if startOuterChan != nil {
   123  		startOuterChan <- prev
   124  	}
   125  	var total int64
   126  	buf := pool.Get(20 * 1024)
   127  	defer pool.Put(buf)
   128  	for {
   129  		// buf := pool.Get(20 * 1024)
   130  		nr, er := response.Body.Read(buf)
   131  		total += int64(nr)
   132  		// pool.Put(buf)
   133  		now := time.Now()
   134  		if now.Sub(prev) >= time.Second || er != nil {
   135  			prev = now
   136  			if resultChan != nil {
   137  				resultChan <- total
   138  			}
   139  			if max < total {
   140  				max = total
   141  			}
   142  			total = 0
   143  		}
   144  		if er != nil {
   145  			if er != io.EOF {
   146  				err = er
   147  			}
   148  			break
   149  		}
   150  	}
   151  	return max, nil
   152  }
   153  
   154  func DownloadComplete(link string, timeout time.Duration, handshakeTimeout time.Duration) (int64, error) {
   155  	ctx := context.Background()
   156  	client, err := createClient(ctx, link)
   157  	if err != nil {
   158  		return 0, err
   159  	}
   160  	return downloadCompleteInternal(ctx, cachefly100, timeout, handshakeTimeout, client.Dial)
   161  }
   162  
   163  func downloadCompleteInternal(ctx context.Context, url string, timeout time.Duration, handshakeTimeout time.Duration, dial func(network, addr string) (net.Conn, error)) (int64, error) {
   164  	var max int64 = 0
   165  	httpTransport := &http.Transport{}
   166  	httpClient := &http.Client{Transport: httpTransport, Timeout: handshakeTimeout}
   167  	if dial != nil {
   168  		httpTransport.Dial = dial
   169  	}
   170  	req, err := http.NewRequest("GET", url, nil)
   171  	if err != nil {
   172  		return max, err
   173  	}
   174  	response, err := httpClient.Do(req)
   175  	if err != nil {
   176  		return max, err
   177  	}
   178  	defer response.Body.Close()
   179  	ctx, cancel := context.WithTimeout(ctx, timeout)
   180  	defer cancel()
   181  	start := time.Now()
   182  	var total int64
   183  	buf := pool.Get(20 * 1024)
   184  	defer pool.Put(buf)
   185  	for ctx.Err() == nil {
   186  		nr, er := response.Body.Read(buf)
   187  		total += int64(nr)
   188  		if er != nil {
   189  			if er != io.EOF {
   190  				err = er
   191  			}
   192  			break
   193  		}
   194  	}
   195  
   196  	now := time.Now()
   197  	max = total * 1000 / now.Sub(start).Milliseconds()
   198  	return max, nil
   199  }
   200  
   201  func WSDownload(link string, timeout time.Duration, handshakeTimeout time.Duration, resultChan chan<- int64) (int64, error) {
   202  	return 0, nil
   203  }