SSL Support and cleaning code

This commit is contained in:
Alexis Delhaie
2020-08-24 19:36:47 +02:00
parent cb8fbaba9b
commit 99008528ce
6 changed files with 350 additions and 123 deletions

138
.gitignore vendored Normal file
View 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
View File

@@ -0,0 +1,3 @@
@echo off
pip install -r src\requirements.txt
pyinstaller --onefile --clean --console -n=st src\main.py

View File

@@ -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
View 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
View File

@@ -0,0 +1 @@
rich

66
src/thread.py Normal file
View 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))