# -*- coding: utf-8 -*-
"""Kid-savvy HTTP Server.
Written by Christoph Zwerschke based on CGIHTTPServer 0.4.
This module builds on SimpleHTTPServer by implementing GET and POST
requests to Kid templates.
In all cases, the implementation is intentionally naive -- all
requests are executed by the same process and sychronously.
Code to create and run the server looks like this:
from kid.server import HTTPServer
host, port = 'localhost', 8000
HTTPServer((host, port)).serve_forever()
This serves files and kid templates from the current directory
and any of its subdirectories.
If you want the server to be accessible via the network,
use your local host name or an empty string as the host.
(Security warning: Don't do this unless you are inside a firewall.)
You can also call the test() function to run the server, or run this
module as a script, providing host and port as command line arguments.
The Kid templates have access to the following predefined objects:
FieldStorage (access to GET/POST variables)
environ (CGI environment)
request (the request handler object)
Here is a simple Kid template you can use to test the server:
Python Expression Evaluator
${FieldStorage.getvalue('expr')} =
${eval(FieldStorage.getvalue('expr'))}
"""
__revision__ = "$Rev: 492 $"
__date__ = "$Date: 2007-07-06 21:38:45 -0400 (Fri, 06 Jul 2007) $"
__author__ = "Christoph Zwerschke (cito@online.de)"
__copyright__ = "Copyright 2005, Christoph Zwerschke"
__license__ = "MIT "
import os.path
from urllib import unquote
from BaseHTTPServer import HTTPServer as BaseHTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
from cgi import FieldStorage
from kid import load_template
__all__ = ["HTTPServer", "HTTPRequestHandler"]
default_host = 'localhost'
default_port = 8000
class HTTPRequestHandler(SimpleHTTPRequestHandler):
"""Complete HTTP server with GET, HEAD and POST commands.
GET and HEAD also support running Kid templates.
The POST command is *only* implemented for Kid templates."""
def do_POST(self):
"""Serve a POST request implemented for Kid templates."""
if self.is_kid():
self.run_kid()
else:
self.send_error(501, "Can only POST to Kid templates")
def send_head(self):
"""Version of send_head that supports Kid templates."""
if self.is_kid():
return self.run_kid()
else:
return SimpleHTTPRequestHandler.send_head(self)
def is_kid(self):
"""Test whether self.path corresponds to a Kid template.
The default implementation tests whether the path ends
with one of the strings in the list self.kid_extensions.
"""
path = self.path
i = path.rfind('?')
if i >= 0:
path, query = path[:i], path[i+1:]
else:
query = ''
for x in self.kid_extensions:
if path.endswith(x):
self.cgi_info = path, query
return True
return False
kid_extensions = ['.kid', '.kid.html']
def run_kid(self):
"""Execute a Kid template."""
scriptname, query = self.cgi_info
scriptfile = self.translate_path(scriptname)
if not os.path.exists(scriptfile):
self.send_error(404, "No such Kid template (%r)"
% scriptname)
return
if not os.path.isfile(scriptfile):
self.send_error(403, "Kid template is not a plain file (%r)"
% scriptname)
return
env = {}
env['SERVER_SOFTWARE'] = self.version_string()
env['SERVER_NAME'] = self.server.server_name
env['GATEWAY_INTERFACE'] = 'CGI/1.1'
env['SERVER_PROTOCOL'] = self.protocol_version
env['SERVER_PORT'] = str(self.server.server_port)
env['REQUEST_METHOD'] = self.command
uqpath = unquote(scriptname)
env['PATH_INFO'] = uqpath
env['PATH_TRANSLATED'] = self.translate_path(uqpath)
env['SCRIPT_NAME'] = scriptname
if query:
env['QUERY_STRING'] = query
host = self.address_string()
if host != self.client_address[0]:
env['REMOTE_HOST'] = host
env['REMOTE_ADDR'] = self.client_address[0]
authorization = self.headers.getheader("authorization")
if authorization:
authorization = authorization.split()
if len(authorization) == 2:
import base64, binascii
env['AUTH_TYPE'] = authorization[0]
if authorization[0].lower() == "basic":
try:
authorization = base64.decodestring(authorization[1])
except binascii.Error:
pass
else:
authorization = authorization.split(':')
if len(authorization) == 2:
env['REMOTE_USER'] = authorization[0]
if self.headers.typeheader is None:
env['CONTENT_TYPE'] = self.headers.type
else:
env['CONTENT_TYPE'] = self.headers.typeheader
length = self.headers.getheader('content-length')
if length:
env['CONTENT_LENGTH'] = length
accept = []
for line in self.headers.getallmatchingheaders('accept'):
if line[:1] in "\t\n\r ":
accept.append(line.strip())
else:
accept = accept + line[7:].split(',')
env['HTTP_ACCEPT'] = ','.join(accept)
ua = self.headers.getheader('user-agent')
if ua:
env['HTTP_USER_AGENT'] = ua
co = filter(None, self.headers.getheaders('cookie'))
if co:
env['HTTP_COOKIE'] = ', '.join(co)
self.send_response(200, "Script output follows")
# Execute template in this process
try:
template_module = load_template(scriptfile, cache=True)
template = template_module.Template(
request=self, environ=env,
FieldStorage=FieldStorage(self.rfile, environ=env))
s = str(template)
self.send_header("Content-type", "text/html")
self.send_header("Content-Length", str(len(s)))
self.end_headers()
self.wfile.write(s)
except Exception, e:
self.log_error("Kid template exception: %s", str(e))
else:
self.log_message("Kid template exited OK")
class HTTPServer(BaseHTTPServer):
def __init__(self,
server_address=None,
RequestHandlerClass=HTTPRequestHandler):
if server_address is None:
server_address = (default_host, default_port)
BaseHTTPServer.__init__(self,
server_address, HTTPRequestHandler)
def test(server_address=None,
HandlerClass=HTTPRequestHandler,
ServerClass=HTTPServer,
protocol="HTTP/1.0"):
"""Test the HTTP request handler class."""
HandlerClass.protocol_version = protocol
server = ServerClass(server_address, HandlerClass)
sa = server.socket.getsockname()
print "Serving HTTP on", sa[0], "port", sa[1], "..."
server.serve_forever()
def main():
"""This runs the Kid-savvy HTTP server.
Provide host and port as command line arguments.
The current directory serves as the root directory.
"""
from sys import argv, exit
if len(argv) > 3:
print "Usage:", argv[0], "[host]:[port]"
exit(2)
if len(argv) < 2:
server_address = (default_host, default_port)
else:
if len(argv) == 3:
host = argv[1]
port = argv[2]
else:
host = argv[1].split(':', 1)
if len(host) < 2:
host = host[0]
if host.isdigit():
port = host
host = ''
else:
port = None
else:
host, port = host
if port:
if port.isdigit():
port = int(port)
else:
print "Bad port number."
exit(1)
else:
port = default_port
server_address = (host, port)
test(server_address)
if __name__ == '__main__':
main()