| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- #!/usr/bin/env node
- 'use strict'
- /**
- * 测试通过 HTTP 代理访问 u.xxoo365.top(HTTPS)。
- *
- * 用法(项目根目录执行):
- * node scripts/proxy-u-xxoo365-test.js direct
- * node scripts/proxy-u-xxoo365-test.js proxy 103.217.191.15:30104
- * node scripts/proxy-u-xxoo365-test.js qg-fetch
- *
- * 可选环境变量:
- * TEST_URL=https://u.xxoo365.top/
- * TEST_TIMEOUT_MS=20000
- * TEST_METHOD=GET # GET / HEAD
- * TEST_PATH=/health # 会拼到 TEST_URL 的 origin 后
- * QG_AREA=... QG_AREA_EX=... QG_ISP=1|2|3 QG_DISTINCT=0|1
- */
- const fs = require('fs')
- const path = require('path')
- const axios = require('axios')
- const HttpsProxyAgent = require('https-proxy-agent')
- const QG_GET_URL = 'https://share.proxy.qg.net/get'
- function readConfig() {
- const cfgPath = path.join(__dirname, '..', 'config.json')
- return JSON.parse(fs.readFileSync(cfgPath, 'utf8'))
- }
- function buildTargetUrl() {
- const base = (process.env.TEST_URL || 'https://u.xxoo365.top/').trim()
- const pathOverride = (process.env.TEST_PATH || '').trim()
- if (!pathOverride) return base
- const u = new URL(base)
- u.pathname = pathOverride.startsWith('/') ? pathOverride : `/${pathOverride}`
- return u.toString()
- }
- function normalizeMethod() {
- const m = (process.env.TEST_METHOD || 'GET').toUpperCase()
- return m === 'HEAD' ? 'HEAD' : 'GET'
- }
- function toProxyUrl(server, user, pass) {
- const [host, portRaw] = String(server || '').trim().split(':')
- const port = Number(portRaw)
- if (!host || !Number.isFinite(port)) {
- throw new Error(`代理地址非法: ${server}`)
- }
- let auth = ''
- if (user) {
- auth = `${encodeURIComponent(user)}:${encodeURIComponent(pass || '')}@`
- }
- return `http://${auth}${host}:${port}`
- }
- async function fetchProxyFromQg(cfg) {
- const q = cfg.qgChannelProxy || {}
- const key = String(q.extractKey || '').trim()
- if (!key) {
- throw new Error('config.json 缺少 qgChannelProxy.extractKey')
- }
- const params = { key, num: 1 }
- const area = String(process.env.QG_AREA || '').trim()
- const areaEx = String(process.env.QG_AREA_EX || '').trim()
- if (area) params.area = area
- if (areaEx) params.area_ex = areaEx
- if (['1', '2', '3'].includes(String(process.env.QG_ISP || ''))) {
- params.isp = Number(process.env.QG_ISP)
- }
- params.distinct = String(process.env.QG_DISTINCT || '1') !== '0'
- const res = await axios.get(QG_GET_URL, {
- params,
- timeout: 20000,
- proxy: false,
- validateStatus: () => true
- })
- const body = res.data || {}
- if (body.code !== 'SUCCESS' || !Array.isArray(body.data) || !body.data[0]?.server) {
- throw new Error(`青果 /get 失败: ${body.code || JSON.stringify(body).slice(0, 180)}`)
- }
- return {
- server: body.data[0].server,
- proxy_ip: body.data[0].proxy_ip || null,
- deadline: body.data[0].deadline || null,
- request_id: body.request_id || null
- }
- }
- async function probe(label, requestConfig) {
- const timeout = Number(process.env.TEST_TIMEOUT_MS || 20000)
- const url = buildTargetUrl()
- const method = normalizeMethod()
- const t0 = Date.now()
- console.log(`\n=== ${label} ===`)
- console.log('REQUEST:', method, url)
- try {
- const res = await axios({
- url,
- method,
- timeout,
- validateStatus: () => true,
- ...requestConfig
- })
- const ms = Date.now() - t0
- console.log('RESULT: OK', `${ms}ms`, 'HTTP', res.status)
- if (method !== 'HEAD') {
- const body = typeof res.data === 'string' ? res.data : JSON.stringify(res.data)
- console.log('BODY_PREVIEW:', body.slice(0, 200).replace(/\s+/g, ' '))
- }
- } catch (e) {
- const ms = Date.now() - t0
- console.log('RESULT: FAIL', `${ms}ms`)
- console.log('message:', e.message)
- console.log('code:', e.code)
- if (e.response) console.log('http_status:', e.response.status)
- }
- }
- async function main() {
- const mode = (process.argv[2] || 'direct').toLowerCase()
- const cfg = readConfig()
- const q = cfg.qgChannelProxy || {}
- const user = String(process.env.QG_PROXY_USER || q.authUser || '').trim()
- const pass = String(process.env.QG_PROXY_PASSWORD || q.authPassword || '').trim()
- const rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0'
- if (mode === 'direct') {
- await probe('直连', { proxy: false })
- return
- }
- let server
- if (mode === 'proxy') {
- server = process.argv[3]
- if (!server) throw new Error('proxy 模式需传 host:port')
- } else if (mode === 'qg-fetch') {
- const got = await fetchProxyFromQg(cfg)
- server = got.server
- console.log(
- `[Qg] server=${got.server} proxy_ip=${got.proxy_ip || '—'} deadline=${got.deadline || '—'} request_id=${got.request_id || '—'}`
- )
- } else {
- throw new Error('模式仅支持: direct | proxy <host:port> | qg-fetch')
- }
- const proxyUrl = toProxyUrl(server, user, pass)
- const httpsAgent = new HttpsProxyAgent(proxyUrl, { rejectUnauthorized })
- await probe(`代理 ${server}`, { proxy: false, httpsAgent })
- }
- main().catch(err => {
- console.error(err.stack || err)
- process.exit(1)
- })
|