### # # 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 ' '") 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 (,) 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