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 if accept_preference is None: 98 return [] 99 100 accept_defs = accept_preference.split(",") 101 accept_prefs = [] 102 for accept_def in accept_defs: 103 t = accept_def.split(";") 104 if len(t) >= 1: 105 accept_prefs.append(t[0].strip()) 106 return accept_prefs 107 108 def convert_to_list(self, value): 109 110 """ 111 Returns a single element list containing 'value' if it is not itself a list, a 112 tuple, or None. If 'value' is a list then it is itself returned; if 'value' is a 113 tuple then a new list containing the same elements is returned; if 'value' is None 114 then an empty list is returned. 115 """ 116 117 if type(value) == type([]): 118 return value 119 elif type(value) == type(()): 120 return list(value) 121 elif value is None: 122 return [] 123 else: 124 return [value] 125 126 # Request-related methods. 127 128 def get_request_stream(self): 129 130 """ 131 A framework-specific method which returns the request stream for 132 the transaction. 133 """ 134 135 raise NotImplementedError, "get_request_stream" 136 137 def get_request_method(self): 138 139 """ 140 A framework-specific method which gets the request method. 141 """ 142 143 raise NotImplementedError, "get_request_method" 144 145 def get_headers(self): 146 147 """ 148 A framework-specific method which returns all request headers. 149 """ 150 151 raise NotImplementedError, "get_headers" 152 153 def get_header_values(self, key): 154 155 """ 156 A framework-specific method which returns a list of all request header 157 values associated with the given 'key'. Note that according to RFC 2616, 158 'key' is treated as a case-insensitive string. 159 """ 160 161 raise NotImplementedError, "get_header_values" 162 163 def get_content_type(self): 164 165 """ 166 A framework-specific method which gets the content type specified on the 167 request, along with the charset employed. 168 """ 169 170 raise NotImplementedError, "get_content_type" 171 172 def get_content_charsets(self): 173 174 """ 175 Returns the character set preferences. 176 """ 177 178 raise NotImplementedError, "get_content_charsets" 179 180 def get_content_languages(self): 181 182 """ 183 A framework-specific method which extracts language information from 184 the transaction. 185 """ 186 187 raise NotImplementedError, "get_content_languages" 188 189 def get_path(self): 190 191 """ 192 A framework-specific method which gets the entire path from the request. 193 """ 194 195 raise NotImplementedError, "get_path" 196 197 def get_path_info(self): 198 199 """ 200 A framework-specific method which gets the "path info" (the part of the 201 URL after the resource name handling the current request) from the 202 request. 203 """ 204 205 raise NotImplementedError, "get_path_info" 206 207 def get_query_string(self): 208 209 """ 210 A framework-specific method which gets the query string from the path in 211 the request. 212 """ 213 214 raise NotImplementedError, "get_query_string" 215 216 # Higher level request-related methods. 217 218 def get_fields_from_path(self): 219 220 """ 221 A framework-specific method which extracts the form fields from the 222 path specified in the transaction. The underlying framework may refuse 223 to supply fields from the path if handling a POST transaction. 224 225 Returns a dictionary mapping field names to lists of values (even if a 226 single value is associated with any given field name). 227 """ 228 229 raise NotImplementedError, "get_fields_from_path" 230 231 def get_fields_from_body(self): 232 233 """ 234 A framework-specific method which extracts the form fields from the 235 message body in the transaction. 236 237 Returns a dictionary mapping field names to lists of values (even if a 238 single value is associated with any given field name). 239 """ 240 241 raise NotImplementedError, "get_fields_from_body" 242 243 def get_user(self): 244 245 """ 246 A framework-specific method which extracts user information from the 247 transaction. 248 """ 249 250 raise NotImplementedError, "get_user" 251 252 def get_cookies(self): 253 254 """ 255 A framework-specific method which obtains cookie information from the 256 request. 257 258 Returns a dictionary mapping cookie names to cookie objects. 259 """ 260 261 raise NotImplementedError, "get_cookies" 262 263 def get_cookie(self, cookie_name): 264 265 """ 266 A framework-specific method which obtains cookie information from the 267 request. 268 269 Returns a cookie object for the given 'cookie_name' or None if no such 270 cookie exists. 271 """ 272 273 raise NotImplementedError, "get_cookie" 274 275 # Response-related methods. 276 277 def get_response_stream(self): 278 279 """ 280 A framework-specific method which returns the response stream for 281 the transaction. 282 """ 283 284 raise NotImplementedError, "get_response_stream" 285 286 def get_response_code(self): 287 288 """ 289 Get the response code associated with the transaction. If no response 290 code is defined, None is returned. 291 """ 292 293 raise NotImplementedError, "get_response_code" 294 295 def set_response_code(self, response_code): 296 297 """ 298 Set the 'response_code' using a numeric constant defined in the HTTP 299 specification. 300 """ 301 302 raise NotImplementedError, "set_response_code" 303 304 def set_header_value(self, header, value): 305 306 """ 307 Set the HTTP 'header' with the given 'value'. 308 """ 309 310 raise NotImplementedError, "set_header_value" 311 312 def set_content_type(self, content_type): 313 314 """ 315 A framework-specific method which sets the 'content_type' for the 316 response. 317 """ 318 319 raise NotImplementedError, "set_content_type" 320 321 # Higher level response-related methods. 322 323 def set_cookie(self, cookie): 324 325 """ 326 A framework-specific method which stores the given 'cookie' object in 327 the response. 328 """ 329 330 raise NotImplementedError, "set_cookie" 331 332 def set_cookie_value(self, name, value, path=None, expires=None): 333 334 """ 335 A framework-specific method which stores a cookie with the given 'name' 336 and 'value' in the response. 337 338 The optional 'path' is a string which specifies the scope of the cookie, 339 and the optional 'expires' parameter is a value compatible with the 340 time.time function, and indicates the expiry date/time of the cookie. 341 """ 342 343 raise NotImplementedError, "set_cookie_value" 344 345 def delete_cookie(self, cookie_name): 346 347 """ 348 A framework-specific method which adds to the response a request that 349 the cookie with the given 'cookie_name' be deleted/discarded by the 350 client. 351 """ 352 353 raise NotImplementedError, "delete_cookie" 354 355 class Resource: 356 357 "A generic resource interface." 358 359 def respond(self, trans): 360 361 """ 362 An application-specific method which performs activities on the basis of 363 the transaction object 'trans'. 364 """ 365 366 raise NotImplementedError, "respond" 367 368 class Authenticator: 369 370 "A generic authentication component." 371 372 def authenticate(self, trans): 373 374 """ 375 An application-specific method which authenticates the sender of the 376 request described by the transaction object 'trans'. This method should 377 consider 'trans' to be read-only and not attempt to change the state of 378 the transaction. 379 380 If the sender of the request is authenticated successfully, the result 381 of this method evaluates to true; otherwise the result of this method 382 evaluates to false. 383 """ 384 385 raise NotImplementedError, "authenticate" 386 387 def get_auth_type(self): 388 389 """ 390 An application-specific method which returns the authentication type to 391 be used. An example value is 'Basic' which specifies HTTP basic 392 authentication. 393 """ 394 395 raise NotImplementedError, "get_auth_type" 396 397 def get_realm(self): 398 399 """ 400 An application-specific method which returns the name of the realm for 401 which authentication is taking place. 402 """ 403 404 raise NotImplementedError, "get_realm" 405 406 # vim: tabstop=4 expandtab shiftwidth=4