86 from google.appengine.api import mail_stub |
87 from google.appengine.api import mail_stub |
87 from google.appengine.api import urlfetch_stub |
88 from google.appengine.api import urlfetch_stub |
88 from google.appengine.api import user_service_stub |
89 from google.appengine.api import user_service_stub |
89 from google.appengine.api import yaml_errors |
90 from google.appengine.api import yaml_errors |
90 from google.appengine.api.capabilities import capability_stub |
91 from google.appengine.api.capabilities import capability_stub |
|
92 from google.appengine.api.labs.taskqueue import taskqueue_stub |
91 from google.appengine.api.memcache import memcache_stub |
93 from google.appengine.api.memcache import memcache_stub |
92 |
94 |
93 from google.appengine import dist |
95 from google.appengine import dist |
94 |
96 |
95 from google.appengine.tools import dev_appserver_index |
97 from google.appengine.tools import dev_appserver_index |
515 |
520 |
516 |
521 |
517 _IGNORE_REQUEST_HEADERS = frozenset(['content-type', 'content-length', |
522 _IGNORE_REQUEST_HEADERS = frozenset(['content-type', 'content-length', |
518 'accept-encoding', 'transfer-encoding']) |
523 'accept-encoding', 'transfer-encoding']) |
519 |
524 |
|
525 |
520 def SetupEnvironment(cgi_path, |
526 def SetupEnvironment(cgi_path, |
521 relative_url, |
527 relative_url, |
522 headers, |
528 headers, |
|
529 infile, |
523 split_url=SplitURL, |
530 split_url=SplitURL, |
524 get_user_info=dev_appserver_login.GetUserInfo): |
531 get_user_info=dev_appserver_login.GetUserInfo): |
525 """Sets up environment variables for a CGI. |
532 """Sets up environment variables for a CGI. |
526 |
533 |
527 Args: |
534 Args: |
528 cgi_path: Full file-system path to the CGI being executed. |
535 cgi_path: Full file-system path to the CGI being executed. |
529 relative_url: Relative URL used to access the CGI. |
536 relative_url: Relative URL used to access the CGI. |
530 headers: Instance of mimetools.Message containing request headers. |
537 headers: Instance of mimetools.Message containing request headers. |
|
538 infile: File-like object with input data from the request. |
531 split_url, get_user_info: Used for dependency injection. |
539 split_url, get_user_info: Used for dependency injection. |
532 |
540 |
533 Returns: |
541 Returns: |
534 Dictionary containing CGI environment variables. |
542 Dictionary containing CGI environment variables. |
535 """ |
543 """ |
556 if key in _IGNORE_REQUEST_HEADERS: |
564 if key in _IGNORE_REQUEST_HEADERS: |
557 continue |
565 continue |
558 adjusted_name = key.replace('-', '_').upper() |
566 adjusted_name = key.replace('-', '_').upper() |
559 env['HTTP_' + adjusted_name] = ', '.join(headers.getheaders(key)) |
567 env['HTTP_' + adjusted_name] = ', '.join(headers.getheaders(key)) |
560 |
568 |
|
569 PAYLOAD_HEADER = 'HTTP_X_APPENGINE_DEVELOPMENT_PAYLOAD' |
|
570 if PAYLOAD_HEADER in env: |
|
571 del env[PAYLOAD_HEADER] |
|
572 new_data = base64.standard_b64decode(infile.getvalue()) |
|
573 infile.seek(0) |
|
574 infile.truncate() |
|
575 infile.write(new_data) |
|
576 infile.seek(0) |
|
577 env['CONTENT_LENGTH'] = str(len(new_data)) |
|
578 |
561 return env |
579 return env |
562 |
580 |
563 |
581 |
564 def NotImplementedFake(*args, **kwargs): |
582 def NotImplementedFake(*args, **kwargs): |
565 """Fake for methods/functions that are not implemented in the production |
583 """Fake for methods/functions that are not implemented in the production |
803 NOT_ALLOWED_DIRS = set([ |
821 NOT_ALLOWED_DIRS = set([ |
804 |
822 |
805 |
823 |
806 |
824 |
807 |
825 |
808 os.path.normcase(os.path.join(os.path.dirname(os.__file__), |
826 SITE_PACKAGES, |
809 'site-packages')) |
|
810 ]) |
827 ]) |
811 |
828 |
812 ALLOWED_SITE_PACKAGE_DIRS = set( |
829 ALLOWED_SITE_PACKAGE_DIRS = set( |
813 os.path.normcase(os.path.abspath(os.path.join( |
830 os.path.normcase(os.path.abspath(os.path.join(SITE_PACKAGES, path))) |
814 os.path.dirname(os.__file__), 'site-packages', path))) |
|
815 for path in [ |
831 for path in [ |
816 |
832 |
817 ]) |
833 ]) |
818 |
834 |
819 ALLOWED_SITE_PACKAGE_FILES = set( |
835 ALLOWED_SITE_PACKAGE_FILES = set( |
903 """ |
919 """ |
904 FakeFile._allow_skipped_files = allow_skipped_files |
920 FakeFile._allow_skipped_files = allow_skipped_files |
905 FakeFile._availability_cache = {} |
921 FakeFile._availability_cache = {} |
906 |
922 |
907 @staticmethod |
923 @staticmethod |
|
924 def SetAllowedModule(name): |
|
925 """Allow the use of a module based on where it is located. |
|
926 |
|
927 Meant to be used by use_library() so that it has a link back into the |
|
928 trusted part of the interpreter. |
|
929 |
|
930 Args: |
|
931 name: Name of the module to allow. |
|
932 """ |
|
933 stream, pathname, description = imp.find_module(name) |
|
934 pathname = os.path.normcase(os.path.abspath(pathname)) |
|
935 if stream: |
|
936 stream.close() |
|
937 FakeFile.ALLOWED_FILES.add(pathname) |
|
938 FakeFile.ALLOWED_FILES.add(os.path.realpath(pathname)) |
|
939 else: |
|
940 assert description[2] == imp.PKG_DIRECTORY |
|
941 if pathname.startswith(SITE_PACKAGES): |
|
942 FakeFile.ALLOWED_SITE_PACKAGE_DIRS.add(pathname) |
|
943 FakeFile.ALLOWED_SITE_PACKAGE_DIRS.add(os.path.realpath(pathname)) |
|
944 else: |
|
945 FakeFile.ALLOWED_DIRS.add(pathname) |
|
946 FakeFile.ALLOWED_DIRS.add(os.path.realpath(pathname)) |
|
947 |
|
948 @staticmethod |
908 def SetSkippedFiles(skip_files): |
949 def SetSkippedFiles(skip_files): |
909 """Sets which files in the application directory are to be ignored. |
950 """Sets which files in the application directory are to be ignored. |
910 |
951 |
911 Must be called at least once before any file objects are created in the |
952 Must be called at least once before any file objects are created in the |
912 hardened environment. |
953 hardened environment. |
1020 |
1061 |
1021 if not FakeFile.IsFileAccessible(filename): |
1062 if not FakeFile.IsFileAccessible(filename): |
1022 raise IOError(errno.EACCES, 'file not accessible', filename) |
1063 raise IOError(errno.EACCES, 'file not accessible', filename) |
1023 |
1064 |
1024 super(FakeFile, self).__init__(filename, mode, bufsize, **kwargs) |
1065 super(FakeFile, self).__init__(filename, mode, bufsize, **kwargs) |
|
1066 |
|
1067 |
|
1068 from google.appengine.dist import _library |
|
1069 _library.SetAllowedModule = FakeFile.SetAllowedModule |
1025 |
1070 |
1026 |
1071 |
1027 class RestrictedPathFunction(object): |
1072 class RestrictedPathFunction(object): |
1028 """Enforces access restrictions for functions that have a file or |
1073 """Enforces access restrictions for functions that have a file or |
1029 directory path as their first argument.""" |
1074 directory path as their first argument.""" |
2051 |
2096 |
2052 try: |
2097 try: |
2053 ClearAllButEncodingsModules(sys.modules) |
2098 ClearAllButEncodingsModules(sys.modules) |
2054 sys.modules.update(module_dict) |
2099 sys.modules.update(module_dict) |
2055 sys.argv = [cgi_path] |
2100 sys.argv = [cgi_path] |
2056 sys.stdin = infile |
2101 sys.stdin = cStringIO.StringIO(infile.getvalue()) |
2057 sys.stdout = outfile |
2102 sys.stdout = outfile |
2058 os.environ.clear() |
2103 os.environ.clear() |
2059 os.environ.update(env) |
2104 os.environ.update(env) |
2060 before_path = sys.path[:] |
2105 before_path = sys.path[:] |
2061 cgi_dir = os.path.normpath(os.path.dirname(cgi_path)) |
2106 cgi_dir = os.path.normpath(os.path.dirname(cgi_path)) |
2151 try: |
2196 try: |
2152 env = {} |
2197 env = {} |
2153 if base_env_dict: |
2198 if base_env_dict: |
2154 env.update(base_env_dict) |
2199 env.update(base_env_dict) |
2155 cgi_path = self._path_adjuster.AdjustPath(path) |
2200 cgi_path = self._path_adjuster.AdjustPath(path) |
2156 env.update(self._setup_env(cgi_path, relative_url, headers)) |
2201 env.update(self._setup_env(cgi_path, relative_url, headers, infile)) |
2157 self._exec_cgi(self._root_path, |
2202 self._exec_cgi(self._root_path, |
2158 path, |
2203 path, |
2159 cgi_path, |
2204 cgi_path, |
2160 env, |
2205 env, |
2161 infile, |
2206 infile, |
2853 [implicit_matcher, explicit_matcher]) |
2898 [implicit_matcher, explicit_matcher]) |
2854 |
2899 |
2855 if require_indexes: |
2900 if require_indexes: |
2856 dev_appserver_index.SetupIndexes(config.application, root_path) |
2901 dev_appserver_index.SetupIndexes(config.application, root_path) |
2857 |
2902 |
2858 infile = cStringIO.StringIO(self.rfile.read( |
2903 infile = cStringIO.StringIO() |
|
2904 infile.write(self.rfile.read( |
2859 int(self.headers.get('content-length', 0)))) |
2905 int(self.headers.get('content-length', 0)))) |
|
2906 infile.seek(0) |
2860 |
2907 |
2861 request_size = len(infile.getvalue()) |
2908 request_size = len(infile.getvalue()) |
2862 if request_size > MAX_REQUEST_SIZE: |
2909 if request_size > MAX_REQUEST_SIZE: |
2863 msg = ('HTTP request was too large: %d. The limit is: %d.' |
2910 msg = ('HTTP request was too large: %d. The limit is: %d.' |
2864 % (request_size, MAX_REQUEST_SIZE)) |
2911 % (request_size, MAX_REQUEST_SIZE)) |
3151 |
3198 |
3152 Args: |
3199 Args: |
3153 app_id: Application ID being served. |
3200 app_id: Application ID being served. |
3154 |
3201 |
3155 Keywords: |
3202 Keywords: |
|
3203 root_path: Root path to the directory of the application which should |
|
3204 contain the app.yaml, indexes.yaml, and queues.yaml files. |
3156 login_url: Relative URL which should be used for handling user login/logout. |
3205 login_url: Relative URL which should be used for handling user login/logout. |
3157 datastore_path: Path to the file to store Datastore file stub data in. |
3206 datastore_path: Path to the file to store Datastore file stub data in. |
3158 history_path: Path to the file to store Datastore history in. |
3207 history_path: Path to the file to store Datastore history in. |
3159 clear_datastore: If the datastore and history should be cleared on startup. |
3208 clear_datastore: If the datastore and history should be cleared on startup. |
3160 smtp_host: SMTP host used for sending test mail. |
3209 smtp_host: SMTP host used for sending test mail. |
3166 remove: Used for dependency injection. |
3215 remove: Used for dependency injection. |
3167 trusted: True if this app can access data belonging to other apps. This |
3216 trusted: True if this app can access data belonging to other apps. This |
3168 behavior is different from the real app server and should be left False |
3217 behavior is different from the real app server and should be left False |
3169 except for advanced uses of dev_appserver. |
3218 except for advanced uses of dev_appserver. |
3170 """ |
3219 """ |
|
3220 root_path = config.get('root_path', None) |
3171 login_url = config['login_url'] |
3221 login_url = config['login_url'] |
3172 datastore_path = config['datastore_path'] |
3222 datastore_path = config['datastore_path'] |
3173 history_path = config['history_path'] |
3223 history_path = config['history_path'] |
3174 clear_datastore = config['clear_datastore'] |
3224 clear_datastore = config['clear_datastore'] |
3175 require_indexes = config.get('require_indexes', False) |
3225 require_indexes = config.get('require_indexes', False) |
3229 |
3279 |
3230 apiproxy_stub_map.apiproxy.RegisterStub( |
3280 apiproxy_stub_map.apiproxy.RegisterStub( |
3231 'capability_service', |
3281 'capability_service', |
3232 capability_stub.CapabilityServiceStub()) |
3282 capability_stub.CapabilityServiceStub()) |
3233 |
3283 |
|
3284 apiproxy_stub_map.apiproxy.RegisterStub( |
|
3285 'taskqueue', |
|
3286 taskqueue_stub.TaskQueueServiceStub(root_path=root_path)) |
|
3287 |
3234 |
3288 |
3235 try: |
3289 try: |
3236 from google.appengine.api.images import images_stub |
3290 from google.appengine.api.images import images_stub |
3237 apiproxy_stub_map.apiproxy.RegisterStub( |
3291 apiproxy_stub_map.apiproxy.RegisterStub( |
3238 'images', |
3292 'images', |