1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved. 2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# found in the LICENSE file. 4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 5ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport logging 6ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport posixpath 7ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport traceback 8ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 9ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochfrom branch_utility import BranchUtility 10d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)from compiled_file_system import SingleFile 11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)from extensions_paths import PUBLIC_TEMPLATES 12ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochfrom file_system import FileNotFoundError 13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 15ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochdef _SimplifyFileName(file_name): 16ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return (posixpath.splitext(file_name)[0] 17ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch .lower() 18ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch .replace('.', '') 19ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch .replace('-', '') 20ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch .replace('_', '')) 21ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class PathCanonicalizer(object): 24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) '''Transforms paths into their canonical forms. Since the dev server has had 25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) many incarnations - e.g. there didn't use to be apps/ - there may be old 26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) paths lying around the webs. We try to redirect those to where they are now. 27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ''' 284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) def __init__(self, compiled_fs_factory, file_system): 29ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # Map of simplified API names (for typo detection) to their real paths. 300f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) @SingleFile 31ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch def make_public_apis(_, file_names): 32ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return dict((_SimplifyFileName(name), name) for name in file_names) 334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self._public_apis = compiled_fs_factory.Create(file_system, 344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) make_public_apis, 35ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch PathCanonicalizer) 36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) def Canonicalize(self, path): 38ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch '''Returns the canonical path for |path|, and whether that path is a 39ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch permanent canonicalisation (e.g. when we redirect from a channel to a 40ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch channel-less URL) or temporary (e.g. when we redirect from an apps-only API 41ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch to an extensions one - we may at some point enable it for extensions). 42ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch ''' 43ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch class ReturnType(object): 44ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch def __init__(self, path, permanent): 45ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch self.path = path 46ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch self.permanent = permanent 47ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 48ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # Catch incorrect comparisons by disabling ==/!=. 49ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch def __eq__(self, _): raise NotImplementedError() 50ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch def __ne__(self, _): raise NotImplementedError() 51ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 52ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # Strip any channel info off it. There are no channels anymore. 53ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch for channel_name in BranchUtility.GetAllChannelNames(): 54ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch channel_prefix = channel_name + '/' 55ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch if path.startswith(channel_prefix): 56ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # Redirect now so that we can set the permanent-redirect bit. Channel 57ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # redirects are the only things that should be permanent redirects; 58ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # anything else *could* change, so is temporary. 59ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return ReturnType(path[len(channel_prefix):], True) 60ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 61ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # No further work needed for static. 62ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch if path.startswith('static/'): 63ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return ReturnType(path, False) 64ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 65ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # People go to just "extensions" or "apps". Redirect to the directory. 66ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch if path in ('extensions', 'apps'): 67ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return ReturnType(path + '/', False) 68ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 69ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # The rest of this function deals with trying to figure out what API page 70ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # for extensions/apps to redirect to, if any. We see a few different cases 71ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # here: 72ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # - Unqualified names ("browserAction.html"). These are easy to resolve; 73ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # figure out whether it's an extension or app API and redirect. 74ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # - but what if it's both? Well, assume extensions. Maybe later we can 75ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # check analytics and see which is more popular. 76ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # - Wrong names ("apps/browserAction.html"). This really does happen, 77ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # damn it, so do the above logic but record which is the default. 78ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch if path.startswith(('extensions/', 'apps/')): 79ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch default_platform, reference_path = path.split('/', 1) 80ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch else: 81ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch default_platform, reference_path = ('extensions', path) 82ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 83ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch try: 84ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch apps_public = self._public_apis.GetFromFileListing( 85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) '%s/apps' % PUBLIC_TEMPLATES).Get() 86ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch extensions_public = self._public_apis.GetFromFileListing( 87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) '%s/extensions' % PUBLIC_TEMPLATES).Get() 88ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch except FileNotFoundError: 89ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # Probably offline. 90ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch logging.warning(traceback.format_exc()) 91ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return ReturnType(path, False) 92ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 93ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch simple_reference_path = _SimplifyFileName(reference_path) 94ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch apps_path = apps_public.get(simple_reference_path) 95ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch extensions_path = extensions_public.get(simple_reference_path) 96ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 97ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch if apps_path is None: 98ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch if extensions_path is None: 99ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch # No idea. Just return the original path. It'll probably 404. 100ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch pass 101ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch else: 102ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch path = 'extensions/%s' % extensions_path 103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) else: 104ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch if extensions_path is None: 105ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch path = 'apps/%s' % apps_path 106ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch else: 107ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch assert apps_path == extensions_path 108ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch path = '%s/%s' % (default_platform, apps_path) 109ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 110ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return ReturnType(path, False) 111