1 #!/usr/bin/env python 2 3 """ 4 Mapping from names to resources. 5 6 Copyright (C) 2004, 2005 Paul Boddie <paul@boddie.org.uk> 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Lesser General Public 10 License as published by the Free Software Foundation; either 11 version 2.1 of the License, or (at your option) any later version. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Lesser General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public 19 License along with this library; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 """ 22 23 import WebStack.Generic 24 25 class MapResource: 26 27 "A resource mapping names to other resources." 28 29 def __init__(self, mapping, directory_redirects=1): 30 31 """ 32 Initialise the resource with a 'mapping' of names to resources. The 33 'mapping' should be a dictionary-like object employing simple names 34 without "/" characters; the special value None is used where no name 35 corresponds to that used in the request path and may be used to map to 36 a "catch all" resource. 37 38 The optional 'directory_redirects' parameter, if set to a true value (as 39 is the default setting), causes a redirect adding a trailing "/" 40 character if the request path does not end with such a character. 41 """ 42 43 self.mapping = mapping 44 self.directory_redirects = directory_redirects 45 46 def respond(self, trans): 47 48 """ 49 Using the path information from the given transaction 'trans', invoke 50 mapped resources. Otherwise return an error condition. 51 """ 52 53 # Get the path info. 54 55 parts = trans.get_virtual_path_info().split("/") 56 57 # Where the published resource has a path info value defined (ie. its 58 # path info consists of a "/" character plus some other text), the first 59 # part should always be empty and there should always be a second part. 60 # Where the published resource has no path info defined, there will only 61 # be one part. In the latter case, we define the name to be None, 62 # although the name will not be relevant if directory_redirects is set. 63 64 if len(parts) > 1: 65 name = parts[1] 66 elif self.directory_redirects: 67 self.send_redirect(trans) 68 else: 69 name = None 70 71 # Get the mapped resource. 72 73 resource = self.mapping.get(name) 74 if resource is None: 75 resource = self.mapping.get(None) 76 77 # If a resource was found, change the transaction's path info. 78 # eg. "/this/next" -> "/next" 79 # eg. "/this/" -> "/" 80 # eg. "/this" -> "" 81 82 new_path = parts[0:1] + parts[2:] 83 new_path_info = "/".join(new_path) 84 trans.set_virtual_path_info(new_path_info) 85 86 # Invoke the transaction, transferring control completely. 87 88 if resource is not None: 89 resource.respond(trans) 90 return 91 92 # Otherwise, signal an error. 93 94 self.send_error(trans) 95 96 def send_error(self, trans): 97 98 "Send the error using the given 'trans'." 99 100 trans.set_response_code(404) 101 trans.set_content_type(WebStack.Generic.ContentType("text/plain")) 102 out = trans.get_response_stream() 103 out.write("Resource '%s' not found." % trans.get_path_info()) 104 105 def send_redirect(self, trans): 106 107 """ 108 Send a redirect using the given 'trans', adding a "/" character to the 109 end of the request path. 110 """ 111 112 query_string = trans.get_query_string() 113 if query_string: 114 query_string = "?" + query_string 115 116 trans.set_response_code(302) 117 trans.set_header_value("Location", trans.get_path_without_query() + "/" + query_string) 118 raise WebStack.Generic.EndOfResponse 119 120 # vim: tabstop=4 expandtab shiftwidth=4