fastmirror/mirror.go

122 lines
3.1 KiB
Go

package fastmirror
import (
"fmt"
"io"
"log"
"net/http"
"strings"
"time"
"github.com/schollz/progressbar/v3"
)
func findFastestMirror(suites []string) (string, time.Duration, []time.Duration, error) {
log.Println("Fetching mirrors list")
resp, err := http.Get("http://mirrors.ubuntu.com/mirrors.txt")
if err != nil {
return "", 0, nil, fmt.Errorf("failed to fetch mirrors list: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", 0, nil, fmt.Errorf("failed to read mirrors list: %v", err)
}
mirrors := strings.Split(string(body), "\n")
log.Printf("Found %d mirrors", len(mirrors))
var fastestMirrorURL string
var fastestTime time.Duration
var latencies []time.Duration
downCount := 0
noIndexCount := 0
codename, err := getUbuntuCodename()
if err != nil {
return "", 0, nil, err
}
httpClient := http.Client{
Timeout: 1 * time.Second,
}
log.Println("Testing mirrors for latency")
bar := progressbar.Default(int64(len(mirrors)))
for _, mirror := range mirrors {
if err := bar.Add(1); err != nil {
log.Printf("Error updating progress bar: %v", err)
}
if strings.HasPrefix(mirror, "https://") {
mirror = strings.TrimSuffix(mirror, "/")
startTime := time.Now()
isValid := isValidMirror(httpClient, mirror, codename, suites)
elapsedTime := time.Since(startTime)
if isValid {
latencies = append(latencies, elapsedTime)
if fastestMirrorURL == "" || elapsedTime < fastestTime {
fastestMirrorURL = mirror
fastestTime = elapsedTime
}
} else {
noIndexCount++
}
} else {
downCount++
}
}
if fastestMirrorURL == "" {
return "", 0, nil, fmt.Errorf("no suitable HTTPS mirror found")
}
displayLatencyStatistics(latencies, fastestMirrorURL, fastestTime)
log.Printf("Number of mirrors that were down: %d", downCount)
log.Printf("Number of mirrors without required package index: %d", noIndexCount)
return fastestMirrorURL, fastestTime, latencies, nil
}
func isValidMirror(httpClient http.Client, mirrorURL, codename string, suites []string) bool {
for _, suite := range suites {
uri := fmt.Sprintf("%s/dists/%s/Release", mirrorURL, suite)
resp, err := httpClient.Get(uri)
if err != nil || resp.StatusCode != http.StatusOK {
return false
}
resp.Body.Close()
}
return true
}
func displayLatencyStatistics(latencies []time.Duration, fastestMirrorURL string, fastestTime time.Duration) {
if len(latencies) == 0 {
log.Println("No latencies recorded")
return
}
var totalLatency time.Duration
var minLatency time.Duration = latencies[0]
var maxLatency time.Duration = latencies[0]
for _, latency := range latencies {
totalLatency += latency
if latency < minLatency {
minLatency = latency
}
if latency > maxLatency {
maxLatency = latency
}
}
averageLatency := totalLatency / time.Duration(len(latencies))
log.Printf("Minimum latency: %s", minLatency)
log.Printf("Maximum latency: %s", maxLatency)
log.Printf("Average latency: %s", averageLatency)
log.Printf("Chosen fastest mirror: %s with latency: %s", fastestMirrorURL, fastestTime)
}