114 lines
3.8 KiB
Python
114 lines
3.8 KiB
Python
|
###
|
||
|
#
|
||
|
# Copyright Alan Kennedy.
|
||
|
#
|
||
|
# You may contact the copyright holder at this uri:
|
||
|
#
|
||
|
# http://www.xhaus.com/contact/modjy
|
||
|
#
|
||
|
# The licence under which this code is released is the Apache License v2.0.
|
||
|
#
|
||
|
# The terms and conditions of this license are listed in a file contained
|
||
|
# in the distribution that also contained this file, under the name
|
||
|
# LICENSE.txt.
|
||
|
#
|
||
|
# You may also read a copy of the license at the following web address.
|
||
|
#
|
||
|
# http://modjy.xhaus.com/LICENSE.txt
|
||
|
#
|
||
|
###
|
||
|
|
||
|
import types
|
||
|
|
||
|
from java.lang import System
|
||
|
|
||
|
from modjy_exceptions import *
|
||
|
from modjy_write import write_object
|
||
|
|
||
|
# From: http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
|
||
|
|
||
|
hop_by_hop_headers = {
|
||
|
'connection': None,
|
||
|
'keep-alive': None,
|
||
|
'proxy-authenticate': None,
|
||
|
'proxy-authorization': None,
|
||
|
'te': None,
|
||
|
'trailers': None,
|
||
|
'transfer-encoding': None,
|
||
|
'upgrade': None,
|
||
|
}
|
||
|
|
||
|
class start_response_object:
|
||
|
|
||
|
def __init__(self, req, resp):
|
||
|
self.http_req = req
|
||
|
self.http_resp = resp
|
||
|
self.write_callable = None
|
||
|
self.called = 0
|
||
|
self.content_length = None
|
||
|
|
||
|
# I'm doing the parameters this way to facilitate porting back to java
|
||
|
def __call__(self, *args, **keywords):
|
||
|
if len(args) < 2 or len(args) > 3:
|
||
|
raise BadArgument("Start response callback requires either two or three arguments: got %s" % str(args))
|
||
|
if len(args) == 3:
|
||
|
exc_info = args[2]
|
||
|
try:
|
||
|
try:
|
||
|
self.http_resp.reset()
|
||
|
except IllegalStateException, isx:
|
||
|
raise exc_info[0], exc_info[1], exc_info[2]
|
||
|
finally:
|
||
|
exc_info = None
|
||
|
else:
|
||
|
if self.called > 0:
|
||
|
raise StartResponseCalledTwice("Start response callback may only be called once, without exception information.")
|
||
|
status_str = args[0]
|
||
|
headers_list = args[1]
|
||
|
if not isinstance(status_str, types.StringType):
|
||
|
raise BadArgument("Start response callback requires string as first argument")
|
||
|
if not isinstance(headers_list, types.ListType):
|
||
|
raise BadArgument("Start response callback requires list as second argument")
|
||
|
try:
|
||
|
status_code, status_message_str = status_str.split(" ", 1)
|
||
|
self.http_resp.setStatus(int(status_code))
|
||
|
except ValueError:
|
||
|
raise BadArgument("Status string must be of the form '<int> <string>'")
|
||
|
self.make_write_object()
|
||
|
try:
|
||
|
for header_name, header_value in headers_list:
|
||
|
header_name_lower = header_name.lower()
|
||
|
if hop_by_hop_headers.has_key(header_name_lower):
|
||
|
raise HopByHopHeaderSet("Under WSGI, it is illegal to set hop-by-hop headers, i.e. '%s'" % header_name)
|
||
|
if header_name_lower == "content-length":
|
||
|
try:
|
||
|
self.set_content_length(int(header_value))
|
||
|
except ValueError, v:
|
||
|
raise BadArgument("Content-Length header value must be a string containing an integer, not '%s'" % header_value)
|
||
|
else:
|
||
|
final_value = header_value.encode('latin-1')
|
||
|
# Here would be the place to check for control characters, whitespace, etc
|
||
|
self.http_resp.addHeader(header_name, final_value)
|
||
|
except (AttributeError, TypeError), t:
|
||
|
raise BadArgument("Start response callback headers must contain a list of (<string>,<string>) tuples")
|
||
|
except UnicodeError, u:
|
||
|
raise BadArgument("Encoding error: header values may only contain latin-1 characters, not '%s'" % repr(header_value))
|
||
|
except ValueError, v:
|
||
|
raise BadArgument("Headers list must contain 2-tuples")
|
||
|
self.called += 1
|
||
|
return self.write_callable
|
||
|
|
||
|
def set_content_length(self, length):
|
||
|
if self.write_callable.num_writes == 0:
|
||
|
self.content_length = length
|
||
|
self.http_resp.setContentLength(length)
|
||
|
else:
|
||
|
raise ResponseCommitted("Cannot set content-length: response is already commited.")
|
||
|
|
||
|
def make_write_object(self):
|
||
|
try:
|
||
|
self.write_callable = write_object(self.http_resp.getOutputStream())
|
||
|
except IOException, iox:
|
||
|
raise IOError(iox)
|
||
|
return self.write_callable
|