Files
pipenv/tests/pytest-pypi/pytest_pypi/serve.py
T
2021-07-28 10:59:00 +08:00

135 lines
3.9 KiB
Python

import os
import threading
import ssl
from wsgiref.simple_server import WSGIServer, make_server, WSGIRequestHandler
from wsgiref.handlers import SimpleHandler
from six.moves.urllib.parse import urljoin
CERT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'certs')
class ServerHandler(SimpleHandler):
server_software = 'Pytest-HTTPBIN/0.1.0'
http_version = '1.1'
def cleanup_headers(self):
SimpleHandler.cleanup_headers(self)
self.headers['Connection'] = 'Close'
def close(self):
try:
self.request_handler.log_request(
self.status.split(' ', 1)[0], self.bytes_sent
)
finally:
SimpleHandler.close(self)
class Handler(WSGIRequestHandler):
def handle(self):
"""Handle a single HTTP request"""
self.raw_requestline = self.rfile.readline()
if not self.parse_request(): # An error code has been sent, just exit
return
handler = ServerHandler(
self.rfile, self.wfile, self.get_stderr(), self.get_environ()
)
handler.request_handler = self # backpointer for logging
handler.run(self.server.get_app())
def get_environ(self):
"""
wsgiref simple server adds content-type text/plain to everything, this
removes it if it's not actually in the headers.
"""
# Note: Can't use super since this is an oldstyle class in python 2.x
environ = WSGIRequestHandler.get_environ(self).copy()
if self.headers.get('content-type') is None:
del environ['CONTENT_TYPE']
return environ
class SecureWSGIServer(WSGIServer):
def finish_request(self, request, client_address):
"""
Negotiates SSL and then mimics BaseServer behavior.
"""
request.settimeout(1.0)
try:
ssock = ssl.wrap_socket(
request,
keyfile=os.path.join(CERT_DIR, 'key.pem'),
certfile=os.path.join(CERT_DIR, 'cert.pem'),
server_side=True,
suppress_ragged_eofs=False,
)
self.RequestHandlerClass(ssock, client_address, self)
except Exception as e:
print("pytest-httpbin server hit an exception serving request: %s" % e)
print("attempting to ignore so the rest of the tests can run")
# WSGIRequestHandler seems to close the socket for us.
# Thanks, WSGIRequestHandler!!
class Server:
"""
HTTP server running a WSGI application in its own thread.
"""
port_envvar = 'HTTPBIN_HTTP_PORT'
def __init__(self, host='127.0.0.1', port=0, application=None, **kwargs):
self.app = application
if self.port_envvar in os.environ:
port = int(os.environ[self.port_envvar])
self._server = make_server(
host,
port,
self.app,
handler_class=Handler,
**kwargs
)
self.host = self._server.server_address[0]
self.port = self._server.server_address[1]
self.protocol = 'http'
self._thread = threading.Thread(
name=self.__class__,
target=self._server.serve_forever,
)
def __del__(self):
if hasattr(self, '_server'):
self.stop()
def start(self):
self._thread.start()
def __add__(self, other):
return self.url + other
def stop(self):
self._server.shutdown()
@property
def url(self):
return f'{self.protocol}://{self.host}:{self.port}'
def join(self, url, allow_fragments=True):
return urljoin(self.url, url, allow_fragments=allow_fragments)
class SecureServer(Server):
port_envvar = 'HTTPBIN_HTTPS_PORT'
def __init__(self, host='127.0.0.1', port=0, application=None, **kwargs):
kwargs['server_class'] = SecureWSGIServer
super().__init__(host, port, application, **kwargs)
self.protocol = 'https'