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_info(self): 126 127 """ 128 A framework-specific method which gets the "path info" (the part of the 129 URL after the resource name handling the current request) from the 130 request. 131 """ 132 133 return self.trans.request().extraURLPath() 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 return self.trans.request().queryString() 143 144 # Higher level request-related methods. 145 146 def get_fields_from_path(self): 147 148 """ 149 A framework-specific method which extracts the form fields from the 150 path specified in the transaction. The underlying framework may refuse 151 to supply fields from the path if handling a POST transaction. 152 153 Returns a dictionary mapping field names to lists of values (even if a 154 single value is associated with any given field name). 155 """ 156 157 return parse_qs(self.get_query_string(), keep_blank_values=1) 158 159 def get_fields_from_body(self): 160 161 """ 162 A framework-specific method which extracts the form fields from the 163 message body in the transaction. 164 165 Returns a dictionary mapping field names to lists of values (even if a 166 single value is associated with any given field name). 167 """ 168 169 # Fix the non-list results. 170 171 fields = {} 172 for field_name, field_value in self.trans.request().fields().items(): 173 if type(field_value) == type([]): 174 fields[field_name] = field_value 175 else: 176 fields[field_name] = [field_value] 177 return fields 178 179 def get_user(self): 180 181 """ 182 A framework-specific method which extracts user information from the 183 transaction. 184 185 Returns a username as a string or None if no user is defined. 186 """ 187 188 # NOTE: Webware relies entirely on a CGI-style environment where the 189 # NOTE: actual headers are not available. Therefore, the Web server must 190 # NOTE: itself be set up to provide user support. 191 192 if self.user is not None: 193 return self.user 194 195 try: 196 return self.trans.request().remoteUser() 197 except KeyError, exc: 198 return None 199 200 def get_cookies(self): 201 202 """ 203 A framework-specific method which obtains cookie information from the 204 request. 205 206 Returns a dictionary mapping cookie names to cookie objects. 207 """ 208 209 cookies = {} 210 for name, value in self.trans.request().cookies().items(): 211 cookies[name] = Cookie(name, value) 212 return cookies 213 214 def get_cookie(self, cookie_name): 215 216 """ 217 A framework-specific method which obtains cookie information from the 218 request. 219 220 Returns a cookie object for the given 'cookie_name' or None if no such 221 cookie exists. 222 """ 223 224 try: 225 return Cookie(cookie_name, self.trans.request().cookie(cookie_name)) 226 except KeyError: 227 return None 228 229 # Response-related methods. 230 231 def get_response_stream(self): 232 233 """ 234 A framework-specific method which returns the response stream for 235 the transaction. 236 """ 237 238 return self.trans.response() 239 240 def get_response_code(self): 241 242 """ 243 Get the response code associated with the transaction. If no response 244 code is defined, None is returned. 245 """ 246 247 # NOTE: Webware treats the response code as just another header. 248 249 status = self.trans.response().header("Status", None) 250 try: 251 if status is not None: 252 return int(status) 253 else: 254 return None 255 except ValueError: 256 return None 257 258 def set_response_code(self, response_code): 259 260 """ 261 Set the 'response_code' using a numeric constant defined in the HTTP 262 specification. 263 """ 264 265 self.trans.response().setStatus(response_code) 266 267 def set_header_value(self, header, value): 268 269 """ 270 Set the HTTP 'header' with the given 'value'. 271 """ 272 273 self.trans.response().setHeader(self.format_header_value(header), self.format_header_value(value)) 274 275 def set_content_type(self, content_type): 276 277 """ 278 A framework-specific method which sets the 'content_type' for the 279 response. 280 """ 281 282 return self.trans.response().setHeader("Content-Type", self.format_content_type(content_type)) 283 284 # Higher level response-related methods. 285 286 def set_cookie(self, cookie): 287 288 """ 289 A framework-specific method which stores the given 'cookie' object in 290 the response. 291 """ 292 293 self.trans.response().addCookie(cookie) 294 295 def set_cookie_value(self, name, value, path=None, expires=None): 296 297 """ 298 A framework-specific method which stores a cookie with the given 'name' 299 and 'value' in the response. 300 301 The optional 'path' is a string which specifies the scope of the cookie, 302 and the optional 'expires' parameter is a value compatible with the 303 time.time function, and indicates the expiry date/time of the cookie. 304 """ 305 306 self.trans.response().setCookie(name, value, path, expires) 307 308 def delete_cookie(self, cookie_name): 309 310 """ 311 A framework-specific method which adds to the response a request that 312 the cookie with the given 'cookie_name' be deleted/discarded by the 313 client. 314 """ 315 316 self.trans.response().delCookie(cookie_name) 317 318 # Application-specific methods. 319 320 def set_user(self, username): 321 322 """ 323 An application-specific method which sets the user information with 324 'username' in the transaction. This affects subsequent calls to 325 'get_user'. 326 """ 327 328 self.user = username 329 330 # vim: tabstop=4 expandtab shiftwidth=4