4

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

Mikko Ohtamaa
  • 82,057
  • 50
  • 264
  • 435
HeyItsJono
  • 51
  • 6
  • https://docs.python.org/2/library/socketserver.html#SocketServer.BaseServer.shutdown – ρss Jan 09 '15 at 12:29
  • Just wanted to note that I'm aware of the BaseServer.shutdown() method but I have no clue how to implement it in this situation. – HeyItsJono Jan 09 '15 at 12:30
  • Doesn't work, sadly. Says that there is no global variable httpd. Moving everything except the "httpd.serve_forever()" statement outside of the server() function just makes the program hang when download() is called. – HeyItsJono Jan 09 '15 at 12:38
  • Error: "Global name 'self' is not defined" – HeyItsJono Jan 09 '15 at 12:43
  • httpd.shutdown() still has no effect. – HeyItsJono Jan 09 '15 at 13:11
  • Sure I'll pastebin it. It's written in pythonista though, made for iOS devices so it uses certain modules that aren't available on PC. And I really appreciate the effort to help! – HeyItsJono Jan 09 '15 at 13:15
  • http://pastebin.com/bR5428Jt Here's the code. The major functions in question are server(), export_tapped() - specifically the cal_button part, and done_tapped(). Those involve the server code. – HeyItsJono Jan 09 '15 at 13:26
  • This one might help: http://stackoverflow.com/questions/11493985/python-basehttpserver-how-to-get-it-to-stop – ρss Jan 09 '15 at 13:39
  • I saw that one but I couldn't figure out how to adapt it to my code. – HeyItsJono Jan 09 '15 at 13:53

3 Answers3

1

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
ρss
  • 5,115
  • 8
  • 43
  • 73
  • Thanks for the help, this seems to work in that after runFlag is changed to False the server stops serving requests, however when I try running the script again I get error: _[Errno 48] Address already in use_ indicating something is still tying up the address. So the server is stopping for the most part but not freeing up the address or port. Any idea how to fix this? – HeyItsJono Jan 10 '15 at 00:28
  • Also is there any way to go about this without using global variables. Everything I've read tells me to stay away from them. – HeyItsJono Jan 10 '15 at 00:28
  • @HeyItsJono Sorry for the late reply as I don't play with computers on the weekend. `Error:48` has a solution here http://stackoverflow.com/a/19071568/2382792 Its good that you already posted the solution!! – ρss Jan 12 '15 at 12:21
1

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')
HeyItsJono
  • 51
  • 6
0

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
Mikko Ohtamaa
  • 82,057
  • 50
  • 264
  • 435
  • So any instance of this will run in a separate thread and I can call stop() from anywhere to stop it? – HeyItsJono Jan 09 '15 at 13:52
  • @HeyItsJono: Yep, that's pretty much it. – Mikko Ohtamaa Jan 09 '15 at 14:04
  • This still doesn't work unfortunately. Calling stop() succeeds but the server itself does not actually stop so http.shutdown() isn't working for some reason. – HeyItsJono Jan 10 '15 at 00:09
  • Can you clarify "It's not working". Is there an exception, traceback, does code after `shutdown()` get executed or is `shutdown()` stuck forever? – Mikko Ohtamaa Jan 10 '15 at 00:12
  • Basically I added a print statement to the stop() method before the if statement to make sure that stop was being called when I called it. Then I made an instance 'server' of the StatusHTTPServer class and ran server.start() then threading.Thread(target=server.run). Then when I tried calling server.stop() from the console I got the print statement 'server stopping' which I added in, but the server continued to serve requests when I tested it by going to localhost:8000 in my browser which is the ip the server was running on. There were no error messages but it just continued to serve. – HeyItsJono Jan 10 '15 at 00:24
  • I'm running on Python 2.7 but I adapted the code you posted to make it run on 2.7. – HeyItsJono Jan 10 '15 at 00:25