2021-11-10 17:12:12 +08:00
package task
import (
"context"
"fmt"
"io"
"net"
"net/http"
"sort"
2023-01-31 20:11:13 +08:00
"strconv"
2021-11-10 17:12:12 +08:00
"time"
2023-03-27 11:49:59 +08:00
"github.com/XIU2/CloudflareSpeedTest/utils"
2021-11-10 17:12:12 +08:00
"github.com/VividCortex/ewma"
)
const (
bufferSize = 1024
2022-02-09 20:58:59 +08:00
defaultURL = "https://cf.xiu2.xyz/url"
2021-11-10 17:12:12 +08:00
defaultTimeout = 10 * time . Second
2021-11-10 23:58:40 +08:00
defaultDisableDownload = false
defaultTestNum = 10
2021-11-10 17:12:12 +08:00
defaultMinSpeed float64 = 0.0
)
var (
2023-01-31 20:11:13 +08:00
URL = defaultURL
2021-11-10 17:12:12 +08:00
Timeout = defaultTimeout
2021-11-10 23:58:40 +08:00
Disable = defaultDisableDownload
2021-11-10 17:12:12 +08:00
TestCount = defaultTestNum
MinSpeed = defaultMinSpeed
)
func checkDownloadDefault ( ) {
if URL == "" {
URL = defaultURL
}
if Timeout <= 0 {
Timeout = defaultTimeout
}
if TestCount <= 0 {
TestCount = defaultTestNum
}
if MinSpeed <= 0.0 {
MinSpeed = defaultMinSpeed
}
}
2021-11-13 22:57:18 +08:00
func TestDownloadSpeed ( ipSet utils . PingDelaySet ) ( speedSet utils . DownloadSpeedSet ) {
2021-11-10 17:12:12 +08:00
checkDownloadDefault ( )
if Disable {
return utils . DownloadSpeedSet ( ipSet )
}
if len ( ipSet ) <= 0 { // IP数组长度(IP数量) 大于 0 时才会继续下载测速
fmt . Println ( "\n[信息] 延迟测速结果 IP 数量为 0, 跳过下载测速。" )
return
}
testNum := TestCount
if len ( ipSet ) < TestCount || MinSpeed > 0 { // 如果IP数组长度(IP数量) 小于下载测速数量(-dn) , 则次数修正为IP数
testNum = len ( ipSet )
}
2021-11-27 23:21:02 +08:00
if testNum < TestCount {
TestCount = testNum
}
2021-11-10 17:12:12 +08:00
2023-11-25 15:18:18 +08:00
fmt . Printf ( "开始下载测速(下限:%.2f MB/s, 数量:%d, 队列:%d) \n" , MinSpeed , TestCount , testNum )
2023-01-31 20:11:13 +08:00
// 控制 下载测速进度条 与 延迟测速进度条 长度一致(强迫症)
bar_a := len ( strconv . Itoa ( len ( ipSet ) ) )
bar_b := " "
for i := 0 ; i < bar_a ; i ++ {
bar_b += " "
}
bar := utils . NewBar ( TestCount , bar_b , "" )
2021-11-10 17:12:12 +08:00
for i := 0 ; i < testNum ; i ++ {
2021-11-12 17:58:37 +08:00
speed := downloadHandler ( ipSet [ i ] . IP )
2021-11-10 17:12:12 +08:00
ipSet [ i ] . DownloadSpeed = speed
// 在每个 IP 下载测速后,以 [下载速度下限] 条件过滤结果
if speed >= MinSpeed * 1024 * 1024 {
2023-01-31 12:48:28 +08:00
bar . Grow ( 1 , "" )
2021-11-13 22:57:18 +08:00
speedSet = append ( speedSet , ipSet [ i ] ) // 高于下载速度下限时,添加到新数组中
if len ( speedSet ) == TestCount { // 凑够满足条件的 IP 时(下载测速数量 -dn) , 就跳出循环
2021-11-10 17:12:12 +08:00
break
}
}
}
bar . Done ( )
2021-11-13 22:57:18 +08:00
if len ( speedSet ) == 0 { // 没有符合速度限制的数据,返回所有测试数据
speedSet = utils . DownloadSpeedSet ( ipSet )
}
2021-11-10 17:12:12 +08:00
// 按速度排序
2021-11-13 22:57:18 +08:00
sort . Sort ( speedSet )
2021-11-10 17:12:12 +08:00
return
}
func getDialContext ( ip * net . IPAddr ) func ( ctx context . Context , network , address string ) ( net . Conn , error ) {
2022-11-08 15:47:19 +08:00
var fakeSourceAddr string
if isIPv4 ( ip . String ( ) ) {
fakeSourceAddr = fmt . Sprintf ( "%s:%d" , ip . String ( ) , TCPPort )
} else {
fakeSourceAddr = fmt . Sprintf ( "[%s]:%d" , ip . String ( ) , TCPPort )
2021-11-10 17:12:12 +08:00
}
return func ( ctx context . Context , network , address string ) ( net . Conn , error ) {
return ( & net . Dialer { } ) . DialContext ( ctx , network , fakeSourceAddr )
}
}
2021-11-12 17:58:37 +08:00
// return download Speed
func downloadHandler ( ip * net . IPAddr ) float64 {
2021-11-10 17:12:12 +08:00
client := & http . Client {
Transport : & http . Transport { DialContext : getDialContext ( ip ) } ,
Timeout : Timeout ,
2022-02-09 20:58:59 +08:00
CheckRedirect : func ( req * http . Request , via [ ] * http . Request ) error {
if len ( via ) > 10 { // 限制最多重定向 10 次
return http . ErrUseLastResponse
}
if req . Header . Get ( "Referer" ) == defaultURL { // 当使用默认下载测速地址时,重定向不携带 Referer
req . Header . Del ( "Referer" )
}
return nil
} ,
}
req , err := http . NewRequest ( "GET" , URL , nil )
if err != nil {
return 0.0
2021-11-10 17:12:12 +08:00
}
2022-02-09 20:58:59 +08:00
req . Header . Set ( "User-Agent" , "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36" )
response , err := client . Do ( req )
2021-11-10 17:12:12 +08:00
if err != nil {
return 0.0
}
defer response . Body . Close ( )
if response . StatusCode != 200 {
return 0.0
}
2022-02-09 20:58:59 +08:00
timeStart := time . Now ( ) // 开始时间(当前)
timeEnd := timeStart . Add ( Timeout ) // 加上下载测速时间得到的结束时间
2021-11-10 17:12:12 +08:00
2022-02-09 20:58:59 +08:00
contentLength := response . ContentLength // 文件大小
2021-11-10 17:12:12 +08:00
buffer := make ( [ ] byte , bufferSize )
var (
contentRead int64 = 0
timeSlice = Timeout / 100
timeCounter = 1
lastContentRead int64 = 0
)
var nextTime = timeStart . Add ( timeSlice * time . Duration ( timeCounter ) )
e := ewma . NewMovingAverage ( )
2022-02-09 20:58:59 +08:00
// 循环计算,如果文件下载完了(两者相等),则退出循环(终止测速)
2021-11-10 17:12:12 +08:00
for contentLength != contentRead {
currentTime := time . Now ( )
if currentTime . After ( nextTime ) {
timeCounter ++
nextTime = timeStart . Add ( timeSlice * time . Duration ( timeCounter ) )
e . Add ( float64 ( contentRead - lastContentRead ) )
lastContentRead = contentRead
}
2022-02-09 20:58:59 +08:00
// 如果超出下载测速时间,则退出循环(终止测速)
2021-11-10 17:12:12 +08:00
if currentTime . After ( timeEnd ) {
break
}
bufferRead , err := response . Body . Read ( buffer )
if err != nil {
2023-02-12 16:35:30 +08:00
if err != io . EOF { // 如果文件下载过程中遇到报错(如 Timeout) , 且并不是因为文件下载完了, 则退出循环( 终止测速)
break
2023-02-12 17:57:53 +08:00
} else if contentLength == - 1 { // 文件下载完成 且 文件大小未知, 则退出循环( 终止测速) , 例如: https://speed.cloudflare.com/__down?bytes=200000000 这样的,如果在 10 秒内就下载完成了,会导致测速结果明显偏低甚至显示为 0.00(下载速度太快时)
break
2023-02-12 16:35:30 +08:00
}
2023-02-11 12:40:06 +08:00
// 获取上个时间片
last_time_slice := timeStart . Add ( timeSlice * time . Duration ( timeCounter - 1 ) )
// 下载数据量 / (用当前时间 - 上个时间片/ 时间片)
e . Add ( float64 ( contentRead - lastContentRead ) / ( float64 ( currentTime . Sub ( last_time_slice ) ) / float64 ( timeSlice ) ) )
2021-11-10 17:12:12 +08:00
}
2021-11-12 17:58:37 +08:00
contentRead += int64 ( bufferRead )
2021-11-10 17:12:12 +08:00
}
return e . Value ( ) / ( Timeout . Seconds ( ) / 120 )
}