faqts : Computers : Programming : Languages : Python : Snippets

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

15 of 16 people (94%) answered Yes
Recently 9 of 10 people (90%) answered Yes


Non-file-based web server?

Jul 5th, 2000 10:01
Nathan Wallace, Hans Nowak, Snippet 183, T.G. Fuchs

Packages: networking.internet
> Wow, nice technical terminology in the subject line, no? :)
> Out of curiousity, is there an HTTP server written in Python which
> doesn't depend on files? Instead, what I'd like to do is to use
> HTMLgen to generate dynamic content within the program, and serve
> that via the web server.
> My goal, in a nutshell, is to implement a simple package installer
> which uses a web interface. Basically, the installer launches a web
> server and either gives the user the URL to connect to, or launches a
> specific web browser to view the page and start the
> installation. Though I'm sure I could provide HTML with the package,
> it'd be great if the pages could all originate within the install
> script.
It is easy to write your own http server derived from BaseHTTPServer.py.
This is my preferred method for a simple GUI.
See the following code as an example.
simple http server
it executes a system command and returns stdout or return code of command
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
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
        clen = self.headers.getheader('content-length')
        if clen:
            clen = string.atoi(clen)
            print 'POST ERROR: missing content-length'
        data = self.rfile.read(clen)
        dummy = self.rfile.read(2)
        self.path = '%s?%s' % (self.path, data)
    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)
            self.send_response(404)        # http return code: not found
        return info
# url handling
def urlParse(url):
    """ return path as list and query string as 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]
                v = urllib.unquote_plus(y[1])
                if v:               # ignore empty values
                    d[k] = v
    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):
# 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
        host = socket.gethostname()
        host = "???"
    doc = '''\
<TITLE>command %(host)s</TITLE>
<H1>command %(host)s</H1>
<FORM ACTION="/command" METHOD="%(method)s">
command:  <INPUT TYPE=TEXT NAME="cmd" SIZE=80>
''' % locals()
    return doc
# --------------  edit end ----------------------------
if __name__ == '__main__':
    # argument handling
        i = sys.argv.index('-port')
        port = string.atoi(sys.argv[i+1])
        del sys.argv[i:i+1]
        port = defaultPort
    # start server
    print 'Serving on port', port
    server = SocketServer.TCPServer(('', port), RequestHandler)