web status display for long running program

Peter Hansen peter at engcorp.com
Thu Feb 24 22:01:22 EST 2005


Brian Roberts wrote:
> I have a command line Python program ...
> I think an optional web page would be convenient interface.  The
> Python program would listen on some port, and if queried (by me
> browsing to localhost:12345 for example) would return a pretty status
> display.  Hitting reload would update the status etc.
> 
> My problem is that I'm not sure how to do this:
> - I don't want to embed a full web server into the application or
> require any special PC setup.
> - I think I know how to listen on a socket, but not sure how to send
> stuff to to a web browser -- just start with <HTML>?  

See below.  I don't care if you use the code, it was fun to
whip up on the spur of the moment.  I'm quite certain that
if you try searching for "smallest web server" or something you'll
find a bunch of Perl guys who've competed to produce something
that does the same in a tenth the space...

> - Do I need a separate thread to listen and send the HTML?  The
> application is currently single threaded.  I'm confortable with
> threads, but would prefer to avoid them if possible.

If you don't want a thread, you probably don't need one, but
this might be a little cleaner to put in a thread.  I'm not
sure exactly what approach would be best to avoid a thread,
but using select() on the server socket, with a timeout of 0
to poll, instead of blocking as the following code does, would
probably be the simplest.

Try this out and maybe you can morph it into something like
what you want...

'''miniweb: microscopic web server'''

import socket
from select import select
from datetime import datetime

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
while True:
     cs = s.accept()[0]
     try:
         header = ''
         while True:
             r, w, e = select([cs], [], [cs], 2)
             if not r or e:
                 break

             header += cs.recv(1024)
             if '\r\n\r\n' in header:
                 break

         msg = ('<html><head><title>Miniweb!</title></head>'
             '<body><h3>Clock</h3><p>The time is %s</p></body></html>' % 
datetime.now())

         headers = '\r\n'.join([
             'HTTP/1.0 200 OK',
             'Server: Miniweb-0.1',
             'Content-Type: text/html',
             'Connection: close',
             'Content-Length: %s' % len(msg),
             ])

         cs.send(headers + '\r\n\r\n' + msg + '\n')

     finally:
         cs.close()


I make no claims that this will survive any particular kind
of use or abuse.  I did test with telnet, Firefox, Opera, and Explorer,
however.  A couple of key points found while testing:

1. Explorer needs you to read the header in its entirety, so I
had to add the part that actually scans the header for the
terminating sequence (\r\n\r\n).

2. There's a two-second timeout on reading the header,
in case something connects but doesn't send anything... not
really tested.

3. Some or most of the headers in the reply may not be
required.  Perhaps the first line and the Content-Length
(or not even that?) are all that are required.  Play if you want.
No doubt others will pick this apart and we'll all learn
something. ;-)

4. MIT license ;-)  as in don't complain to me if this
doesn't do what you need, but use it any way you wish...

-Peter



More information about the Python-list mailing list