I regularly work on two machines, my MacBook with OS X and a client computer with Windows. OS X provides the usual suite of free software tools including a web server. It is impractical for me to set up a web server on the Windows machine, but I installed Python 2, and it comes with a server sufficient for local development, CGIHTTPServer. It took me a couple of days to get it working right. Here are my notes for future reference.
Unix practice is to reserve the first 1024 port numbers to the root user, so other programs use higher numbers. Since the default port for HTTP is 80, most examples on the web use 8000 or 8080 for a local server. I use 8088 as an homage to the Intel processors that ignited the personal computer explosion.
My first attempts to launch the server seemed to fail after the first page load. After banging my head against the wall for a couple of days, a discussion on web2r.com gave me the solution. When the default HTTP handler writes a log message, it tries to do a reverse DNS lookup to find the requester's domain name. In this environment, there is no DNS server, but the handler keeps trying until it times out. On my machine, this takes about 25 seconds! The same post gives a solution: subclass the handler and override the address_string() method.
#! /usr/bin/env python HOST = "127.0.0.1" PORT = 8088 import BaseHTTPServer, SimpleHTTPServer, CGIHTTPServer import urlparse import cgitb; cgitb.enable() import sys import select import socket import thread import time import webbrowser input = raw_input class MyHandler(CGIHTTPServer.CGIHTTPRequestHandler): # Disable logging DNS lookups to avoid ridiculous delays. def address_string(self): return str(self.client_address) handler = MyHandler class StoppableHTTPServer(BaseHTTPServer.HTTPServer): def server_bind(self): BaseHTTPServer.HTTPServer.server_bind(self) self.socket.settimeout(1) self.run = True def get_request(self): while self.run: try: sock, addr = self.socket.accept() sock.settimeout(None) return (sock, addr) except socket.timeout: pass def stop(self): self.run = False sys.stderr.write('Server is stopping.\n') def serve(self): while self.run: self.handle_request() def load_url(path): httpd = StoppableHTTPServer((HOST,PORT), handler) thread.start_new_thread(httpd.serve, ()) time.sleep(0.10) webbrowser.open_new(path%(HOST,PORT)) sys.stderr.write('Server running\n' ) while httpd.run: cmd = input("Command?\n") if 'stop' == cmd or 'quit' == cmd or 'q' == cmd: httpd.stop() elif 'help' == cmd: sys.stderr.write('Type stop or quit or q to stop the server.\n' ) else: sys.stderr.write('Unknown command ' + cmd + '\n' ); load_url("http://%s:%u/home.html")
The default implementation of CGIHTTPHandler supports executable scripts, but requires them all to be kept in the /cgi-bin subdirectory. A trivial example is shown below. Watch your line endings. If you create a Python file with DOS line endings (CR-LF) the Python interpreter will choke on it. Make sure to use Unix line endings (LF) only.
#! /usr/bin/env python import sys # Send headers. sys.stdout.write("Content-type: text/plain\n\n" ) # Send body. sys.stdout.write("I got this!\n" )I adapted the StoppableHTTPServer code from Stack Overflow. I use only the Python 2 version but that post includes a Python 3 version as well. More links: