Skip to content

Commit

Permalink
支持websocket测试
Browse files Browse the repository at this point in the history
  • Loading branch information
jackrun123 committed Apr 29, 2024
1 parent 19ae34d commit a55bb46
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 47 deletions.
57 changes: 35 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,51 @@

Cloudflare IP 测速器是一个使用 Golang 编写的小工具,用于测试一些 Cloudflare 的 IP 地址的延迟和下载速度,并将结果输出到 CSV 文件中。

# 运行
默认测速地址不能正常访问,请使用仓库中的_worker.js在cloudflare的worker或者page上部署,支持websocket和下载测速,可以参考[这个视频](https://www.youtube.com/watch?v=S4AZkvgnmmA)自己搭建一个

准备一个ip.txt文件,内容格式为IP[,端口],其中端口可以省略,如果省略则使用命令行的默认端口

ip.txt例子
```
127.0.0.1,2053
127.0.0.1/24,2053
2400:cb00:2049:0:33f9:7045:cf64:7d93/120,2053
127.0.0.2
```

在终端中运行以下命令来启动程序:
```
# example.com 是指你自己部署在cf的域名,这里只是例子
./cfiptest -f=ip.txt -mins 5 -url example.com/50m -delay_url example.com
```
请替换参数值以符合您的实际需求。

# 环境变量

可选配置,如果配置了,优先以环境变量为准

| 环境变量 | 备注 |
|-------|----------|
| CFIPTEST_DELAY_TEST_URL | 指定延迟测试地址 |
| CFIPTEST_SPEED_TEST_URL | 指定速度测试地址 |

# 参数说明
可以使用 cfiptest -h 获取使用说明
```
cfiptest -h
使用方法:
例子:cfiptest -f ./ip.txt -url speed.cloudflare.com/__down?bytes=100000000
参数:
-delay_url string
延迟测试地址,要求是使用cloudflare的地址 (default "www.visa.com.hk/cdn-cgi/trace")
延迟测试地址,要求是使用cloudflare的地址,只用填域名 (default "www.visa.com.hk")
-dt int
并发请求最大协程数 (default 100)
-f string
IP地址文件名称,格式1.0.0.127,443 (default "ip.txt")
-h 帮助
-maxdc int
延迟测试,最多测试多少个IP (default 1000)
延迟测试,最多测试多少个IP,如果不限制则设置为0
-maxsc int
速度测试,最多测试多少个IP (default 10)
-mins float
Expand All @@ -36,32 +66,15 @@ cfiptest -h
-url string
测速文件地址 (default "speed.cloudflare.com/__down?bytes=100000000")
-v 打印程序版本
-vv
详细日志模式,打印出错信息
-w 是否验证websocket,如果要验证,delay_url需要支持websocket,客户端会请求xx.com/ws地址
cfiptest asn 用于根据asn获取ip段
例子:cfiptest asn -as 13335
-as string
ASN号码,例如13335
```

# 运行
默认测速地址可能不能正常访问,可以参考[这个视频](https://www.youtube.com/watch?v=S4AZkvgnmmA)自己搭建一个

准备一个ip.txt文件,内容格式为IP[,端口],其中端口可以省略,如果省略则使用命令行的默认端口

ip.txt例子
```
127.0.0.1,2053
127.0.0.1/24,2053
2400:cb00:2049:0:33f9:7045:cf64:7d93/120,2053
127.0.0.2
```

在终端中运行以下命令来启动程序:
```
./cfiptest -f=ip.txt -mins 5 -url speed.cloudflare.com/__down?bytes=100000000
```
请替换参数值以符合您的实际需求。

# 使用建议
可以先使用`masscan`扫描开放的端口,再使用这个工具二次扫描
Expand Down
99 changes: 99 additions & 0 deletions _worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* Welcome to Cloudflare Workers! This is your first worker.
*
* - Run "npm run dev" in your terminal to start a development server
* - Open a browser tab at http://localhost:8787/ to see your worker in action
* - Run "npm run deploy" to publish your worker
*
* Learn more at https://developers.cloudflare.com/workers/
*/

export default {
async fetch(request, env, ctx) {
// 创建一个新的 URL 对象
let url = new URL(request.url);
let path = url.pathname.substring(1);
let isSecure = url.protocol.startsWith("https");

if (path === "ws") {
return handleWSRequest(request);
} else if (path === "locations") {
let targetUrl = `http${isSecure ? 's' : ''}://speed.cloudflare.com/locations`;
let cfRequest = new Request(targetUrl, request);
let response = await fetch(cfRequest);
return response;
} else {
return handleSpeedTestRequest(request, path, isSecure);
}
}
};

async function handleSpeedTestRequest(request, path, isSecure) {
let bytes;
if (!path) {
// 路径为空,将 bytes 赋值为 100MB
bytes = 100000000;
} else {
// 其他路径,进行正常的处理
const regex = /^(\d+)([a-z]?)$/i;
const match = path.match(regex);
if (!match) {
// 路径格式不正确,返回错误
return new Response("路径格式不正确", {
status: 400,
});
}

const bytesStr = match[1];
const unit = match[2].toLowerCase();

// 转换单位
bytes = parseInt(bytesStr, 10);
if (unit === "k") {
bytes *= 1000;
} else if (unit === "m") {
bytes *= 1000000;
} else if (unit === "g") {
bytes *= 1000000000;
}
}

let targetUrl = `http${isSecure ? 's' : ''}://speed.cloudflare.com/__down?bytes=${bytes}`;
let cfRequest = new Request(targetUrl, request);
let response = await fetch(cfRequest);

// 将测试结果反馈给用户
return response;
}

async function handleWSRequest(request) {
const upgradeHeader = request.headers.get('Upgrade');
if (!upgradeHeader || upgradeHeader !== 'websocket') {
return new Response('Expected Upgrade: websocket', { status: 426 });
}

/** @type {import("@cloudflare/workers-types").WebSocket[]} */
// @ts-ignore
const webSocketPair = new WebSocketPair();
const [client, webSocket] = Object.values(webSocketPair);

handleSession(webSocket);

return new Response(null, {
status: 101,
// @ts-ignore
webSocket: client,
});
}

async function handleSession(websocket) {
websocket.accept()
websocket.addEventListener("message", async message => {
console.log(message)
})

websocket.addEventListener("close", async evt => {
// Handle when a client closes the WebSocket connection
console.log(evt)
})
}
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ module github.com/jackrun123/cfiptest

go 1.22.1

require github.com/anaskhan96/soup v1.2.5
require github.com/PuerkitoBio/goquery v1.9.1

require (
github.com/PuerkitoBio/goquery v1.9.1 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/text v0.14.0 // indirect
)
17 changes: 0 additions & 17 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,24 +1,13 @@
github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI=
github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY=
github.com/anaskhan96/soup v1.2.5 h1:V/FHiusdTrPrdF4iA1YkVxsOpdNcgvqT1hG+YtcZ5hM=
github.com/anaskhan96/soup v1.2.5/go.mod h1:6YnEp9A2yywlYdM4EgDz9NEHclocMepEtku7wg6Cq3s=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
Expand All @@ -39,19 +28,13 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3 changes: 2 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ func init() {
flag.IntVar(&st.SpeedTestTimeout, "sto", 5, "速度测试超时时间")
flag.IntVar(&st.SpeedTestThread, "st", 1, "下载测速协程数量,设为0禁用测速")
flag.StringVar(&st.SpeedTestURL, "url", "speed.cloudflare.com/__down?bytes=100000000", "测速文件地址")
flag.StringVar(&st.DelayTestURL, "delay_url", "www.visa.com.hk/cdn-cgi/trace", "延迟测试地址,要求是使用cloudflare的地址")
flag.StringVar(&st.DelayTestURL, "delay_url", "www.visa.com.hk", "延迟测试地址,要求是使用cloudflare的地址,只用填域名")
flag.IntVar(&st.MaxSpeedTestCount, "maxsc", 10, "速度测试,最多测试多少个IP")
flag.IntVar(&st.MaxDelayCount, "maxdc", 0, "延迟测试,最多测试多少个IP,如果不限制则设置为0")
flag.Float64Var(&st.MinSpeed, "mins", 1, "最低速度")
flag.BoolVar(&st.EnableTLS, "tls", true, "是否启用TLS")
flag.BoolVar(&st.Shuffle, "s", false, "是否打乱顺序测速")
flag.BoolVar(&st.TestWebSocket, "w", false, "是否验证websocket,如果要验证,delay_url需要支持websocket,客户端会请求xx.com/ws地址")
flag.BoolVar(&st.VerboseMode, "vv", false, "详细日志模式,打印出错信息")
flag.BoolVar(&printVersion, "v", false, "打印程序版本")
flag.BoolVar(&isShowHelp, "h", false, "帮助")
Expand Down
59 changes: 58 additions & 1 deletion pkgs/speed/delay.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func (st *CFSpeedTest) TestDelayOnce(ipPair IpPair, locationMap map[string]Locat
} else {
protocol = "http://"
}
requestURL := protocol + st.DelayTestURL
requestURL := fmt.Sprintf("%s%s/cdn-cgi/trace", protocol, st.DelayTestURL)

req, _ := http.NewRequest("GET", requestURL, nil)

Expand Down Expand Up @@ -127,6 +127,13 @@ func (st *CFSpeedTest) TestDelayOnce(ipPair IpPair, locationMap map[string]Locat

if strings.Contains(string(body), "uag=Mozilla/5.0") {
if matches := regexp.MustCompile(`colo=([A-Z]+)`).FindStringSubmatch(string(body)); len(matches) > 1 {
if st.TestWebSocket {
ok, err := st.TestWebSocketDelay(ipPair)
if !ok {
return nil, err
}
}

dataCenter := matches[1]
loc, ok := locationMap[dataCenter]
if ok {
Expand All @@ -140,3 +147,53 @@ func (st *CFSpeedTest) TestDelayOnce(ipPair IpPair, locationMap map[string]Locat
}
return nil, fmt.Errorf("not match")
}

func (st *CFSpeedTest) TestWebSocketDelay(ipPair IpPair) (bool, error) {
dialer := &net.Dialer{
Timeout: timeout,
KeepAlive: 0,
}
conn, err := dialer.Dial("tcp", net.JoinHostPort(ipPair.ip, strconv.Itoa(ipPair.port)))
if err != nil {
if st.VerboseMode {
fmt.Printf("connect failed, ip: %s err: %s\n", ipPair.String(), err)
}
return false, err
}
defer conn.Close()

client := http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 跳过证书验证
Dial: func(network, addr string) (net.Conn, error) {
return conn, nil
},
},
Timeout: timeout,
}

var protocol string
if st.EnableTLS {
protocol = "https://"
} else {
protocol = "http://"
}
requestURL := fmt.Sprintf("%s%s/ws", protocol, st.DelayTestURL)

req, _ := http.NewRequest("GET", requestURL, nil)

// 添加用户代理
req.Header.Set("Upgrade", "websocket")
req.Header.Set("Connection", "Upgrade")
req.Header.Set("Sec-WebSocket-Version", "13")
req.Header.Set("Sec-WebSocket-Key", "B5ReGbZ38Rrogrznmh1TFQ==")
req.Close = true
ctx, cancel := context.WithTimeout(context.Background(), maxDuration)
defer cancel()
resp, err := client.Do(req.WithContext(ctx))
result := false
if err == nil && resp != nil && resp.StatusCode == 101 {
result = true
}
return result, fmt.Errorf("websocket: %s", err)
}
5 changes: 3 additions & 2 deletions pkgs/speed/delay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ func TestCFSpeedTest_TestDelayOnce(t *testing.T) {
EnableTLS: true,
SpeedTestTimeout: 5,
MinSpeed: 4,
DelayTestURL: "www.visa.com.hk/cdn-cgi/trace",
DelayTestURL: "www.visa.com.hk",
TestWebSocket: true,
}

locationMap := st.GetLocationMap()
result, err := st.TestDelayOnce(IpPair{ip: "216.116.134.221", port: 2053}, locationMap)
result, err := st.TestDelayOnce(IpPair{ip: "8.212.26.41", port: 443}, locationMap)
fmt.Println(result, err)
}
2 changes: 1 addition & 1 deletion pkgs/speed/download_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "testing"

func TestGetDownloadSpeed(t *testing.T) {
st := &CFSpeedTest{
SpeedTestURL: "speed.fatkun.cloudns.ch/100m",
SpeedTestURL: "",
EnableTLS: true,
SpeedTestTimeout: 5,
MinSpeed: 4,
Expand Down
17 changes: 17 additions & 0 deletions pkgs/speed/speed.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type CFSpeedTest struct {
SpeedTestThread int
DelayTestURL string
SpeedTestURL string
TestWebSocket bool
MaxSpeedTestCount int
MaxDelayCount int
MinSpeed float64
Expand All @@ -68,7 +69,23 @@ type CFSpeedTest struct {
VerboseMode bool
}

func (st *CFSpeedTest) SetFromEnv() {
val, ok := os.LookupEnv("CFIPTEST_DELAY_TEST_URL")
if ok && val != "" {
st.DelayTestURL = val
fmt.Println("延迟测试地址从环境变量获取:", st.DelayTestURL)
}

val, ok = os.LookupEnv("CFIPTEST_SPEED_TEST_URL")
if ok && val != "" {
st.SpeedTestURL = val
fmt.Println("速度测试地址从环境变量获取:", st.SpeedTestURL)
}
}

func (st *CFSpeedTest) Run() {
st.SetFromEnv()

startTime := time.Now()
locationMap := st.GetLocationMap()
if locationMap == nil {
Expand Down

0 comments on commit a55bb46

Please sign in to comment.