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_info(self): 103 104 """ 105 A framework-specific method which gets the "path info" (the part of the 106 URL after the resource name handling the current request) from the 107 request. 108 """ 109 110 return "/%s" % "/".join(self.trans.postpath) 111 112 def get_query_string(self): 113 114 """ 115 A framework-specific method which gets the query string from the path in 116 the request. 117 """ 118 119 t = self.get_path().split("?") 120 if len(t) == 1: 121 return "" 122 else: 123 124 # NOTE: Overlook erroneous usage of "?" characters in the path. 125 126 return "?".join(t[1:]) 127 128 # Higher level request-related methods. 129 130 def get_fields_from_path(self): 131 132 """ 133 A framework-specific method which extracts the form fields from the 134 path specified in the transaction. The underlying framework may refuse 135 to supply fields from the path if handling a POST transaction. 136 137 Returns a dictionary mapping field names to lists of values (even if a 138 single value is associated with any given field name). 139 """ 140 141 return parse_qs(self.get_query_string(), keep_blank_values=1) 142 143 def get_fields_from_body(self): 144 145 """ 146 A framework-specific method which extracts the form fields from the 147 message body in the transaction. 148 149 Returns a dictionary mapping field names to lists of values (even if a 150 single value is associated with any given field name). 151 """ 152 153 return self.trans.args 154 155 def get_user(self): 156 157 """ 158 A framework-specific method which extracts user information from the 159 transaction. 160 161 Returns a username as a string or None if no user is defined. 162 """ 163 164 # Twisted makes headers lower case. 165 166 if self.user is not None: 167 return self.user 168 169 auth_header = self.get_headers().get("authorization") 170 if auth_header: 171 return UserInfo(auth_header).username 172 else: 173 return None 174 175 def get_cookies(self): 176 177 """ 178 A framework-specific method which obtains cookie information from the 179 request. 180 181 Returns a dictionary mapping cookie names to cookie objects. 182 NOTE: Twisted does not seem to support this operation via methods. Thus, 183 NOTE: direct access has been employed to get the dictionary. 184 NOTE: Twisted also returns a plain string - a Cookie object is therefore 185 NOTE: introduced. 186 """ 187 188 cookies = {} 189 for name, value in self.trans.received_cookies.items(): 190 cookies[name] = Cookie(name, value) 191 return cookies 192 193 def get_cookie(self, cookie_name): 194 195 """ 196 A framework-specific method which obtains cookie information from the 197 request. 198 199 Returns a cookie object for the given 'cookie_name' or None if no such 200 cookie exists. 201 NOTE: Twisted also returns a plain string - a Cookie object is therefore 202 NOTE: introduced. 203 """ 204 205 return Cookie(cookie_name, self.trans.getCookie(cookie_name)) 206 207 # Response-related methods. 208 209 def get_response_stream(self): 210 211 """ 212 A framework-specific method which returns the response stream for 213 the transaction. 214 """ 215 216 return self.trans 217 218 def get_response_code(self): 219 220 """ 221 Get the response code associated with the transaction. If no response 222 code is defined, None is returned. 223 """ 224 225 # NOTE: Accessing the request attribute directly. 226 227 return self.trans.code 228 229 def set_response_code(self, response_code): 230 231 """ 232 Set the 'response_code' using a numeric constant defined in the HTTP 233 specification. 234 """ 235 236 self.trans.setResponseCode(response_code) 237 238 def set_header_value(self, header, value): 239 240 """ 241 Set the HTTP 'header' with the given 'value'. 242 """ 243 244 self.trans.setHeader(self.format_header_value(header), self.format_header_value(value)) 245 246 def set_content_type(self, content_type): 247 248 """ 249 A framework-specific method which sets the 'content_type' for the 250 response. 251 """ 252 253 self.trans.setHeader("Content-Type", self.format_content_type(content_type)) 254 255 # Higher level response-related methods. 256 257 def set_cookie(self, cookie): 258 259 """ 260 A framework-specific method which stores the given 'cookie' object in 261 the response. 262 """ 263 264 self.trans.addCookie(cookie.name, cookie.value, expires=cookie.expires, path=cookie.path) 265 266 def set_cookie_value(self, name, value, path=None, expires=None): 267 268 """ 269 A framework-specific method which stores a cookie with the given 'name' 270 and 'value' in the response. 271 272 The optional 'path' is a string which specifies the scope of the cookie, 273 and the optional 'expires' parameter is a value compatible with the 274 time.time function, and indicates the expiry date/time of the cookie. 275 """ 276 277 self.trans.addCookie(self.format_header_value(name), 278 self.format_header_value(value), expires=expires, path=path) 279 280 def delete_cookie(self, cookie_name): 281 282 """ 283 A framework-specific method which adds to the response a request that 284 the cookie with the given 'cookie_name' be deleted/discarded by the 285 client. 286 """ 287 288 # Create a special cookie, given that we do not know whether the browser 289 # has been sent the cookie or not. 290 # NOTE: Magic discovered in Webware. 291 292 self.trans.addCookie(cookie_name, "", expires=0, path="/", max_age=0) 293 294 # Application-specific methods. 295 296 def set_user(self, username): 297 298 """ 299 An application-specific method which sets the user information with 300 'username' in the transaction. This affects subsequent calls to 301 'get_user'. 302 """ 303 304 self.user = username 305 306 # vim: tabstop=4 expandtab shiftwidth=4