github.com/xxf098/lite-proxy@v0.15.1-0.20230422081941-12c69f323218/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/xxf098/lite-proxy/common/pool" 15 "github.com/xxf098/lite-proxy/outbound" 16 "github.com/xxf098/lite-proxy/proxy" 17 "github.com/xxf098/lite-proxy/stats" 18 "github.com/xxf098/lite-proxy/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 }