web status display for long running program

Kamilche klachemin at comcast.net
Fri Feb 25 02:26:31 EST 2005


Cute! Thanks for posting that. I too like the 'web interface' concept,
it has made SmoothWall a pleasure to use, even on older machines.

I was inspired to enhance your code, and perform a critical bug-fix.
Your code would not have sent large files out to dialup users, because
it assumed all data was sent on the 'send' command. I added code to
check for the number of bytes sent, and loop until it's all gone. I
also turned it into a class, and made it 'command' based.

Have fun with it, Brian!

'''
miniweb: microscopic web server by Peter Hansen

This is a simple web server that handles one request at
a time. Therefore, it's useful mainly for administrative
tasks that have one person connected at a time.

It doesn't actually serve pages, it executes commands
based on the web page you request. The commands it
currently includes are:

    time      -  Send the time
    header    -  Echo back the original header
    largefile -  Test sending a large file back to the person
    quit      -  Shut down the web server

To add more commands, add new functions prefixed with
'request_' similar to the sample functions below.
As you add functions, the menu choices displayed to
the user are automatically updated.

I fixed the 'send' feature so it could send large files.
Before, it would send the first X bytes, then stop
without checking to make sure all bytes were went.
I also made it a class, created the 'command based'
feature, and added the auto-updating menu choices.

Have fun with it!

--Kamilche

'''

import socket
import select
import datetime

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

_BODY = '\r\n'.join([
    '<html><head><title>%s</title></head>',
    '<body><h3>%s</h3>\r\n%s\r\n</body></html>',
    ])

_QUIT = False


class WebServer(object):

    def __init__(self, port):
        ' Start listening on the specified port'
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind(('', port))
        self.socket.listen(5)

        ' Build the list of menu choices'
        self.menu = '<pre>\n'
        for key, value in self.__class__.__dict__.items():
            if key[:8] == 'request_':
                self.menu += "%-10s- %s\r\n" % (key[8:], value.__doc__)
        self.menu += '</pre>\n'

    def Timer(self):
        ' Process new requests'
        r, w, e = select.select([self.socket], [], [], 0.0)
        if not r:
            return
        cs = self.socket.accept()[0]
        header = ''
        while not '\r\n\r\n' in header:
            r, w, e = select.select([cs], [], [cs], 2.0)
            if not r or e:
                break
            header += cs.recv(1024)
        status = '200 OK'
        title = "Miniweb!"
        if not '\r\n\r\n' in header:
            status = '408 Request Timeout'
            body = ['Your request was not received in time.']
        else:
            lines = header.split('\r\n')
            words = lines[0].split(' ')
            request = ' '.join(words[1:-1])
            request = request[1:]
            if request == '':
                fn = self.default
            else:
                fn = getattr(self, 'request_' + request, None)
                if not fn:
                    status = '404 Not Found'
                    fn = self.notfound
            body = fn(header, request)

        body = _BODY % (title, title,
               ''.join([str(arg) for arg in body]))
        header = _HEADER % (status, len(body))
        data = header + body
        while len(data) > 0:
            cc = cs.send(data)
            data = data[cc:]
        cs.close()

    def default(self, header, request):
        ' Print the available choices'
        return ('Welcome to Miniweb!',
                'Available commands are:<p>',
                self.menu)

    def notfound(self, header, request):
        ' Handle unknown requests'
        return ('Unknown request <pre>', request, '</pre>',
            'Your header was:<br><pre>', header, '</pre>',
            'Available commands are:<p>', self.menu)

    def request_time(self, header, request):
        ' Send the time'
        return ('The time is ', datetime.datetime.now(), '<p>')

    def request_header(self, header, request):
        ' Echo back the original header'
        return ('Your header was:<br><pre>', header, '</pre>')

    def request_largefile(self, header, request):
        ' Test sending a large file back to the person'
        temp = ['<pre>\r\n']
        for i in range(10000):
            temp.append("This is line %d of the result.\r\n" % i)
        temp.append('</pre>')
        return temp

    def request_quit(self, header, request):
        ' Shut down the web server'
        global _QUIT
        _QUIT = True
        return ('Web server shut down at ', datetime.datetime.now())


def main():
    ' Main loop'
    server = WebServer(80)
    while not _QUIT:
        server.Timer()
    print "Done!"

main()




More information about the Python-list mailing list