Merge pull request #945 from apernet/fix-geo-dl

fix: fail to load GeoIP or GeoSite if previous download was interrupted by network error
This commit is contained in:
Toby 2024-02-21 18:14:15 -08:00 committed by GitHub
commit ea66299d0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,6 +1,7 @@
package utils package utils
import ( import (
"fmt"
"io" "io"
"net/http" "net/http"
"os" "os"
@ -15,6 +16,7 @@ const (
geoipURL = "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat" geoipURL = "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat"
geositeFilename = "geosite.dat" geositeFilename = "geosite.dat"
geositeURL = "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat" geositeURL = "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat"
geoDlTmpPattern = ".hysteria-geoloader.dlpart.*"
geoDefaultUpdateInterval = 7 * 24 * time.Hour // 7 days geoDefaultUpdateInterval = 7 * 24 * time.Hour // 7 days
) )
@ -41,6 +43,10 @@ func (l *GeoLoader) shouldDownload(filename string) bool {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return true return true
} }
if info.Size() == 0 {
// empty files are loadable by v2geo, but we consider it broken
return true
}
dt := time.Now().Sub(info.ModTime()) dt := time.Now().Sub(info.ModTime())
if l.UpdateInterval == 0 { if l.UpdateInterval == 0 {
return dt > geoDefaultUpdateInterval return dt > geoDefaultUpdateInterval
@ -49,7 +55,7 @@ func (l *GeoLoader) shouldDownload(filename string) bool {
} }
} }
func (l *GeoLoader) download(filename, url string) error { func (l *GeoLoader) downloadAndCheck(filename, url string, checkFunc func(filename string) error) error {
l.DownloadFunc(filename, url) l.DownloadFunc(filename, url)
resp, err := http.Get(url) resp, err := http.Get(url)
@ -59,16 +65,34 @@ func (l *GeoLoader) download(filename, url string) error {
} }
defer resp.Body.Close() defer resp.Body.Close()
f, err := os.Create(filename) f, err := os.CreateTemp(".", geoDlTmpPattern)
if err != nil { if err != nil {
l.DownloadErrFunc(err) l.DownloadErrFunc(err)
return err return err
} }
defer f.Close() defer os.Remove(f.Name())
_, err = io.Copy(f, resp.Body) _, err = io.Copy(f, resp.Body)
l.DownloadErrFunc(err) if err != nil {
return err f.Close()
l.DownloadErrFunc(err)
return err
}
f.Close()
err = checkFunc(f.Name())
if err != nil {
l.DownloadErrFunc(fmt.Errorf("integrity check failed: %w", err))
return err
}
err = os.Rename(f.Name(), filename)
if err != nil {
l.DownloadErrFunc(fmt.Errorf("rename failed: %w", err))
return err
}
return nil
} }
func (l *GeoLoader) LoadGeoIP() (map[string]*v2geo.GeoIP, error) { func (l *GeoLoader) LoadGeoIP() (map[string]*v2geo.GeoIP, error) {
@ -81,10 +105,24 @@ func (l *GeoLoader) LoadGeoIP() (map[string]*v2geo.GeoIP, error) {
autoDL = true autoDL = true
filename = geoipFilename filename = geoipFilename
} }
if autoDL && l.shouldDownload(filename) { if autoDL {
err := l.download(filename, geoipURL) if !l.shouldDownload(filename) {
m, err := v2geo.LoadGeoIP(filename)
if err == nil {
l.geoipMap = m
return m, nil
}
// file is broken, download it again
}
err := l.downloadAndCheck(filename, geoipURL, func(filename string) error {
_, err := v2geo.LoadGeoIP(filename)
return err
})
if err != nil { if err != nil {
return nil, err // as long as the previous download exists, fallback to it
if _, serr := os.Stat(filename); os.IsNotExist(serr) {
return nil, err
}
} }
} }
m, err := v2geo.LoadGeoIP(filename) m, err := v2geo.LoadGeoIP(filename)
@ -105,10 +143,24 @@ func (l *GeoLoader) LoadGeoSite() (map[string]*v2geo.GeoSite, error) {
autoDL = true autoDL = true
filename = geositeFilename filename = geositeFilename
} }
if autoDL && l.shouldDownload(filename) { if autoDL {
err := l.download(filename, geositeURL) if !l.shouldDownload(filename) {
m, err := v2geo.LoadGeoSite(filename)
if err == nil {
l.geositeMap = m
return m, nil
}
// file is broken, download it again
}
err := l.downloadAndCheck(filename, geositeURL, func(filename string) error {
_, err := v2geo.LoadGeoSite(filename)
return err
})
if err != nil { if err != nil {
return nil, err // as long as the previous download exists, fallback to it
if _, serr := os.Stat(filename); os.IsNotExist(serr) {
return nil, err
}
} }
} }
m, err := v2geo.LoadGeoSite(filename) m, err := v2geo.LoadGeoSite(filename)