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