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