Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Changeset 11

Show
Ignore:
Timestamp:
11/21/04 11:25:38
Author:
rdelon
Message:

--

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/cherrypy/_cphttptools.py

    r9 r11  
    2525monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 
    2626 
     27class IndexRedirect(Exception): pass 
     28 
    2729def parseFirstLine(data): 
    2830    cpg.request.path = data.split()[1] 
    29     if  cpg.request.path[0] == '/': 
    30         cpg.request.path = cpg.request.path[1:] 
     31    cpg.request.queryString = "" 
    3132    cpg.request.browserUrl = cpg.request.path 
    3233    cpg.request.paramMap = {} 
     
    6263                    else: 
    6364                        cpg.request.paramMap[key] = value 
     65        cpg.request.queryString = cpg.request.path[i+1:] 
    6466        cpg.request.path = cpg.request.path[:i] 
    65  
    66     if cpg.request.path and cpg.request.path[-1] == '/': 
    67         cpg.request.path = cpg.request.path[:-1] # Remove trailing '/' if any 
    6867 
    6968def parsePostData(rfile): 
     
    281280        cpg.response.simpleCookie[cpg.configOption.sessionCookieName]['version'] = 1 
    282281 
    283     path = cpg.request.path 
    284  
    285     # Traverse path: 
    286     # for /a/b?arg=val, we'll try: 
    287     #   root.a.b.index(arg='val') 
    288     #   root.a.b(arg='val') 
    289     #   root.a.b.default(arg='val') 
    290     #   root.a.default('b', arg='val') 
    291     #   root.default('a', 'b', arg='val') 
    292  
    293     # Also, we ignore trailing slashes 
    294  
    295     # Also, a method has to have ".exposed = True" in order to be exposed 
    296  
    297     if not path: 
    298         pathList = [] 
    299     else: 
    300         pathList = path.split('/') 
    301  
    302     pathList = ['root'] + pathList 
    303  
    304     # try root.a.b, then root.a, then root 
    305     func = None 
    306  
    307     obj = cpg 
    308     previousObj = None 
    309     objList = [] 
    310     searchedPathList = [] 
    311     myPath = '' 
    312     bestKnownDefaultMethod = None 
    313     # Successively get objects from the path: 'root', then 'a' then 'b' 
    314     for pathItem in pathList: 
    315         previousObj = obj 
    316         try: 
    317             # find contained object 
    318             obj = getattr(obj, pathItem) 
    319  
    320             # add object 
    321             objList.append(obj) 
    322             searchedPathList.append(pathItem) 
    323  
    324             # if found object has a default method, remember it for later 
    325             default = getattr(obj, 'default', None) 
    326             if default: 
    327                 # TODO: check if default is callable? 
    328                 bestKnownDefaultMethod = default 
    329                 bestKnownDefaultMethodMyPath = '/'.join(searchedPathList[1:]) 
    330  
    331         except AttributeError: 
    332             break 
    333  
    334     # TODO: the following code could probably be KISSed somewhat. 
    335  
    336     if len(objList) == len(pathList): 
    337         # root_a_b exists 
    338         root_a_b = objList[-1] 
    339  
    340         # Try root.a.b.index() 
    341         root_a_b_dot_index = getattr(root_a_b, 'index', None) 
    342         if root_a_b_dot_index and getattr(root_a_b_dot_index, 'exposed', None): 
    343             myPath = '/'.join(pathList[1:]) 
    344             func = root_a_b_dot_index 
    345         else: 
    346             # Try root.a.b.default() 
    347             root_a_b_dot_default = getattr(root_a_b, 'default', None) 
    348             if root_a_b_dot_default and getattr(root_a_b_dot_default, 'exposed', None): 
    349                 # XXX: I don't think this ever gets called? 
    350                 myPath = 'root_a_b_dot_default' 
    351                 func = root_a_b_dot_default 
    352             elif callable(root_a_b) and getattr(root_a_b, 'exposed', None): 
    353                 # We use root.a.b() 
    354                 myPath = '/'.join(pathList[1:-1]) 
    355                 func = root_a_b 
    356  
    357     if func == None: 
    358         # None of these exist: use the default method we found earlier 
    359         if bestKnownDefaultMethod: 
    360             func = bestKnownDefaultMethod 
    361             myPath = bestKnownDefaultMethodMyPath 
    362  
    363     if func == None: 
    364         raise cperror.NotFound 
    365  
    366     myPath += '/' 
    367     if len(myPath) > 1: 
    368         myPath = '/' + myPath 
    369  
    370     cpg.request.objectPath = myPath 
    371     cpg.request.virtualPath = cpg.request.path[len(myPath)-1:] 
    372     cpg.response.body = func(**(cpg.request.paramMap)) 
     282    try: 
     283        func, objectPathList, virtualPathList = mapPathToObject() 
     284    except IndexRedirect, inst: 
     285        # For an IndexRedirect, we don't go through the regular 
     286        #   mechanism: we return the redirect immediately 
     287        newUrl = canonicalizeUrl(inst.args[0]) 
     288        wfile.write('%s 302\r\n' % (cpg.response.headerMap['protocolVersion'])) 
     289        cpg.response.headerMap['Location'] = newUrl 
     290        for key, valueList in cpg.response.headerMap.items(): 
     291            if key not in ('Status', 'protocolVersion'): 
     292                if type(valueList) != type([]): valueList = [valueList] 
     293                for value in valueList: 
     294                    wfile.write('%s: %s\r\n'%(key, value)) 
     295        wfile.write('\r\n') 
     296        return 
     297          
     298    cpg.request.objectPath = '/'.join(objectPathList) 
     299    cpg.response.body = func(*virtualPathList, **(cpg.request.paramMap)) 
    373300 
    374301    if cpg.response.sendResponse: 
     
    382309    return sha.sha(s).hexdigest() 
    383310 
    384  
     311def getObjFromPath(objPathList, objCache): 
     312    """ For a given objectPathList (like ['root', 'a', 'b', 'index']), 
     313        return the object (or None if it doesn't exist). 
     314        Also keep a cache for maximum efficiency 
     315    """ 
     316    if not objPathList: return cpg 
     317    cacheKey = tuple(objPathList) 
     318    if cacheKey in objCache: return objCache[cacheKey] 
     319    previousObj = getObjFromPath(objPathList[:-1], objCache) 
     320    obj = getattr(previousObj, objPathList[-1], None) 
     321    objCache[cacheKey] = obj 
     322    return obj 
     323 
     324def mapPathToObject(): 
     325    # Traverse path: 
     326    # for /a/b?arg=val, we'll try: 
     327    #   root.a.b.index -> redirect to /a/b/?arg=val 
     328    #   root.a.b.default(arg='val') -> redirect to /a/b/?arg=val 
     329    #   root.a.b(arg='val') 
     330    #   root.a.default('b', arg='val') 
     331    #   root.default('a', 'b', arg='val') 
     332 
     333    # Also, we ignore trailing slashes 
     334    # Also, a method has to have ".exposed = True" in order to be exposed 
     335 
     336    path = cpg.request.path 
     337    if path.startswith('/'): path = path[1:] # Remove leading slash 
     338    if path.endswith('/'): path = path[:-1] # Remove trailing slash 
     339 
     340    if not path: 
     341        objectPathList = [] 
     342    else: 
     343        objectPathList = path.split('/') 
     344    objectPathList = ['root'] + objectPathList + ['index'] 
     345 
     346    # Try successive objects... (and also keep the remaining object list) 
     347    objCache = {} 
     348    isFirst = True 
     349    isDefault = False 
     350    foundIt = False 
     351    virtualPathList = [] 
     352    while objectPathList: 
     353        candidate = getObjFromPath(objectPathList, objCache) 
     354        if callable(candidate) and getattr(candidate, 'exposed', False): 
     355            foundIt = True 
     356            break 
     357        # Couldn't find the object: pop one from the list and try "default" 
     358        lastObj = objectPathList.pop() 
     359        if not isFirst: 
     360            virtualPathList.insert(0, lastObj) 
     361            objectPathList.append('default') 
     362            candidate = getObjFromPath(objectPathList, objCache) 
     363            if callable(candidate) and getattr(candidate, 'exposed', False): 
     364                foundIt = True 
     365                isDefault = True 
     366                break 
     367            objectPathList.pop() # Remove "default" 
     368        isFirst = False 
     369 
     370    # Check results of traversal 
     371    if not foundIt: 
     372        raise cperror.NotFound # We didn't find anything 
     373 
     374    if isFirst: 
     375        # We found the extra ".index" 
     376        # Check if the original path had a trailing slash (otherwise, do 
     377        #   a redirect) 
     378        if cpg.request.path[-1] != '/': 
     379            newUrl = cpg.request.path + '/' 
     380            if cpg.request.queryString: newUrl += cpg.request.queryString 
     381            raise IndexRedirect(newUrl) 
     382 
     383    return candidate, objectPathList, virtualPathList 
     384     
     385def canonicalizeUrl(newUrl): 
     386    if not newUrl.startswith('http://') and not newUrl.startswith('https://'): 
     387        # If newUrl is not canonical, we must make it canonical 
     388        if newUrl[0] == '/': 
     389            # URL was absolute: we just add the request.base in front of it 
     390            newUrl = cpg.request.base + newUrl 
     391        else: 
     392            # URL was relative 
     393            if cpg.request.browserUrl == cpg.request.base: 
     394                # browserUrl is request.base 
     395                newUrl = cpg.request.base + '/' + newUrl 
     396            else: 
     397                newUrl = cpg.request.browserUrl[:i+1] + newUrl 
     398    return newUrl 
  • trunk/cherrypy/tutorial/tutorial07.py

    r8 r11  
    3333 
    3434 
    35     def default(self): 
     35    def default(self, user): 
    3636        # Here we react depending on the virtualPath -- the part of the 
    3737        # path that could not be mapped to an object method. In a real 
    3838        # application, we would probably do some database lookups here 
    3939        # instead of the silly if/elif/else construct. 
    40         if cpg.request.virtualPath == 'remi': 
     40        if user == 'remi': 
    4141            out = "Remi Delon, CherryPy lead developer" 
    42         elif cpg.request.virtualPath == 'hendrik': 
     42        elif user == 'hendrik': 
    4343            out = "Hendrik Mans, CherryPy co-developer & crazy German" 
    44         elif cpg.request.virtualPath == 'lorenzo': 
     44        elif user == 'lorenzo': 
    4545            out = "Lorenzo Lamas, famous actor and singer!" 
    4646        else: 
  • trunk/cherrypy/unittest/testObjectMapping.py

    r8 r11  
    2323        return name 
    2424    index.exposed = True 
     25    def default(self, *params): 
     26        return "default:"+repr(params) 
     27    default.exposed = True 
     28    def other(self): 
     29        return "other" 
     30    other.exposed = True 
     31    def notExposed(self): 
     32        return "not exposed" 
     33class Dir1: 
     34    def index(self): 
     35        return "index for dir1" 
     36    index.exposed = True 
     37    def default(self, *params): 
     38        return repr(params) 
     39    default.exposed = True 
     40class Dir2: 
     41    def index(self): 
     42        return "index for dir2, path is:" + cpg.request.path 
     43    index.exposed = True 
     44    def method(self): 
     45        return "method for dir2" 
     46    method.exposed = True 
    2547cpg.root = Root() 
     48cpg.root.dir1 = Dir1() 
     49cpg.root.dir1.dir2 = Dir2() 
    2650cpg.server.start(configFile = 'testsite.cfg') 
    2751""" 
    2852config = "" 
    29 expectedResult = "" 
     53testList = [ 
     54    ("", "world"), 
     55    ("/this/method/does/not/exist", "default:('this', 'method', 'does', 'not', 'exist')"), 
     56    ("/other", "other"), 
     57    ("/notExposed", "default:('notExposed',)"), 
     58    ("/dir1/dir2/", "index for dir2, path is:/dir1/dir2/"), 
     59
     60urlList = [test[0] for test in testList] 
     61expectedResultList = [test[1] for test in testList] 
    3062 
    3163def test(infoMap, failedList, skippedList): 
    3264    print "    Testing object mapping...", 
    33     helper.checkPageResult('Object mapping', infoMap, code, config, [""], ["world"], failedList) 
     65    helper.checkPageResult('Object mapping', infoMap, code, config, urlList, expectedResultList, failedList) 

Hosted by WebFaction

Log in as guest/cpguest to create tickets