Commit 33e04236 authored by Jim Fulton's avatar Jim Fulton

Many changes, including:

  - Butter realm management
  - Automatic type conversion
  - Improved documentation
  - ...
parent 04e63f3f
This diff is collapsed.
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
__doc__='''CGI Response Output formatter __doc__='''CGI Response Output formatter
$Id: Response.py,v 1.3 1996/07/03 18:25:50 jfulton Exp $''' $Id: Response.py,v 1.4 1996/07/08 20:34:09 jfulton Exp $'''
# Copyright # Copyright
# #
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne # Copyright 1996 Digital Creations, L.C., 910 Princess Anne
...@@ -55,6 +55,14 @@ $Id: Response.py,v 1.3 1996/07/03 18:25:50 jfulton Exp $''' ...@@ -55,6 +55,14 @@ $Id: Response.py,v 1.3 1996/07/03 18:25:50 jfulton Exp $'''
# (540) 371-6909 # (540) 371-6909
# #
# $Log: Response.py,v $ # $Log: Response.py,v $
# Revision 1.4 1996/07/08 20:34:09 jfulton
# Many changes, including:
#
# - Butter realm management
# - Automatic type conversion
# - Improved documentation
# - ...
#
# Revision 1.3 1996/07/03 18:25:50 jfulton # Revision 1.3 1996/07/03 18:25:50 jfulton
# Added support for file upload via newcgi module. # Added support for file upload via newcgi module.
# #
...@@ -72,7 +80,7 @@ $Id: Response.py,v 1.3 1996/07/03 18:25:50 jfulton Exp $''' ...@@ -72,7 +80,7 @@ $Id: Response.py,v 1.3 1996/07/03 18:25:50 jfulton Exp $'''
# #
# #
# #
__version__='$Revision: 1.3 $'[11:-2] __version__='$Revision: 1.4 $'[11:-2]
import string, types, sys, regex import string, types, sys, regex
...@@ -81,6 +89,7 @@ status_reasons={ ...@@ -81,6 +89,7 @@ status_reasons={
201: 'Created', 201: 'Created',
202: 'Accepted', 202: 'Accepted',
204: 'No Content', 204: 'No Content',
300: 'Multiple Choices',
301: 'Moved Permanently', 301: 'Moved Permanently',
302: 'Moved Temporarily', 302: 'Moved Temporarily',
304: 'Not Modified', 304: 'Not Modified',
...@@ -99,6 +108,8 @@ status_codes={ ...@@ -99,6 +108,8 @@ status_codes={
'created':201, 'created':201,
'accepted':202, 'accepted':202,
'nocontent':204, 'nocontent':204,
'multiplechoices':300,
'redirect':300,
'movedpermanently':301, 'movedpermanently':301,
'movedtemporarily':302, 'movedtemporarily':302,
'notmodified':304, 'notmodified':304,
...@@ -110,6 +121,17 @@ status_codes={ ...@@ -110,6 +121,17 @@ status_codes={
'notimplemented':501, 'notimplemented':501,
'badgateway':502, 'badgateway':502,
'serviceunavailable':503, 'serviceunavailable':503,
'no content':204,
'multiple choices':300,
'moved permanently':301,
'moved temporarily':302,
'not modified':304,
'bad request':400,
'not found':404,
'internal error':500,
'not implemented':501,
'bad gateway':502,
'service unavailable':503,
200: 200, 200: 200,
201: 201, 201: 201,
202: 202, 202: 202,
...@@ -127,8 +149,8 @@ status_codes={ ...@@ -127,8 +149,8 @@ status_codes={
503: 503, 503: 503,
# Map standard python exceptions to status codes: # Map standard python exceptions to status codes:
'accesserror':403, 'accesserror':500,
'attributeerror':501, 'attributeerror':500,
'conflicterror':500, 'conflicterror':500,
'eoferror':500, 'eoferror':500,
'ioerror':500, 'ioerror':500,
...@@ -149,7 +171,29 @@ status_codes={ ...@@ -149,7 +171,29 @@ status_codes={
end_of_header_re=regex.compile('</head>',regex.casefold) end_of_header_re=regex.compile('</head>',regex.casefold)
base_re=regex.compile('<base',regex.casefold) base_re=regex.compile('<base',regex.casefold)
absuri_re=regex.compile("[a-zA-Z0-9+.-]+:[^\0- \"\#<>]+\(#[^\0- \"\#<>]*\)?")
class Response: class Response:
"""\
An object representation of an HTTP response.
The Response type encapsulates all possible responses to HTTP
requests. Responses are normally created by the object publisher.
A published object may recieve the response abject as an argument
named 'RESPONSE'. A published object may also create it's own
response object. Normally, published objects use response objects
to:
- Provide specific control over output headers,
- Set cookies, or
- Provide stream-oriented output.
If stream oriented output is used, then the response object
passed into the object must be used.
"""
def __init__(self,body='',status=200,headers=None, def __init__(self,body='',status=200,headers=None,
stdout=sys.stdout, stderr=sys.stderr,): stdout=sys.stdout, stderr=sys.stderr,):
...@@ -192,16 +236,21 @@ class Response: ...@@ -192,16 +236,21 @@ class Response:
the previous value set for the header, if one exists. ''' the previous value set for the header, if one exists. '''
self.headers[string.lower(name)]=value self.headers[string.lower(name)]=value
def __getitem__(self, name):
'Get the value of an output header'
return self.headers[name]
__setitem__=setHeader __setitem__=setHeader
def setBody(self, body, title=''): def setBody(self, body, title=''):
'''\ '''\
Set the body of the response
Sets the return body equal to the (string) argument "body". Also Sets the return body equal to the (string) argument "body". Also
updates the "content-length" return header. ''' updates the "content-length" return header.
You can also specify a title, in which case the title and body
will be wrapped up in html, head, title, and body tags.
If the body is a 2-element tuple, then it will be treated
as (title,body)
'''
if type(body)==types.TupleType: if type(body)==types.TupleType:
title,body=body title,body=body
if(title): if(title):
...@@ -244,7 +293,9 @@ class Response: ...@@ -244,7 +293,9 @@ class Response:
def expireCookie(self, name): def expireCookie(self, name):
'''\ '''\
Returns an HTTP header that will remove the cookie Cause an HTTP cookie to be removed from the browser
The response will include an HTTP header that will remove the cookie
corresponding to "name" on the client, if one exists. This is corresponding to "name" on the client, if one exists. This is
accomplished by sending a new cookie with an expiration date accomplished by sending a new cookie with an expiration date
that has already passed. ''' that has already passed. '''
...@@ -253,7 +304,9 @@ class Response: ...@@ -253,7 +304,9 @@ class Response:
def setCookie(self,name, value=None, def setCookie(self,name, value=None,
expires=None, domain=None, path=None, secure=None): expires=None, domain=None, path=None, secure=None):
'''\ '''\
Returns an HTTP header that sets a cookie on cookie-enabled Set an HTTP cookie on the browser
The response will include an HTTP header that sets a cookie on cookie-enabled
browsers with a key "name" and value "value". This overwrites browsers with a key "name" and value "value". This overwrites
any previously set value for the cookie in the Response object. ''' any previously set value for the cookie in the Response object. '''
try: cookie=self.cookies[name] try: cookie=self.cookies[name]
...@@ -270,13 +323,20 @@ class Response: ...@@ -270,13 +323,20 @@ class Response:
def appendBody(self, body): def appendBody(self, body):
''
self.setBody(self.getBody() + body) self.setBody(self.getBody() + body)
def getHeader(self, name): def getHeader(self, name):
'''\ '''\
Get a header value
Returns the value associated with a HTTP return header, or Returns the value associated with a HTTP return header, or
"None" if no such header has been set in the response yet. ''' "None" if no such header has been set in the response
yet. '''
try: return self.headers[name]
except: return None
def __getitem__(self, name):
'Get the value of an output header'
return self.headers[name] return self.headers[name]
def getBody(self): def getBody(self):
...@@ -285,6 +345,8 @@ class Response: ...@@ -285,6 +345,8 @@ class Response:
def appendHeader(self, name, value, delimiter=","): def appendHeader(self, name, value, delimiter=","):
'''\ '''\
Append a value to a cookie
Sets an HTTP return header "name" with value "value", Sets an HTTP return header "name" with value "value",
appending it following a comma if there was a previous value appending it following a comma if there was a previous value
set for the header. ''' set for the header. '''
...@@ -295,7 +357,8 @@ class Response: ...@@ -295,7 +357,8 @@ class Response:
self.setHeader(name,h) self.setHeader(name,h)
def isHTML(self,str): def isHTML(self,str):
return string.lower(string.strip(str)[:6]) == '<html>' return (string.lower(string.strip(str)[:6]) == '<html>' or
string.find(str,'</') > 0)
def _traceback(self,t,v,tb): def _traceback(self,t,v,tb):
import traceback import traceback
...@@ -307,8 +370,21 @@ class Response: ...@@ -307,8 +370,21 @@ class Response:
t,v,tb=sys.exc_type, sys.exc_value,sys.exc_traceback t,v,tb=sys.exc_type, sys.exc_value,sys.exc_traceback
self.setStatus(t) self.setStatus(t)
if self.status >= 300 and self.status < 400:
if type(v) == types.StringType and absuri_re.match(v) >= 0:
self.setHeader('location', v)
return self
else:
try:
l,b=v
if type(l) == types.StringType and absuri_re.match(l) >= 0:
self.setHeader('location', l)
self.setBody(b)
return self
except: pass
b=v b=v
if type(b) is not type(''): if type(b) is not types.StringType:
return self.setBody( return self.setBody(
(str(t), (str(t),
'Sorry, an error occurred.<p>' 'Sorry, an error occurred.<p>'
...@@ -357,7 +433,23 @@ class Response: ...@@ -357,7 +433,23 @@ class Response:
return string.joinfields(headersl,'\n') return string.joinfields(headersl,'\n')
def flush(self): pass
def write(self,data): def write(self,data):
"""\
Return data as a stream
HTML data may be returned using a stream-oriented interface.
This allows the browser to display partial results while
computation of a response to proceed.
The published object should first set any output headers or
cookies on the response object.
Note that published objects must not generate any errors
after beginning stream-oriented output.
"""
self.body=self.body+data self.body=self.body+data
if end_of_header_re.search(self.body) >= 0: if end_of_header_re.search(self.body) >= 0:
try: del self.headers['content-length'] try: del self.headers['content-length']
...@@ -368,14 +460,13 @@ class Response: ...@@ -368,14 +460,13 @@ class Response:
body=self.body body=self.body
self.body='' self.body=''
self.write=write=self.stdout.write self.write=write=self.stdout.write
try: self.flush=self.stdout.flush
except: pass
write(str(self)) write(str(self))
self._wrote=1 self._wrote=1
write('\n\n') write('\n\n')
write(body) write(body)
def ExceptionResponse():
return Response().exception()
def main(): def main():
print Response('hello world') print Response('hello world')
print '-' * 70 print '-' * 70
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment