github.com/metacubex/mihomo@v1.18.5/component/updater/utils.go (about) 1 package updater 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 "time" 10 11 mihomoHttp "github.com/metacubex/mihomo/component/http" 12 C "github.com/metacubex/mihomo/constant" 13 14 "golang.org/x/exp/constraints" 15 ) 16 17 func downloadForBytes(url string) ([]byte, error) { 18 ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) 19 defer cancel() 20 resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) 21 if err != nil { 22 return nil, err 23 } 24 defer resp.Body.Close() 25 26 return io.ReadAll(resp.Body) 27 } 28 29 func saveFile(bytes []byte, path string) error { 30 return os.WriteFile(path, bytes, 0o644) 31 } 32 33 // LimitReachedError records the limit and the operation that caused it. 34 type LimitReachedError struct { 35 Limit int64 36 } 37 38 // Error implements the [error] interface for *LimitReachedError. 39 // 40 // TODO(a.garipov): Think about error string format. 41 func (lre *LimitReachedError) Error() string { 42 return fmt.Sprintf("attempted to read more than %d bytes", lre.Limit) 43 } 44 45 // limitedReader is a wrapper for [io.Reader] limiting the input and dealing 46 // with errors package. 47 type limitedReader struct { 48 r io.Reader 49 limit int64 50 n int64 51 } 52 53 // Read implements the [io.Reader] interface. 54 func (lr *limitedReader) Read(p []byte) (n int, err error) { 55 if lr.n == 0 { 56 return 0, &LimitReachedError{ 57 Limit: lr.limit, 58 } 59 } 60 61 p = p[:Min(lr.n, int64(len(p)))] 62 63 n, err = lr.r.Read(p) 64 lr.n -= int64(n) 65 66 return n, err 67 } 68 69 // LimitReader wraps Reader to make it's Reader stop with ErrLimitReached after 70 // n bytes read. 71 func LimitReader(r io.Reader, n int64) (limited io.Reader, err error) { 72 if n < 0 { 73 return nil, &updateError{Message: "limit must be non-negative"} 74 } 75 76 return &limitedReader{ 77 r: r, 78 limit: n, 79 n: n, 80 }, nil 81 } 82 83 // Min returns the smaller of x or y. 84 func Min[T constraints.Integer | ~string](x, y T) (res T) { 85 if x < y { 86 return x 87 } 88 89 return y 90 }