1 #!/usr/bin/env python 2 3 "Mapping from names to resources." 4 5 import WebStack.Generic 6 7 class MapResource: 8 9 "A resource mapping names to other resources." 10 11 def __init__(self, mapping, directory_redirects=1): 12 13 """ 14 Initialise the resource with a 'mapping' of names to resources. The 15 'mapping' should be a dictionary-like object employing simple names 16 without "/" characters; the special value None is used where no name 17 corresponds to that used in the request path and may be used to map to 18 a "catch all" resource. 19 20 The optional 'directory_redirects' parameter, if set to a true value (as 21 is the default setting), causes a redirect adding a trailing "/" 22 character if the request path does not end with such a character. 23 """ 24 25 self.mapping = mapping 26 self.directory_redirects = directory_redirects 27 28 def respond(self, trans): 29 30 """ 31 Using the path information from the given transaction 'trans', invoke 32 mapped resources. Otherwise return an error condition. 33 """ 34 35 # Get the path info. 36 37 parts = trans.get_virtual_path_info().split("/") 38 39 # Where the published resource has a path info value defined (ie. its 40 # path info consists of a "/" character plus some other text), the first 41 # part should always be empty and there should always be a second part. 42 # Where the published resource has no path info defined, there will only 43 # be one part. In the latter case, we define the name to be None, 44 # although the name will not be relevant if directory_redirects is set. 45 46 if len(parts) > 1: 47 name = parts[1] 48 elif self.directory_redirects: 49 self.send_redirect(trans) 50 else: 51 name = None 52 53 # Get the mapped resource. 54 55 resource = self.mapping.get(name) 56 if resource is None: 57 resource = self.mapping.get(None) 58 59 # If a resource was found, change the transaction's path info. 60 # eg. "/this/next" -> "/next" 61 # eg. "/this/" -> "/" 62 # eg. "/this" -> "" 63 64 new_path = parts[0:1] + parts[2:] 65 new_path_info = "/".join(new_path) 66 trans.set_virtual_path_info(new_path_info) 67 68 # Invoke the transaction, transferring control completely. 69 70 if resource is not None: 71 resource.respond(trans) 72 return 73 74 # Otherwise, signal an error. 75 76 self.send_error(trans) 77 78 def send_error(self, trans): 79 80 "Send the error using the given 'trans'." 81 82 trans.set_response_code(404) 83 trans.set_content_type(WebStack.Generic.ContentType("text/plain")) 84 out = trans.get_response_stream() 85 out.write("Resource '%s' not found." % trans.get_path_info()) 86 87 def send_redirect(self, trans): 88 89 """ 90 Send a redirect using the given 'trans', adding a "/" character to the 91 end of the request path. 92 """ 93 94 query_string = trans.get_query_string() 95 if query_string: 96 query_string = "?" + query_string 97 98 trans.set_response_code(302) 99 trans.set_header_value("Location", trans.get_path_without_query() + "/" + query_string) 100 raise WebStack.Generic.EndOfResponse 101 102 # vim: tabstop=4 expandtab shiftwidth=4