2

This is what I have:

http.py:

class HTTPServer():

    def __init__(self, port):
        self.port = port
        self.thread = None
        self.run = True
    def serve(self):
        self.thread = threading.Thread(target=self._serve)
        self.thread.start()
    def _serve(self):
        serverAddress = ("", self.port)
        self.server = MyBaseHTTPServer(serverAddress,MyRequestHandler)
        logging.log(logging.INFO, "HTTP server started on port %s"%self.port)
        while self.run:
            self.server.handle_request()
    def stop(self):
        self.run = False
        self.server.server_close()

Then in another file, to restart it:

def restartHTTP(self):
    try:
        self.httpserver.stop()
        reload(http)
        self.httpserver = http.HTTPServer(80)
        self.httpserver.serve()
    except:
        traceback.print_exc()

This gives me an address already in use error, so it seems the HTTP server isn't stopping properly. What else do I need to do to stop it?

EDIT:

Where I call restartHTTP:

def commandHTTPReload(self, parts, byuser, overriderank):
    self.client.factory.restartHTTP()
    self.client.sendServerMessage("HTTP server reloaded.")

I do know the command is executing because I get the message it's supposed to send.

Joseph
  • 85
  • 1
  • 10

2 Answers2

3

You just need to let the OS know that you really do want to reuse the port immediately after closing it. Normally it's held in a closed state for a while, in case any extra packets show up. You do this with SO_REUSEADDR:

mysocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

..after opening mysocket. A good place to do this with HTTPServer could be in an overridden server_bind method:

def server_bind(self):
    HTTPServer.server_bind(self)
    self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

Edit: Having looked more closely at your code, I see that your threading model is also likely causing problems here. You're closing the socket in the main(?) thread while the other thread is waiting on a connection on that same socket (in accept()). This arrangement does not have well-defined semantics, and I believe it does different things on different OSes. In any case, it is something you ought to avoid in order to minimize confusion (already lots of that to go around in a multithreaded program). Your old thread will not actually go away until after it gets a connection and handles its request (because it won't re-check self.run until then), and so the port may not be re-bindable until after that.

There isn't really a simple solution to this. You could add a communication pipe between the threads, and then use select()/poll() in the server thread to wait for activity on either of them, or you could timeout the accept() calls after a short amount of time so that self.run gets checked more frequently. Or you could have the main thread connect to the listening socket itself. But whatever you do, you're probably approaching the level of complexity where you ought to look at using a "real" httpd or network framework instead of rolling your own: apache, lighttpd, Tornado, Twisted, etc.

the paul
  • 8,972
  • 1
  • 36
  • 53
  • This hasn't worked. Also, I'm thinking that's not the (only) problem because I also tried delaying the starting of the server after it's shutdown by 5 seconds with still no success. – Joseph May 22 '12 at 19:25
  • That (waiting for 5 seconds) doesn't necessarily make a difference here; normally, a socket will hang around in `TIME_WAIT`, blocking other sockets from being bound to the same port, for twice the maximum segment lifetime (so, 4 minutes). Do you still get the exact same error after having added the SO_REUSEADDR? – the paul May 23 '12 at 04:53
  • Yes, it doesn't seem to make a difference. – Joseph May 23 '12 at 13:11
  • I edited the answer with some additional information, which ought to help. – the paul May 23 '12 at 15:01
  • Looking at the implementation of HTTPServer in BaseHTTPServer, socket.SO_REUSEADDR is already used by default, so this answer is not correct. – Chris Withers Apr 23 '13 at 11:04
  • @ChrisWithers there is more to the answer than SO_REUSEADDR. – the paul May 01 '13 at 20:52
1

For gracefully stop HTTPServer and close socket one should use:

# Start server
httpd = HTTPServer(...)
httpd.serve_forever()
# Stop server
httpd.shutdown()
httpd.server_close()