Standalone http server, cgi software?

Thomas Fuchs tgfuchs at my-deja.com
Thu Jun 15 02:10:02 EDT 2000


In article <39479D02.42B5AD06 at erols.com>,
  "Edward C. Jones" <edcjones at erols.com> wrote:
> I am looking for a system that resides entirely on one PC. The idea is
> to pass arguments to a Python program via a HTML form. The program
> would look things up in a database, process them, etc. and return to
> the browser a page of html containing the answers.
>
This is also my preferred method for a simple GUI.
Here is a minimal sample program:
"""
simple http server
it executes a system command and returns stdout of command
or return code as plain text

arguments: -port portnumber
default port: 8000
accepted urls:
      /command?cmd=command       # perform command
      /command/form/get          # send form for get
      /command/form/post         # send form for post

usage: start the program and in the browser type the address
    http://localhost:8000/command/form/get

This server is intended to be a starting point for writing any simple
http server.
Modify the part between -- edit begin -- and -- edit end --
supported methods are GET, HEAD and POST,
where POST content-type must be application/x-www-form-urlencoded
Two functions must be provided:
analyse(pathlist, d, headOnly = None)
    pathlist is the path part as list splitted by '/'
    d is the query string as dictionary
    headOnly is set, if method is HEAD
    the function must return value a triple:
    typ       content-type, if not set, the http return code is 404
    size      optional
    info      forwarded to do function
do(info, fout)
    info is the result of analyse
    fout the output stream
    the function must write the content to fout

"""

import os
import sys
import string
import SocketServer
import BaseHTTPServer
import urllib
import socket

class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(self):
        info = self.send_head()
        do(info, self.wfile)

    def do_HEAD(self):
        self.send_head(headOnly = 1)

    def do_POST(self):
        ctyp = self.headers.getheader('content-type')
        if ctyp != 'application/x-www-form-urlencoded':
            print 'POST ERROR: unexpected content-tpye:', ctyp
            return
        clen = self.headers.getheader('content-length')
        if clen:
            clen = string.atoi(clen)
        else:
            print 'POST ERROR: missing content-length'
            return
        data = self.rfile.read(clen)
        dummy = self.rfile.read(2)
        self.path = '%s?%s' % (self.path, data)
        self.do_GET()

    def send_head(self, headOnly = None):
        pathlist, d = urlParse(self.path)
        typ, size, info = analyse(pathlist, d, headOnly)
        if typ:
            self.send_response(200)        # http return code: ok
            self.send_header('Content-type', typ)
            if size:
                self.send_header('Content-length', size)
        else:
            self.send_response(404)        # http return code: not found
        self.end_headers()
        return info

# url handling

def urlParse(url):
    """ return path as list and query string dictionary
        strip / from path
        ignore empty values in query string
        for example:
            if url is: /xyz?a1=&a2=0%3A1
            then result is: (['xyz'], { 'a2' : '0:1' } )
            if url is: /a/b/c/
            then result is: (['a', 'b', 'c'], None )
            if url is: /?
            then result is: ([], {} )
    """
    x = string.split(url, '?')
    pathlist = filter(None, string.split(x[0], '/'))
    d = {}
    if len(x) > 1:
        q = x[-1]                  # eval query string
        x = string.split(q, '&')
        for kv in x:
            y = string.split(kv, '=')
            k = y[0]
            try:
                v = urllib.unquote_plus(y[1])
                if v:               # ignore empty values
                    d[k] = v
            except:
                pass
    return (pathlist, d)

# --------------  edit begin ----------------------------

defaultPort = 8000                 #   set default port for server

def analyse(pathlist, d, headOnly = None):
    size = None
    info = ''
    typ = None
    #print pathlist, d    # test only
    if pathlist == ['command'] and d.keys() == ['cmd']:
        typ = 'text/plain'
        if not headOnly:
            info = syscmd(d['cmd'])
            size = len(info)
    elif pathlist[:2] == ['command', 'form'] and d == {} and \
         pathlist[2:] in [['get'], ['post']]:
        typ = 'text/html'
        if not headOnly:
            info = buildform(pathlist[2])
            size = len(info)
    return (typ, size, info)

def do(info, fout):
    fout.write(info)

# task specific helper functions
def syscmd(cmd):
    f = os.popen(cmd, 'rb')
    rsp = f.read()
    rc = f.close()
    return rsp or 'RC: %s' % rc

def buildform(method):
    # get hostname of server
    try:
        host = socket.gethostname()
    except:
        host = "???"
    doc = '''\
<TITLE>command %(host)s</TITLE>
<H1>command %(host)s</H1>
<P>
<FORM ACTION="/command" METHOD="%(method)s">
command:  <INPUT TYPE=TEXT NAME="cmd" SIZE=80>
<BR>
<INPUT TYPE=SUBMIT>
</FORM>
''' % locals()
    return doc

# --------------  edit end ----------------------------

if __name__ == '__main__':
    # argument handling
    try:
        i = sys.argv.index('-port')
        port = string.atoi(sys.argv[i+1])
        del sys.argv[i:i+1]
    except:
        port = defaultPort

    # start server
    print 'Serving on port', port
    server = SocketServer.TCPServer(('', port), RequestHandler)
    server.serve_forever()


Sent via Deja.com http://www.deja.com/
Before you buy.



More information about the Python-list mailing list