1 #!/usr/bin/env python 2 3 """ 4 Webware classes. 5 """ 6 7 import Generic 8 from cgi import parse_qs 9 import StringIO 10 from Helpers import Environment 11 from Helpers.Request import Cookie 12 from Helpers.Response import ConvertingStream 13 14 class Transaction(Generic.Transaction): 15 16 """ 17 Webware transaction interface. 18 """ 19 20 def __init__(self, trans): 21 22 "Initialise the transaction using the Webware transaction 'trans'." 23 24 self.trans = trans 25 self.user = None 26 self.content_type = None 27 28 # Request-related methods. 29 30 def get_request_stream(self): 31 32 """ 33 A framework-specific method which returns the request stream for 34 the transaction. 35 """ 36 37 request = self.trans.request() 38 try: 39 stream = request.rawInput(rewind=1) 40 if stream is None: 41 return StringIO.StringIO("") 42 43 # NOTE: Dubious catch-all situation, but it is difficult to control 44 # NOTE: cases where Webware's internals themselves fail. 45 46 except: 47 return StringIO.StringIO("") 48 49 return stream 50 51 def get_request_method(self): 52 53 """ 54 A framework-specific method which gets the request method. 55 """ 56 57 return self.trans.request().method() 58 59 def get_headers(self): 60 61 """ 62 A framework-specific method which returns all request headers as a 63 dictionary-like object mapping header names to values. 64 NOTE: If duplicate header names are permitted, then this interface will 65 NOTE: need to change. 66 """ 67 68 # Use the Webware environment and some assumptions about variable names. 69 # NOTE: Using lower case for the header names. 70 71 env = self.trans.request().environ() 72 return Environment.get_headers(env) 73 74 def get_header_values(self, key): 75 76 """ 77 A framework-specific method which returns a list of all request header 78 values associated with the given 'key'. Note that according to RFC 2616, 79 'key' is treated as a case-insensitive string. 80 """ 81 82 # Use the Webware environment and some assumptions about variable names. 83 84 env = self.trans.request().environ() 85 cgi_key = "HTTP_" + key.replace("-", "_").upper() 86 if env.has_key(cgi_key): 87 return [env[cgi_key]] 88 else: 89 return [] 90 91 def get_content_type(self): 92 93 """ 94 A framework-specific method which gets the content type specified on the 95 request, along with the charset employed. 96 """ 97 98 return self.parse_content_type(self.trans.request().contentType()) 99 100 def get_content_charsets(self): 101 102 """ 103 Returns the character set preferences. 104 NOTE: Requires enhancements to HTTPRequest. 105 """ 106 107 return self.trans.request().contentCharsets() 108 109 def get_content_languages(self): 110 111 """ 112 A framework-specific method which extracts language information from 113 the transaction. 114 NOTE: Requires enhancements to HTTPRequest. 115 """ 116 117 return self.trans.request().contentLanguages() 118 119 def get_path(self): 120 121 """ 122 A framework-specific method which gets the entire path from the request. 123 """ 124 125 return self.trans.request().uri() 126 127 def get_path_without_query(self): 128 129 """ 130 A framework-specific method which gets the entire path from the request 131 minus the query string. 132 """ 133 134 return self.get_path().split("?")[0] 135 136 def get_path_info(self): 137 138 """ 139 A framework-specific method which gets the "path info" (the part of the 140 URL after the resource name handling the current request) from the 141 request. 142 """ 143 144 return self.trans.request().extraURLPath() 145 146 def get_query_string(self): 147 148 """ 149 A framework-specific method which gets the query string from the path in 150 the request. 151 """ 152 153 return self.trans.request().queryString() 154 155 # Higher level request-related methods. 156 157 def get_fields_from_path(self): 158 159 """ 160 A framework-specific method which extracts the form fields from the 161 path specified in the transaction. The underlying framework may refuse 162 to supply fields from the path if handling a POST transaction. 163 164 Returns a dictionary mapping field names to lists of values (even if a 165 single value is associated with any given field name). 166 """ 167 168 return parse_qs(self.get_query_string(), keep_blank_values=1) 169 170 def get_fields_from_body(self, encoding=None): 171 172 """ 173 A framework-specific method which extracts the form fields from the 174 message body in the transaction. The optional 'encoding' parameter 175 specifies the character encoding of the message body for cases where no 176 such information is available, but where the default encoding is to be 177 overridden. 178 179 Returns a dictionary mapping field names to lists of values (even if a 180 single value is associated with any given field name). 181 """ 182 183 encoding = self.get_content_type().charset or encoding or "iso-8859-1" 184 185 # Fix the non-list results. 186 187 fields = {} 188 for field_name, field_values in self.trans.request().fields().items(): 189 if type(field_values) == type([]): 190 fields[field_name] = [] 191 for field_value in field_values: 192 fields[field_name].append(unicode(field_value, encoding)) 193 else: 194 fields[field_name] = [unicode(field_values, encoding)] 195 return fields 196 197 def get_user(self): 198 199 """ 200 A framework-specific method which extracts user information from the 201 transaction. 202 203 Returns a username as a string or None if no user is defined. 204 """ 205 206 # NOTE: Webware relies entirely on a CGI-style environment where the 207 # NOTE: actual headers are not available. Therefore, the Web server must 208 # NOTE: itself be set up to provide user support. 209 210 if self.user is not None: 211 return self.user 212 213 try: 214 return self.trans.request().remoteUser() 215 except KeyError, exc: 216 return None 217 218 def get_cookies(self): 219 220 """ 221 A framework-specific method which obtains cookie information from the 222 request. 223 224 Returns a dictionary mapping cookie names to cookie objects. 225 """ 226 227 cookies = {} 228 for name, value in self.trans.request().cookies().items(): 229 cookies[name] = Cookie(name, value) 230 return cookies 231 232 def get_cookie(self, cookie_name): 233 234 """ 235 A framework-specific method which obtains cookie information from the 236 request. 237 238 Returns a cookie object for the given 'cookie_name' or None if no such 239 cookie exists. 240 """ 241 242 try: 243 return Cookie(cookie_name, self.trans.request().cookie(cookie_name)) 244 except KeyError: 245 return None 246 247 # Response-related methods. 248 249 def get_response_stream(self): 250 251 """ 252 A framework-specific method which returns the response stream for 253 the transaction. 254 """ 255 256 # Unicode can upset this operation. Using either the specified charset, 257 # the same charset as that used in the request, or a default encoding. 258 259 encoding = self.get_content_type().charset or "utf-8" 260 if self.content_type: 261 encoding = self.content_type.charset or encoding 262 return ConvertingStream(self.trans.response(), encoding) 263 264 def get_response_code(self): 265 266 """ 267 Get the response code associated with the transaction. If no response 268 code is defined, None is returned. 269 """ 270 271 # NOTE: Webware treats the response code as just another header. 272 273 status = self.trans.response().header("Status", None) 274 try: 275 if status is not None: 276 return int(status) 277 else: 278 return None 279 except ValueError: 280 return None 281 282 def set_response_code(self, response_code): 283 284 """ 285 Set the 'response_code' using a numeric constant defined in the HTTP 286 specification. 287 """ 288 289 self.trans.response().setStatus(response_code) 290 291 def set_header_value(self, header, value): 292 293 """ 294 Set the HTTP 'header' with the given 'value'. 295 """ 296 297 self.trans.response().setHeader(self.format_header_value(header), self.format_header_value(value)) 298 299 def set_content_type(self, content_type): 300 301 """ 302 A framework-specific method which sets the 'content_type' for the 303 response. 304 """ 305 306 # Remember the content type for encoding purposes later. 307 308 self.content_type = content_type 309 return self.trans.response().setHeader("Content-Type", self.format_content_type(content_type)) 310 311 # Higher level response-related methods. 312 313 def set_cookie(self, cookie): 314 315 """ 316 A framework-specific method which stores the given 'cookie' object in 317 the response. 318 """ 319 320 self.trans.response().addCookie(cookie) 321 322 def set_cookie_value(self, name, value, path=None, expires=None): 323 324 """ 325 A framework-specific method which stores a cookie with the given 'name' 326 and 'value' in the response. 327 328 The optional 'path' is a string which specifies the scope of the cookie, 329 and the optional 'expires' parameter is a value compatible with the 330 time.time function, and indicates the expiry date/time of the cookie. 331 """ 332 333 self.trans.response().setCookie(name, value, path, expires) 334 335 def delete_cookie(self, cookie_name): 336 337 """ 338 A framework-specific method which adds to the response a request that 339 the cookie with the given 'cookie_name' be deleted/discarded by the 340 client. 341 """ 342 343 self.trans.response().delCookie(cookie_name) 344 345 # Application-specific methods. 346 347 def set_user(self, username): 348 349 """ 350 An application-specific method which sets the user information with 351 'username' in the transaction. This affects subsequent calls to 352 'get_user'. 353 """ 354 355 self.user = username 356 357 # vim: tabstop=4 expandtab shiftwidth=4