thirdparty/google_appengine/google/appengine/tools/dev_appserver.py
changeset 2864 2e0b0af889be
parent 2413 d0b7dac5325c
child 3031 7678f72140e6
equal deleted inserted replaced
2862:27971a13089f 2864:2e0b0af889be
    47   pass
    47   pass
    48 
    48 
    49 import dummy_thread
    49 import dummy_thread
    50 import email.Utils
    50 import email.Utils
    51 import errno
    51 import errno
       
    52 import heapq
    52 import httplib
    53 import httplib
    53 import imp
    54 import imp
    54 import inspect
    55 import inspect
    55 import itertools
    56 import itertools
    56 import locale
    57 import locale
    59 import mimetypes
    60 import mimetypes
    60 import os
    61 import os
    61 import pickle
    62 import pickle
    62 import pprint
    63 import pprint
    63 import random
    64 import random
       
    65 import select
    64 
    66 
    65 import re
    67 import re
    66 import sre_compile
    68 import sre_compile
    67 import sre_constants
    69 import sre_constants
    68 import sre_parse
    70 import sre_parse
    69 
    71 
    70 import mimetypes
       
    71 import socket
    72 import socket
    72 import sys
    73 import sys
    73 import time
    74 import time
    74 import traceback
    75 import traceback
    75 import types
    76 import types
    89 from google.appengine.api import user_service_stub
    90 from google.appengine.api import user_service_stub
    90 from google.appengine.api import yaml_errors
    91 from google.appengine.api import yaml_errors
    91 from google.appengine.api.capabilities import capability_stub
    92 from google.appengine.api.capabilities import capability_stub
    92 from google.appengine.api.labs.taskqueue import taskqueue_stub
    93 from google.appengine.api.labs.taskqueue import taskqueue_stub
    93 from google.appengine.api.memcache import memcache_stub
    94 from google.appengine.api.memcache import memcache_stub
       
    95 from google.appengine.api.xmpp import xmpp_service_stub
    94 
    96 
    95 from google.appengine import dist
    97 from google.appengine import dist
    96 
    98 
    97 from google.appengine.tools import dev_appserver_index
    99 from google.appengine.tools import dev_appserver_index
    98 from google.appengine.tools import dev_appserver_login
   100 from google.appengine.tools import dev_appserver_login
   109 SCRIPT_TEMPLATE = 'logging_console.js'
   111 SCRIPT_TEMPLATE = 'logging_console.js'
   110 MIDDLE_TEMPLATE = 'logging_console_middle.html'
   112 MIDDLE_TEMPLATE = 'logging_console_middle.html'
   111 FOOTER_TEMPLATE = 'logging_console_footer.html'
   113 FOOTER_TEMPLATE = 'logging_console_footer.html'
   112 
   114 
   113 DEFAULT_ENV = {
   115 DEFAULT_ENV = {
   114   'GATEWAY_INTERFACE': 'CGI/1.1',
   116     'GATEWAY_INTERFACE': 'CGI/1.1',
   115   'AUTH_DOMAIN': 'gmail.com',
   117     'AUTH_DOMAIN': 'gmail.com',
   116   'TZ': 'UTC',
   118     'TZ': 'UTC',
   117 }
   119 }
       
   120 
       
   121 DEFAULT_SELECT_DELAY = 30.0
   118 
   122 
   119 for ext, mime_type in (('.asc', 'text/plain'),
   123 for ext, mime_type in (('.asc', 'text/plain'),
   120                        ('.diff', 'text/plain'),
   124                        ('.diff', 'text/plain'),
   121                        ('.csv', 'text/comma-separated-values'),
   125                        ('.csv', 'text/comma-separated-values'),
   122                        ('.rss', 'application/rss+xml'),
   126                        ('.rss', 'application/rss+xml'),
   132 
   136 
   133 SITE_PACKAGES = os.path.normcase(os.path.join(os.path.dirname(os.__file__),
   137 SITE_PACKAGES = os.path.normcase(os.path.join(os.path.dirname(os.__file__),
   134                                               'site-packages'))
   138                                               'site-packages'))
   135 
   139 
   136 
   140 
       
   141 
   137 class Error(Exception):
   142 class Error(Exception):
   138   """Base-class for exceptions in this module."""
   143   """Base-class for exceptions in this module."""
   139 
   144 
       
   145 
   140 class InvalidAppConfigError(Error):
   146 class InvalidAppConfigError(Error):
   141   """The supplied application configuration file is invalid."""
   147   """The supplied application configuration file is invalid."""
   142 
   148 
       
   149 
   143 class AppConfigNotFoundError(Error):
   150 class AppConfigNotFoundError(Error):
   144   """Application configuration file not found."""
   151   """Application configuration file not found."""
   145 
   152 
       
   153 
   146 class TemplatesNotLoadedError(Error):
   154 class TemplatesNotLoadedError(Error):
   147   """Templates for the debugging console were not loaded."""
   155   """Templates for the debugging console were not loaded."""
       
   156 
   148 
   157 
   149 
   158 
   150 def SplitURL(relative_url):
   159 def SplitURL(relative_url):
   151   """Splits a relative URL into its path and query-string components.
   160   """Splits a relative URL into its path and query-string components.
   152 
   161 
   157   Returns:
   166   Returns:
   158     Tuple (script_name, query_string) where:
   167     Tuple (script_name, query_string) where:
   159       script_name: Relative URL of the script that was accessed.
   168       script_name: Relative URL of the script that was accessed.
   160       query_string: String containing everything after the '?' character.
   169       query_string: String containing everything after the '?' character.
   161   """
   170   """
   162   scheme, netloc, path, query, fragment = urlparse.urlsplit(relative_url)
   171   (unused_scheme, unused_netloc, path, query,
       
   172    unused_fragment) = urlparse.urlsplit(relative_url)
   163   return path, query
   173   return path, query
   164 
   174 
   165 
   175 
   166 def GetFullURL(server_name, server_port, relative_url):
   176 def GetFullURL(server_name, server_port, relative_url):
   167   """Returns the full, original URL used to access the relative URL.
   177   """Returns the full, original URL used to access the relative URL.
   178   if str(server_port) != '80':
   188   if str(server_port) != '80':
   179     netloc = '%s:%s' % (server_name, server_port)
   189     netloc = '%s:%s' % (server_name, server_port)
   180   else:
   190   else:
   181     netloc = server_name
   191     netloc = server_name
   182   return 'http://%s%s' % (netloc, relative_url)
   192   return 'http://%s%s' % (netloc, relative_url)
       
   193 
   183 
   194 
   184 
   195 
   185 class URLDispatcher(object):
   196 class URLDispatcher(object):
   186   """Base-class for handling HTTP requests."""
   197   """Base-class for handling HTTP requests."""
   187 
   198 
   229     redirect.
   240     redirect.
   230 
   241 
   231     Args:
   242     Args:
   232       dispatched_output: StringIO buffer containing the results from the
   243       dispatched_output: StringIO buffer containing the results from the
   233        dispatched
   244        dispatched
       
   245       original_output: The original output file.
   234     """
   246     """
   235     original_output.write(dispatched_output.read())
   247     original_output.write(dispatched_output.read())
   236 
   248 
   237 
   249 
   238 class URLMatcher(object):
   250 class URLMatcher(object):
   265         matched by the regex if present.
   277         matched by the regex if present.
   266       requires_login: True if the user must be logged-in before accessing this
   278       requires_login: True if the user must be logged-in before accessing this
   267         URL; False if anyone can access this URL.
   279         URL; False if anyone can access this URL.
   268       admin_only: True if the user must be a logged-in administrator to
   280       admin_only: True if the user must be a logged-in administrator to
   269         access the URL; False if anyone can access the URL.
   281         access the URL; False if anyone can access the URL.
       
   282 
       
   283     Raises:
       
   284       TypeError: if dispatcher is not a URLDispatcher sub-class instance.
       
   285       InvalidAppConfigError: if regex isn't valid.
   270     """
   286     """
   271     if not isinstance(dispatcher, URLDispatcher):
   287     if not isinstance(dispatcher, URLDispatcher):
   272       raise TypeError('dispatcher must be a URLDispatcher sub-class')
   288       raise TypeError('dispatcher must be a URLDispatcher sub-class')
   273 
   289 
   274     if regex.startswith('^') or regex.endswith('$'):
   290     if regex.startswith('^') or regex.endswith('$'):
   292     The supplied relative_url may include the query string (i.e., the '?'
   308     The supplied relative_url may include the query string (i.e., the '?'
   293     character and everything following).
   309     character and everything following).
   294 
   310 
   295     Args:
   311     Args:
   296       relative_url: Relative URL being accessed in a request.
   312       relative_url: Relative URL being accessed in a request.
       
   313       split_url: Used for dependency injection.
   297 
   314 
   298     Returns:
   315     Returns:
   299       Tuple (dispatcher, matched_path, requires_login, admin_only), which are
   316       Tuple (dispatcher, matched_path, requires_login, admin_only), which are
   300       the corresponding values passed to AddURL when the matching URL pattern
   317       the corresponding values passed to AddURL when the matching URL pattern
   301       was added to this matcher. The matched_path will have back-references
   318       was added to this matcher. The matched_path will have back-references
   302       replaced using values matched by the URL pattern. If no match was found,
   319       replaced using values matched by the URL pattern. If no match was found,
   303       dispatcher will be None.
   320       dispatcher will be None.
   304     """
   321     """
   305     adjusted_url, query_string = split_url(relative_url)
   322     adjusted_url, unused_query_string = split_url(relative_url)
   306 
   323 
   307     for url_tuple in self._url_patterns:
   324     for url_tuple in self._url_patterns:
   308       url_re, dispatcher, path, requires_login, admin_only = url_tuple
   325       url_re, dispatcher, path, requires_login, admin_only = url_tuple
   309       the_match = url_re.match(adjusted_url)
   326       the_match = url_re.match(adjusted_url)
   310 
   327 
   321 
   338 
   322     Returns:
   339     Returns:
   323       A set of URLDispatcher objects.
   340       A set of URLDispatcher objects.
   324     """
   341     """
   325     return set([url_tuple[1] for url_tuple in self._url_patterns])
   342     return set([url_tuple[1] for url_tuple in self._url_patterns])
       
   343 
   326 
   344 
   327 
   345 
   328 class MatcherDispatcher(URLDispatcher):
   346 class MatcherDispatcher(URLDispatcher):
   329   """Dispatcher across multiple URLMatcher instances."""
   347   """Dispatcher across multiple URLMatcher instances."""
   330 
   348 
   336     """Initializer.
   354     """Initializer.
   337 
   355 
   338     Args:
   356     Args:
   339       login_url: Relative URL which should be used for handling user logins.
   357       login_url: Relative URL which should be used for handling user logins.
   340       url_matchers: Sequence of URLMatcher objects.
   358       url_matchers: Sequence of URLMatcher objects.
   341       get_user_info, login_redirect: Used for dependency injection.
   359       get_user_info: Used for dependency injection.
       
   360       login_redirect: Used for dependency injection.
   342     """
   361     """
   343     self._login_url = login_url
   362     self._login_url = login_url
   344     self._url_matchers = tuple(url_matchers)
   363     self._url_matchers = tuple(url_matchers)
   345     self._get_user_info = get_user_info
   364     self._get_user_info = get_user_info
   346     self._login_redirect = login_redirect
   365     self._login_redirect = login_redirect
   357     Matchers are checked in the order they were supplied to the constructor.
   376     Matchers are checked in the order they were supplied to the constructor.
   358     If no matcher matches, a 404 error will be written to the outfile. The
   377     If no matcher matches, a 404 error will be written to the outfile. The
   359     path variable supplied to this method is ignored.
   378     path variable supplied to this method is ignored.
   360     """
   379     """
   361     cookies = ', '.join(headers.getheaders('cookie'))
   380     cookies = ', '.join(headers.getheaders('cookie'))
   362     email, admin, user_id = self._get_user_info(cookies)
   381     email_addr, admin, user_id = self._get_user_info(cookies)
   363 
   382 
   364     for matcher in self._url_matchers:
   383     for matcher in self._url_matchers:
   365       dispatcher, matched_path, requires_login, admin_only = matcher.Match(relative_url)
   384       dispatcher, matched_path, requires_login, admin_only = matcher.Match(
       
   385           relative_url)
   366       if dispatcher is None:
   386       if dispatcher is None:
   367         continue
   387         continue
   368 
   388 
   369       logging.debug('Matched "%s" to %s with path %s',
   389       logging.debug('Matched "%s" to %s with path %s',
   370                     relative_url, dispatcher, matched_path)
   390                     relative_url, dispatcher, matched_path)
   371 
   391 
   372       if (requires_login or admin_only) and not email:
   392       if (requires_login or admin_only) and not email_addr:
   373         logging.debug('Login required, redirecting user')
   393         logging.debug('Login required, redirecting user')
   374         self._login_redirect(
   394         self._login_redirect(self._login_url,
   375           self._login_url,
   395                              base_env_dict['SERVER_NAME'],
   376           base_env_dict['SERVER_NAME'],
   396                              base_env_dict['SERVER_PORT'],
   377           base_env_dict['SERVER_PORT'],
   397                              relative_url,
   378           relative_url,
   398                              outfile)
   379           outfile)
       
   380       elif admin_only and not admin:
   399       elif admin_only and not admin:
   381         outfile.write('Status: %d Not authorized\r\n'
   400         outfile.write('Status: %d Not authorized\r\n'
   382                       '\r\n'
   401                       '\r\n'
   383                       'Current logged in user %s is not '
   402                       'Current logged in user %s is not '
   384                       'authorized to view this page.'
   403                       'authorized to view this page.'
   385                       % (httplib.FORBIDDEN, email))
   404                       % (httplib.FORBIDDEN, email_addr))
   386       else:
   405       else:
   387         forward = dispatcher.Dispatch(relative_url,
   406         forward = dispatcher.Dispatch(relative_url,
   388                                       matched_path,
   407                                       matched_path,
   389                                       headers,
   408                                       headers,
   390                                       infile,
   409                                       infile,
   391                                       outfile,
   410                                       outfile,
   392                                       base_env_dict=base_env_dict)
   411                                       base_env_dict=base_env_dict)
   393 
   412 
   394         if forward:
   413         if forward:
   395           new_path, new_headers, new_input = forward
   414           new_path, new_headers, new_input = forward
   396           logging.info('Internal redirection to %s' % new_path)
   415           logging.info('Internal redirection to %s', new_path)
   397           new_outfile = cStringIO.StringIO()
   416           new_outfile = cStringIO.StringIO()
   398           self.Dispatch(new_path,
   417           self.Dispatch(new_path,
   399                         None,
   418                         None,
   400                         new_headers,
   419                         new_headers,
   401                         new_input,
   420                         new_input,
   411                   'Not found error: %s did not match any patterns '
   430                   'Not found error: %s did not match any patterns '
   412                   'in application configuration.'
   431                   'in application configuration.'
   413                   % (httplib.NOT_FOUND, relative_url))
   432                   % (httplib.NOT_FOUND, relative_url))
   414 
   433 
   415 
   434 
       
   435 
   416 class ApplicationLoggingHandler(logging.Handler):
   436 class ApplicationLoggingHandler(logging.Handler):
   417   """Python Logging handler that displays the debugging console to users."""
   437   """Python Logging handler that displays the debugging console to users."""
   418 
   438 
   419   _COOKIE_NAME = '_ah_severity'
   439   _COOKIE_NAME = '_ah_severity'
   420 
   440 
   485         HTTP_COOKIE entry to see if the accessing user has any logging-related
   505         HTTP_COOKIE entry to see if the accessing user has any logging-related
   486         cookies set.
   506         cookies set.
   487       outfile: Output stream to which the console should be written if either
   507       outfile: Output stream to which the console should be written if either
   488         a debug parameter was supplied or a logging cookie is present.
   508         a debug parameter was supplied or a logging cookie is present.
   489     """
   509     """
   490     script_name, query_string = SplitURL(relative_url)
   510     unused_script_name, query_string = SplitURL(relative_url)
   491     param_dict = cgi.parse_qs(query_string, True)
   511     param_dict = cgi.parse_qs(query_string, True)
   492     cookie_dict = Cookie.SimpleCookie(env.get('HTTP_COOKIE', ''))
   512     cookie_dict = Cookie.SimpleCookie(env.get('HTTP_COOKIE', ''))
   493     if 'debug' not in param_dict and self._COOKIE_NAME not in cookie_dict:
   513     if 'debug' not in param_dict and self._COOKIE_NAME not in cookie_dict:
   494       return
   514       return
   495 
   515 
   552   env['CONTENT_TYPE'] = headers.getheader('content-type',
   572   env['CONTENT_TYPE'] = headers.getheader('content-type',
   553                                           'application/x-www-form-urlencoded')
   573                                           'application/x-www-form-urlencoded')
   554   env['CONTENT_LENGTH'] = headers.getheader('content-length', '')
   574   env['CONTENT_LENGTH'] = headers.getheader('content-length', '')
   555 
   575 
   556   cookies = ', '.join(headers.getheaders('cookie'))
   576   cookies = ', '.join(headers.getheaders('cookie'))
   557   email, admin, user_id = get_user_info(cookies)
   577   email_addr, admin, user_id = get_user_info(cookies)
   558   env['USER_EMAIL'] = email
   578   env['USER_EMAIL'] = email_addr
   559   env['USER_ID'] = user_id
   579   env['USER_ID'] = user_id
   560   if admin:
   580   if admin:
   561     env['USER_IS_ADMIN'] = '1'
   581     env['USER_IS_ADMIN'] = '1'
   562 
   582 
   563   for key in headers:
   583   for key in headers:
   581 
   601 
   582 def NotImplementedFake(*args, **kwargs):
   602 def NotImplementedFake(*args, **kwargs):
   583   """Fake for methods/functions that are not implemented in the production
   603   """Fake for methods/functions that are not implemented in the production
   584   environment.
   604   environment.
   585   """
   605   """
   586   raise NotImplementedError("This class/method is not available.")
   606   raise NotImplementedError('This class/method is not available.')
   587 
   607 
   588 
   608 
   589 class NotImplementedFakeClass(object):
   609 class NotImplementedFakeClass(object):
   590   """Fake class for classes that are not implemented in the production
   610   """Fake class for classes that are not implemented in the production env.
   591   environment.
       
   592   """
   611   """
   593   __init__ = NotImplementedFake
   612   __init__ = NotImplementedFake
   594 
   613 
   595 
   614 
   596 def IsEncodingsModule(module_name):
   615 def IsEncodingsModule(module_name):
   625 
   644 
   626 
   645 
   627 def FakeURandom(n):
   646 def FakeURandom(n):
   628   """Fake version of os.urandom."""
   647   """Fake version of os.urandom."""
   629   bytes = ''
   648   bytes = ''
   630   for i in xrange(n):
   649   for _ in range(n):
   631     bytes += chr(random.randint(0, 255))
   650     bytes += chr(random.randint(0, 255))
   632   return bytes
   651   return bytes
   633 
   652 
   634 
   653 
   635 def FakeUname():
   654 def FakeUname():
   663   if value not in (None, '', 'C', 'POSIX'):
   682   if value not in (None, '', 'C', 'POSIX'):
   664     raise locale.Error('locale emulation only supports "C" locale')
   683     raise locale.Error('locale emulation only supports "C" locale')
   665   return original_setlocale(category, 'C')
   684   return original_setlocale(category, 'C')
   666 
   685 
   667 
   686 
   668 def FakeOpen(file, flags, mode=0777):
   687 def FakeOpen(filename, flags, mode=0777):
   669   """Fake version of os.open."""
   688   """Fake version of os.open."""
   670   raise OSError(errno.EPERM, "Operation not permitted", file)
   689   raise OSError(errno.EPERM, "Operation not permitted", filename)
   671 
   690 
   672 
   691 
   673 def FakeRename(src, dst):
   692 def FakeRename(src, dst):
   674   """Fake version of os.rename."""
   693   """Fake version of os.rename."""
   675   raise OSError(errno.EPERM, "Operation not permitted", src)
   694   raise OSError(errno.EPERM, "Operation not permitted", src)
   709     if os.path.commonprefix([file_dir, fixed_parent]) == fixed_parent:
   728     if os.path.commonprefix([file_dir, fixed_parent]) == fixed_parent:
   710       return True
   729       return True
   711   return False
   730   return False
   712 
   731 
   713 SHARED_MODULE_PREFIXES = set([
   732 SHARED_MODULE_PREFIXES = set([
   714   'google',
   733     'google',
   715   'logging',
   734     'logging',
   716   'sys',
   735     'sys',
   717   'warnings',
   736     'warnings',
   718 
   737 
   719 
   738 
   720 
   739 
   721 
   740 
   722   're',
   741     're',
   723   'sre_compile',
   742     'sre_compile',
   724   'sre_constants',
   743     'sre_constants',
   725   'sre_parse',
   744     'sre_parse',
   726 
   745 
   727 
   746 
   728 
   747 
   729 
   748 
   730   'wsgiref',
   749     'wsgiref',
   731 ])
   750 ])
   732 
   751 
   733 NOT_SHARED_MODULE_PREFIXES = set([
   752 NOT_SHARED_MODULE_PREFIXES = set([
   734   'google.appengine.ext',
   753     'google.appengine.ext',
   735 ])
   754 ])
   736 
   755 
   737 
   756 
   738 def ModuleNameHasPrefix(module_name, prefix_set):
   757 def ModuleNameHasPrefix(module_name, prefix_set):
   739   """Determines if a module's name belongs to a set of prefix strings.
   758   """Determines if a module's name belongs to a set of prefix strings.
   786 
   805 
   787   return output_dict
   806   return output_dict
   788 
   807 
   789 
   808 
   790 def GeneratePythonPaths(*p):
   809 def GeneratePythonPaths(*p):
   791   """Generate all valid filenames for the given file
   810   """Generate all valid filenames for the given file.
   792 
   811 
   793   Args:
   812   Args:
   794     p: Positional args are the folders to the file and finally the file
   813     p: Positional args are the folders to the file and finally the file
   795        without a suffix.
   814        without a suffix.
   796 
   815 
   812   ALLOWED_FILES = set(os.path.normcase(filename)
   831   ALLOWED_FILES = set(os.path.normcase(filename)
   813                       for filename in mimetypes.knownfiles
   832                       for filename in mimetypes.knownfiles
   814                       if os.path.isfile(filename))
   833                       if os.path.isfile(filename))
   815 
   834 
   816   ALLOWED_DIRS = set([
   835   ALLOWED_DIRS = set([
   817     os.path.normcase(os.path.realpath(os.path.dirname(os.__file__))),
   836       os.path.normcase(os.path.realpath(os.path.dirname(os.__file__))),
   818     os.path.normcase(os.path.abspath(os.path.dirname(os.__file__))),
   837       os.path.normcase(os.path.abspath(os.path.dirname(os.__file__))),
   819   ])
   838   ])
   820 
   839 
   821   NOT_ALLOWED_DIRS = set([
   840   NOT_ALLOWED_DIRS = set([
   822 
   841 
   823 
   842 
   824 
   843 
   825 
   844 
   826     SITE_PACKAGES,
   845       SITE_PACKAGES,
   827   ])
   846   ])
   828 
   847 
   829   ALLOWED_SITE_PACKAGE_DIRS = set(
   848   ALLOWED_SITE_PACKAGE_DIRS = set(
   830     os.path.normcase(os.path.abspath(os.path.join(SITE_PACKAGES, path)))
   849       os.path.normcase(os.path.abspath(os.path.join(SITE_PACKAGES, path)))
   831     for path in [
   850       for path in [
   832 
   851 
   833   ])
   852           ])
   834 
   853 
   835   ALLOWED_SITE_PACKAGE_FILES = set(
   854   ALLOWED_SITE_PACKAGE_FILES = set(
   836     os.path.normcase(os.path.abspath(os.path.join(
   855       os.path.normcase(os.path.abspath(os.path.join(
   837       os.path.dirname(os.__file__), 'site-packages', path)))
   856           os.path.dirname(os.__file__), 'site-packages', path)))
   838     for path in itertools.chain(*[
   857       for path in itertools.chain(*[
   839 
   858 
   840       [os.path.join('Crypto')],
   859           [os.path.join('Crypto')],
   841       GeneratePythonPaths('Crypto', '__init__'),
   860           GeneratePythonPaths('Crypto', '__init__'),
   842       [os.path.join('Crypto', 'Cipher')],
   861           [os.path.join('Crypto', 'Cipher')],
   843       GeneratePythonPaths('Crypto', 'Cipher', '__init__'),
   862           GeneratePythonPaths('Crypto', 'Cipher', '__init__'),
   844       GeneratePythonPaths('Crypto', 'Cipher', 'AES'),
   863           GeneratePythonPaths('Crypto', 'Cipher', 'AES'),
   845       GeneratePythonPaths('Crypto', 'Cipher', 'ARC2'),
   864           GeneratePythonPaths('Crypto', 'Cipher', 'ARC2'),
   846       GeneratePythonPaths('Crypto', 'Cipher', 'ARC4'),
   865           GeneratePythonPaths('Crypto', 'Cipher', 'ARC4'),
   847       GeneratePythonPaths('Crypto', 'Cipher', 'Blowfish'),
   866           GeneratePythonPaths('Crypto', 'Cipher', 'Blowfish'),
   848       GeneratePythonPaths('Crypto', 'Cipher', 'CAST'),
   867           GeneratePythonPaths('Crypto', 'Cipher', 'CAST'),
   849       GeneratePythonPaths('Crypto', 'Cipher', 'DES'),
   868           GeneratePythonPaths('Crypto', 'Cipher', 'DES'),
   850       GeneratePythonPaths('Crypto', 'Cipher', 'DES3'),
   869           GeneratePythonPaths('Crypto', 'Cipher', 'DES3'),
   851       GeneratePythonPaths('Crypto', 'Cipher', 'XOR'),
   870           GeneratePythonPaths('Crypto', 'Cipher', 'XOR'),
   852       [os.path.join('Crypto', 'Hash')],
   871           [os.path.join('Crypto', 'Hash')],
   853       GeneratePythonPaths('Crypto', 'Hash', '__init__'),
   872           GeneratePythonPaths('Crypto', 'Hash', '__init__'),
   854       GeneratePythonPaths('Crypto', 'Hash', 'HMAC'),
   873           GeneratePythonPaths('Crypto', 'Hash', 'HMAC'),
   855       os.path.join('Crypto', 'Hash', 'MD2'),
   874           os.path.join('Crypto', 'Hash', 'MD2'),
   856       os.path.join('Crypto', 'Hash', 'MD4'),
   875           os.path.join('Crypto', 'Hash', 'MD4'),
   857       GeneratePythonPaths('Crypto', 'Hash', 'MD5'),
   876           GeneratePythonPaths('Crypto', 'Hash', 'MD5'),
   858       GeneratePythonPaths('Crypto', 'Hash', 'SHA'),
   877           GeneratePythonPaths('Crypto', 'Hash', 'SHA'),
   859       os.path.join('Crypto', 'Hash', 'SHA256'),
   878           os.path.join('Crypto', 'Hash', 'SHA256'),
   860       os.path.join('Crypto', 'Hash', 'RIPEMD'),
   879           os.path.join('Crypto', 'Hash', 'RIPEMD'),
   861       [os.path.join('Crypto', 'Protocol')],
   880           [os.path.join('Crypto', 'Protocol')],
   862       GeneratePythonPaths('Crypto', 'Protocol', '__init__'),
   881           GeneratePythonPaths('Crypto', 'Protocol', '__init__'),
   863       GeneratePythonPaths('Crypto', 'Protocol', 'AllOrNothing'),
   882           GeneratePythonPaths('Crypto', 'Protocol', 'AllOrNothing'),
   864       GeneratePythonPaths('Crypto', 'Protocol', 'Chaffing'),
   883           GeneratePythonPaths('Crypto', 'Protocol', 'Chaffing'),
   865       [os.path.join('Crypto', 'PublicKey')],
   884           [os.path.join('Crypto', 'PublicKey')],
   866       GeneratePythonPaths('Crypto', 'PublicKey', '__init__'),
   885           GeneratePythonPaths('Crypto', 'PublicKey', '__init__'),
   867       GeneratePythonPaths('Crypto', 'PublicKey', 'DSA'),
   886           GeneratePythonPaths('Crypto', 'PublicKey', 'DSA'),
   868       GeneratePythonPaths('Crypto', 'PublicKey', 'ElGamal'),
   887           GeneratePythonPaths('Crypto', 'PublicKey', 'ElGamal'),
   869       GeneratePythonPaths('Crypto', 'PublicKey', 'RSA'),
   888           GeneratePythonPaths('Crypto', 'PublicKey', 'RSA'),
   870       GeneratePythonPaths('Crypto', 'PublicKey', 'pubkey'),
   889           GeneratePythonPaths('Crypto', 'PublicKey', 'pubkey'),
   871       GeneratePythonPaths('Crypto', 'PublicKey', 'qNEW'),
   890           GeneratePythonPaths('Crypto', 'PublicKey', 'qNEW'),
   872       [os.path.join('Crypto', 'Util')],
   891           [os.path.join('Crypto', 'Util')],
   873       GeneratePythonPaths('Crypto', 'Util', '__init__'),
   892           GeneratePythonPaths('Crypto', 'Util', '__init__'),
   874       GeneratePythonPaths('Crypto', 'Util', 'RFC1751'),
   893           GeneratePythonPaths('Crypto', 'Util', 'RFC1751'),
   875       GeneratePythonPaths('Crypto', 'Util', 'number'),
   894           GeneratePythonPaths('Crypto', 'Util', 'number'),
   876       GeneratePythonPaths('Crypto', 'Util', 'randpool'),
   895           GeneratePythonPaths('Crypto', 'Util', 'randpool'),
   877   ]))
   896           ]))
   878 
   897 
   879   _original_file = file
   898   _original_file = file
   880 
   899 
   881   _root_path = None
   900   _root_path = None
   882   _application_paths = None
   901   _application_paths = None
   910 
   929 
   911     FakeFile._availability_cache = {}
   930     FakeFile._availability_cache = {}
   912 
   931 
   913   @staticmethod
   932   @staticmethod
   914   def SetAllowSkippedFiles(allow_skipped_files):
   933   def SetAllowSkippedFiles(allow_skipped_files):
   915     """Configures access to files matching FakeFile._skip_files
   934     """Configures access to files matching FakeFile._skip_files.
   916 
   935 
   917     Args:
   936     Args:
   918       allow_skipped_files: Boolean whether to allow access to skipped files
   937       allow_skipped_files: Boolean whether to allow access to skipped files
   919     """
   938     """
   920     FakeFile._allow_skipped_files = allow_skipped_files
   939     FakeFile._allow_skipped_files = allow_skipped_files
  1104     'stuff'), the returned value will just be that module name ('stuff').
  1123     'stuff'), the returned value will just be that module name ('stuff').
  1105   """
  1124   """
  1106   return fullname.rsplit('.', 1)[-1]
  1125   return fullname.rsplit('.', 1)[-1]
  1107 
  1126 
  1108 
  1127 
       
  1128 
  1109 class CouldNotFindModuleError(ImportError):
  1129 class CouldNotFindModuleError(ImportError):
  1110   """Raised when a module could not be found.
  1130   """Raised when a module could not be found.
  1111 
  1131 
  1112   In contrast to when a module has been found, but cannot be loaded because of
  1132   In contrast to when a module has been found, but cannot be loaded because of
  1113   hardening restrictions.
  1133   hardening restrictions.
  1114   """
  1134   """
  1115 
  1135 
  1116 
  1136 
  1117 def Trace(func):
  1137 def Trace(func):
  1118   """Decorator that logs the call stack of the HardenedModulesHook class as
  1138   """Call stack logging decorator for HardenedModulesHook class.
       
  1139 
       
  1140   This decorator logs the call stack of the HardenedModulesHook class as
  1119   it executes, indenting logging messages based on the current stack depth.
  1141   it executes, indenting logging messages based on the current stack depth.
  1120   """
  1142 
  1121   def decorate(self, *args, **kwargs):
  1143   Args:
       
  1144     func: the function to decorate.
       
  1145 
       
  1146   Returns:
       
  1147     The decorated function.
       
  1148   """
       
  1149 
       
  1150   def Decorate(self, *args, **kwargs):
  1122     args_to_show = []
  1151     args_to_show = []
  1123     if args is not None:
  1152     if args is not None:
  1124       args_to_show.extend(str(argument) for argument in args)
  1153       args_to_show.extend(str(argument) for argument in args)
  1125     if kwargs is not None:
  1154     if kwargs is not None:
  1126       args_to_show.extend('%s=%s' % (key, value)
  1155       args_to_show.extend('%s=%s' % (key, value)
  1134       return func(self, *args, **kwargs)
  1163       return func(self, *args, **kwargs)
  1135     finally:
  1164     finally:
  1136       self._indent_level -= 1
  1165       self._indent_level -= 1
  1137       self.log('Exiting %s(%s)', func.func_name, args_string)
  1166       self.log('Exiting %s(%s)', func.func_name, args_string)
  1138 
  1167 
  1139   return decorate
  1168   return Decorate
  1140 
  1169 
  1141 
  1170 
  1142 class HardenedModulesHook(object):
  1171 class HardenedModulesHook(object):
  1143   """Meta import hook that restricts the modules used by applications to match
  1172   """Meta import hook that restricts the modules used by applications to match
  1144   the production environment.
  1173   the production environment.
  1171     if HardenedModulesHook.ENABLE_LOGGING:
  1200     if HardenedModulesHook.ENABLE_LOGGING:
  1172       indent = self._indent_level * '  '
  1201       indent = self._indent_level * '  '
  1173       print >>sys.stderr, indent + (message % args)
  1202       print >>sys.stderr, indent + (message % args)
  1174 
  1203 
  1175   _WHITE_LIST_C_MODULES = [
  1204   _WHITE_LIST_C_MODULES = [
  1176     'AES',
  1205       'AES',
  1177     'ARC2',
  1206       'ARC2',
  1178     'ARC4',
  1207       'ARC4',
  1179     'Blowfish',
  1208       'Blowfish',
  1180     'CAST',
  1209       'CAST',
  1181     'DES',
  1210       'DES',
  1182     'DES3',
  1211       'DES3',
  1183     'MD2',
  1212       'MD2',
  1184     'MD4',
  1213       'MD4',
  1185     'RIPEMD',
  1214       'RIPEMD',
  1186     'SHA256',
  1215       'SHA256',
  1187     'XOR',
  1216       'XOR',
  1188 
  1217 
  1189     '_Crypto_Cipher__AES',
  1218       '_Crypto_Cipher__AES',
  1190     '_Crypto_Cipher__ARC2',
  1219       '_Crypto_Cipher__ARC2',
  1191     '_Crypto_Cipher__ARC4',
  1220       '_Crypto_Cipher__ARC4',
  1192     '_Crypto_Cipher__Blowfish',
  1221       '_Crypto_Cipher__Blowfish',
  1193     '_Crypto_Cipher__CAST',
  1222       '_Crypto_Cipher__CAST',
  1194     '_Crypto_Cipher__DES',
  1223       '_Crypto_Cipher__DES',
  1195     '_Crypto_Cipher__DES3',
  1224       '_Crypto_Cipher__DES3',
  1196     '_Crypto_Cipher__XOR',
  1225       '_Crypto_Cipher__XOR',
  1197     '_Crypto_Hash__MD2',
  1226       '_Crypto_Hash__MD2',
  1198     '_Crypto_Hash__MD4',
  1227       '_Crypto_Hash__MD4',
  1199     '_Crypto_Hash__RIPEMD',
  1228       '_Crypto_Hash__RIPEMD',
  1200     '_Crypto_Hash__SHA256',
  1229       '_Crypto_Hash__SHA256',
  1201     'array',
  1230       'array',
  1202     'binascii',
  1231       'binascii',
  1203     'bz2',
  1232       'bz2',
  1204     'cmath',
  1233       'cmath',
  1205     'collections',
  1234       'collections',
  1206     'crypt',
  1235       'crypt',
  1207     'cStringIO',
  1236       'cStringIO',
  1208     'datetime',
  1237       'datetime',
  1209     'errno',
  1238       'errno',
  1210     'exceptions',
  1239       'exceptions',
  1211     'gc',
  1240       'gc',
  1212     'itertools',
  1241       'itertools',
  1213     'math',
  1242       'math',
  1214     'md5',
  1243       'md5',
  1215     'operator',
  1244       'operator',
  1216     'posix',
  1245       'posix',
  1217     'posixpath',
  1246       'posixpath',
  1218     'pyexpat',
  1247       'pyexpat',
  1219     'sha',
  1248       'sha',
  1220     'struct',
  1249       'struct',
  1221     'sys',
  1250       'sys',
  1222     'time',
  1251       'time',
  1223     'timing',
  1252       'timing',
  1224     'unicodedata',
  1253       'unicodedata',
  1225     'zlib',
  1254       'zlib',
  1226     '_ast',
  1255       '_ast',
  1227     '_bisect',
  1256       '_bisect',
  1228     '_codecs',
  1257       '_codecs',
  1229     '_codecs_cn',
  1258       '_codecs_cn',
  1230     '_codecs_hk',
  1259       '_codecs_hk',
  1231     '_codecs_iso2022',
  1260       '_codecs_iso2022',
  1232     '_codecs_jp',
  1261       '_codecs_jp',
  1233     '_codecs_kr',
  1262       '_codecs_kr',
  1234     '_codecs_tw',
  1263       '_codecs_tw',
  1235     '_collections',
  1264       '_collections',
  1236     '_csv',
  1265       '_csv',
  1237     '_elementtree',
  1266       '_elementtree',
  1238     '_functools',
  1267       '_functools',
  1239     '_hashlib',
  1268       '_hashlib',
  1240     '_heapq',
  1269       '_heapq',
  1241     '_locale',
  1270       '_locale',
  1242     '_lsprof',
  1271       '_lsprof',
  1243     '_md5',
  1272       '_md5',
  1244     '_multibytecodec',
  1273       '_multibytecodec',
  1245     '_random',
  1274       '_random',
  1246     '_sha',
  1275       '_sha',
  1247     '_sha256',
  1276       '_sha256',
  1248     '_sha512',
  1277       '_sha512',
  1249     '_sre',
  1278       '_sre',
  1250     '_struct',
  1279       '_struct',
  1251     '_types',
  1280       '_types',
  1252     '_weakref',
  1281       '_weakref',
  1253     '__main__',
  1282       '__main__',
  1254   ]
  1283   ]
  1255 
  1284 
  1256   __CRYPTO_CIPHER_ALLOWED_MODULES = [
  1285   __CRYPTO_CIPHER_ALLOWED_MODULES = [
  1257     'MODE_CBC',
  1286       'MODE_CBC',
  1258     'MODE_CFB',
  1287       'MODE_CFB',
  1259     'MODE_CTR',
  1288       'MODE_CTR',
  1260     'MODE_ECB',
  1289       'MODE_ECB',
  1261     'MODE_OFB',
  1290       'MODE_OFB',
  1262     'block_size',
  1291       'block_size',
  1263     'key_size',
  1292       'key_size',
  1264     'new',
  1293       'new',
  1265   ]
  1294   ]
  1266   _WHITE_LIST_PARTIAL_MODULES = {
  1295   _WHITE_LIST_PARTIAL_MODULES = {
  1267     'Crypto.Cipher.AES': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1296       'Crypto.Cipher.AES': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1268     'Crypto.Cipher.ARC2': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1297       'Crypto.Cipher.ARC2': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1269     'Crypto.Cipher.Blowfish': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1298       'Crypto.Cipher.Blowfish': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1270     'Crypto.Cipher.CAST': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1299       'Crypto.Cipher.CAST': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1271     'Crypto.Cipher.DES': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1300       'Crypto.Cipher.DES': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1272     'Crypto.Cipher.DES3': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1301       'Crypto.Cipher.DES3': __CRYPTO_CIPHER_ALLOWED_MODULES,
  1273 
  1302 
  1274     'gc': [
  1303       'gc': [
  1275       'enable',
  1304           'enable',
  1276       'disable',
  1305           'disable',
  1277       'isenabled',
  1306           'isenabled',
  1278       'collect',
  1307           'collect',
  1279       'get_debug',
  1308           'get_debug',
  1280       'set_threshold',
  1309           'set_threshold',
  1281       'get_threshold',
  1310           'get_threshold',
  1282       'get_count'
  1311           'get_count'
  1283     ],
  1312       ],
  1284 
  1313 
  1285 
  1314 
  1286 
  1315 
  1287     'os': [
  1316       'os': [
  1288       'access',
  1317           'access',
  1289       'altsep',
  1318           'altsep',
  1290       'curdir',
  1319           'curdir',
  1291       'defpath',
  1320           'defpath',
  1292       'devnull',
  1321           'devnull',
  1293       'environ',
  1322           'environ',
  1294       'error',
  1323           'error',
  1295       'extsep',
  1324           'extsep',
  1296       'EX_NOHOST',
  1325           'EX_NOHOST',
  1297       'EX_NOINPUT',
  1326           'EX_NOINPUT',
  1298       'EX_NOPERM',
  1327           'EX_NOPERM',
  1299       'EX_NOUSER',
  1328           'EX_NOUSER',
  1300       'EX_OK',
  1329           'EX_OK',
  1301       'EX_OSERR',
  1330           'EX_OSERR',
  1302       'EX_OSFILE',
  1331           'EX_OSFILE',
  1303       'EX_PROTOCOL',
  1332           'EX_PROTOCOL',
  1304       'EX_SOFTWARE',
  1333           'EX_SOFTWARE',
  1305       'EX_TEMPFAIL',
  1334           'EX_TEMPFAIL',
  1306       'EX_UNAVAILABLE',
  1335           'EX_UNAVAILABLE',
  1307       'EX_USAGE',
  1336           'EX_USAGE',
  1308       'F_OK',
  1337           'F_OK',
  1309       'getcwd',
  1338           'getcwd',
  1310       'getcwdu',
  1339           'getcwdu',
  1311       'getenv',
  1340           'getenv',
  1312       'listdir',
  1341           'listdir',
  1313       'lstat',
  1342           'lstat',
  1314       'name',
  1343           'name',
  1315       'NGROUPS_MAX',
  1344           'NGROUPS_MAX',
  1316       'O_APPEND',
  1345           'O_APPEND',
  1317       'O_CREAT',
  1346           'O_CREAT',
  1318       'O_DIRECT',
  1347           'O_DIRECT',
  1319       'O_DIRECTORY',
  1348           'O_DIRECTORY',
  1320       'O_DSYNC',
  1349           'O_DSYNC',
  1321       'O_EXCL',
  1350           'O_EXCL',
  1322       'O_LARGEFILE',
  1351           'O_LARGEFILE',
  1323       'O_NDELAY',
  1352           'O_NDELAY',
  1324       'O_NOCTTY',
  1353           'O_NOCTTY',
  1325       'O_NOFOLLOW',
  1354           'O_NOFOLLOW',
  1326       'O_NONBLOCK',
  1355           'O_NONBLOCK',
  1327       'O_RDONLY',
  1356           'O_RDONLY',
  1328       'O_RDWR',
  1357           'O_RDWR',
  1329       'O_RSYNC',
  1358           'O_RSYNC',
  1330       'O_SYNC',
  1359           'O_SYNC',
  1331       'O_TRUNC',
  1360           'O_TRUNC',
  1332       'O_WRONLY',
  1361           'O_WRONLY',
  1333       'open',
  1362           'open',
  1334       'pardir',
  1363           'pardir',
  1335       'path',
  1364           'path',
  1336       'pathsep',
  1365           'pathsep',
  1337       'R_OK',
  1366           'R_OK',
  1338       'readlink',
  1367           'readlink',
  1339       'remove',
  1368           'remove',
  1340       'rename',
  1369           'rename',
  1341       'SEEK_CUR',
  1370           'SEEK_CUR',
  1342       'SEEK_END',
  1371           'SEEK_END',
  1343       'SEEK_SET',
  1372           'SEEK_SET',
  1344       'sep',
  1373           'sep',
  1345       'stat',
  1374           'stat',
  1346       'stat_float_times',
  1375           'stat_float_times',
  1347       'stat_result',
  1376           'stat_result',
  1348       'strerror',
  1377           'strerror',
  1349       'TMP_MAX',
  1378           'TMP_MAX',
  1350       'unlink',
  1379           'unlink',
  1351       'urandom',
  1380           'urandom',
  1352       'utime',
  1381           'utime',
  1353       'walk',
  1382           'walk',
  1354       'WCOREDUMP',
  1383           'WCOREDUMP',
  1355       'WEXITSTATUS',
  1384           'WEXITSTATUS',
  1356       'WIFEXITED',
  1385           'WIFEXITED',
  1357       'WIFSIGNALED',
  1386           'WIFSIGNALED',
  1358       'WIFSTOPPED',
  1387           'WIFSTOPPED',
  1359       'WNOHANG',
  1388           'WNOHANG',
  1360       'WSTOPSIG',
  1389           'WSTOPSIG',
  1361       'WTERMSIG',
  1390           'WTERMSIG',
  1362       'WUNTRACED',
  1391           'WUNTRACED',
  1363       'W_OK',
  1392           'W_OK',
  1364       'X_OK',
  1393           'X_OK',
  1365     ],
  1394       ],
  1366   }
  1395   }
  1367 
  1396 
  1368   _MODULE_OVERRIDES = {
  1397   _MODULE_OVERRIDES = {
  1369     'locale': {
  1398       'locale': {
  1370       'setlocale': FakeSetLocale,
  1399           'setlocale': FakeSetLocale,
  1371     },
  1400       },
  1372 
  1401 
  1373     'os': {
  1402       'os': {
  1374       'access': FakeAccess,
  1403           'access': FakeAccess,
  1375       'listdir': RestrictedPathFunction(os.listdir),
  1404           'listdir': RestrictedPathFunction(os.listdir),
  1376 
  1405 
  1377       'lstat': RestrictedPathFunction(os.stat),
  1406           'lstat': RestrictedPathFunction(os.stat),
  1378       'open': FakeOpen,
  1407           'open': FakeOpen,
  1379       'readlink': FakeReadlink,
  1408           'readlink': FakeReadlink,
  1380       'remove': FakeUnlink,
  1409           'remove': FakeUnlink,
  1381       'rename': FakeRename,
  1410           'rename': FakeRename,
  1382       'stat': RestrictedPathFunction(os.stat),
  1411           'stat': RestrictedPathFunction(os.stat),
  1383       'uname': FakeUname,
  1412           'uname': FakeUname,
  1384       'unlink': FakeUnlink,
  1413           'unlink': FakeUnlink,
  1385       'urandom': FakeURandom,
  1414           'urandom': FakeURandom,
  1386       'utime': FakeUTime,
  1415           'utime': FakeUTime,
  1387     },
  1416       },
  1388 
  1417 
  1389     'distutils.util': {
  1418       'distutils.util': {
  1390       'get_platform': FakeGetPlatform,
  1419           'get_platform': FakeGetPlatform,
  1391     },
  1420       },
  1392   }
  1421   }
  1393 
  1422 
  1394   _ENABLED_FILE_TYPES = (
  1423   _ENABLED_FILE_TYPES = (
  1395     imp.PKG_DIRECTORY,
  1424       imp.PKG_DIRECTORY,
  1396     imp.PY_SOURCE,
  1425       imp.PY_SOURCE,
  1397     imp.PY_COMPILED,
  1426       imp.PY_COMPILED,
  1398     imp.C_BUILTIN,
  1427       imp.C_BUILTIN,
  1399   )
  1428   )
  1400 
  1429 
  1401   def __init__(self,
  1430   def __init__(self,
  1402                module_dict,
  1431                module_dict,
  1403                imp_module=imp,
  1432                imp_module=imp,
  1820       source_code += '\n'
  1849       source_code += '\n'
  1821 
  1850 
  1822     return compile(source_code, full_path, 'exec')
  1851     return compile(source_code, full_path, 'exec')
  1823 
  1852 
  1824 
  1853 
       
  1854 
  1825 def ModuleHasValidMainFunction(module):
  1855 def ModuleHasValidMainFunction(module):
  1826   """Determines if a module has a main function that takes no arguments.
  1856   """Determines if a module has a main function that takes no arguments.
  1827 
  1857 
  1828   This includes functions that have arguments with defaults that are all
  1858   This includes functions that have arguments with defaults that are all
  1829   assigned, thus requiring no additional arguments in order to be called.
  1859   assigned, thus requiring no additional arguments in order to be called.
  1833 
  1863 
  1834   Returns:
  1864   Returns:
  1835     True if the module has a valid, reusable main function; False otherwise.
  1865     True if the module has a valid, reusable main function; False otherwise.
  1836   """
  1866   """
  1837   if hasattr(module, 'main') and type(module.main) is types.FunctionType:
  1867   if hasattr(module, 'main') and type(module.main) is types.FunctionType:
  1838     arg_names, var_args, var_kwargs, default_values = inspect.getargspec(module.main)
  1868     arg_names, var_args, var_kwargs, default_values = inspect.getargspec(
       
  1869         module.main)
  1839     if len(arg_names) == 0:
  1870     if len(arg_names) == 0:
  1840       return True
  1871       return True
  1841     if default_values is not None and len(arg_names) == len(default_values):
  1872     if default_values is not None and len(arg_names) == len(default_values):
  1842       return True
  1873       return True
  1843   return False
  1874   return False
  1876 
  1907 
  1877   Args:
  1908   Args:
  1878     cgi_path: Absolute path of the CGI module file on disk.
  1909     cgi_path: Absolute path of the CGI module file on disk.
  1879     module_fullname: Fully qualified Python module name used to import the
  1910     module_fullname: Fully qualified Python module name used to import the
  1880       cgi_path module.
  1911       cgi_path module.
       
  1912     isfile: Used for testing.
  1881 
  1913 
  1882   Returns:
  1914   Returns:
  1883     List containing the paths to the missing __init__.py files.
  1915     List containing the paths to the missing __init__.py files.
  1884   """
  1916   """
  1885   missing_init_files = []
  1917   missing_init_files = []
  1933         and has a main() function that can be reused, this will be None.
  1965         and has a main() function that can be reused, this will be None.
  1934   """
  1966   """
  1935   module_fullname = GetScriptModuleName(handler_path)
  1967   module_fullname = GetScriptModuleName(handler_path)
  1936   script_module = module_dict.get(module_fullname)
  1968   script_module = module_dict.get(module_fullname)
  1937   module_code = None
  1969   module_code = None
  1938   if script_module != None and ModuleHasValidMainFunction(script_module):
  1970   if script_module is not None and ModuleHasValidMainFunction(script_module):
  1939     logging.debug('Reusing main() function of module "%s"', module_fullname)
  1971     logging.debug('Reusing main() function of module "%s"', module_fullname)
  1940   else:
  1972   else:
  1941     if script_module is None:
  1973     if script_module is None:
  1942       script_module = imp.new_module(module_fullname)
  1974       script_module = imp.new_module(module_fullname)
  1943       script_module.__loader__ = import_hook
  1975       script_module.__loader__ = import_hook
  1944 
  1976 
  1945     try:
  1977     try:
  1946       module_code = import_hook.get_code(module_fullname)
  1978       module_code = import_hook.get_code(module_fullname)
  1947       full_path, search_path, submodule = import_hook.GetModuleInfo(module_fullname)
  1979       full_path, search_path, submodule = (
       
  1980         import_hook.GetModuleInfo(module_fullname))
  1948       script_module.__file__ = full_path
  1981       script_module.__file__ = full_path
  1949       if search_path is not None:
  1982       if search_path is not None:
  1950         script_module.__path__ = search_path
  1983         script_module.__path__ = search_path
  1951     except:
  1984     except:
  1952       exc_type, exc_value, exc_tb = sys.exc_info()
  1985       exc_type, exc_value, exc_tb = sys.exc_info()
  1953       import_error_message = str(exc_type)
  1986       import_error_message = str(exc_type)
  1954       if exc_value:
  1987       if exc_value:
  1955         import_error_message += ': ' + str(exc_value)
  1988         import_error_message += ': ' + str(exc_value)
  1956 
  1989 
  1957       logging.exception('Encountered error loading module "%s": %s',
  1990       logging.exception('Encountered error loading module "%s": %s',
  1958                     module_fullname, import_error_message)
  1991                         module_fullname, import_error_message)
  1959       missing_inits = FindMissingInitFiles(cgi_path, module_fullname)
  1992       missing_inits = FindMissingInitFiles(cgi_path, module_fullname)
  1960       if missing_inits:
  1993       if missing_inits:
  1961         logging.warning('Missing package initialization files: %s',
  1994         logging.warning('Missing package initialization files: %s',
  1962                         ', '.join(missing_inits))
  1995                         ', '.join(missing_inits))
  1963       else:
  1996       else:
  1987 
  2020 
  1988   return module_fullname, script_module, module_code
  2021   return module_fullname, script_module, module_code
  1989 
  2022 
  1990 
  2023 
  1991 def ExecuteOrImportScript(handler_path, cgi_path, import_hook):
  2024 def ExecuteOrImportScript(handler_path, cgi_path, import_hook):
  1992   """Executes a CGI script by importing it as a new module; possibly reuses
  2025   """Executes a CGI script by importing it as a new module.
  1993   the module's main() function if it is defined and takes no arguments.
  2026 
       
  2027   This possibly reuses the module's main() function if it is defined and
       
  2028   takes no arguments.
  1994 
  2029 
  1995   Basic technique lifted from PEP 338 and Python2.5's runpy module. See:
  2030   Basic technique lifted from PEP 338 and Python2.5's runpy module. See:
  1996     http://www.python.org/dev/peps/pep-0338/
  2031     http://www.python.org/dev/peps/pep-0338/
  1997 
  2032 
  1998   See the section entitled "Import Statements and the Main Module" to understand
  2033   See the section entitled "Import Statements and the Main Module" to understand
  2258   def __str__(self):
  2293   def __str__(self):
  2259     """Returns a string representation of this dispatcher."""
  2294     """Returns a string representation of this dispatcher."""
  2260     return 'Local CGI dispatcher for %s' % self._cgi_func
  2295     return 'Local CGI dispatcher for %s' % self._cgi_func
  2261 
  2296 
  2262 
  2297 
       
  2298 
  2263 class PathAdjuster(object):
  2299 class PathAdjuster(object):
  2264   """Adjusts application file paths to paths relative to the application or
  2300   """Adjusts application file paths to paths relative to the application or
  2265   external library directories."""
  2301   external library directories."""
  2266 
  2302 
  2267   def __init__(self, root_path):
  2303   def __init__(self, root_path):
  2271       root_path: Path to the root of the application running on the server.
  2307       root_path: Path to the root of the application running on the server.
  2272     """
  2308     """
  2273     self._root_path = os.path.abspath(root_path)
  2309     self._root_path = os.path.abspath(root_path)
  2274 
  2310 
  2275   def AdjustPath(self, path):
  2311   def AdjustPath(self, path):
  2276     """Adjusts application file path to paths relative to the application or
  2312     """Adjusts application file paths to relative to the application.
  2277     external library directories.
  2313 
       
  2314     More precisely this method adjusts application file path to paths
       
  2315     relative to the application or external library directories.
  2278 
  2316 
  2279     Handler paths that start with $PYTHON_LIB will be converted to paths
  2317     Handler paths that start with $PYTHON_LIB will be converted to paths
  2280     relative to the google directory.
  2318     relative to the google directory.
  2281 
  2319 
  2282     Args:
  2320     Args:
  2290                           path[len(PYTHON_LIB_VAR) + 1:])
  2328                           path[len(PYTHON_LIB_VAR) + 1:])
  2291     else:
  2329     else:
  2292       path = os.path.join(self._root_path, path)
  2330       path = os.path.join(self._root_path, path)
  2293 
  2331 
  2294     return path
  2332     return path
       
  2333 
  2295 
  2334 
  2296 
  2335 
  2297 class StaticFileConfigMatcher(object):
  2336 class StaticFileConfigMatcher(object):
  2298   """Keeps track of file/directory specific application configuration.
  2337   """Keeps track of file/directory specific application configuration.
  2299 
  2338 
  2380 
  2419 
  2381     Returns:
  2420     Returns:
  2382       String containing the mime type to use. Will be 'application/octet-stream'
  2421       String containing the mime type to use. Will be 'application/octet-stream'
  2383       if we have no idea what it should be.
  2422       if we have no idea what it should be.
  2384     """
  2423     """
  2385     for (path_re, mime_type, expiration) in self._patterns:
  2424     for (path_re, mimetype, unused_expiration) in self._patterns:
  2386       if mime_type is not None:
  2425       if mimetype is not None:
  2387         the_match = path_re.match(path)
  2426         the_match = path_re.match(path)
  2388         if the_match:
  2427         if the_match:
  2389           return mime_type
  2428           return mimetype
  2390 
  2429 
  2391     filename, extension = os.path.splitext(path)
  2430     unused_filename, extension = os.path.splitext(path)
  2392     return mimetypes.types_map.get(extension, 'application/octet-stream')
  2431     return mimetypes.types_map.get(extension, 'application/octet-stream')
  2393 
  2432 
  2394   def GetExpiration(self, path):
  2433   def GetExpiration(self, path):
  2395     """Returns the cache expiration duration to be users for the given file.
  2434     """Returns the cache expiration duration to be users for the given file.
  2396 
  2435 
  2398       path: String containing the file's path relative to the app.
  2437       path: String containing the file's path relative to the app.
  2399 
  2438 
  2400     Returns:
  2439     Returns:
  2401       Integer number of seconds to be used for browser cache expiration time.
  2440       Integer number of seconds to be used for browser cache expiration time.
  2402     """
  2441     """
  2403     for (path_re, mime_type, expiration) in self._patterns:
  2442     for (path_re, unused_mimetype, expiration) in self._patterns:
  2404       the_match = path_re.match(path)
  2443       the_match = path_re.match(path)
  2405       if the_match:
  2444       if the_match:
  2406         return expiration
  2445         return expiration
  2407 
  2446 
  2408     return self._default_expiration or 0
  2447     return self._default_expiration or 0
       
  2448 
  2409 
  2449 
  2410 
  2450 
  2411 
  2451 
  2412 def ReadDataFile(data_path, openfile=file):
  2452 def ReadDataFile(data_path, openfile=file):
  2413   """Reads a file on disk, returning a corresponding HTTP status and data.
  2453   """Reads a file on disk, returning a corresponding HTTP status and data.
  2546 
  2586 
  2547 def CacheRewriter(status_code, status_message, headers, body):
  2587 def CacheRewriter(status_code, status_message, headers, body):
  2548   """Update the cache header."""
  2588   """Update the cache header."""
  2549   if not 'Cache-Control' in headers:
  2589   if not 'Cache-Control' in headers:
  2550     headers['Cache-Control'] = 'no-cache'
  2590     headers['Cache-Control'] = 'no-cache'
       
  2591     if not 'Expires' in headers:
       
  2592       headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT'
  2551   return status_code, status_message, headers, body
  2593   return status_code, status_message, headers, body
  2552 
  2594 
  2553 
  2595 
  2554 def ContentLengthRewriter(status_code, status_message, headers, body):
  2596 def ContentLengthRewriter(status_code, status_message, headers, body):
  2555   """Rewrite the Content-Length header.
  2597   """Rewrite the Content-Length header.
  2607   """
  2649   """
  2608   return [IgnoreHeadersRewriter,
  2650   return [IgnoreHeadersRewriter,
  2609           ParseStatusRewriter,
  2651           ParseStatusRewriter,
  2610           CacheRewriter,
  2652           CacheRewriter,
  2611           ContentLengthRewriter,
  2653           ContentLengthRewriter,
  2612   ]
  2654          ]
  2613 
  2655 
  2614 
  2656 
  2615 def RewriteResponse(response_file, response_rewriters=None):
  2657 def RewriteResponse(response_file, response_rewriters=None):
  2616   """Allows final rewrite of dev_appserver response.
  2658   """Allows final rewrite of dev_appserver response.
  2617 
  2659 
  2661 
  2703 
  2662   header_data = '\r\n'.join(header_list) + '\r\n'
  2704   header_data = '\r\n'.join(header_list) + '\r\n'
  2663   return status_code, status_message, header_data, response_file.read()
  2705   return status_code, status_message, header_data, response_file.read()
  2664 
  2706 
  2665 
  2707 
       
  2708 
  2666 class ModuleManager(object):
  2709 class ModuleManager(object):
  2667   """Manages loaded modules in the runtime.
  2710   """Manages loaded modules in the runtime.
  2668 
  2711 
  2669   Responsible for monitoring and reporting about file modification times.
  2712   Responsible for monitoring and reporting about file modification times.
  2670   Modules can be loaded from source or precompiled byte-code files.  When a
  2713   Modules can be loaded from source or precompiled byte-code files.  When a
  2693 
  2736 
  2694     Returns:
  2737     Returns:
  2695       Path of the module's corresponding Python source file if it exists, or
  2738       Path of the module's corresponding Python source file if it exists, or
  2696       just the module's compiled Python file. If the module has an invalid
  2739       just the module's compiled Python file. If the module has an invalid
  2697       __file__ attribute, None will be returned.
  2740       __file__ attribute, None will be returned.
  2698       """
  2741     """
  2699     module_file = getattr(module, '__file__', None)
  2742     module_file = getattr(module, '__file__', None)
  2700     if module_file is None:
  2743     if module_file is None:
  2701       return None
  2744       return None
  2702 
  2745 
  2703     source_file = module_file[:module_file.rfind('py') + 2]
  2746     source_file = module_file[:module_file.rfind('py') + 2]
  2725         return True
  2768         return True
  2726 
  2769 
  2727     return False
  2770     return False
  2728 
  2771 
  2729   def UpdateModuleFileModificationTimes(self):
  2772   def UpdateModuleFileModificationTimes(self):
  2730     """Records the current modification times of all monitored modules.
  2773     """Records the current modification times of all monitored modules."""
  2731     """
       
  2732     self._modification_times.clear()
  2774     self._modification_times.clear()
  2733     for name, module in self._modules.items():
  2775     for name, module in self._modules.items():
  2734       if not isinstance(module, types.ModuleType):
  2776       if not isinstance(module, types.ModuleType):
  2735         continue
  2777         continue
  2736       module_file = self.GetModuleFile(module)
  2778       module_file = self.GetModuleFile(module)
  2748     self._modules.clear()
  2790     self._modules.clear()
  2749     self._modules.update(self._default_modules)
  2791     self._modules.update(self._default_modules)
  2750     sys.path_hooks[:] = self._save_path_hooks
  2792     sys.path_hooks[:] = self._save_path_hooks
  2751 
  2793 
  2752 
  2794 
       
  2795 
  2753 def _ClearTemplateCache(module_dict=sys.modules):
  2796 def _ClearTemplateCache(module_dict=sys.modules):
  2754   """Clear template cache in webapp.template module.
  2797   """Clear template cache in webapp.template module.
  2755 
  2798 
  2756   Attempts to load template module.  Ignores failure.  If module loads, the
  2799   Attempts to load template module.  Ignores failure.  If module loads, the
  2757   template cache is cleared.
  2800   template cache is cleared.
       
  2801 
       
  2802   Args:
       
  2803     module_dict: Used for dependency injection.
  2758   """
  2804   """
  2759   template_module = module_dict.get('google.appengine.ext.webapp.template')
  2805   template_module = module_dict.get('google.appengine.ext.webapp.template')
  2760   if template_module is not None:
  2806   if template_module is not None:
  2761     template_module.template_cache.clear()
  2807     template_module.template_cache.clear()
       
  2808 
  2762 
  2809 
  2763 
  2810 
  2764 def CreateRequestHandler(root_path,
  2811 def CreateRequestHandler(root_path,
  2765                          login_url,
  2812                          login_url,
  2766                          require_indexes=False,
  2813                          require_indexes=False,
  2767                          static_caching=True):
  2814                          static_caching=True):
  2768   """Creates a new BaseHTTPRequestHandler sub-class for use with the Python
  2815   """Creates a new BaseHTTPRequestHandler sub-class.
  2769   BaseHTTPServer module's HTTP server.
  2816 
       
  2817   This class will be used with the Python BaseHTTPServer module's HTTP server.
  2770 
  2818 
  2771   Python's built-in HTTP server does not support passing context information
  2819   Python's built-in HTTP server does not support passing context information
  2772   along to instances of its request handlers. This function gets around that
  2820   along to instances of its request handlers. This function gets around that
  2773   by creating a sub-class of the handler in a closure that has access to
  2821   by creating a sub-class of the handler in a closure that has access to
  2774   this context information.
  2822   this context information.
  2790     index_yaml_updater = dev_appserver_index.IndexYamlUpdater(root_path)
  2838     index_yaml_updater = dev_appserver_index.IndexYamlUpdater(root_path)
  2791 
  2839 
  2792   application_config_cache = AppConfigCache()
  2840   application_config_cache = AppConfigCache()
  2793 
  2841 
  2794   class DevAppServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  2842   class DevAppServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  2795     """Dispatches URLs using patterns from a URLMatcher, which is created by
  2843     """Dispatches URLs using patterns from a URLMatcher.
  2796     loading an application's configuration file. Executes CGI scripts in the
  2844 
  2797     local process so the scripts can use mock versions of APIs.
  2845     The URLMatcher is created by loading an application's configuration file.
       
  2846     Executes CGI scripts in the local process so the scripts can use mock
       
  2847     versions of APIs.
  2798 
  2848 
  2799     HTTP requests that correctly specify a user info cookie
  2849     HTTP requests that correctly specify a user info cookie
  2800     (dev_appserver_login.COOKIE_NAME) will have the 'USER_EMAIL' environment
  2850     (dev_appserver_login.COOKIE_NAME) will have the 'USER_EMAIL' environment
  2801     variable set accordingly. If the user is also an admin, the
  2851     variable set accordingly. If the user is also an admin, the
  2802     'USER_IS_ADMIN' variable will exist and be set to '1'. If the user is not
  2852     'USER_IS_ADMIN' variable will exist and be set to '1'. If the user is not
  2817 
  2867 
  2818     def __init__(self, *args, **kwargs):
  2868     def __init__(self, *args, **kwargs):
  2819       """Initializer.
  2869       """Initializer.
  2820 
  2870 
  2821       Args:
  2871       Args:
  2822         args, kwargs: Positional and keyword arguments passed to the constructor
  2872         args: Positional arguments passed to the superclass constructor.
  2823           of the super class.
  2873         kwargs: Keyword arguments passed to the superclass constructor.
  2824       """
  2874       """
  2825       BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
  2875       BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
  2826 
  2876 
  2827     def version_string(self):
  2877     def version_string(self):
  2828       """Returns server's version string used for Server HTTP header"""
  2878       """Returns server's version string used for Server HTTP header."""
  2829       return self.server_version
  2879       return self.server_version
  2830 
  2880 
  2831     def do_GET(self):
  2881     def do_GET(self):
  2832       """Handle GET requests."""
  2882       """Handle GET requests."""
  2833       self._HandleRequest()
  2883       self._HandleRequest()
  2860       """Handles any type of request and prints exceptions if they occur."""
  2910       """Handles any type of request and prints exceptions if they occur."""
  2861       server_name = self.headers.get('host') or self.server.server_name
  2911       server_name = self.headers.get('host') or self.server.server_name
  2862       server_name = server_name.split(':', 1)[0]
  2912       server_name = server_name.split(':', 1)[0]
  2863 
  2913 
  2864       env_dict = {
  2914       env_dict = {
  2865         'REQUEST_METHOD': self.command,
  2915           'REQUEST_METHOD': self.command,
  2866         'REMOTE_ADDR': self.client_address[0],
  2916           'REMOTE_ADDR': self.client_address[0],
  2867         'SERVER_SOFTWARE': self.server_version,
  2917           'SERVER_SOFTWARE': self.server_version,
  2868         'SERVER_NAME': server_name,
  2918           'SERVER_NAME': server_name,
  2869         'SERVER_PROTOCOL': self.protocol_version,
  2919           'SERVER_PROTOCOL': self.protocol_version,
  2870         'SERVER_PORT': str(self.server.server_port),
  2920           'SERVER_PORT': str(self.server.server_port),
  2871       }
  2921       }
  2872 
  2922 
  2873       full_url = GetFullURL(server_name, self.server.server_port, self.path)
  2923       full_url = GetFullURL(server_name, self.server.server_port, self.path)
  2874       if len(full_url) > MAX_URL_LENGTH:
  2924       if len(full_url) > MAX_URL_LENGTH:
  2875         msg = 'Requested URI too long: %s' % full_url
  2925         msg = 'Requested URI too long: %s' % full_url
  2887                                                  login_url)
  2937                                                  login_url)
  2888         config, explicit_matcher = LoadAppConfig(root_path, self.module_dict,
  2938         config, explicit_matcher = LoadAppConfig(root_path, self.module_dict,
  2889                                                  cache=self.config_cache,
  2939                                                  cache=self.config_cache,
  2890                                                  static_caching=static_caching)
  2940                                                  static_caching=static_caching)
  2891         if config.api_version != API_VERSION:
  2941         if config.api_version != API_VERSION:
  2892           logging.error("API versions cannot be switched dynamically: %r != %r"
  2942           logging.error(
  2893                         % (config.api_version, API_VERSION))
  2943               "API versions cannot be switched dynamically: %r != %r",
       
  2944               config.api_version, API_VERSION)
  2894           sys.exit(1)
  2945           sys.exit(1)
  2895         env_dict['CURRENT_VERSION_ID'] = config.version + ".1"
  2946         env_dict['CURRENT_VERSION_ID'] = config.version + ".1"
  2896         env_dict['APPLICATION_ID'] = config.application
  2947         env_dict['APPLICATION_ID'] = config.application
  2897         dispatcher = MatcherDispatcher(login_url,
  2948         dispatcher = MatcherDispatcher(login_url,
  2898                                        [implicit_matcher, explicit_matcher])
  2949                                        [implicit_matcher, explicit_matcher])
  2925           self.module_manager.UpdateModuleFileModificationTimes()
  2976           self.module_manager.UpdateModuleFileModificationTimes()
  2926 
  2977 
  2927         outfile.flush()
  2978         outfile.flush()
  2928         outfile.seek(0)
  2979         outfile.seek(0)
  2929 
  2980 
  2930         status_code, status_message, header_data, body = RewriteResponse(outfile, self.rewriter_chain)
  2981         status_code, status_message, header_data, body = (
       
  2982             RewriteResponse(outfile, self.rewriter_chain))
  2931 
  2983 
  2932         runtime_response_size = len(outfile.getvalue())
  2984         runtime_response_size = len(outfile.getvalue())
  2933         if runtime_response_size > MAX_RUNTIME_RESPONSE_SIZE:
  2985         if runtime_response_size > MAX_RUNTIME_RESPONSE_SIZE:
  2934           status_code = 403
  2986           status_code = 403
  2935           status_message = 'Forbidden'
  2987           status_message = 'Forbidden'
  2982       logging.info(format, *args)
  3034       logging.info(format, *args)
  2983 
  3035 
  2984   return DevAppServerRequestHandler
  3036   return DevAppServerRequestHandler
  2985 
  3037 
  2986 
  3038 
       
  3039 
  2987 def ReadAppConfig(appinfo_path, parse_app_config=appinfo.LoadSingleAppInfo):
  3040 def ReadAppConfig(appinfo_path, parse_app_config=appinfo.LoadSingleAppInfo):
  2988   """Reads app.yaml file and returns its app id and list of URLMap instances.
  3041   """Reads app.yaml file and returns its app id and list of URLMap instances.
  2989 
  3042 
  2990   Args:
  3043   Args:
  2991     appinfo_path: String containing the path to the app.yaml file.
  3044     appinfo_path: String containing the path to the app.yaml file.
  2999     URLMap instances, this function will raise an InvalidAppConfigError
  3052     URLMap instances, this function will raise an InvalidAppConfigError
  3000     exception.
  3053     exception.
  3001   """
  3054   """
  3002   try:
  3055   try:
  3003     appinfo_file = file(appinfo_path, 'r')
  3056     appinfo_file = file(appinfo_path, 'r')
  3004   except IOError, e:
  3057   except IOError, unused_e:
  3005     raise InvalidAppConfigError(
  3058     raise InvalidAppConfigError(
  3006       'Application configuration could not be read from "%s"' % appinfo_path)
  3059         'Application configuration could not be read from "%s"' % appinfo_path)
  3007   try:
  3060   try:
  3008     return parse_app_config(appinfo_file)
  3061     return parse_app_config(appinfo_file)
  3009   finally:
  3062   finally:
  3010     appinfo_file.close()
  3063     appinfo_file.close()
  3011 
  3064 
  3033       preserved between requests. This dictionary must be separate from the
  3086       preserved between requests. This dictionary must be separate from the
  3034       sys.modules dictionary.
  3087       sys.modules dictionary.
  3035     default_expiration: String describing default expiration time for browser
  3088     default_expiration: String describing default expiration time for browser
  3036       based caching of static files.  If set to None this disallows any
  3089       based caching of static files.  If set to None this disallows any
  3037       browser caching of static content.
  3090       browser caching of static content.
  3038     create_url_matcher, create_cgi_dispatcher, create_file_dispatcher,
  3091     create_url_matcher: Used for dependency injection.
       
  3092     create_cgi_dispatcher: Used for dependency injection.
       
  3093     create_file_dispatcher: Used for dependency injection.
  3039     create_path_adjuster: Used for dependency injection.
  3094     create_path_adjuster: Used for dependency injection.
       
  3095     normpath: Used for dependency injection.
  3040 
  3096 
  3041   Returns:
  3097   Returns:
  3042     Instance of URLMatcher with the supplied URLMap objects properly loaded.
  3098     Instance of URLMatcher with the supplied URLMap objects properly loaded.
       
  3099 
       
  3100   Raises:
       
  3101     InvalidAppConfigError: if the handler in url_map_list is an unknown type.
  3043   """
  3102   """
  3044   url_matcher = create_url_matcher()
  3103   url_matcher = create_url_matcher()
  3045   path_adjuster = create_path_adjuster(root_path)
  3104   path_adjuster = create_path_adjuster(root_path)
  3046   cgi_dispatcher = create_cgi_dispatcher(module_dict, root_path, path_adjuster)
  3105   cgi_dispatcher = create_cgi_dispatcher(module_dict, root_path, path_adjuster)
  3047   static_file_config_matcher = StaticFileConfigMatcher(url_map_list,
  3106   static_file_config_matcher = StaticFileConfigMatcher(url_map_list,
  3119     module_dict: Dictionary in which application-loaded modules should be
  3178     module_dict: Dictionary in which application-loaded modules should be
  3120       preserved between requests. This dictionary must be separate from the
  3179       preserved between requests. This dictionary must be separate from the
  3121       sys.modules dictionary.
  3180       sys.modules dictionary.
  3122     cache: Instance of AppConfigCache or None.
  3181     cache: Instance of AppConfigCache or None.
  3123     static_caching: True if browser caching of static files should be allowed.
  3182     static_caching: True if browser caching of static files should be allowed.
  3124     read_app_config, create_matcher: Used for dependency injection.
  3183     read_app_config: Used for dependency injection.
       
  3184     create_matcher: Used for dependency injection.
  3125 
  3185 
  3126   Returns:
  3186   Returns:
  3127      tuple: (AppInfoExternal, URLMatcher)
  3187      tuple: (AppInfoExternal, URLMatcher)
       
  3188 
       
  3189   Raises:
       
  3190     AppConfigNotFound: if an app.yaml file cannot be found.
  3128   """
  3191   """
  3129   for appinfo_path in [os.path.join(root_path, 'app.yaml'),
  3192   for appinfo_path in [os.path.join(root_path, 'app.yaml'),
  3130                        os.path.join(root_path, 'app.yml')]:
  3193                        os.path.join(root_path, 'app.yml')]:
  3131 
  3194 
  3132     if os.path.isfile(appinfo_path):
  3195     if os.path.isfile(appinfo_path):
  3179     A CronInfoExternal object.
  3242     A CronInfoExternal object.
  3180 
  3243 
  3181   Raises:
  3244   Raises:
  3182     If the config file is unreadable, empty or invalid, this function will
  3245     If the config file is unreadable, empty or invalid, this function will
  3183     raise an InvalidAppConfigError or a MalformedCronConfiguration exception.
  3246     raise an InvalidAppConfigError or a MalformedCronConfiguration exception.
  3184     """
  3247   """
  3185   try:
  3248   try:
  3186     croninfo_file = file(croninfo_path, 'r')
  3249     croninfo_file = file(croninfo_path, 'r')
  3187   except IOError, e:
  3250   except IOError, e:
  3188     raise InvalidAppConfigError(
  3251     raise InvalidAppConfigError(
  3189         'Cron configuration could not be read from "%s"' % croninfo_path)
  3252         'Cron configuration could not be read from "%s": %s'
       
  3253         % (croninfo_path, e))
  3190   try:
  3254   try:
  3191     return parse_cron_config(croninfo_file)
  3255     return parse_cron_config(croninfo_file)
  3192   finally:
  3256   finally:
  3193     croninfo_file.close()
  3257     croninfo_file.close()
  3194 
  3258 
  3195 
  3259 
       
  3260 
  3196 def SetupStubs(app_id, **config):
  3261 def SetupStubs(app_id, **config):
  3197   """Sets up testing stubs of APIs.
  3262   """Sets up testing stubs of APIs.
  3198 
  3263 
  3199   Args:
  3264   Args:
  3200     app_id: Application ID being served.
  3265     app_id: Application ID being served.
       
  3266     config: keyword arguments.
  3201 
  3267 
  3202   Keywords:
  3268   Keywords:
  3203     root_path: Root path to the directory of the application which should
  3269     root_path: Root path to the directory of the application which should
  3204         contain the app.yaml, indexes.yaml, and queues.yaml files.
  3270         contain the app.yaml, indexes.yaml, and queues.yaml files.
  3205     login_url: Relative URL which should be used for handling user login/logout.
  3271     login_url: Relative URL which should be used for handling user login/logout.
  3254                                    dev_appserver_login.CONTINUE_PARAM)
  3320                                    dev_appserver_login.CONTINUE_PARAM)
  3255   fixed_logout_url = '%s&%s' % (fixed_login_url,
  3321   fixed_logout_url = '%s&%s' % (fixed_login_url,
  3256                                 dev_appserver_login.LOGOUT_PARAM)
  3322                                 dev_appserver_login.LOGOUT_PARAM)
  3257 
  3323 
  3258   apiproxy_stub_map.apiproxy.RegisterStub(
  3324   apiproxy_stub_map.apiproxy.RegisterStub(
  3259     'user',
  3325       'user',
  3260     user_service_stub.UserServiceStub(login_url=fixed_login_url,
  3326       user_service_stub.UserServiceStub(login_url=fixed_login_url,
  3261                                       logout_url=fixed_logout_url))
  3327                                         logout_url=fixed_logout_url))
  3262 
  3328 
  3263   apiproxy_stub_map.apiproxy.RegisterStub(
  3329   apiproxy_stub_map.apiproxy.RegisterStub(
  3264     'urlfetch',
  3330       'urlfetch',
  3265     urlfetch_stub.URLFetchServiceStub())
  3331       urlfetch_stub.URLFetchServiceStub())
  3266 
  3332 
  3267   apiproxy_stub_map.apiproxy.RegisterStub(
  3333   apiproxy_stub_map.apiproxy.RegisterStub(
  3268     'mail',
  3334       'mail',
  3269     mail_stub.MailServiceStub(smtp_host,
  3335       mail_stub.MailServiceStub(smtp_host,
  3270                               smtp_port,
  3336                                 smtp_port,
  3271                               smtp_user,
  3337                                 smtp_user,
  3272                               smtp_password,
  3338                                 smtp_password,
  3273                               enable_sendmail=enable_sendmail,
  3339                                 enable_sendmail=enable_sendmail,
  3274                               show_mail_body=show_mail_body))
  3340                                 show_mail_body=show_mail_body))
  3275 
  3341 
  3276   apiproxy_stub_map.apiproxy.RegisterStub(
  3342   apiproxy_stub_map.apiproxy.RegisterStub(
  3277     'memcache',
  3343       'memcache',
  3278     memcache_stub.MemcacheServiceStub())
  3344       memcache_stub.MemcacheServiceStub())
  3279 
  3345 
  3280   apiproxy_stub_map.apiproxy.RegisterStub(
  3346   apiproxy_stub_map.apiproxy.RegisterStub(
  3281     'capability_service',
  3347       'capability_service',
  3282     capability_stub.CapabilityServiceStub())
  3348       capability_stub.CapabilityServiceStub())
  3283 
  3349 
  3284   apiproxy_stub_map.apiproxy.RegisterStub(
  3350   apiproxy_stub_map.apiproxy.RegisterStub(
  3285     'taskqueue',
  3351       'taskqueue',
  3286     taskqueue_stub.TaskQueueServiceStub(root_path=root_path))
  3352       taskqueue_stub.TaskQueueServiceStub(root_path=root_path))
       
  3353 
       
  3354   apiproxy_stub_map.apiproxy.RegisterStub(
       
  3355       'xmpp',
       
  3356       xmpp_service_stub.XmppServiceStub())
       
  3357 
  3287 
  3358 
  3288 
  3359 
  3289   try:
  3360   try:
  3290     from google.appengine.api.images import images_stub
  3361     from google.appengine.api.images import images_stub
  3291     apiproxy_stub_map.apiproxy.RegisterStub(
  3362     apiproxy_stub_map.apiproxy.RegisterStub(
  3292       'images',
  3363         'images',
  3293       images_stub.ImagesServiceStub())
  3364         images_stub.ImagesServiceStub())
  3294   except ImportError, e:
  3365   except ImportError, e:
  3295     logging.warning('Could not initialize images API; you are likely missing '
  3366     logging.warning('Could not initialize images API; you are likely missing '
  3296                     'the Python "PIL" module. ImportError: %s', e)
  3367                     'the Python "PIL" module. ImportError: %s', e)
  3297     from google.appengine.api.images import images_not_implemented_stub
  3368     from google.appengine.api.images import images_not_implemented_stub
  3298     apiproxy_stub_map.apiproxy.RegisterStub('images',
  3369     apiproxy_stub_map.apiproxy.RegisterStub(
  3299       images_not_implemented_stub.ImagesNotImplementedServiceStub())
  3370         'images',
       
  3371         images_not_implemented_stub.ImagesNotImplementedServiceStub())
  3300 
  3372 
  3301 
  3373 
  3302 def CreateImplicitMatcher(module_dict,
  3374 def CreateImplicitMatcher(module_dict,
  3303                           root_path,
  3375                           root_path,
  3304                           login_url,
  3376                           login_url,
  3312 
  3384 
  3313   Args:
  3385   Args:
  3314     module_dict: Dictionary in the form used by sys.modules.
  3386     module_dict: Dictionary in the form used by sys.modules.
  3315     root_path: Path to the root of the application.
  3387     root_path: Path to the root of the application.
  3316     login_url: Relative URL which should be used for handling user login/logout.
  3388     login_url: Relative URL which should be used for handling user login/logout.
       
  3389     create_path_adjuster: Used for dependedency injection.
  3317     create_local_dispatcher: Used for dependency injection.
  3390     create_local_dispatcher: Used for dependency injection.
       
  3391     create_cgi_dispatcher: Used for dependedency injection.
  3318 
  3392 
  3319   Returns:
  3393   Returns:
  3320     Instance of URLMatcher with appropriate dispatchers.
  3394     Instance of URLMatcher with appropriate dispatchers.
  3321   """
  3395   """
  3322   url_matcher = URLMatcher()
  3396   url_matcher = URLMatcher()
  3391     port: Port to start the application server on.
  3465     port: Port to start the application server on.
  3392     template_dir: Path to the directory in which the debug console templates
  3466     template_dir: Path to the directory in which the debug console templates
  3393       are stored.
  3467       are stored.
  3394     serve_address: Address on which the server should serve.
  3468     serve_address: Address on which the server should serve.
  3395     require_indexes: True if index.yaml is read-only gospel; default False.
  3469     require_indexes: True if index.yaml is read-only gospel; default False.
       
  3470     allow_skipped_files: True if skipped files should be accessible.
  3396     static_caching: True if browser caching of static files should be allowed.
  3471     static_caching: True if browser caching of static files should be allowed.
  3397     python_path_list: Used for dependency injection.
  3472     python_path_list: Used for dependency injection.
  3398     sdk_dir: Directory where the SDK is stored.
  3473     sdk_dir: Directory where the SDK is stored.
  3399 
  3474 
  3400   Returns:
  3475   Returns:
  3413                                        require_indexes,
  3488                                        require_indexes,
  3414                                        static_caching)
  3489                                        static_caching)
  3415 
  3490 
  3416   if absolute_root_path not in python_path_list:
  3491   if absolute_root_path not in python_path_list:
  3417     python_path_list.insert(0, absolute_root_path)
  3492     python_path_list.insert(0, absolute_root_path)
  3418 
  3493   return HTTPServerWithScheduler((serve_address, port), handler_class)
  3419   return BaseHTTPServer.HTTPServer((serve_address, port), handler_class)
  3494 
       
  3495 
       
  3496 class HTTPServerWithScheduler(BaseHTTPServer.HTTPServer):
       
  3497   """A BaseHTTPServer subclass that calls a method at a regular interval."""
       
  3498 
       
  3499   def __init__(self, server_address, request_handler_class):
       
  3500     """Constructor.
       
  3501 
       
  3502     Args:
       
  3503       server_address: the bind address of the server.
       
  3504       request_handler_class: class used to handle requests.
       
  3505     """
       
  3506     BaseHTTPServer.HTTPServer.__init__(self, server_address,
       
  3507                                        request_handler_class)
       
  3508     self._events = []
       
  3509 
       
  3510   def get_request(self, time_func=time.time, select_func=select.select):
       
  3511     """Overrides the base get_request call.
       
  3512 
       
  3513     Args:
       
  3514       time_func: used for testing.
       
  3515       select_func: used for testing.
       
  3516 
       
  3517     Returns:
       
  3518       a (socket_object, address info) tuple.
       
  3519     """
       
  3520     while True:
       
  3521       if self._events:
       
  3522         current_time = time_func()
       
  3523         next_eta = self._events[0][0]
       
  3524         delay = next_eta - current_time
       
  3525       else:
       
  3526         delay = DEFAULT_SELECT_DELAY
       
  3527       readable, _, _ = select_func([self.socket], [], [], max(delay, 0))
       
  3528       if readable:
       
  3529         return self.socket.accept()
       
  3530       current_time = time_func()
       
  3531       if self._events and current_time >= self._events[0][0]:
       
  3532         unused_eta, runnable = heapq.heappop(self._events)
       
  3533         runnable()
       
  3534 
       
  3535   def AddEvent(self, eta, runnable):
       
  3536     """Add a runnable event to be run at the specified time.
       
  3537 
       
  3538     Args:
       
  3539       eta: when to run the event, in seconds since epoch.
       
  3540       runnable: a callable object.
       
  3541     """
       
  3542     heapq.heappush(self._events, (eta, runnable))