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