├── .editorconfig ├── .gitignore ├── config.json5 ├── index.js ├── ip_list.txt ├── ip_list_bk.txt ├── libs ├── proxy-map.js └── proxy.js ├── package-lock.json ├── package.json ├── readme.md └── utils ├── chinazPing.js └── getGoodServer.js /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | end_of_line = lf 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | max_line_length = 100 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn.lock 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | 40 | # Typings directories 41 | typings 42 | 43 | # Build flies 44 | build 45 | .vscode 46 | options 47 | temp 48 | node_modules 49 | ffmpeg 50 | cache 51 | options.js 52 | yarn.lock 53 | # ide file 54 | .idea 55 | -------------------------------------------------------------------------------- /config.json5: -------------------------------------------------------------------------------- 1 | { 2 | /* B站海外CDN域名(除非你知道这是什么,否则请不要修改) */ 3 | host: 'upos-hz-mirrorakam.akamaized.net', 4 | /* 代理服务器运行的端口 */ 5 | port: 2689, 6 | /* 重新查找最优节点的间隔时间(s) */ 7 | refreshInterval: 3600, 8 | refreshIpList: { 9 | /* 重新查找新增节点的间隔时间(s) */ 10 | interval: 86400, 11 | /* 有关chinaz服务器节点的重试配置(除非你知道这是什么,否则请不要修改) */ 12 | retry: { 13 | times: 50, 14 | interval: 10000 15 | } 16 | }, 17 | /* 是否保存新增的节点 */ 18 | saveChinazResult: true, 19 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const proxy = require('./libs/proxy') 2 | const fs = require('fs') 3 | const chinazPing = require('./utils/chinazPing') 4 | const getGoodServer = require('./utils/getGoodServer') 5 | require('json5/lib/register') 6 | const config = require('./config.json5') 7 | 8 | const ipListText = fs.readFileSync('ip_list.txt', 'utf-8') 9 | let ipList = ipListText.split(/\r\n|\r|\n/) 10 | 11 | let best = {host: ipList[0], avg: Number.MAX_SAFE_INTEGER, originalHost: config.host} 12 | 13 | function refreshBest(ipList) { 14 | console.log('Pinging ipList') 15 | getGoodServer(ipList) 16 | .then(goodList => { 17 | if(goodList.length) { 18 | best.host = goodList[0].host 19 | best.avg = goodList[0].avg 20 | console.log(`The best server is ${best.host} which delay is ${best.avg}ms`) 21 | } else { 22 | console.log(`Could not find any available server`) 23 | } 24 | }) 25 | } 26 | 27 | function refreshIpList() { 28 | chinazPing(config.host, {times: config.refreshIpList.retry.time, interval: config.refreshIpList.retry.interval}) 29 | .then(res => { 30 | const sumIpList = Array.from( 31 | new Set( 32 | [...ipList, ...res] 33 | // remove ipv6 addresses and empty addresses 34 | .filter(item => !!item && !/\:/.test(item)) 35 | ) 36 | ) 37 | console.log(`available servers count: ${sumIpList.length}`) 38 | if(config.saveChinazResult) { 39 | fs.writeFile('./ip_list.txt', sumIpList.join('\n'), 'utf-8', (error) => { 40 | if(!error) { 41 | ipList = sumIpList 42 | console.log(`save chinaz results successfully`) 43 | } 44 | }) 45 | } 46 | refreshBest(sumIpList) 47 | }) 48 | .catch(err => { 49 | console.log('get chinaz results error:', err) 50 | refreshBest(ipList) 51 | }) 52 | } 53 | 54 | refreshIpList() 55 | setInterval(() => refreshBest(ipList), config.refreshInterval * 1000) 56 | setInterval(refreshIpList, config.refreshIpList.interval * 1000) 57 | 58 | proxy(best, config.port) 59 | -------------------------------------------------------------------------------- /ip_list.txt: -------------------------------------------------------------------------------- 1 | 202.183.253.8 2 | 195.175.116.41 3 | 202.183.253.9 4 | 95.101.128.104 5 | 23.222.29.240 6 | 23.63.74.42 7 | 23.34.61.139 8 | 95.101.128.96 9 | 182.176.156.18 10 | 23.15.179.193 11 | 23.15.179.138 12 | 200.136.36.162 13 | 210.55.204.218 14 | 109.105.109.32 15 | 23.222.29.208 16 | 203.106.94.24 17 | 23.48.39.26 18 | 24.156.130.185 19 | 146.88.61.8 20 | 146.88.61.9 21 | 182.176.156.82 22 | 110.93.233.11 23 | 23.215.131.184 24 | 23.209.183.16 25 | 182.176.156.104 26 | 104.96.221.168 27 | 63.243.242.232 28 | 23.206.194.35 29 | 193.140.13.73 30 | 193.140.13.81 31 | 203.13.161.9 32 | 67.69.196.139 33 | 23.32.3.81 34 | 204.237.143.16 35 | 63.243.242.218 36 | 124.124.252.19 37 | 104.80.88.81 38 | 24.156.130.186 39 | 172.232.0.146 40 | 2.16.106.49 41 | 23.219.93.27 42 | 95.101.1.99 43 | 210.55.204.208 44 | 124.155.223.104 45 | 23.215.131.194 46 | 59.167.22.34 47 | 163.28.5.8 48 | 188.43.72.40 49 | 2.16.106.104 50 | 200.136.36.163 51 | 23.63.74.18 52 | 23.34.61.152 53 | 110.93.233.17 54 | 195.175.116.18 55 | 67.69.196.154 56 | 188.43.72.18 57 | 109.105.109.24 58 | 95.101.0.107 59 | 110.164.253.145 60 | 23.209.183.11 61 | 23.219.93.33 62 | 59.167.22.81 63 | 172.232.0.160 64 | 203.106.94.11 65 | 204.237.143.88 66 | 23.32.3.91 67 | 104.80.88.120 68 | 163.28.5.25 69 | 104.96.221.185 70 | 203.13.161.10 71 | 182.176.156.56 72 | 110.164.253.152 73 | 124.155.223.118 74 | 23.206.194.33 75 | 124.124.252.24 76 | -------------------------------------------------------------------------------- /ip_list_bk.txt: -------------------------------------------------------------------------------- 1 | 202.183.253.8 2 | 195.175.116.41 3 | 202.183.253.9 4 | 95.101.128.104 5 | 23.222.29.240 6 | 23.63.74.42 7 | 23.34.61.139 8 | 95.101.128.96 9 | 182.176.156.18 10 | 23.15.179.193 11 | 23.15.179.138 12 | 200.136.36.162 13 | 210.55.204.218 14 | 109.105.109.32 15 | 23.222.29.208 16 | 203.106.94.24 17 | 23.48.39.26 18 | 24.156.130.185 19 | 146.88.61.8 20 | 146.88.61.9 21 | 182.176.156.82 22 | 110.93.233.11 23 | 23.215.131.184 24 | 23.209.183.16 25 | 182.176.156.104 26 | 104.96.221.168 27 | 63.243.242.232 28 | 23.206.194.35 29 | 193.140.13.73 30 | 193.140.13.81 31 | 203.13.161.9 32 | 67.69.196.139 33 | 23.32.3.81 34 | 204.237.143.16 35 | 63.243.242.218 36 | 124.124.252.19 37 | 104.80.88.81 38 | 24.156.130.186 39 | 172.232.0.146 40 | 2.16.106.49 41 | 23.219.93.27 42 | 95.101.1.99 43 | 210.55.204.208 44 | 124.155.223.104 45 | 23.215.131.194 46 | 59.167.22.34 47 | 163.28.5.8 48 | 188.43.72.40 49 | 2.16.106.104 50 | 200.136.36.163 51 | 23.63.74.18 52 | 23.34.61.152 53 | 110.93.233.17 54 | 195.175.116.18 55 | 67.69.196.154 56 | 188.43.72.18 57 | 109.105.109.24 58 | 95.101.0.107 59 | 110.164.253.145 60 | 23.209.183.11 61 | 23.219.93.33 62 | 59.167.22.81 63 | 172.232.0.160 64 | 203.106.94.11 65 | 204.237.143.88 66 | 23.32.3.91 67 | 104.80.88.120 68 | 163.28.5.25 69 | 104.96.221.185 70 | 203.13.161.10 71 | 182.176.156.56 72 | 110.164.253.152 73 | 124.155.223.118 74 | 23.206.194.33 75 | 124.124.252.24 76 | -------------------------------------------------------------------------------- /libs/proxy-map.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = (mapper, options) => { 4 | const { hostname, port } = options 5 | const result = options 6 | 7 | if(hostname == mapper.originalHost) { 8 | result.hostname = mapper.host 9 | } 10 | 11 | if(hostname !== result.hostname) { 12 | console.log(`proxy request: ${hostname}:${port} => ${result.hostname}:${result.port}`) 13 | } 14 | 15 | return result 16 | } -------------------------------------------------------------------------------- /libs/proxy.js: -------------------------------------------------------------------------------- 1 | var net = require('net'); 2 | var http = require('http'); 3 | var url = require('url'); 4 | var proxyMap = require('./proxy-map'); 5 | 6 | module.exports = (mapper, serverPort) => { 7 | var proxyServer = http.createServer(httpOptions); 8 | 9 | // handle http proxy requests 10 | function httpOptions(clientReq, clientRes) { 11 | var reqUrl = url.parse(clientReq.url); 12 | console.log('proxy for http request: ' + reqUrl.href); 13 | const { hostname, port } = proxyMap(reqUrl) 14 | 15 | var options = { 16 | hostname: hostname, 17 | port: port, 18 | path: reqUrl.path, 19 | method: clientReq.method, 20 | headers: clientReq.headers 21 | }; 22 | 23 | // create socket connection on behalf of client, then pipe the response to client response (pass it on) 24 | var serverConnection = http.request(options, function (res) { 25 | clientRes.writeHead(res.statusCode, res.headers) 26 | res.pipe(clientRes); 27 | }); 28 | 29 | clientReq.pipe(serverConnection); 30 | 31 | clientReq.on('error', (e) => { 32 | console.log('client socket error: ' + e); 33 | }); 34 | 35 | serverConnection.on('error', (e) => { 36 | console.log('server connection error: ' + e); 37 | }); 38 | } 39 | 40 | // handle https proxy requests (CONNECT method) 41 | proxyServer.on('connect', (clientReq, clientSocket, head) => { 42 | var reqUrl = url.parse('https://' + clientReq.url); 43 | console.log('proxy for https request: ' + reqUrl.href + '(path encrypted by ssl)'); 44 | const { hostname, port } = proxyMap(mapper, reqUrl) 45 | 46 | var options = { 47 | port: port, 48 | host: hostname 49 | }; 50 | 51 | // create socket connection for client, then pipe (redirect) it to client socket 52 | var serverSocket = net.connect(options, () => { 53 | clientSocket.write('HTTP/' + clientReq.httpVersion + ' 200 Connection Established\r\n' + 54 | 'Proxy-agent: Node.js-Proxy\r\n' + 55 | '\r\n', 'UTF-8', () => { 56 | // creating pipes in both ends 57 | serverSocket.write(head); 58 | serverSocket.pipe(clientSocket); 59 | clientSocket.pipe(serverSocket); 60 | }); 61 | }); 62 | 63 | clientSocket.on('error', (e) => { 64 | console.log("client socket error: " + e); 65 | serverSocket.end(); 66 | }); 67 | 68 | serverSocket.on('error', (e) => { 69 | console.log("forward proxy server connection error: " + e); 70 | clientSocket.end(); 71 | }); 72 | }); 73 | 74 | proxyServer.on('clientError', (err, clientSocket) => { 75 | console.log('client error: ' + err); 76 | clientSocket.end('HTTP/1.1 400 Bad Request\r\n\r\n'); 77 | }); 78 | 79 | proxyServer.listen(serverPort); 80 | 81 | console.log('forward proxy server started, listening on port ' + serverPort); 82 | 83 | return proxyServer 84 | }; 85 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "akam-proxy", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "14.6.0", 9 | "resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-14.6.0.tgz?cache=0&sync_timestamp=1597673244667&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.0.tgz", 10 | "integrity": "sha1-fUQRv1FXM5M318/4ZNn/RfF3tJk=" 11 | }, 12 | "async": { 13 | "version": "3.2.0", 14 | "resolved": "https://registry.npm.taobao.org/async/download/async-3.2.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fasync%2Fdownload%2Fasync-3.2.0.tgz", 15 | "integrity": "sha1-s6JoXF67ZB094C0WEALGD8n4VyA=" 16 | }, 17 | "asynckit": { 18 | "version": "0.4.0", 19 | "resolved": "https://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz", 20 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 21 | }, 22 | "boolbase": { 23 | "version": "1.0.0", 24 | "resolved": "https://registry.npm.taobao.org/boolbase/download/boolbase-1.0.0.tgz", 25 | "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" 26 | }, 27 | "cheerio": { 28 | "version": "1.0.0-rc.3", 29 | "resolved": "https://registry.npm.taobao.org/cheerio/download/cheerio-1.0.0-rc.3.tgz", 30 | "integrity": "sha1-CUY21CWy6cD065GkbAVjDJoai/Y=", 31 | "requires": { 32 | "css-select": "~1.2.0", 33 | "dom-serializer": "~0.1.1", 34 | "entities": "~1.1.1", 35 | "htmlparser2": "^3.9.1", 36 | "lodash": "^4.15.0", 37 | "parse5": "^3.0.1" 38 | } 39 | }, 40 | "combined-stream": { 41 | "version": "1.0.8", 42 | "resolved": "https://registry.npm.taobao.org/combined-stream/download/combined-stream-1.0.8.tgz", 43 | "integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=", 44 | "requires": { 45 | "delayed-stream": "~1.0.0" 46 | } 47 | }, 48 | "component-emitter": { 49 | "version": "1.3.0", 50 | "resolved": "https://registry.npm.taobao.org/component-emitter/download/component-emitter-1.3.0.tgz", 51 | "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=" 52 | }, 53 | "cookiejar": { 54 | "version": "2.1.2", 55 | "resolved": "https://registry.npm.taobao.org/cookiejar/download/cookiejar-2.1.2.tgz", 56 | "integrity": "sha1-3YojVTB1L5iPmghE8/xYnjERElw=" 57 | }, 58 | "css-select": { 59 | "version": "1.2.0", 60 | "resolved": "https://registry.npm.taobao.org/css-select/download/css-select-1.2.0.tgz", 61 | "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", 62 | "requires": { 63 | "boolbase": "~1.0.0", 64 | "css-what": "2.1", 65 | "domutils": "1.5.1", 66 | "nth-check": "~1.0.1" 67 | } 68 | }, 69 | "css-what": { 70 | "version": "2.1.3", 71 | "resolved": "https://registry.npm.taobao.org/css-what/download/css-what-2.1.3.tgz", 72 | "integrity": "sha1-ptdgRXM2X+dGhsPzEcVlE9iChfI=" 73 | }, 74 | "debug": { 75 | "version": "4.1.1", 76 | "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.1.1.tgz?cache=0&sync_timestamp=1593529720846&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.1.1.tgz", 77 | "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", 78 | "requires": { 79 | "ms": "^2.1.1" 80 | } 81 | }, 82 | "delayed-stream": { 83 | "version": "1.0.0", 84 | "resolved": "https://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz", 85 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 86 | }, 87 | "dom-serializer": { 88 | "version": "0.1.1", 89 | "resolved": "https://registry.npm.taobao.org/dom-serializer/download/dom-serializer-0.1.1.tgz", 90 | "integrity": "sha1-HsQFnihLq+027sKUHUqXChic58A=", 91 | "requires": { 92 | "domelementtype": "^1.3.0", 93 | "entities": "^1.1.1" 94 | } 95 | }, 96 | "domelementtype": { 97 | "version": "1.3.1", 98 | "resolved": "https://registry.npm.taobao.org/domelementtype/download/domelementtype-1.3.1.tgz", 99 | "integrity": "sha1-0EjESzew0Qp/Kj1f7j9DM9eQSB8=" 100 | }, 101 | "domhandler": { 102 | "version": "2.4.2", 103 | "resolved": "https://registry.npm.taobao.org/domhandler/download/domhandler-2.4.2.tgz", 104 | "integrity": "sha1-iAUJfpM9ZehVRvcm1g9euItE+AM=", 105 | "requires": { 106 | "domelementtype": "1" 107 | } 108 | }, 109 | "domutils": { 110 | "version": "1.5.1", 111 | "resolved": "https://registry.npm.taobao.org/domutils/download/domutils-1.5.1.tgz?cache=0&sync_timestamp=1597680498121&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdomutils%2Fdownload%2Fdomutils-1.5.1.tgz", 112 | "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", 113 | "requires": { 114 | "dom-serializer": "0", 115 | "domelementtype": "1" 116 | } 117 | }, 118 | "entities": { 119 | "version": "1.1.2", 120 | "resolved": "https://registry.npm.taobao.org/entities/download/entities-1.1.2.tgz", 121 | "integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY=" 122 | }, 123 | "fast-safe-stringify": { 124 | "version": "2.0.7", 125 | "resolved": "https://registry.npm.taobao.org/fast-safe-stringify/download/fast-safe-stringify-2.0.7.tgz", 126 | "integrity": "sha1-EkqohYmSYfaK7bQqfAgN6dpgh0M=" 127 | }, 128 | "form-data": { 129 | "version": "3.0.0", 130 | "resolved": "https://registry.npm.taobao.org/form-data/download/form-data-3.0.0.tgz", 131 | "integrity": "sha1-MbfjnIXxNVtxOe4MZHzw3n+DxoI=", 132 | "requires": { 133 | "asynckit": "^0.4.0", 134 | "combined-stream": "^1.0.8", 135 | "mime-types": "^2.1.12" 136 | } 137 | }, 138 | "formidable": { 139 | "version": "1.2.2", 140 | "resolved": "https://registry.npm.taobao.org/formidable/download/formidable-1.2.2.tgz", 141 | "integrity": "sha1-v2muopcpgmdfAIZTQrmCmG9rjdk=" 142 | }, 143 | "htmlparser2": { 144 | "version": "3.10.1", 145 | "resolved": "https://registry.npm.taobao.org/htmlparser2/download/htmlparser2-3.10.1.tgz", 146 | "integrity": "sha1-vWedw/WYl7ajS7EHSchVu1OpOS8=", 147 | "requires": { 148 | "domelementtype": "^1.3.1", 149 | "domhandler": "^2.3.0", 150 | "domutils": "^1.5.1", 151 | "entities": "^1.1.1", 152 | "inherits": "^2.0.1", 153 | "readable-stream": "^3.1.1" 154 | } 155 | }, 156 | "inherits": { 157 | "version": "2.0.4", 158 | "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz", 159 | "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=" 160 | }, 161 | "json5": { 162 | "version": "2.1.3", 163 | "resolved": "https://registry.npm.taobao.org/json5/download/json5-2.1.3.tgz", 164 | "integrity": "sha1-ybD3+pIzv+WAf+ZvzzpWF+1ZfUM=", 165 | "requires": { 166 | "minimist": "^1.2.5" 167 | } 168 | }, 169 | "lodash": { 170 | "version": "4.17.20", 171 | "resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.20.tgz?cache=0&sync_timestamp=1597336125847&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.20.tgz", 172 | "integrity": "sha1-tEqbYpe8tpjxxRo1RaKzs2jVnFI=" 173 | }, 174 | "methods": { 175 | "version": "1.1.2", 176 | "resolved": "https://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz", 177 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 178 | }, 179 | "mime": { 180 | "version": "2.4.6", 181 | "resolved": "https://registry.npm.taobao.org/mime/download/mime-2.4.6.tgz?cache=0&sync_timestamp=1593529693583&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime%2Fdownload%2Fmime-2.4.6.tgz", 182 | "integrity": "sha1-5bQHyQ20QvK+tbFiNz0Htpr/pNE=" 183 | }, 184 | "mime-db": { 185 | "version": "1.44.0", 186 | "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.44.0.tgz", 187 | "integrity": "sha1-+hHF6wrKEzS0Izy01S8QxaYnL5I=" 188 | }, 189 | "mime-types": { 190 | "version": "2.1.27", 191 | "resolved": "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.27.tgz", 192 | "integrity": "sha1-R5SfmOJ56lMRn1ci4PNOUpvsAJ8=", 193 | "requires": { 194 | "mime-db": "1.44.0" 195 | } 196 | }, 197 | "minimist": { 198 | "version": "1.2.5", 199 | "resolved": "https://registry.npm.taobao.org/minimist/download/minimist-1.2.5.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fminimist%2Fdownload%2Fminimist-1.2.5.tgz", 200 | "integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=" 201 | }, 202 | "ms": { 203 | "version": "2.1.2", 204 | "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz", 205 | "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=" 206 | }, 207 | "nth-check": { 208 | "version": "1.0.2", 209 | "resolved": "https://registry.npm.taobao.org/nth-check/download/nth-check-1.0.2.tgz", 210 | "integrity": "sha1-sr0pXDfj3VijvwcAN2Zjuk2c8Fw=", 211 | "requires": { 212 | "boolbase": "~1.0.0" 213 | } 214 | }, 215 | "parse5": { 216 | "version": "3.0.3", 217 | "resolved": "https://registry.npm.taobao.org/parse5/download/parse5-3.0.3.tgz?cache=0&sync_timestamp=1595850974091&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse5%2Fdownload%2Fparse5-3.0.3.tgz", 218 | "integrity": "sha1-BC95L/3TaFFVHPTp4Gazh0q0W1w=", 219 | "requires": { 220 | "@types/node": "*" 221 | } 222 | }, 223 | "qs": { 224 | "version": "6.9.4", 225 | "resolved": "https://registry.npm.taobao.org/qs/download/qs-6.9.4.tgz", 226 | "integrity": "sha1-kJCykNH5FyjTwi5UhDykSupatoc=" 227 | }, 228 | "readable-stream": { 229 | "version": "3.6.0", 230 | "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz", 231 | "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", 232 | "requires": { 233 | "inherits": "^2.0.3", 234 | "string_decoder": "^1.1.1", 235 | "util-deprecate": "^1.0.1" 236 | } 237 | }, 238 | "safe-buffer": { 239 | "version": "5.2.1", 240 | "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.1.tgz", 241 | "integrity": "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=" 242 | }, 243 | "semver": { 244 | "version": "7.3.2", 245 | "resolved": "https://registry.npm.taobao.org/semver/download/semver-7.3.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.3.2.tgz", 246 | "integrity": "sha1-YElisFK4HtB4aq6EOJ/7pw/9OTg=" 247 | }, 248 | "string_decoder": { 249 | "version": "1.3.0", 250 | "resolved": "https://registry.npm.taobao.org/string_decoder/download/string_decoder-1.3.0.tgz", 251 | "integrity": "sha1-QvEUWUpGzxqOMLCoT1bHjD7awh4=", 252 | "requires": { 253 | "safe-buffer": "~5.2.0" 254 | } 255 | }, 256 | "superagent": { 257 | "version": "6.0.0", 258 | "resolved": "https://registry.npm.taobao.org/superagent/download/superagent-6.0.0.tgz", 259 | "integrity": "sha1-iCpz3N9M4rn1q66gHpBkWF1ccHI=", 260 | "requires": { 261 | "component-emitter": "^1.3.0", 262 | "cookiejar": "^2.1.2", 263 | "debug": "^4.1.1", 264 | "fast-safe-stringify": "^2.0.7", 265 | "form-data": "^3.0.0", 266 | "formidable": "^1.2.2", 267 | "methods": "^1.1.2", 268 | "mime": "^2.4.6", 269 | "qs": "^6.9.4", 270 | "readable-stream": "^3.6.0", 271 | "semver": "^7.3.2" 272 | } 273 | }, 274 | "tcp-ping": { 275 | "version": "0.1.1", 276 | "resolved": "https://registry.npm.taobao.org/tcp-ping/download/tcp-ping-0.1.1.tgz", 277 | "integrity": "sha1-At1/QrW/fXy3jVt6rO+hVf2PfAw=" 278 | }, 279 | "util-deprecate": { 280 | "version": "1.0.2", 281 | "resolved": "https://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz", 282 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 283 | } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "akam-proxy", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index" 8 | }, 9 | "author": "nopast", 10 | "license": "MIT", 11 | "dependencies": { 12 | "async": "^3.2.0", 13 | "cheerio": "^1.0.0-rc.3", 14 | "json5": "^2.1.3", 15 | "superagent": "^6.0.0", 16 | "tcp-ping": "^0.1.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # akam-proxy 2 | 自动优选B站海外CDN(upos-hz-mirrorakam.akamaized.net)节点,自动选择最低延迟的节点。 3 | 支持自动更新最新的可用节点。 4 | 5 | ## 安装 6 | ### 安装环境 7 | [NodeJS](https://nodejs.org/) 8 | ### 下载项目并安装依赖 9 | 克隆或下载此项目,在项目目录执行 10 | ```bash 11 | npm install 12 | ``` 13 | 14 | ## 配置文件 15 | 配置文件为`config.json5`,请按照[JSON5](https://github.com/json5/json5)规范配置 16 | 17 | ## 运行 18 | ```bash 19 | npm start 20 | ``` 21 | 22 | ## 使用 23 | 配置浏览器代理到配置文件配置的端口,例如使用默认端口 24 | `http://127.0.0.1:2689` 25 | 推荐使用插件[Proxy SwitchyOmega](https://github.com/FelisCatus/SwitchyOmega)来指定代理 26 | 27 | ## 扩展 28 | 可通过修改配置文件`config.json5`的`host`为其他域名,并修改本地缓存的`ip_list.txt`,可以做到为其他服务优选节点的效果。 29 | 本项目使用的`ip_list.txt`本地缓存,是参考了[akamTester](https://github.com/miyouzi/akamTester)项目中的`ip_list.txt`,在这里对[miyouzi](https://github.com/miyouzi)进行感谢。 -------------------------------------------------------------------------------- /utils/chinazPing.js: -------------------------------------------------------------------------------- 1 | const request = require('superagent') 2 | const cheerio = require('cheerio') 3 | const json5 = require('json5') 4 | const { retry } = require('async') 5 | 6 | 7 | function chinazPing (host, options) { 8 | return new Promise((resolve, reject) => { 9 | request.get('https://ping.chinaz.com/' + host) 10 | .then(res => { 11 | const $ = cheerio.load(res.text) 12 | const enkey = $('#enkey').val() 13 | const serverList = $('#speedlist .listw') 14 | 15 | console.log(`chinaz servers count: ${serverList.length}`) 16 | 17 | const taskList = [] 18 | 19 | serverList.each((i, elem) => { 20 | const guid = $(elem).attr('id') 21 | const task = new Promise((childResolve) => { 22 | retry( 23 | { times: options.retryTime, interval: options.waittingInterval }, 24 | (retry_callback) => { 25 | request.post('https://ping.chinaz.com/iframe.ashx?t=ping') 26 | .type('form') 27 | .send({ 28 | guid, 29 | host: host, 30 | ishost: 0, 31 | isipv6: 0, 32 | checktype: 0, 33 | encode: enkey, 34 | }) 35 | .then(res => { 36 | const resReg = res.text.match(/^\((.*)\)$/) 37 | const data = (resReg)? json5.parse(resReg[1]) : {} 38 | retry_callback((data.state != 1)? 'pending' : null, data) 39 | }) 40 | .catch(err => { 41 | retry_callback(err) 42 | }) 43 | } 44 | ) 45 | .then(childResolve) 46 | .catch(childResolve) 47 | }) 48 | taskList.push(task) 49 | }) 50 | 51 | Promise.all(taskList) 52 | .then(resluts => { 53 | resolve( 54 | resluts 55 | .filter(item => item.state == 1) 56 | .map(item => item.result.ip) 57 | ) 58 | }) 59 | }) 60 | .catch(reject) 61 | }) 62 | } 63 | 64 | module.exports = chinazPing -------------------------------------------------------------------------------- /utils/getGoodServer.js: -------------------------------------------------------------------------------- 1 | const tcpp = require('tcp-ping') 2 | 3 | module.exports = (ipList) => { 4 | return Promise.all( 5 | ipList 6 | .map(item => { 7 | return new Promise((resolve) => { 8 | tcpp.ping({ address: item, attempts: 3, timeout: 3000}, function(err, data) { 9 | if(err) { 10 | resolve({...err, alive: false, host: item}) 11 | } else { 12 | resolve({...data, alive: !isNaN(data.avg), host: item}) 13 | } 14 | }); 15 | }) 16 | }) 17 | ) 18 | .then(results => { 19 | return results 20 | .filter(item => item.alive) 21 | .sort((prev, next) => { 22 | return prev.avg - next.avg 23 | }) 24 | }) 25 | } --------------------------------------------------------------------------------