Fixed typo in comment.
1 # Very simple web framework for a CGI app.
2 # The only things implemented are the things needed for this app
4 from cgi import parse_qsl
8 from Cookie import BaseCookie
10 # Simple version of 'sorted' for Python 2.3 compat
16 def _decode_utf8_namevals(s):
17 """Decodes x-www-form-urlencoded data, interpreting as
18 UTF-8 bytestrings, and returning as a dictionary"""
19 # Don't care about multiple values at the moment
20 return dict([(k.decode('UTF-8'), v.decode('UTF-8'))
21 for k, v in parse_qsl(s)])
23 class HttpRequest(object):
24 def __init__(self, environ, inputstream=None):
25 self.environ = environ
26 self.GET = _decode_utf8_namevals(environ['QUERY_STRING'])
27 self.COOKIES = BaseCookie(environ.get('HTTP_COOKIE', ''))
28 self.path = environ['PATH_INFO']
29 self.method = environ['REQUEST_METHOD']
30 if self.method == 'POST' and inputstream is not None:
31 self.POST = _decode_utf8_namevals(''.join(inputstream.readlines()))
35 def get_cookie(self, name, default):
36 morsel = self.COOKIES.get(name)
43 """Converts unicode object to UTF-8, returns bytestring as is
44 (without checking it is valid UTF-8)"""
45 if isinstance(s, str):
48 return s.encode("UTF-8")
51 if isinstance(s, str):
54 return s.encode("us-ascii")
56 class HttpResponse(object):
57 def __init__(self, content=u"", content_type="text/html",
59 self.content = content
61 self.content_type = content_type
64 def _get_content_type(self):
65 return self._content_type
67 def _set_content_type(self, val):
68 self._content_type = val
69 self.headers['Content-Type'] = "%s; charset=UTF-8" % val
71 content_type = property(_get_content_type, _set_content_type)
73 def _set_status(self, val):
74 self.headers['Status'] = str(val)
76 def _get_status(self):
77 return int(self.headers['Status'])
79 status = property(_get_status, _set_status)
82 return '\n'.join(["%s: %s" % (force_ascii(k), force_ascii(v))
83 for k, v in sorted(self.headers.iteritems())]) + \
84 "\n\n" + force_utf8(self.content)
86 class Http404(HttpResponse):
87 def __init__(self, *args, **kwargs):
88 kwargs['status'] = 404
89 kwargs['content'] = "Not found"
90 super(Http404, self).__init__(*args, **kwargs)
93 """Accepts a list of (regex, callable) pairs and calls the
94 callable whose regex matches the GET request. Named groups in the
95 regex will be used as keyword arguments to the callable."""
97 request = HttpRequest(os.environ, sys.stdin)
101 for regex, acallable in urls:
102 m = re.match(regex, request.path)
104 resp = acallable(request, **m.groupdict())
111 sys.stdout.write(str(resp))