1 #!/usr/bin/env python 2 3 """ 4 BaseHTTPRequestHandler classes. 5 """ 6 7 import Generic 8 from Helpers.Request import MessageBodyStream 9 from Helpers.Auth import UserInfo 10 from cgi import FieldStorage 11 from StringIO import StringIO 12 13 class Transaction(Generic.Transaction): 14 15 """ 16 BaseHTTPRequestHandler transaction interface. 17 """ 18 19 def __init__(self, trans): 20 21 """ 22 Initialise the transaction using the BaseHTTPRequestHandler instance 23 'trans'. 24 """ 25 26 self.trans = trans 27 28 # Other attributes of interest in instances of this class. 29 30 self.content_type = None 31 self.response_code = 200 32 self.content = StringIO() 33 self.headers = {} 34 35 def commit(self): 36 37 """ 38 A special method, synchronising the transaction with framework-specific 39 objects. 40 """ 41 42 self.trans.send_response(self.response_code) 43 if self.content_type is not None: 44 self.trans.send_header("Content-Type", self.format_content_type(self.content_type)) 45 for header, value in self.headers.items(): 46 self.trans.send_header(self.format_header_value(header), self.format_header_value(value)) 47 self.trans.end_headers() 48 self.content.seek(0) 49 self.trans.wfile.write(self.content.read()) 50 51 # Request-related methods. 52 53 def get_request_stream(self): 54 55 """ 56 A framework-specific method which returns the request stream for 57 the transaction. 58 """ 59 60 return MessageBodyStream(self.trans.rfile, self.get_headers()) 61 62 def get_request_method(self): 63 64 """ 65 A framework-specific method which gets the request method. 66 """ 67 68 return self.trans.command 69 70 def get_headers(self): 71 72 """ 73 A framework-specific method which returns all request headers. 74 """ 75 76 return self.trans.headers 77 78 def get_header_values(self, key): 79 80 """ 81 A framework-specific method which returns a list of all request header 82 values associated with the given 'key'. Note that according to RFC 2616, 83 'key' is treated as a case-insensitive string. 84 """ 85 86 return self.convert_to_list(self.trans.headers.get(key)) 87 88 def get_content_type(self): 89 90 """ 91 A framework-specific method which gets the content type specified on the 92 request, along with the charset employed. 93 """ 94 95 return self.parse_content_type(self.trans.headers.get("Content-type") or 96 self.trans.headers.get("Content-Type")) 97 98 def get_content_charsets(self): 99 100 """ 101 Returns the character set preferences. 102 """ 103 104 return self.parse_content_preferences(self.trans.headers["Accept-Charset"]) 105 106 def get_content_languages(self): 107 108 """ 109 A framework-specific method which extracts language information from 110 the transaction. 111 """ 112 113 return self.parse_content_preferences(self.trans.headers["Accept-Language"]) 114 115 def get_path(self): 116 117 """ 118 A framework-specific method which gets the entire path from the request. 119 """ 120 121 return self.trans.path 122 123 def get_path_info(self): 124 125 """ 126 A framework-specific method which gets the "path info" (the part of the 127 URL after the resource name handling the current request) from the 128 request. 129 """ 130 131 # NOTE: No attempt is made to deduce the "path info". 132 133 return self.trans.path 134 135 def get_query_string(self): 136 137 """ 138 A framework-specific method which gets the query string from the path in 139 the request. 140 """ 141 142 t = self.trans.path.split("?") 143 if len(t) == 1: 144 return "" 145 else: 146 147 # NOTE: Overlook erroneous usage of "?" characters in the path. 148 149 return "?".join(t[1:]) 150 151 # Higher level request-related methods. 152 153 def get_fields_from_path(self): 154 155 """ 156 A framework-specific method which extracts the form fields from the 157 path specified in the transaction. The underlying framework may refuse 158 to supply fields from the path if handling a POST transaction. 159 160 The returned object should employ the cgi.FieldStorage interface. 161 """ 162 163 # NOTE: Since the cgi.FieldStorage class employed in this implementation 164 # NOTE: will only provide fields from the path for GET transactions, the 165 # NOTE: environment is adjusted to persuade it to give the desired 166 # NOTE: output. 167 168 return FieldStorage(headers=self.get_headers(), environ={"REQUEST_METHOD" : "GET", 169 "QUERY_STRING" : self.get_query_string()}, keep_blank_values=1) 170 171 def get_fields_from_body(self): 172 173 """ 174 A framework-specific method which extracts the form fields from the 175 message body in the transaction. 176 177 The returned object should employ the cgi.FieldStorage interface. 178 """ 179 180 return FieldStorage(fp=self.get_request_stream(), headers=self.get_headers(), 181 environ={"REQUEST_METHOD" : "POST"}, keep_blank_values=1) 182 183 def get_fields(self): 184 185 """ 186 A framework-specific method which extracts the form fields from the 187 transaction. Typically, the origin of the form fields will be affected 188 by the method specified in the transaction. 189 190 The returned object should employ the cgi.FieldStorage interface. 191 """ 192 193 return FieldStorage(fp=self.get_request_stream(), headers=self.get_headers(), 194 environ={"REQUEST_METHOD" : self.get_request_method()}, keep_blank_values=1) 195 196 def get_user(self): 197 198 """ 199 A framework-specific method which extracts user information from the 200 transaction. 201 """ 202 203 auth_header = self.get_headers().get("Authorization") 204 if auth_header: 205 return UserInfo(auth_header).username 206 else: 207 return None 208 209 # Response-related methods. 210 211 def get_response_stream(self): 212 213 """ 214 A framework-specific method which returns the response stream for 215 the transaction. 216 """ 217 218 # Return a stream which is later emptied into the real stream. 219 220 return self.content 221 222 def get_response_code(self): 223 224 """ 225 Get the response code associated with the transaction. If no response 226 code is defined, None is returned. 227 """ 228 229 return self.response_code 230 231 def set_response_code(self, response_code): 232 233 """ 234 Set the 'response_code' using a numeric constant defined in the HTTP 235 specification. 236 """ 237 238 self.response_code = response_code 239 240 def set_header_value(self, header, value): 241 242 """ 243 Set the HTTP 'header' with the given 'value'. 244 """ 245 246 # The header is not written out immediately due to the buffering in use. 247 248 self.headers[header] = value 249 250 def set_content_type(self, content_type): 251 252 """ 253 A framework-specific method which sets the 'content_type' for the 254 response. 255 """ 256 257 # The content type has to be written as a header, before actual content, 258 # but after the response line. This means that some kind of buffering is 259 # required. Hence, we don't write the header out immediately. 260 261 self.content_type = content_type 262 263 # vim: tabstop=4 expandtab shiftwidth=4