thirdparty/google_appengine/google/appengine/tools/dev_appserver.py
changeset 686 df109be0567c
parent 297 35211afcd563
child 828 f5fd65cc3bf3
equal deleted inserted replaced
685:a440ced9a75f 686:df109be0567c
    44 import errno
    44 import errno
    45 import httplib
    45 import httplib
    46 import imp
    46 import imp
    47 import inspect
    47 import inspect
    48 import itertools
    48 import itertools
       
    49 import locale
    49 import logging
    50 import logging
    50 import mimetools
    51 import mimetools
    51 import mimetypes
    52 import mimetypes
    52 import os
    53 import os
    53 import pickle
    54 import pickle
   108                        ('.csv', 'text/comma-separated-values'),
   109                        ('.csv', 'text/comma-separated-values'),
   109                        ('.rss', 'application/rss+xml'),
   110                        ('.rss', 'application/rss+xml'),
   110                        ('.text', 'text/plain'),
   111                        ('.text', 'text/plain'),
   111                        ('.wbmp', 'image/vnd.wap.wbmp')):
   112                        ('.wbmp', 'image/vnd.wap.wbmp')):
   112   mimetypes.add_type(mime_type, ext)
   113   mimetypes.add_type(mime_type, ext)
       
   114 
       
   115 MAX_RUNTIME_RESPONSE_SIZE = 1 << 20
       
   116 
       
   117 MAX_REQUEST_SIZE = 10 * 1024 * 1024
   113 
   118 
   114 
   119 
   115 class Error(Exception):
   120 class Error(Exception):
   116   """Base-class for exceptions in this module."""
   121   """Base-class for exceptions in this module."""
   117 
   122 
   568 def FakeUname():
   573 def FakeUname():
   569   """Fake version of os.uname."""
   574   """Fake version of os.uname."""
   570   return ('Linux', '', '', '', '')
   575   return ('Linux', '', '', '', '')
   571 
   576 
   572 
   577 
       
   578 def FakeSetLocale(category, value=None, original_setlocale=locale.setlocale):
       
   579   """Fake version of locale.setlocale that only supports the default."""
       
   580   if value not in (None, '', 'C', 'POSIX'):
       
   581     raise locale.Error, 'locale emulation only supports "C" locale'
       
   582   return original_setlocale(category, 'C')
       
   583 
       
   584 
   573 def IsPathInSubdirectories(filename,
   585 def IsPathInSubdirectories(filename,
   574                            subdirectories,
   586                            subdirectories,
   575                            normcase=os.path.normcase):
   587                            normcase=os.path.normcase):
   576   """Determines if a filename is contained within one of a set of directories.
   588   """Determines if a filename is contained within one of a set of directories.
   577 
   589 
   679   ALLOWED_FILES = set(os.path.normcase(filename)
   691   ALLOWED_FILES = set(os.path.normcase(filename)
   680                       for filename in mimetypes.knownfiles
   692                       for filename in mimetypes.knownfiles
   681                       if os.path.isfile(filename))
   693                       if os.path.isfile(filename))
   682 
   694 
   683   ALLOWED_DIRS = set([
   695   ALLOWED_DIRS = set([
   684     os.path.normcase(os.path.realpath(os.path.dirname(os.__file__)))
   696     os.path.normcase(os.path.realpath(os.path.dirname(os.__file__))),
       
   697     os.path.normcase(os.path.abspath(os.path.dirname(os.__file__))),
   685   ])
   698   ])
   686 
   699 
   687   NOT_ALLOWED_DIRS = set([
   700   NOT_ALLOWED_DIRS = set([
   688 
   701 
   689 
   702 
   711     hardened environment.
   724     hardened environment.
   712 
   725 
   713     Args:
   726     Args:
   714       root_path: Path to the root of the application.
   727       root_path: Path to the root of the application.
   715     """
   728     """
   716     FakeFile._application_paths = set(os.path.abspath(path)
   729     FakeFile._application_paths = (set(os.path.realpath(path)
   717                                       for path in application_paths)
   730                                        for path in application_paths) |
       
   731                                    set(os.path.abspath(path)
       
   732                                        for path in application_paths))
   718 
   733 
   719   @staticmethod
   734   @staticmethod
   720   def IsFileAccessible(filename, normcase=os.path.normcase):
   735   def IsFileAccessible(filename, normcase=os.path.normcase):
   721     """Determines if a file's path is accessible.
   736     """Determines if a file's path is accessible.
   722 
   737 
   754                                    normcase=normcase)):
   769                                    normcase=normcase)):
   755       return True
   770       return True
   756 
   771 
   757     return False
   772     return False
   758 
   773 
   759   def __init__(self, filename, mode='r', **kwargs):
   774   def __init__(self, filename, mode='r', bufsize=-1, **kwargs):
   760     """Initializer. See file built-in documentation."""
   775     """Initializer. See file built-in documentation."""
   761     if mode not in FakeFile.ALLOWED_MODES:
   776     if mode not in FakeFile.ALLOWED_MODES:
   762       raise IOError('invalid mode: %s' % mode)
   777       raise IOError('invalid mode: %s' % mode)
   763 
   778 
   764     if not FakeFile.IsFileAccessible(filename):
   779     if not FakeFile.IsFileAccessible(filename):
   765       raise IOError(errno.EACCES, 'file not accessible')
   780       raise IOError(errno.EACCES, 'file not accessible')
   766 
   781 
   767     super(FakeFile, self).__init__(filename, mode, **kwargs)
   782     super(FakeFile, self).__init__(filename, mode, bufsize, **kwargs)
   768 
   783 
   769 
   784 
   770 class RestrictedPathFunction(object):
   785 class RestrictedPathFunction(object):
   771   """Enforces access restrictions for functions that have a file or
   786   """Enforces access restrictions for functions that have a file or
   772   directory path as their first argument."""
   787   directory path as their first argument."""
  1022     'socket',
  1037     'socket',
  1023     'tempfile',
  1038     'tempfile',
  1024   ]
  1039   ]
  1025 
  1040 
  1026   _MODULE_OVERRIDES = {
  1041   _MODULE_OVERRIDES = {
       
  1042     'locale': {
       
  1043       'setlocale': FakeSetLocale,
       
  1044     },
       
  1045 
  1027     'os': {
  1046     'os': {
  1028       'listdir': RestrictedPathFunction(os.listdir),
  1047       'listdir': RestrictedPathFunction(os.listdir),
  1029       'lstat': RestrictedPathFunction(os.lstat),
  1048 
       
  1049       'lstat': RestrictedPathFunction(os.stat),
  1030       'stat': RestrictedPathFunction(os.stat),
  1050       'stat': RestrictedPathFunction(os.stat),
  1031       'uname': FakeUname,
  1051       'uname': FakeUname,
  1032       'urandom': FakeURandom,
  1052       'urandom': FakeURandom,
  1033     },
  1053     },
  1034 
  1054 
  1533   depth_count = module_fullname.count('.')
  1553   depth_count = module_fullname.count('.')
  1534   if cgi_path.endswith('__init__.py') or not cgi_path.endswith('.py'):
  1554   if cgi_path.endswith('__init__.py') or not cgi_path.endswith('.py'):
  1535     depth_count += 1
  1555     depth_count += 1
  1536 
  1556 
  1537   for index in xrange(depth_count):
  1557   for index in xrange(depth_count):
  1538     current_init_file = os.path.join(module_base, '__init__.py')
  1558     current_init_file = os.path.abspath(
       
  1559         os.path.join(module_base, '__init__.py'))
  1539 
  1560 
  1540     if not isfile(current_init_file):
  1561     if not isfile(current_init_file):
  1541       missing_init_files.append(current_init_file)
  1562       missing_init_files.append(current_init_file)
  1542 
  1563 
  1543     module_base = os.path.abspath(os.path.join(module_base, os.pardir))
  1564     module_base = os.path.abspath(os.path.join(module_base, os.pardir))
  2401         if require_indexes:
  2422         if require_indexes:
  2402           dev_appserver_index.SetupIndexes(config.application, root_path)
  2423           dev_appserver_index.SetupIndexes(config.application, root_path)
  2403 
  2424 
  2404         infile = cStringIO.StringIO(self.rfile.read(
  2425         infile = cStringIO.StringIO(self.rfile.read(
  2405             int(self.headers.get('content-length', 0))))
  2426             int(self.headers.get('content-length', 0))))
       
  2427 
       
  2428         request_size = len(infile.getvalue())
       
  2429         if request_size > MAX_REQUEST_SIZE:
       
  2430           msg = ('HTTP request was too large: %d.  The limit is: %d.'
       
  2431                  % (request_size, MAX_REQUEST_SIZE))
       
  2432           logging.error(msg)
       
  2433           self.send_response(httplib.REQUEST_ENTITY_TOO_LARGE, msg)
       
  2434           return
       
  2435 
  2406         outfile = cStringIO.StringIO()
  2436         outfile = cStringIO.StringIO()
  2407         try:
  2437         try:
  2408           dispatcher.Dispatch(self.path,
  2438           dispatcher.Dispatch(self.path,
  2409                               None,
  2439                               None,
  2410                               self.headers,
  2440                               self.headers,
  2414         finally:
  2444         finally:
  2415           self.module_manager.UpdateModuleFileModificationTimes()
  2445           self.module_manager.UpdateModuleFileModificationTimes()
  2416 
  2446 
  2417         outfile.flush()
  2447         outfile.flush()
  2418         outfile.seek(0)
  2448         outfile.seek(0)
       
  2449 
  2419         status_code, status_message, header_data, body = RewriteResponse(outfile)
  2450         status_code, status_message, header_data, body = RewriteResponse(outfile)
       
  2451 
       
  2452         runtime_response_size = len(outfile.getvalue())
       
  2453         if runtime_response_size > MAX_RUNTIME_RESPONSE_SIZE:
       
  2454           status_code = 403
       
  2455           status_message = 'Forbidden'
       
  2456           new_headers = []
       
  2457           for header in header_data.split('\n'):
       
  2458             if not header.lower().startswith('content-length'):
       
  2459               new_headers.append(header)
       
  2460           header_data = '\n'.join(new_headers)
       
  2461           body = ('HTTP response was too large: %d.  The limit is: %d.'
       
  2462                   % (runtime_response_size, MAX_RUNTIME_RESPONSE_SIZE))
  2420 
  2463 
  2421       except yaml_errors.EventListenerError, e:
  2464       except yaml_errors.EventListenerError, e:
  2422         title = 'Fatal error when loading application configuration'
  2465         title = 'Fatal error when loading application configuration'
  2423         msg = '%s:\n%s' % (title, str(e))
  2466         msg = '%s:\n%s' % (title, str(e))
  2424         logging.error(msg)
  2467         logging.error(msg)