web-dev-qa-db-fra.com

Comment arrêter BaseHTTPServer.serve_forever () dans une sous-classe BaseHTTPRequestHandler?

J'exécute mon HTTPServer dans un thread séparé (en utilisant le module de thread qui n'a aucun moyen d'arrêter les threads ...) et je veux arrêter de servir les requêtes lorsque le thread principal s'arrête également.

La documentation Python indique que BaseHTTPServer.HTTPServer est une sous-classe de SocketServer.TCPServer, qui prend en charge une méthode shutdown, mais elle est absente dans HTTPServer.

L'ensemble du module BaseHTTPServer contient très peu de documentation :(

42
Daren Thomas

Je devrais commencer par dire que "je ne ferais probablement pas cela moi-même, mais je l'ai fait par le passé". La méthode serve_forever (from SocketServer.py) ressemble à ceci:

def serve_forever(self):
    """Handle one request at a time until doomsday."""
    while 1:
        self.handle_request()

Vous pouvez remplacer (dans la sous-classe) while 1 avec while self.should_be_running, et modifiez cette valeur à partir d'un autre thread. Quelque chose comme:

def stop_serving_forever(self):
    """Stop handling requests"""
    self.should_be_running = 0
    # Make a fake request to the server, to really force it to stop.
    # Otherwise it will just stop on the next request.
    # (Exercise for the reader.)
    self.make_a_fake_request_to_myself()

Edit: j'ai déterré le code que j'utilisais à l'époque:

class StoppableRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):

    stopped = False
    allow_reuse_address = True

    def __init__(self, *args, **kw):
        SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, *args, **kw)
        self.register_function(lambda: 'OK', 'ping')

    def serve_forever(self):
        while not self.stopped:
            self.handle_request()

    def force_stop(self):
        self.server_close()
        self.stopped = True
        self.create_dummy_request()

    def create_dummy_request(self):
        server = xmlrpclib.Server('http://%s:%s' % self.server_address)
        server.ping()
22
Ali Afshar

Dans mon python 2.6 installation, je peux l'appeler sur le TCPServer sous-jacent - il est toujours là dans votre HTTPServer:

TCPServer.shutdown


>>> import BaseHTTPServer
>>> h=BaseHTTPServer.HTTPServer(('',5555), BaseHTTPServer.BaseHTTPRequestHandler)
>>> h.shutdown
<bound method HTTPServer.shutdown of <BaseHTTPServer.HTTPServer instance at 0x0100D800>>
>>> 
21
gimel

Je pense que vous pouvez utiliser [serverName].socket.close()

16
wimerrill

Une autre façon de le faire, basée sur http://docs.python.org/2/library/basehttpserver.html#more-examples , est: au lieu de serve_forever (), continuez à servir aussi longtemps que une condition est remplie, le serveur vérifiant la condition avant et après chaque demande. Par exemple:

import CGIHTTPServer
import BaseHTTPServer

KEEP_RUNNING = True

def keep_running():
    return KEEP_RUNNING

class Handler(CGIHTTPServer.CGIHTTPRequestHandler):
    cgi_directories = ["/cgi-bin"]

httpd = BaseHTTPServer.HTTPServer(("", 8000), Handler)

while keep_running():
    httpd.handle_request()
15
user2852263

La boucle d'événement se termine sur SIGTERM, Ctrl+C ou lorsque shutdown() est appelée.

server_close() doit être appelé après server_forever() pour fermer la prise d'écoute.

import http.server

class StoppableHTTPServer(http.server.HTTPServer):
    def run(self):
        try:
            self.serve_forever()
        except KeyboardInterrupt:
            pass
        finally:
            # Clean-up server (close socket, etc.)
            self.server_close()

Serveur simple pouvant être arrêté avec l'action de l'utilisateur (SIGTERM, Ctrl+C, ...):

server = StoppableHTTPServer(("127.0.0.1", 8080),
                             http.server.BaseHTTPRequestHandler)
server.run()

Serveur fonctionnant dans un thread:

import threading

server = StoppableHTTPServer(("127.0.0.1", 8080),
                             http.server.BaseHTTPRequestHandler)

# Start processing requests
thread = threading.Thread(None, server.run)
thread.start()

