HeyItsJono
HeyItsJono

Reputation: 51

Shutting down Python BasicHTTPServer from another thread

I have a script which sets up a BasicHTTPServer in a thread so that the main script can automatically open a web browser pointing to the server url to download a file. After the file is downloaded, I want to shut down that server but I have no clue how to go about it. This is an example of what I've done currently:

def server():
    HandlerClass = SimpleHTTPRequestHandler
    ServerClass = BaseHTTPServer.HTTPServer
    Protocol = 'HTTP/1.0'

    server_address = ('127.0.0.1', 8000)
    HandlerClass.protocol_version = Protocol
    httpd = ServerClass(server_address, HandlerClass)
    httpd.serve_forever()

def download():
    t = threading.Thread(name='server', target=server)
    t.start()
    webbrowser.open('safari-http://127.0.0.1:8000/')

I want to shut down the server after webbrowser.open().

Thank you

Upvotes: 4

Views: 783

Answers (3)

HeyItsJono
HeyItsJono

Reputation: 51

So after digging deep through many articles I managed to find a kind of messy solution that works for me which closes the server completely. To do it I incorporated code from the following sources:

UI is a module exclusive to the iOS Python IDE Pythonista which basically just creates buttons "start", "stop" and "visit" which bind to their respective _t functions. The ui.in_background decorator just lets the ui remain responsive while things happen in the background. self.httpd.socket.close() is what really closes the server but it's messy and prints an ugly error to stdout/err so I had no choice but to suppress it by redirecting stdout/err to a dead class so the error is dropped. Standard stdout/err behaviour is restored immediately after. Thank you all for the time and effort you took to help me, I appreciate it greatly.

import console
import BaseHTTPServer
import SocketServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
import sys
import threading
import webbrowser
from time import sleep
import ui

original_stdout = sys.stdout
original_stderr = sys.stderr


class BasicServer(SocketServer.TCPServer):
    allow_reuse_address = True

class NullifyOutput():
    def write(self, s):
        pass

class ServerThread(threading.Thread):
    def __init__(self, ip, port):
        super(ServerThread, self).__init__()
        self.ip = ip
        self.port = port
        self.HandlerClass = SimpleHTTPRequestHandler
        self.Protocol = 'HTTP/1.0'
        self.server_address = (self.ip, self.port)
        self.HandlerClass.protocol_version = self.Protocol
        try:
            self.httpd = BasicServer(self.server_address, self.HandlerClass)
        except:
            self.port += 1
            self.server_address = (self.ip, self.port)
            self.httpd = BasicServer(self.server_address, self.HandlerClass)
        self.stoptheserver = threading.Event()


    def run(self):
        while not self.stoptheserver.isSet():
            self.httpd.handle_request()


    def join(self, timeout=None):
        self.stoptheserver.set()
        self.httpd.socket.close()
        super(ServerThread, self).join(timeout)


server = ServerThread('127.0.0.1', 8000)

def start_t(sender):
    print server.isAlive()
    if not server.isAlive():
        server.start()


def visit_t(sender):
    webbrowser.open('http://127.0.0.1:' + str(server.port))
    #webbrowser.open('safari-http://127.0.0.1' + str(server.port))
    # Use the safari- prefix to open in safari. You may need to switch to
    # pythonista then back to safari to get the page to load.

@ui.in_background
def stop_t(sender):
    sys.stdout, sys.stderr = NullifyOutput(), NullifyOutput()
    server.join(3)
    sys.stdout, sys.stderr = original_stdout, original_stderr

ui.load_view('SimpleServer').present('sheet')

Upvotes: 1

ρss
ρss

Reputation: 5315

I tried the example given here. Can you check if it worked for you.

runFlag = True
def server(server_class=BaseHTTPServer.HTTPServer,
                   handler_class=BaseHTTPServer.BaseHTTPRequestHandler):
    global runFlag
    server_address = ('127.0.0.1', 8000)
    HandlerClass.protocol_version = Protocol
    httpd = ServerClass(server_address, HandlerClass)
    while runFlag:
        httpd.handle_request()
    httpd.shutdown()

def download():
    t = threading.Thread(name='server', target=server)
    t.start()
    webbrowser.open('https:\\www.google.com')
    global runFlag 
    runFlag = False

Upvotes: 1

Mikko Ohtamaa
Mikko Ohtamaa

Reputation: 83686

Here is an example from cryptoassets.core project, status server:

class StatusHTTPServer(threading.Thread):

    def __init__(self, ip, port):
        threading.Thread.__init__(self)
        self.running = False
        self.ready = False
        self.ip = ip
        self.port = port

    def run(self):
        self.running = True
        self.ready = True
        self.httpd.serve_forever()
        self.running = False

    def start(self):
        server_address = (self.ip, self.port)
        try:
            self.httpd = HTTPServer(server_address, StatusGetHandler)
        except OSError as e:
            raise RuntimeError("Could not start cryptoassets helper service status server at {}:{}".format(self.ip, self.port)) from e

        threading.Thread.start(self)

    def stop(self):
        if self.httpd and self.running:
            self.httpd.shutdown()
            self.httpd = None

Upvotes: 0

Related Questions