Browse Source

update: enhanced testing experience

master
dnomd343 2 years ago
parent
commit
91ba0efbb4
  1. 8
      Tester/Brook.py
  2. 8
      Tester/Hysteria.py
  3. 6
      Tester/Plugin.py
  4. 12
      Tester/Settings.py
  5. 8
      Tester/Shadowsocks.py
  6. 9
      Tester/ShadowsocksR.py
  7. 10
      Tester/Trojan.py
  8. 8
      Tester/TrojanGo.py
  9. 5
      Tester/V2ray.py
  10. 8
      Tester/VLESS.py
  11. 8
      Tester/VMess.py
  12. 6
      Tester/Xray.py
  13. 113
      Tester/__init__.py
  14. 157
      test.py

8
Tester/Brook.py

@ -4,12 +4,10 @@
import copy
import itertools
from Builder import Brook
from Tester import Settings
from Basis.Logger import logging
from Basis.Process import Process
from Basis.Functions import genFlag
from Basis.Functions import hostFormat
from Basis.Functions import getAvailablePort
from Tester.Settings import Settings
from Basis.Functions import hostFormat, genFlag, getAvailablePort
def originStream(isUot: bool) -> dict:
@ -68,7 +66,7 @@ def loadTest(stream: dict) -> dict:
clientCommand, _, _ = Brook.load(proxyInfo, socksInfo, '')
serverCommand = ['brook', '--debug', '--listen', ':'] + stream['command'](proxyInfo)
testInfo = { # release test info
'title': 'Brook test: ' + stream['caption'],
'caption': 'Brook test: ' + stream['caption'],
'client': Process(Settings['workDir'], cmd = clientCommand, isStart = False),
'server': Process(Settings['workDir'], cmd = serverCommand, isStart = False),
'socks': socksInfo, # exposed socks5 address

8
Tester/Hysteria.py

@ -4,14 +4,12 @@
import os
import json
import itertools
from Tester import Settings
from Builder import Hysteria
from Basis.Logger import logging
from Basis.Process import Process
from Basis.Functions import genFlag
from Basis.Functions import hostFormat
from Tester.Settings import Settings
from Basis.Methods import hysteriaProtocols
from Basis.Functions import getAvailablePort
from Basis.Functions import hostFormat, genFlag, getAvailablePort
def loadServer(configFile: str, hysteriaConfig: dict) -> Process:
@ -71,7 +69,7 @@ def loadTest(protocol: str, isObfs: bool, isAuth: bool) -> dict:
'config': [proxyInfo['passwd']]
}
testInfo = {
'title': caption,
'caption': caption,
'client': loadClient(configName + '_client.json', proxyInfo, socksInfo),
'server': loadServer(configName + '_server.json', serverConfig),
'socks': socksInfo, # exposed socks5 address

6
Tester/Plugin.py

@ -4,13 +4,11 @@
import os
import re
import json
from Tester import Settings
from Basis.Logger import logging
from Basis.Process import Process
from Basis.Methods import plugins
from Basis.Functions import genFlag
from Basis.Functions import hostFormat
from Basis.Functions import getAvailablePort
from Tester.Settings import Settings
from Basis.Functions import genFlag, hostFormat, getAvailablePort
pluginParams = {

12
Tester/Settings.py

@ -0,0 +1,12 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
Settings = {
'workDir': '/tmp/ProxyC',
'serverBind': '127.0.0.1',
'clientBind': '127.0.0.1',
'site': 'www.bing.com',
'host': '343.re',
'cert': '/etc/ssl/certs/343.re/fullchain.pem',
'key': '/etc/ssl/certs/343.re/privkey.pem',
}

8
Tester/Shadowsocks.py

@ -6,14 +6,12 @@ import json
import base64
import itertools
from Tester import Plugin
from Tester import Settings
from Builder import Shadowsocks
from Basis.Logger import logging
from Basis.Process import Process
from Basis.Functions import md5Sum
from Basis.Functions import genFlag
from Basis.Functions import getAvailablePort
from Tester.Settings import Settings
from Basis.Methods import ssMethods, ssAllMethods
from Basis.Functions import md5Sum, genFlag, getAvailablePort
def loadConfig(proxyInfo: dict) -> dict: # load basic config option
@ -126,7 +124,7 @@ def loadTest(serverType: str, clientType: str, method: str, plugin: dict or None
configName += '_%s_%s' % (plugin['type'], md5Sum(plugin['type'] + plugin['caption'])[:8])
pluginText = '' if plugin is None else (' [%s -> %s]' % (plugin['type'], plugin['caption']))
testInfo = { # release test info
'title': 'Shadowsocks test: {%s <- %s -> %s}%s' % (serverType, method, clientType, pluginText),
'caption': 'Shadowsocks test: {%s <- %s -> %s}%s' % (serverType, method, clientType, pluginText),
'client': loadClient(clientType, configName + '_client.json', {**proxyInfo, **pluginClient}, socksInfo),
'server': loadServer(serverType, configName + '_server.json', {**proxyInfo, **pluginServer}),
'socks': socksInfo, # exposed socks5 address

9
Tester/ShadowsocksR.py

@ -3,12 +3,11 @@
import os
import json
from Tester import Settings
from Basis.Logger import logging
from Builder import ShadowsocksR
from Basis.Logger import logging
from Basis.Process import Process
from Basis.Functions import genFlag
from Basis.Functions import getAvailablePort
from Tester.Settings import Settings
from Basis.Functions import genFlag, getAvailablePort
from Basis.Methods import ssrMethods, ssrProtocols, ssrObfuscations
@ -56,7 +55,7 @@ def loadTest(method: str, protocol: str, obfs: str) -> dict:
}
configName = 'ssr_%s_%s_%s' % (method, protocol, obfs) # prefix of config file name
testInfo = { # release test info
'title': 'ShadowsocksR test: method = %s | protocol = %s | obfs = %s' % (method, protocol, obfs),
'caption': 'ShadowsocksR test: method = %s | protocol = %s | obfs = %s' % (method, protocol, obfs),
'client': loadClient(configName + '_client.json', proxyInfo, socksInfo),
'server': loadServer(configName + '_server.json', proxyInfo),
'socks': socksInfo, # exposed socks5 address

10
Tester/Trojan.py

@ -5,13 +5,11 @@ import os
import json
from Tester import Xray
from Builder import Trojan
from Tester import Settings
from Basis.Logger import logging
from Basis.Process import Process
from Basis.Functions import md5Sum
from Basis.Methods import xtlsFlows
from Basis.Functions import genFlag
from Basis.Functions import getAvailablePort
from Tester.Settings import Settings
from Basis.Functions import md5Sum, genFlag, getAvailablePort
def loadServer(configFile: str, proxyInfo: dict, streamConfig: dict, xtlsFlow: str or None) -> Process:
@ -72,7 +70,7 @@ def loadBasicTest(tcpTlsStream: dict) -> dict:
'content': json.dumps(trojanConfig)
}, isStart = False)
testInfo = { # release test info
'title': 'Trojan test: basic connection',
'caption': 'Trojan test: basic connection',
'client': loadClient('trojan_basic_client.json', proxyInfo, socksInfo),
'server': trojanServer,
'socks': socksInfo, # exposed socks5 address
@ -102,7 +100,7 @@ def loadTest(stream: dict) -> dict:
xtlsFlow = xtlsFlow.replace('splice', 'direct') # XTLS on server should use xtls-rprx-direct flow
configName = 'trojan_%s' % (md5Sum(stream['caption'])[:8])
testInfo = { # release test info
'title': 'Trojan test: %s' % stream['caption'],
'caption': 'Trojan test: %s' % stream['caption'],
'client': loadClient(configName + '_client.json', proxyInfo, socksInfo),
'server': loadServer(configName + '_server.json', proxyInfo, stream['server'], xtlsFlow),
'socks': socksInfo, # exposed socks5 address

8
Tester/TrojanGo.py

@ -4,14 +4,12 @@
import os
import json
from Tester import Plugin
from Tester import Settings
from Builder import TrojanGo
from Basis.Logger import logging
from Basis.Process import Process
from Basis.Functions import md5Sum
from Basis.Functions import genFlag
from Tester.Settings import Settings
from Basis.Methods import trojanGoMethods
from Basis.Functions import getAvailablePort
from Basis.Functions import md5Sum, genFlag, getAvailablePort
def loadServer(configFile: str, proxyInfo: dict) -> Process:
@ -76,7 +74,7 @@ def loadTest(wsObject: dict or None, ssObject: dict or None, plugin: dict or Non
('' if wsObject is None else ' (with websocket)') + \
('' if plugin is None else ' [%s -> %s]' % (plugin['type'], plugin['caption']))
testInfo = { # release test info
'title': testTitle,
'caption': testTitle,
'client': loadClient(configName + '_client.json', {**proxyInfo, **pluginClient}, socksInfo),
'server': loadServer(configName + '_server.json', {**proxyInfo, **pluginServer}),
'socks': socksInfo, # exposed socks5 address

5
Tester/V2ray.py

@ -3,10 +3,9 @@
import copy
import itertools
from Tester import Settings
from Basis.Functions import genFlag
from Basis.Methods import quicMethods
from Basis.Methods import udpObfuscations
from Tester.Settings import Settings
from Basis.Methods import quicMethods, udpObfuscations
httpConfig = {
'version': '1.1',

8
Tester/VLESS.py

@ -5,13 +5,11 @@ import os
import json
from Tester import Xray
from Builder import VLESS
from Tester import Settings
from Basis.Logger import logging
from Basis.Process import Process
from Basis.Functions import md5Sum
from Basis.Methods import xtlsFlows
from Basis.Functions import genUUID
from Basis.Functions import getAvailablePort
from Tester.Settings import Settings
from Basis.Functions import md5Sum, genUUID, getAvailablePort
def loadServer(configFile: str, proxyInfo: dict, streamConfig: dict, xtlsFlow: str or None) -> Process:
@ -63,7 +61,7 @@ def loadTest(stream: dict) -> dict:
xtlsFlow = xtlsFlow.replace('splice', 'direct') # XTLS on server should use xtls-rprx-direct flow
configName = 'vless_%s' % (md5Sum(stream['caption'])[:8])
testInfo = { # release test info
'title': 'VLESS test: %s' % stream['caption'],
'caption': 'VLESS test: %s' % stream['caption'],
'client': loadClient(configName + '_client.json', proxyInfo, socksInfo),
'server': loadServer(configName + '_server.json', proxyInfo, stream['server'], xtlsFlow),
'socks': socksInfo, # exposed socks5 address

8
Tester/VMess.py

@ -6,14 +6,12 @@ import json
import itertools
from Tester import V2ray
from Builder import VMess
from Tester import Settings
from Builder import pathEnv
from Basis.Logger import logging
from Basis.Process import Process
from Basis.Functions import md5Sum
from Basis.Functions import genUUID
from Tester.Settings import Settings
from Basis.Methods import vmessMethods
from Basis.Functions import getAvailablePort
from Basis.Functions import md5Sum, genUUID, getAvailablePort
def loadServer(configFile: str, proxyInfo: dict, streamConfig: dict) -> Process: # load server process
@ -63,7 +61,7 @@ def loadTest(method: str, aid: int, stream: dict) -> dict:
}
configName = 'vmess_%s_%i_%s' % (method, aid, md5Sum(stream['caption'])[:8])
testInfo = { # release test info
'title': 'VMess test: %s [security = %s | alterId = %i]' % (stream['caption'], method, aid),
'caption': 'VMess test: %s [security = %s | alterId = %i]' % (stream['caption'], method, aid),
'client': loadClient(configName + '_client.json', proxyInfo, socksInfo),
'server': loadServer(configName + '_server.json', proxyInfo, stream['server']),
'socks': socksInfo, # exposed socks5 address

6
Tester/Xray.py

@ -4,11 +4,9 @@
import copy
import itertools
from Tester import V2ray
from Tester import Settings
from Basis.Methods import xtlsFlows
from Basis.Functions import genFlag
from Basis.Methods import quicMethods
from Basis.Methods import udpObfuscations
from Tester.Settings import Settings
from Basis.Methods import xtlsFlows, quicMethods, udpObfuscations
loadConfig = V2ray.loadConfig
tcpStream = V2ray.tcpStream

113
Tester/__init__.py

@ -1,12 +1,109 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
Settings = {
'workDir': '/tmp/ProxyC',
'serverBind': '127.0.0.1',
'clientBind': '127.0.0.1',
'site': 'www.bing.com',
'host': '343.re',
'cert': '/etc/ssl/certs/343.re/fullchain.pem',
'key': '/etc/ssl/certs/343.re/privkey.pem',
import time
import requests
from threading import Thread
from Basis.Logger import logging
from Basis.Functions import md5Sum, hostFormat, checkPortStatus
from Tester import Brook
from Tester import VMess
from Tester import VLESS
from Tester import Trojan
from Tester import TrojanGo
from Tester import Hysteria
from Tester import Shadowsocks
from Tester import ShadowsocksR
testEntry = {
'ss': Shadowsocks.load(),
'ss-all': Shadowsocks.load(isExtra = True),
'ssr': ShadowsocksR.load(),
'vmess': VMess.load(),
'vless': VLESS.load(),
'trojan': Trojan.load(),
'trojan-go': TrojanGo.load(),
'brook': Brook.load(),
'hysteria': Hysteria.load(),
}
def waitPort(port: int, times: int = 100, delay: int = 100) -> bool: # wait until port occupied
for i in range(times):
if not checkPortStatus(port): # port occupied
return True
time.sleep(delay / 1000) # default wait 100ms
return False # timeout
def httpCheck(socksInfo: dict, url: str, timeout: int = 10):
socksProxy = 'socks5://%s:%i' % (hostFormat(socksInfo['addr'], v6Bracket = True), socksInfo['port'])
try:
proxy = {
'http': socksProxy,
'https': socksProxy,
}
request = requests.get(url, timeout = timeout, proxies = proxy)
request.raise_for_status()
logging.info('%s -> ok' % socksProxy)
except Exception as exp:
logging.error('%s -> error' % socksProxy)
logging.error('requests exception\n' + str(exp))
raise RuntimeError('socks5 test failed')
def runTest(testInfo: dict, testUrl: str, testFilter: set or None, delay: int = 1) -> None:
testInfo['hash'] = md5Sum(testInfo['caption'])[:12]
if testFilter is not None and testInfo['hash'] not in testFilter: return
logging.warning('[%s] %s' % (testInfo['hash'], testInfo['caption']))
testInfo['server'].start() # start test server
if waitPort(testInfo['interface']['port']): # wait for server
logging.debug('server start complete')
testInfo['client'].start() # start test client
if waitPort(testInfo['socks']['port']): # wait for client
logging.debug('client start complete')
try:
logging.debug('start test process')
time.sleep(delay)
httpCheck(testInfo['socks'], testUrl)
except:
# client debug info
logging.warning('client info')
logging.error('command -> %s' % testInfo['client'].cmd)
logging.error('envVar -> %s' % testInfo['client'].env)
logging.error('file -> %s' % testInfo['client'].file)
logging.warning('client capture output')
logging.error('\n%s' % testInfo['client'].output)
# server debug info
logging.warning('server info')
logging.error('command -> %s' % testInfo['server'].cmd)
logging.error('envVar -> %s' % testInfo['server'].env)
logging.error('file -> %s' % testInfo['server'].file)
logging.warning('server capture output')
logging.error('\n%s' % testInfo['server'].output)
finally:
testInfo['client'].quit()
testInfo['server'].quit()
def test(testIter: iter, threadNum: int, testUrl: str, testFilter: set or None = None):
threads = []
while True: # infinite loop
try:
for thread in threads:
if thread.is_alive(): continue
threads.remove(thread) # remove dead thread
if len(threads) < threadNum:
for i in range(threadNum - len(threads)): # start threads within limit
thread = Thread( # create new thread
target = runTest,
args = (next(testIter), testUrl, testFilter)
)
thread.start()
threads.append(thread) # record thread info
time.sleep(0.1)
except StopIteration: # traverse completed
break
for thread in threads: # wait until all threads exit
thread.join()

157
test.py

@ -1,118 +1,59 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
import requests
from threading import Thread
from Tester import Brook
from Tester import VMess
from Tester import VLESS
from Tester import Trojan
from Tester import TrojanGo
from Tester import Hysteria
from Tester import Shadowsocks
from Tester import ShadowsocksR
import sys
import Tester
from Tester import testEntry
from Basis.Logger import logging
from Basis.Functions import hostFormat
from Basis.Functions import checkPortStatus
threadNum = 16
testItem = None
testFilter = None
testUrl = 'http://baidu.com'
helpMsg = '''
./test.py [ITEM] [OPTIONS]
def waitForStart(port: int, times: int = 100, delay: int = 100) -> bool:
for i in range(times):
if not checkPortStatus(port): # port occupied
return True
time.sleep(delay / 1000) # default wait 100ms
return False # timeout
[ITEM]: ss / ss-all / ssr / vmess / vless / trojan / trojan-go / brook / hysteria
[OPTIONS]:
--thread NUM thread number
--url URL http check url
--filter ID1[,ID2...] test the specified id
--all test extra shadowsocks items
--help show this message
'''
def test(testObj: dict) -> None:
logging.warning(testObj['title'])
testObj['server'].start()
time.sleep(0.2)
testObj['client'].start()
if waitForStart(testObj['interface']['port']):
logging.debug('server start complete')
if waitForStart(testObj['socks']['port']):
logging.debug('client start complete')
logging.debug('start test process')
time.sleep(1)
errFlag = False
socks5 = '%s:%i' % (
hostFormat(testObj['socks']['addr'], v6Bracket = True),
testObj['socks']['port']
)
def getArg(field: str) -> str or None:
try:
request = requests.get(
'http://iserv.scutbot.cn',
proxies = {
'http': 'socks5://' + socks5,
'https': 'socks5://' + socks5,
},
timeout = 10
)
request.raise_for_status()
logging.info('socks5 %s -> ok' % socks5)
except Exception as exp:
logging.error('socks5 %s -> error' % socks5)
logging.error('requests exception\n' + str(exp))
errFlag = True
testObj['client'].quit()
testObj['server'].quit()
if errFlag:
logging.warning('client info')
logging.error('command -> %s' % testObj['client'].cmd)
logging.error('envVar -> %s' % testObj['client'].env)
logging.error('file -> %s' % testObj['client'].file)
logging.warning('client capture output')
logging.error('\n' + str(testObj['client'].output))
logging.warning('server info')
logging.error('command -> %s' % testObj['server'].cmd)
logging.error('envVar -> %s' % testObj['server'].env)
logging.error('file -> %s' % testObj['server'].file)
logging.warning('server capture output')
logging.error('\n' + str(testObj['server'].output))
def runTest(testIter: iter, threadNum: int):
threads = []
while True: # infinite loop
try:
for thread in threads:
if thread.is_alive(): continue
threads.remove(thread) # remove dead thread
if len(threads) < threadNum:
for i in range(threadNum - len(threads)): # start threads within limit
thread = Thread(target=test, args=(next(testIter),)) # create new thread
thread.start()
threads.append(thread) # record thread info
time.sleep(0.1)
except StopIteration: # traverse completed
break
for thread in threads: # wait until all threads exit
thread.join()
ss = Shadowsocks.load(isExtra = True)
# ss = Shadowsocks.load(isExtra = False)
ssr = ShadowsocksR.load()
vmess = VMess.load()
vless = VLESS.load()
trojan = Trojan.load()
trojanGo = TrojanGo.load()
brook = Brook.load()
hysteria = Hysteria.load()
logging.critical('test start')
runTest(ss, 64)
runTest(ssr, 64)
runTest(vmess, 64)
runTest(vless, 64)
runTest(trojan, 64)
runTest(trojanGo, 64)
runTest(brook, 64)
runTest(hysteria, 64)
logging.critical('test complete')
index = sys.argv.index(field)
return sys.argv[index + 1]
except:
return None
if '--help' in sys.argv:
print(helpMsg)
sys.exit(0)
if len(sys.argv) > 1 and not sys.argv[1].startswith('--'):
testItem = sys.argv[1]
if getArg('--url') is not None:
testUrl = getArg('--url')
if getArg('--thread') is not None:
threadNum = int(getArg('--thread'))
if getArg('--filter') is not None:
testFilter = set(getArg('--filter').split(','))
logging.critical('test item: ' + ('all' if testItem is None else testItem))
logging.critical('filter: %s' % testFilter)
logging.critical('url: ' + testUrl)
logging.critical('thread number: %i' % threadNum)
logging.critical('TEST START')
if testItem is not None:
Tester.test(testEntry[testItem], threadNum, testUrl, testFilter)
else:
for item in testEntry:
if item == ('ss' if '--all' in sys.argv else 'ss-all'): # skip ss / ss-all
continue
logging.critical('TEST ITEM -> ' + item)
Tester.test(testEntry[item], threadNum, testUrl, testFilter)
logging.critical('TEST COMPLETE')

Loading…
Cancel
Save