# ... do things ...

# Shutdown server
server.shutdown()
thread.join()
13
Vianney Bajart

Dans python 2.7, l'appel à shutdown () fonctionne mais uniquement si vous servez via serve_forever, car il utilise async select et une boucle d'interrogation. L'exécution de votre propre boucle avec handle_request () exclut ironiquement cette fonctionnalité car cela implique un appel de blocage stupide.

Depuis le BaseServer de SocketServer.py:

def serve_forever(self, poll_interval=0.5):
    """Handle one request at a time until shutdown.

    Polls for shutdown every poll_interval seconds. Ignores
    self.timeout. If you need to do periodic tasks, do them in
    another thread.
    """
    self.__is_shut_down.clear()
    try:
        while not self.__shutdown_request:
            # XXX: Consider using another file descriptor or
            # connecting to the socket to wake this up instead of
            # polling. Polling reduces our responsiveness to a
            # shutdown request and wastes cpu at all other times.
            r, w, e = select.select([self], [], [], poll_interval)
            if self in r:
                self._handle_request_noblock()
    finally:
        self.__shutdown_request = False
        self.__is_shut_down.set()

Voici une partie de mon code pour faire un arrêt de blocage à partir d'un autre thread, en utilisant un événement pour attendre la fin:

class MockWebServerFixture(object):
    def start_webserver(self):
        """
        start the web server on a new thread
        """
        self._webserver_died = threading.Event()
        self._webserver_thread = threading.Thread(
                target=self._run_webserver_thread)
        self._webserver_thread.start()

    def _run_webserver_thread(self):
        self.webserver.serve_forever()
        self._webserver_died.set()

    def _kill_webserver(self):
        if not self._webserver_thread:
            return

        self.webserver.shutdown()

        # wait for thread to die for a bit, then give up raising an exception.
        if not self._webserver_died.wait(5):
            raise ValueError("couldn't kill webserver")
8
jsalter

Cette méthode que j'utilise avec succès (Python 3) pour arrêter le serveur à partir de l'application Web elle-même (une page Web):

import http.server
import os
import re

class PatientHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
    stop_server = False
    base_directory = "/static/"
    # A file to use as an "server stopped user information" page.
    stop_command = "/control/stop.html"
    def send_head(self):
        self.path = os.path.normpath(self.path)
        if self.path == PatientHTTPRequestHandler.stop_command and self.address_string() == "127.0.0.1":
            # I wanted that only the local machine could stop the server.
            PatientHTTPRequestHandler.stop_server = True
            # Allow the stop page to be displayed.
            return http.server.SimpleHTTPRequestHandler.send_head(self)
        if self.path.startswith(PatientHTTPRequestHandler.base_directory):
            return http.server.SimpleHTTPRequestHandler.send_head(self)
        else:
            return self.send_error(404, "Not allowed", "The path you requested is forbidden.")

if __== "__main__":
    httpd = http.server.HTTPServer(("127.0.0.1", 8080), PatientHTTPRequestHandler)
    # A timeout is needed for server to check periodically for KeyboardInterrupt
    httpd.timeout = 1
    while not PatientHTTPRequestHandler.stop_server:
        httpd.handle_request()

De cette façon, les pages diffusées via l'adresse de base http://localhost:8080/static/ (exemple http://localhost:8080/static/styles/common.css) sera servi par le gestionnaire par défaut, un accès à http://localhost:8080/control/stop.html depuis l'ordinateur du serveur affichera stop.html puis arrêtez le serveur, toute autre option sera interdite.

2
Vlad Tudorache

J'ai essayé toutes les solutions possibles ci-dessus et j'ai fini par avoir un problème "quelquefois" - d'une manière ou d'une autre, cela ne l'a pas vraiment fait - alors j'ai fini par faire une solution sale qui fonctionnait tout le temps pour moi:

Si tout ce qui précède échoue, alors la force brute tue votre fil en utilisant quelque chose comme ceci:

import subprocess
cmdkill = "kill $(ps aux|grep '<name of your thread> true'|grep -v 'grep'|awk '{print $2}') 2> /dev/null"
subprocess.Popen(cmdkill, stdout=subprocess.PIPE, Shell=True)
1
serup