feature: reproduce so threading http server hanging

This commit is contained in:
RouxAntoine 2024-03-10 19:42:59 +01:00
commit 118301f024
Signed by: antoine
GPG Key ID: 098FB66FC0475E70
5 changed files with 246 additions and 0 deletions

19
.editorconfig Normal file
View File

@ -0,0 +1,19 @@
# formatting rule for this project
root = true
[*]
indent_size = 4
indent_style = space
end_of_line = lf
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
# Docstrings and comments use max_line_length = 79
[*.py]
max_line_length = 119
# Makefiles always use tabs for indentation
[Makefile]
indent_style = tab

134
.gitignore vendored Normal file
View File

@ -0,0 +1,134 @@
# ---> Python
# 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/
pip-wheel-metadata/
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/
# 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
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.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/
# intellij IDE
.idea
*.iml

5
LICENSE Normal file
View File

@ -0,0 +1,5 @@
"THE BEER-WARE LICENSE" (Revision 42):
<phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you can
do whatever you want with this stuff. If we meet some day, and you think this
stuff is worth it, you can buy me a beer in return Poul-Henning Kamp

73
Main.py Executable file
View File

@ -0,0 +1,73 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from random import randint
from http.server import ThreadingHTTPServer, HTTPServer, SimpleHTTPRequestHandler
from signal import signal, SIGINT
from socketserver import ThreadingMixIn, BaseServer, BaseRequestHandler
from threading import Thread
class HandlerExtractCode(SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "application/json")
self.end_headers()
self.wfile.write(b"{ \"key\": \"value\" }")
class BackendThreadHttpServer(ThreadingHTTPServer):
def __init__(self, server_address, request_handler_class):
super().__init__(server_address, request_handler_class)
# properly close HttpServer and close ThreadMixIn with join client threads
def server_close(self):
# stop serve_forever infinite loop (This should be defined into HTTPServer.server_close method)
BaseServer.shutdown(self)
# # close TCP socket and more for future override into HTTPServer class
# HTTPServer.server_close(self)
# # join all process request threads
# ThreadingMixIn.server_close(self)
class BackendApplication(Thread):
def __init__(self, handler, host: str = "0.0.0.0", port: int = 8080):
super().__init__(target=self.application_endpoint)
self.port: int = port
self.httpServer = BackendThreadHttpServer((host, port), handler)
def application_endpoint(self):
print("serving at port", self.port)
self.httpServer.serve_forever()
def stop(self):
print("stop listening at port", self.port)
self.httpServer.server_close()
def start_server():
def handle_sigint(signal_received, frame):
print("stop server thread")
application_endpoint.stop()
print("wait server thread")
application_endpoint.join()
# create server to handle oauth callback request
application_endpoint = BackendApplication(HandlerExtractCode, "127.0.0.1", 65534)
# on sigint close server listen socket
signal(SIGINT, handle_sigint)
# start server handler into new thread
application_endpoint.start()
return application_endpoint
if __name__ == "__main__":
thread = start_server()
thread.join()
print("ended")

15
README.md Normal file
View File

@ -0,0 +1,15 @@
# Reproducer to github issue on cpython repository
This code create a Thread and run a minimal http server into it.
[19556](https://github.com/python/cpython/pull/19556)
## how to reproduce
The tricky part is in `Main.py` line 28 class `BackendThreadHttpServer` method `server_close()`
when running this code and exiting it with ctrl+c
If `BackendThreadHttpServer#server_close()` method doesn't call `BaseServer.shutdown(self)` the thread join never occurred
and so the program never end