22

I'm writing a small web server in Python, using BaseHTTPServer and a custom subclass of BaseHTTPServer.BaseHTTPRequestHandler. Is it possible to make this listen on more than one port?

What I'm doing now:

class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  def doGET
  [...]

class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): 
    pass

server = ThreadingHTTPServer(('localhost', 80), MyRequestHandler)
server.serve_forever()

3 Answers 3

42

Sure; just start two different servers on two different ports in two different threads that each use the same handler. Here's a complete, working example that I just wrote and tested. If you run this code then you'll be able to get a Hello World webpage at both http://localhost:1111/ and http://localhost:2222/

from threading import Thread
from SocketServer import ThreadingMixIn
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-type", "text/plain")
        self.end_headers()
        self.wfile.write("Hello World!")

class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
    daemon_threads = True

def serve_on_port(port):
    server = ThreadingHTTPServer(("localhost",port), Handler)
    server.serve_forever()

Thread(target=serve_on_port, args=[1111]).start()
serve_on_port(2222)

update:

This also works with Python 3 but three lines need to be slightly changed:

from socketserver import ThreadingMixIn
from http.server import HTTPServer, BaseHTTPRequestHandler

and

self.wfile.write(bytes("Hello World!", "utf-8"))
Sign up to request clarification or add additional context in comments.

2 Comments

@scrat: The GIL won't matter much for this code, since this code will mostly be i/o bound, and most i/o in Python is written using low-level C libraries which release the GIL. As with most performance questions, my advice is to not worry about it unless you've benchmarked your code and determined that it's actually a problem.
You must add daemon_threads = True as member of class ThreadingHTTPServer(ThreadingMixIn, HTTPServer), otherwise the spawned threads won't stop and your Python process will not exit until you send more requests to help those thread realize they need to stop. I edited your answer accordingly, please let me know if there is an issue with that.
6

Not easily. You could have two ThreadingHTTPServer instances, write your own serve_forever() function (don't worry it's not a complicated function).

The existing function:

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.__serving = True
    self.__is_shut_down.clear()
    while self.__serving:
        # 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 r:
            self._handle_request_noblock()
    self.__is_shut_down.set()

So our replacement would be something like:

def serve_forever(server1,server2):
    while True:
        r,w,e = select.select([server1,server2],[],[],0)
        if server1 in r:
            server1.handle_request()
        if server2 in r:
            server2.handle_request()

Comments

6

I would say that threading for something this simple is overkill. You're better off using some form of asynchronous programming.

Here is an example using Twisted:

from twisted.internet import reactor
from twisted.web import resource, server

class MyResource(resource.Resource):
    isLeaf = True
    def render_GET(self, request):
        return 'gotten'

site = server.Site(MyResource())

reactor.listenTCP(8000, site)
reactor.listenTCP(8001, site)
reactor.run()

I also thinks it looks a lot cleaner to have each port be handled in the same way, instead of having the main thread handle one port and an additional thread handle the other. Arguably that can be fixed in the thread example, but then you're using three threads.

1 Comment

You make some very good points, and I was tempted to advise using Twisted in my answer. The main advantage of my approach is that it doesn't require anything outside of the standard library. For a real application I'd just use CherryPy behind Apache or something else that's not BaseHTTPServer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.