SSL Support and cleaning code
This commit is contained in:
138
.gitignore
vendored
Normal file
138
.gitignore
vendored
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
3
build.bat
Normal file
3
build.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@echo off
|
||||||
|
pip install -r src\requirements.txt
|
||||||
|
pyinstaller --onefile --clean --console -n=st src\main.py
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
#!/usr/bin/python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import http.client
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
class StressThread (threading.Thread):
|
|
||||||
def __init__(self, host, port, path, timeout, n):
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
self.host = host
|
|
||||||
self.port = port
|
|
||||||
self.path = path
|
|
||||||
self.timeout = timeout
|
|
||||||
self.n = n
|
|
||||||
self.success = False
|
|
||||||
self.time = 0
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
try:
|
|
||||||
print("Request {}: GET on {}".format(self.n, self.path))
|
|
||||||
now = time.time()
|
|
||||||
c = http.client.HTTPConnection(self.host, self.port, timeout=self.timeout)
|
|
||||||
c.request("GET", self.path)
|
|
||||||
res = c.getresponse()
|
|
||||||
processed = round((time.time() - now) * 1000)
|
|
||||||
print("Request {}: {} {} (in {} ms)".format(self.n, res.status, res.reason, processed))
|
|
||||||
self.time = processed
|
|
||||||
if res.status < 400:
|
|
||||||
self.success = True
|
|
||||||
except Exception as e:
|
|
||||||
print("Request {}: {}".format(self.n, e))
|
|
||||||
|
|
||||||
def is_succeeded(self):
|
|
||||||
return self.success
|
|
||||||
|
|
||||||
def get_time(self):
|
|
||||||
return self.time
|
|
||||||
|
|
||||||
def start(host, port, path, timeout, thread_number, one_by_one = False):
|
|
||||||
thread_array = []
|
|
||||||
for i in range(0, thread_number):
|
|
||||||
thread_array.append(StressThread(host, port, path, timeout, i))
|
|
||||||
for t in thread_array:
|
|
||||||
if one_by_one:
|
|
||||||
t.run()
|
|
||||||
else:
|
|
||||||
t.start()
|
|
||||||
if not one_by_one:
|
|
||||||
for t in thread_array:
|
|
||||||
t.join()
|
|
||||||
show_stat(thread_array, (timeout * 1000))
|
|
||||||
|
|
||||||
def show_stat(tArray, timeoutInMs):
|
|
||||||
total, succeeded = [0, 0]
|
|
||||||
Tmax, Tmin, Tavg = [0, timeoutInMs, 0]
|
|
||||||
for t in tArray:
|
|
||||||
total += 1
|
|
||||||
Tavg += t.get_time()
|
|
||||||
if t.get_time() > Tmax:
|
|
||||||
Tmax = t.get_time()
|
|
||||||
if t.get_time() < Tmin:
|
|
||||||
Tmin = t.get_time()
|
|
||||||
if t.is_succeeded():
|
|
||||||
succeeded += 1
|
|
||||||
Tavg = round(Tavg / total)
|
|
||||||
print("====== FINISHED ======")
|
|
||||||
print("Failed : {}, Succeeded : {}, Total : {}".format((total - succeeded), succeeded, total))
|
|
||||||
print("Min : {} ms, Max : {} ms, Average : {} ms".format(Tmin, Tmax, Tavg))
|
|
||||||
|
|
||||||
def show_help():
|
|
||||||
print("")
|
|
||||||
print("Usage: python http_stress_test.py -h host [-p port] -pth path [-t number_of_thread] [-tm timeout_in_second]")
|
|
||||||
print("Exemple: python http_stress_test.py -h www.google.fr -pth / -t 10")
|
|
||||||
print("")
|
|
||||||
print("Available arguments:")
|
|
||||||
print(" -h host The server IP or domain name")
|
|
||||||
print(" -p port The server HTTP port")
|
|
||||||
print(" -pth path The path of the HTTP resource")
|
|
||||||
print(" -t thread Number of threads")
|
|
||||||
print(" -tm second Timeout of the request")
|
|
||||||
print(" --one-by-one Send request one by one")
|
|
||||||
|
|
||||||
def get_args(header, important, notfoundcode, valuenotfoundcode, default = ""):
|
|
||||||
if (header in sys.argv):
|
|
||||||
try:
|
|
||||||
i = sys.argv.index(header)
|
|
||||||
if (len(sys.argv) > (i + 1)):
|
|
||||||
return sys.argv[i + 1]
|
|
||||||
elif important:
|
|
||||||
print ("Error: argument {} found but not the value".format(header))
|
|
||||||
show_help()
|
|
||||||
sys.exit(valuenotfoundcode)
|
|
||||||
except ValueError:
|
|
||||||
print ("Error: argument {} not found".format(header))
|
|
||||||
if important:
|
|
||||||
show_help()
|
|
||||||
sys.exit(notfoundcode)
|
|
||||||
else:
|
|
||||||
if important:
|
|
||||||
print ("Error: argument {} not found".format(header))
|
|
||||||
show_help()
|
|
||||||
sys.exit(notfoundcode)
|
|
||||||
return default
|
|
||||||
|
|
||||||
def get_flag(header):
|
|
||||||
return (header in sys.argv)
|
|
||||||
|
|
||||||
if ((len(sys.argv) >= 2) and (("--help" in sys.argv) or ("/?" in sys.argv))):
|
|
||||||
print("Basic HTTP Stress test")
|
|
||||||
print("by Alexis Delhaie (@alexlegarnd)")
|
|
||||||
show_help()
|
|
||||||
sys.exit(0);
|
|
||||||
|
|
||||||
host = get_args("-h", True, 1001, 1002)
|
|
||||||
port = int(get_args("-p", False, 1003, 1004, "80"))
|
|
||||||
path = get_args("-pth", True, 1005, 1006)
|
|
||||||
thread_number = int(get_args("-t", False, 1007, 1008, "5"))
|
|
||||||
timeout = int(get_args("-tm", False, 1009, 1010, "10"))
|
|
||||||
one_by_one = get_flag("--one-by-one")
|
|
||||||
|
|
||||||
start(host, port, path, timeout, thread_number, one_by_one)
|
|
||||||
142
src/main.py
Normal file
142
src/main.py
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import platform
|
||||||
|
import os
|
||||||
|
from rich import box
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.table import Table
|
||||||
|
from thread import StressThread
|
||||||
|
|
||||||
|
console = Console(highlight=False)
|
||||||
|
VERSION = "1.2"
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if ((len(sys.argv) >= 2) and (("--help" in sys.argv) or ("/?" in sys.argv))):
|
||||||
|
show_author()
|
||||||
|
show_help()
|
||||||
|
return
|
||||||
|
elif ((len(sys.argv) >= 2) and ("--version" in sys.argv)):
|
||||||
|
show_version()
|
||||||
|
return
|
||||||
|
|
||||||
|
host = get_args("-h", True, 1001, 1002)
|
||||||
|
allow_ssl = get_flag("--ssl")
|
||||||
|
port = int(get_args("--port", False, 1003, 1004, "80"))
|
||||||
|
path = get_args("-p", True, 1005, 1006)
|
||||||
|
thread_number = int(get_args("-t", False, 1007, 1008, "5"))
|
||||||
|
timeout = int(get_args("-tm", False, 1009, 1010, "10"))
|
||||||
|
one_by_one = get_flag("--one-by-one")
|
||||||
|
|
||||||
|
self_signed = get_flag("--allow-self-signed")
|
||||||
|
|
||||||
|
start(host, port, path, timeout, thread_number, allow_ssl, self_signed, one_by_one)
|
||||||
|
|
||||||
|
def start(host, port, path, timeout, thread_number, allow_ssl, self_signed, one_by_one = False):
|
||||||
|
thread_array = []
|
||||||
|
for i in range(0, thread_number):
|
||||||
|
thread_array.append(StressThread(host, port, path, timeout, i, allow_ssl, self_signed, VERSION))
|
||||||
|
for t in thread_array:
|
||||||
|
if one_by_one:
|
||||||
|
t.run()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
t.start()
|
||||||
|
except:
|
||||||
|
b = False
|
||||||
|
while not b:
|
||||||
|
try:
|
||||||
|
t.start()
|
||||||
|
b = True
|
||||||
|
except:
|
||||||
|
time.sleep(1)
|
||||||
|
if not one_by_one:
|
||||||
|
for t in thread_array:
|
||||||
|
t.join()
|
||||||
|
show_stat(thread_array, (timeout * 1000))
|
||||||
|
|
||||||
|
def show_stat(tArray, timeoutInMs):
|
||||||
|
total, succeeded = [0, 0]
|
||||||
|
Tmax, Tmin, Tavg = [0, timeoutInMs, 0]
|
||||||
|
for t in tArray:
|
||||||
|
total += 1
|
||||||
|
Tavg += t.get_time()
|
||||||
|
if t.get_time() > Tmax:
|
||||||
|
Tmax = t.get_time()
|
||||||
|
if t.get_time() < Tmin:
|
||||||
|
Tmin = t.get_time()
|
||||||
|
if t.is_succeeded():
|
||||||
|
succeeded += 1
|
||||||
|
Tavg = round(Tavg / total)
|
||||||
|
tresult = Table(title="Result", box=box.ASCII)
|
||||||
|
tresult.add_column("Failed", style="red")
|
||||||
|
tresult.add_column("Succeeded", style="green")
|
||||||
|
tresult.add_column("Total", style="cyan")
|
||||||
|
tresult.add_row(str(total - succeeded), str(succeeded), str(total))
|
||||||
|
# Min, Max and Average Time
|
||||||
|
ttime = Table(box=box.ASCII)
|
||||||
|
ttime.add_column("Minimum", style="green")
|
||||||
|
ttime.add_column("Maximum", style="red")
|
||||||
|
ttime.add_column("Average", style="cyan")
|
||||||
|
ttime.add_row(str(Tmin), str(Tmax), str(Tavg))
|
||||||
|
console.print(tresult)
|
||||||
|
console.print(ttime)
|
||||||
|
|
||||||
|
def show_help():
|
||||||
|
console.print("")
|
||||||
|
console.print("Usage: [bold]<executable> -h host -p path [--port port] [-t number_of_thread] [-tm timeout_in_second] [--ssl [--allow-self-signed]][/]")
|
||||||
|
console.print("Exemple: st.exe -h www.google.fr -p / -t 10")
|
||||||
|
console.print(" python main.py -h www.google.fr -p / -t 10")
|
||||||
|
console.print("")
|
||||||
|
console.print("Available arguments:")
|
||||||
|
console.print(" -h host The server IP or domain name")
|
||||||
|
console.print(" -p path The path of the HTTP resource")
|
||||||
|
console.print(" --port port The server HTTP port")
|
||||||
|
console.print(" -t thread Number of threads")
|
||||||
|
console.print(" -tm second Timeout of the request")
|
||||||
|
console.print(" --one-by-one Send request one by one")
|
||||||
|
console.print(" --ssl Use HTTPS/SSL")
|
||||||
|
console.print(" --allow-self-signed Allow self signed SSL certificate")
|
||||||
|
console.print(" --help")
|
||||||
|
console.print(" /? Show this page")
|
||||||
|
console.print(" --version Get information about the application and the system")
|
||||||
|
|
||||||
|
def show_author():
|
||||||
|
console.print("[bold]Basic HTTP Stress test[/]")
|
||||||
|
console.print("by Alexis Delhaie ([bold blue]@alexlegarnd[/])")
|
||||||
|
|
||||||
|
def show_version():
|
||||||
|
show_author()
|
||||||
|
console.print()
|
||||||
|
console.print("Version: [bold]{}[/]".format(VERSION))
|
||||||
|
console.print("Python version: [bold]{}[/]".format(sys.version))
|
||||||
|
console.print("User-Agent: [bold]PyStressTest/{}({} {} {})[/]".format(VERSION, platform.system(), os.name, platform.release()))
|
||||||
|
|
||||||
|
def get_args(header, important, notfoundcode, valuenotfoundcode, default = ""):
|
||||||
|
if (header in sys.argv):
|
||||||
|
try:
|
||||||
|
i = sys.argv.index(header)
|
||||||
|
if (len(sys.argv) > (i + 1)):
|
||||||
|
return sys.argv[i + 1]
|
||||||
|
elif important:
|
||||||
|
console.print("[red]Error[/]: argument {} found but not the value".format(header), style="bold")
|
||||||
|
show_help()
|
||||||
|
sys.exit(valuenotfoundcode)
|
||||||
|
except ValueError:
|
||||||
|
console.print("[red]Error[/]: argument {} not found".format(header), style="bold")
|
||||||
|
if important:
|
||||||
|
show_help()
|
||||||
|
sys.exit(notfoundcode)
|
||||||
|
else:
|
||||||
|
if important:
|
||||||
|
console.print("[red]Error[/]: argument {} not found".format(header), style="bold")
|
||||||
|
show_help()
|
||||||
|
sys.exit(notfoundcode)
|
||||||
|
return default
|
||||||
|
|
||||||
|
def get_flag(header):
|
||||||
|
return (header in sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
1
src/requirements.txt
Normal file
1
src/requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rich
|
||||||
66
src/thread.py
Normal file
66
src/thread.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import threading
|
||||||
|
import http.client
|
||||||
|
import platform
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import ssl
|
||||||
|
import encodings.idna
|
||||||
|
from rich.console import Console
|
||||||
|
|
||||||
|
console = Console(highlight=False)
|
||||||
|
|
||||||
|
class StressThread (threading.Thread):
|
||||||
|
|
||||||
|
def __init__(self, host, port, path, timeout, n, allow_ssl, self_signed, version):
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
self.user_agent = "PyStressTest/{}({} {} {})".format(version, platform.system(), os.name, platform.release())
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.path = path
|
||||||
|
self.timeout = timeout
|
||||||
|
self.n = n
|
||||||
|
self.allow_ssl = allow_ssl
|
||||||
|
self.self_signed = self_signed
|
||||||
|
self.success = False
|
||||||
|
self.time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
print("Request {}: GET on {}".format(self.n, self.path))
|
||||||
|
now = time.time()
|
||||||
|
if self.allow_ssl:
|
||||||
|
if self.self_signed:
|
||||||
|
c = http.client.HTTPSConnection(self.host, self.port, timeout=self.timeout, key_file=None, cert_file=None, context=ssl._create_unverified_context())
|
||||||
|
else:
|
||||||
|
c = http.client.HTTPSConnection(self.host, self.port, timeout=self.timeout, key_file=None, cert_file=None)
|
||||||
|
else:
|
||||||
|
c = http.client.HTTPConnection(self.host, self.port, timeout=self.timeout)
|
||||||
|
headers = {"User-Agent": self.user_agent}
|
||||||
|
c.request(method="GET", url=self.path, headers=headers)
|
||||||
|
res = c.getresponse()
|
||||||
|
processed = round((time.time() - now) * 1000)
|
||||||
|
self.print_result(res.status, res.reason, processed)
|
||||||
|
self.time = processed
|
||||||
|
if res.status < 400:
|
||||||
|
self.success = True
|
||||||
|
except Exception as e:
|
||||||
|
console.print("Request {}: [bold red]{}[/]".format(self.n, e))
|
||||||
|
print()
|
||||||
|
|
||||||
|
def is_succeeded(self):
|
||||||
|
return self.success
|
||||||
|
|
||||||
|
def get_time(self):
|
||||||
|
return self.time
|
||||||
|
|
||||||
|
def print_result(self, code, reason, processed):
|
||||||
|
if code >= 100 and code < 200:
|
||||||
|
console.print("Request {}: [cyan]{} {}[/] (in [blue]{} ms[/])".format(self.n, code, reason, processed))
|
||||||
|
elif code >= 200 and code < 300:
|
||||||
|
console.print("Request {}: [green]{} {}[/] (in [blue]{} ms[/])".format(self.n, code, reason, processed))
|
||||||
|
elif code >= 300 and code < 400:
|
||||||
|
console.print("Request {}: [cyan]{} {}[/] (in [blue]{} ms[/])".format(self.n, code, reason, processed))
|
||||||
|
elif code >= 400 and code < 500:
|
||||||
|
console.print("Request {}: [orange]{} {}[/] (in [blue]{} ms[/])".format(self.n, code, reason, processed))
|
||||||
|
else:
|
||||||
|
console.print("Request {}: [red]{} {}[/] (in [blue]{} ms[/])".format(self.n, code, reason, processed))
|
||||||
Reference in New Issue
Block a user