1 #!/usr/bin/env python 2 3 """ 4 Generic Web framework interfaces. 5 The WebStack architecture consists of the following layers: 6 7 * Framework - The underlying Web framework implementation. 8 * Adapter - Code operating under the particular framework which creates 9 WebStack abstractions and issues them to the application. 10 * Resources - Units of functionality operating within the hosted Web 11 application. 12 13 Resources can act as both content producers within an application and as request 14 dispatchers to other resources; in the latter role, they may be referred to as 15 directors. 16 """ 17 18 class ContentType: 19 20 "A container for content type information." 21 22 def __init__(self, content_type, charset=None): 23 self.content_type = content_type 24 self.charset = charset 25 26 class Transaction: 27 28 """ 29 A generic transaction interface containing framework-specific methods to be 30 overridden. 31 """ 32 33 def commit(self): 34 35 """ 36 A special method, synchronising the transaction with framework-specific 37 objects. 38 """ 39 40 pass 41 42 # Utility methods. 43 44 def parse_content_type(self, content_type_field): 45 46 """ 47 Determine the content type and charset from the supplied 48 'content_type_field' string. 49 """ 50 51 if content_type_field is None: 52 return ContentType(None, "iso-8859-1") 53 54 t = content_type_field.split("; charset=") 55 if len(t) == 1: 56 return ContentType(t[0], "iso-8859-1") 57 else: 58 return ContentType(t[0], t[1]) 59 60 def format_content_type(self, content_type): 61 62 """ 63 Format the given 'content_type' object, producing a string suitable for 64 the response header field. 65 """ 66 67 if content_type.charset: 68 field = "%s; charset=%s" % (content_type.content_type, content_type.charset) 69 else: 70 field = content_type.content_type 71 72 # Make sure that only ASCII is used in the header. 73 74 return field.encode("US-ASCII") 75 76 def format_header_value(self, value): 77 78 """ 79 Format the given header 'value'. Typically, this just ensures the usage 80 of US-ASCII. 81 """ 82 83 return value.encode("US-ASCII") 84 85 def parse_content_preferences(self, accept_preference): 86 87 """ 88 Returns the preferences as requested by the user agent. The preferences are 89 returned as a list of codes in the same order as they appeared in the 90 appropriate environment variable. In other words, the explicit weighting 91 criteria are ignored. 92 93 As the 'accept_preference' parameter, values for language and charset 94 preferences are appropriate. 95 """ 96 97 accept_defs = accept_preference.split(",") 98 accept_prefs = [] 99 for accept_def in accept_defs: 100 t = accept_def.split(";") 101 if len(t) >= 1: 102 accept_prefs.append(t[0].strip()) 103 return accept_prefs 104 105 def convert_to_list(self, value): 106 107 """ 108 Returns a single element list containing 'value' if it is not itself a list, a 109 tuple, or None. If 'value' is a list then it is itself returned; if 'value' is a 110 tuple then a new list containing the same elements is returned; if 'value' is None 111 then an empty list is returned. 112 """ 113 114 if type(value) == type([]): 115 return value 116 elif type(value) == type(()): 117 return list(value) 118 elif value is None: 119 return [] 120 else: 121 return [value] 122 123 # Request-related methods. 124 125 def get_request_stream(self): 126 127 """ 128 A framework-specific method which returns the request stream for 129 the transaction. 130 """ 131 132 raise NotImplementedError, "get_request_stream" 133 134 def get_request_method(self): 135 136 """ 137 A framework-specific method which gets the request method. 138 """ 139 140 raise NotImplementedError, "get_request_method" 141 142 def get_headers(self): 143 144 """ 145 A framework-specific method which returns all request headers. 146 """ 147 148 raise NotImplementedError, "get_headers" 149 150 def get_header_values(self, key): 151 152 """ 153 A framework-specific method which returns a list of all request header 154 values associated with the given 'key'. Note that according to RFC 2616, 155 'key' is treated as a case-insensitive string. 156 """ 157 158 raise NotImplementedError, "get_header_values" 159 160 def get_content_type(self): 161 162 """ 163 A framework-specific method which gets the content type specified on the 164 request, along with the charset employed. 165 """ 166 167 raise NotImplementedError, "get_content_type" 168 169 def get_content_charsets(self): 170 171 """ 172 Returns the character set preferences. 173 """ 174 175 raise NotImplementedError, "get_content_charsets" 176 177 def get_content_languages(self): 178 179 """ 180 A framework-specific method which extracts language information from 181 the transaction. 182 """ 183 184 raise NotImplementedError, "get_content_languages" 185 186 def get_path(self): 187 188 """ 189 A framework-specific method which gets the entire path from the request. 190 """ 191 192 raise NotImplementedError, "get_path" 193 194 def get_path_info(self): 195 196 """ 197 A framework-specific method which gets the "path info" (the part of the 198 URL after the resource name handling the current request) from the 199 request. 200 """ 201 202 raise NotImplementedError, "get_path_info" 203 204 def get_query_string(self): 205 206 """ 207 A framework-specific method which gets the query string from the path in 208 the request. 209 """ 210 211 raise NotImplementedError, "get_query_string" 212 213 # Higher level request-related methods. 214 215 def get_fields_from_path(self): 216 217 """ 218 A framework-specific method which extracts the form fields from the 219 path specified in the transaction. The underlying framework may refuse 220 to supply fields from the path if handling a POST transaction. 221 222 Returns a dictionary mapping field names to lists of values (even if a 223 single value is associated with any given field name). 224 """ 225 226 raise NotImplementedError, "get_fields_from_path" 227 228 def get_fields_from_body(self): 229 230 """ 231 A framework-specific method which extracts the form fields from the 232 message body in the transaction. 233 234 Returns a dictionary mapping field names to lists of values (even if a 235 single value is associated with any given field name). 236 """ 237 238 raise NotImplementedError, "get_fields_from_body" 239 240 def get_user(self): 241 242 """ 243 A framework-specific method which extracts user information from the 244 transaction. 245 """ 246 247 raise NotImplementedError, "get_user" 248 249 def get_cookies(self): 250 251 """ 252 A framework-specific method which obtains cookie information from the 253 request. 254 255 Returns a dictionary mapping cookie names to cookie objects. 256 """ 257 258 raise NotImplementedError, "get_cookies" 259 260 # Response-related methods. 261 262 def get_response_stream(self): 263 264 """ 265 A framework-specific method which returns the response stream for 266 the transaction. 267 """ 268 269 raise NotImplementedError, "get_response_stream" 270 271 def get_response_code(self): 272 273 """ 274 Get the response code associated with the transaction. If no response 275 code is defined, None is returned. 276 """ 277 278 raise NotImplementedError, "get_response_code" 279 280 def set_response_code(self, response_code): 281 282 """ 283 Set the 'response_code' using a numeric constant defined in the HTTP 284 specification. 285 """ 286 287 raise NotImplementedError, "set_response_code" 288 289 def set_header_value(self, header, value): 290 291 """ 292 Set the HTTP 'header' with the given 'value'. 293 """ 294 295 raise NotImplementedError, "set_header_value" 296 297 def set_content_type(self, content_type): 298 299 """ 300 A framework-specific method which sets the 'content_type' for the 301 response. 302 """ 303 304 raise NotImplementedError, "set_content_type" 305 306 # Higher level response-related methods. 307 308 def set_cookie(self, cookie): 309 310 """ 311 A framework-specific method which stores the given 'cookie' object in 312 the response. 313 """ 314 315 raise NotImplementedError, "set_cookie" 316 317 def set_cookie_value(self, name, value, path=None, expires=None): 318 319 """ 320 A framework-specific method which stores a cookie with the given 'name' 321 and 'value' in the response. 322 323 The optional 'path' is a string which specifies the scope of the cookie, 324 and the optional 'expires' parameter is a value compatible with the 325 time.time function, and indicates the expiry date/time of the cookie. 326 """ 327 328 raise NotImplementedError, "set_cookie_value" 329 330 def delete_cookie(self, cookie_name): 331 332 """ 333 A framework-specific method which adds to the response a request that 334 the cookie with the given 'cookie_name' be deleted/discarded by the 335 client. 336 """ 337 338 raise NotImplementedError, "delete_cookie" 339 340 class Resource: 341 342 "A generic resource interface." 343 344 def respond(self, trans): 345 346 """ 347 An application-specific method which performs activities on the basis of 348 the transaction object 'trans'. 349 """ 350 351 raise NotImplementedError, "respond" 352 353 class Authenticator: 354 355 "A generic authentication component." 356 357 def authenticate(self, trans): 358 359 """ 360 An application-specific method which authenticates the sender of the 361 request described by the transaction object 'trans'. This method should 362 consider 'trans' to be read-only and not attempt to change the state of 363 the transaction. 364 365 If the sender of the request is authenticated successfully, the result 366 of this method evaluates to true; otherwise the result of this method 367 evaluates to false. 368 """ 369 370 raise NotImplementedError, "authenticate" 371 372 def get_auth_type(self): 373 374 """ 375 An application-specific method which returns the authentication type to 376 be used. An example value is 'Basic' which specifies HTTP basic 377 authentication. 378 """ 379 380 raise NotImplementedError, "get_auth_type" 381 382 def get_realm(self): 383 384 """ 385 An application-specific method which returns the name of the realm for 386 which authentication is taking place. 387 """ 388 389 raise NotImplementedError, "get_realm" 390 391 # vim: tabstop=4 expandtab shiftwidth=4