server_forever2/shutdown2

This commit is contained in:
Jakub Warmuz
2015-10-04 12:14:10 +00:00
parent d1fcc422e0
commit 22b1514f51
3 changed files with 65 additions and 27 deletions
+36 -1
View File
@@ -4,6 +4,7 @@ import collections
import functools
import logging
import os
import socket
import sys
import six
@@ -52,18 +53,52 @@ class HTTPSServer(TLSServer, BaseHTTPServer.HTTPServer):
class ACMEServerMixin: # pylint: disable=old-style-class,no-init
"""ACME server common settings mixin."""
"""ACME server common settings mixin.
.. warning::
Subclasses have to init ``_stopped = False`` (it's not done here,
because of old-style classes madness).
"""
server_version = "ACME standalone client"
allow_reuse_address = True
def serve_forever2(self):
"""Serve forever, until other thread calls `shutdown2`."""
while not self._stopped:
self.handle_request()
def shutdown2(self):
"""Shutdown server loop from `serve_forever2`."""
self._stopped = True
# dummy request to terminate last server_forever2.handle_request()
sock = socket.socket()
try:
sock.connect(self.socket.getsockname())
except socket.error:
pass # thread is probably already finished
finally:
sock.close()
self.server_close()
class ACMETLSServer(HTTPSServer, ACMEServerMixin):
"""ACME TLS Server."""
def __init__(self, *args, **kwargs):
self._stopped = False
HTTPSServer.__init__(self, *args, **kwargs)
class ACMEServer(BaseHTTPServer.HTTPServer, ACMEServerMixin):
"""ACME Server (non-TLS)."""
def __init__(self, *args, **kwargs):
self._stopped = False
BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""SimpleHTTP challenge handler.
+22 -1
View File
@@ -25,6 +25,26 @@ class TLSServerTest(unittest.TestCase):
server.server_close() # pylint: disable=no-member
class ACMEServerMixinTest(unittest.TestCase):
"""Tests for acme.standalone.ACMEServerMixin."""
def test_shutdown2_not_running(self):
from acme.standalone import ACMEServer
server = ACMEServer(("", 0), socketserver.BaseRequestHandler)
server.shutdown2()
server.shutdown2()
class ACMEServerTest(unittest.TestCase):
"""Test for acme.standalone.ACMEServer."""
def test_init(self):
from acme.standalone import ACMEServer
server = ACMEServer(("", 0), socketserver.BaseRequestHandler)
# pylint: disable=protected-access
self.assertFalse(server._stopped)
class ACMESimpleHTTPTLSServerTestEndToEnd(unittest.TestCase):
"""End-to-end test for ACME TLS server with SimpleHTTP."""
@@ -45,12 +65,13 @@ class ACMESimpleHTTPTLSServerTestEndToEnd(unittest.TestCase):
self.server = ACMETLSServer(('', 0), handler, certs=self.certs)
self.server_thread = threading.Thread(
# pylint: disable=no-member
target=self.server.handle_request)
target=self.server.serve_forever2)
self.server_thread.start()
self.port = self.server.socket.getsockname()[1]
def tearDown(self):
self.server.shutdown2()
self.server_thread.join()
def test_index(self):
+7 -25
View File
@@ -53,33 +53,15 @@ class ServerManager(object):
except socket.error as error:
errors.StandaloneBindError(error, port)
stop = threading.Event()
thread = threading.Thread(
target=self._serve,
args=(server, stop),
)
thread = threading.Thread(target=server.serve_forever2)
thread.start()
self.servers[port] = (server, thread, stop)
self.servers[port] = (server, thread)
return self.servers[port]
def _serve(self, server, stop):
while not stop.is_set():
server.handle_request()
def stop(self, port):
"""Stop ACME server running on the specified ``port``."""
server, thread, stop = self.servers[port]
stop.set()
# dummy request to terminate last handle_request()
sock = socket.socket()
try:
sock.connect(server.socket.getsockname())
except socket.error:
pass # thread is probably already finished
finally:
sock.close()
server, thread = self.servers[port]
server.shutdown2()
thread.join()
del self.servers[port]
@@ -170,7 +152,7 @@ class Authenticator(common.Plugin):
for achall in achalls:
if isinstance(achall, achallenges.SimpleHTTP):
server, _, _ = self.servers.run(self.config.simple_http_port, tls=tls)
server, _ = self.servers.run(self.config.simple_http_port, tls=tls)
response, validation = achall.gen_response_and_validation(tls=tls)
self.simple_http_resources.add(
acme_standalone.SimpleHTTPRequestHandler.SimpleHTTPResource(
@@ -179,7 +161,7 @@ class Authenticator(common.Plugin):
cert = self.simple_http_cert
domain = achall.domain
else: # DVSNI
server, _, _ = self.servers.run(self.config.dvsni_port, tls=True)
server, _ = self.servers.run(self.config.dvsni_port, tls=True)
response, cert, _ = achall.gen_cert_and_response(self.key)
domain = response.z_domain
self.certs[domain] = (self.key, cert)
@@ -195,6 +177,6 @@ class Authenticator(common.Plugin):
for achall in achalls:
if achall in server_achalls:
server_achalls.remove(achall)
for port, (server, _, _) in self.servers.items():
for port, (server, _) in self.servers.items():
if not self.served[server]:
self.servers.stop(port)