1 #!/usr/bin/env python 2 3 """ 4 Twisted classes. 5 """ 6 7 import Generic 8 from Helpers.Auth import UserInfo 9 from Helpers.Request import Cookie 10 from cgi import parse_qs 11 12 class Transaction(Generic.Transaction): 13 14 """ 15 Twisted transaction interface. 16 """ 17 18 def __init__(self, trans): 19 20 "Initialise the transaction using the Twisted transaction 'trans'." 21 22 self.trans = trans 23 self.user = None 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 return self.trans.content 35 36 def get_request_method(self): 37 38 """ 39 A framework-specific method which gets the request method. 40 """ 41 42 return self.trans.method 43 44 def get_headers(self): 45 46 """ 47 A framework-specific method which returns all request headers as a 48 dictionary-like object mapping header names to values. 49 NOTE: If duplicate header names are permitted, then this interface will 50 NOTE: need to change. 51 """ 52 53 return self.trans.received_headers 54 55 def get_header_values(self, key): 56 57 """ 58 A framework-specific method which returns a list of all request header 59 values associated with the given 'key'. Note that according to RFC 2616, 60 'key' is treated as a case-insensitive string. 61 """ 62 63 # Twisted does not convert the header key to lower case (which is the 64 # stored representation). 65 66 return self.convert_to_list(self.trans.received_headers.get(key.lower())) 67 68 def get_content_type(self): 69 70 """ 71 A framework-specific method which gets the content type specified on the 72 request, along with the charset employed. 73 """ 74 75 return self.parse_content_type(self.trans.getHeader("Content-Type")) 76 77 def get_content_charsets(self): 78 79 """ 80 Returns the character set preferences. 81 """ 82 83 return self.parse_content_preferences(self.trans.getHeader("Accept-Language")) 84 85 def get_content_languages(self): 86 87 """ 88 A framework-specific method which extracts language information from 89 the transaction. 90 """ 91 92 return self.parse_content_preferences(self.trans.getHeader("Accept-Charset")) 93 94 def get_path(self): 95 96 """ 97 A framework-specific method which gets the entire path from the request. 98 """ 99 100 return self.trans.uri 101 102 def get_path_without_query(self): 103 104 """ 105 A framework-specific method which gets the entire path from the request 106 minus the query string. 107 """ 108 109 return self.get_path().split("?")[0] 110 111 def get_path_info(self): 112 113 """ 114 A framework-specific method which gets the "path info" (the part of the 115 URL after the resource name handling the current request) from the 116 request. 117 """ 118 119 return "/%s" % "/".join(self.trans.postpath) 120 121 def get_query_string(self): 122 123 """ 124 A framework-specific method which gets the query string from the path in 125 the request. 126 """ 127 128 t = self.get_path().split("?") 129 if len(t) == 1: 130 return "" 131 else: 132 133 # NOTE: Overlook erroneous usage of "?" characters in the path. 134 135 return "?".join(t[1:]) 136 137 # Higher level request-related methods. 138 139 def get_fields_from_path(self): 140 141 """ 142 A framework-specific method which extracts the form fields from the 143 path specified in the transaction. The underlying framework may refuse 144 to supply fields from the path if handling a POST transaction. 145 146 Returns a dictionary mapping field names to lists of values (even if a 147 single value is associated with any given field name). 148 """ 149 150 return parse_qs(self.get_query_string(), keep_blank_values=1) 151 152 def get_fields_from_body(self): 153 154 """ 155 A framework-specific method which extracts the form fields from the 156 message body in the transaction. 157 158 Returns a dictionary mapping field names to lists of values (even if a 159 single value is associated with any given field name). 160 """ 161 162 return self.trans.args 163 164 def get_user(self): 165 166 """ 167 A framework-specific method which extracts user information from the 168 transaction. 169 170 Returns a username as a string or None if no user is defined. 171 """ 172 173 # Twisted makes headers lower case. 174 175 if self.user is not None: 176 return self.user 177 178 auth_header = self.get_headers().get("authorization") 179 if auth_header: 180 return UserInfo(auth_header).username 181 else: 182 return None 183 184 def get_cookies(self): 185 186 """ 187 A framework-specific method which obtains cookie information from the 188 request. 189 190 Returns a dictionary mapping cookie names to cookie objects. 191 NOTE: Twisted does not seem to support this operation via methods. Thus, 192 NOTE: direct access has been employed to get the dictionary. 193 NOTE: Twisted also returns a plain string - a Cookie object is therefore 194 NOTE: introduced. 195 """ 196 197 cookies = {} 198 for name, value in self.trans.received_cookies.items(): 199 cookies[name] = Cookie(name, value) 200 return cookies 201 202 def get_cookie(self, cookie_name): 203 204 """ 205 A framework-specific method which obtains cookie information from the 206 request. 207 208 Returns a cookie object for the given 'cookie_name' or None if no such 209 cookie exists. 210 NOTE: Twisted also returns a plain string - a Cookie object is therefore 211 NOTE: introduced. 212 """ 213 214 return Cookie(cookie_name, self.trans.getCookie(cookie_name)) 215 216 # Response-related methods. 217 218 def get_response_stream(self): 219 220 """ 221 A framework-specific method which returns the response stream for 222 the transaction. 223 """ 224 225 return self.trans 226 227 def get_response_code(self): 228 229 """ 230 Get the response code associated with the transaction. If no response 231 code is defined, None is returned. 232 """ 233 234 # NOTE: Accessing the request attribute directly. 235 236 return self.trans.code 237 238 def set_response_code(self, response_code): 239 240 """ 241 Set the 'response_code' using a numeric constant defined in the HTTP 242 specification. 243 """ 244 245 self.trans.setResponseCode(response_code) 246 247 def set_header_value(self, header, value): 248 249 """ 250 Set the HTTP 'header' with the given 'value'. 251 """ 252 253 self.trans.setHeader(self.format_header_value(header), self.format_header_value(value)) 254 255 def set_content_type(self, content_type): 256 257 """ 258 A framework-specific method which sets the 'content_type' for the 259 response. 260 """ 261 262 self.trans.setHeader("Content-Type", self.format_content_type(content_type)) 263 264 # Higher level response-related methods. 265 266 def set_cookie(self, cookie): 267 268 """ 269 A framework-specific method which stores the given 'cookie' object in 270 the response. 271 """ 272 273 self.trans.addCookie(cookie.name, cookie.value, expires=cookie.expires, path=cookie.path) 274 275 def set_cookie_value(self, name, value, path=None, expires=None): 276 277 """ 278 A framework-specific method which stores a cookie with the given 'name' 279 and 'value' in the response. 280 281 The optional 'path' is a string which specifies the scope of the cookie, 282 and the optional 'expires' parameter is a value compatible with the 283 time.time function, and indicates the expiry date/time of the cookie. 284 """ 285 286 self.trans.addCookie(self.format_header_value(name), 287 self.format_header_value(value), expires=expires, path=path) 288 289 def delete_cookie(self, cookie_name): 290 291 """ 292 A framework-specific method which adds to the response a request that 293 the cookie with the given 'cookie_name' be deleted/discarded by the 294 client. 295 """ 296 297 # Create a special cookie, given that we do not know whether the browser 298 # has been sent the cookie or not. 299 # NOTE: Magic discovered in Webware. 300 301 self.trans.addCookie(cookie_name, "", expires=0, path="/", max_age=0) 302 303 # Application-specific methods. 304 305 def set_user(self, username): 306 307 """ 308 An application-specific method which sets the user information with 309 'username' in the transaction. This affects subsequent calls to 310 'get_user'. 311 """ 312 313 self.user = username 314 315 # vim: tabstop=4 expandtab shiftwidth=4