wsgi.py (13456B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\werkzeug\wsgi.py 2 import os 3 import urllib 4 import urlparse 5 import posixpath 6 import mimetypes 7 from zlib import adler32 8 from time import time, mktime 9 from datetime import datetime 10 from werkzeug._internal import _patch_wrapper 11 12 def responder(f): 13 return _patch_wrapper(f, lambda *a: f(*a)(*a[-2:])) 14 15 16 def get_current_url(environ, root_only = False, strip_querystring = False, host_only = False): 17 tmp = [environ['wsgi.url_scheme'], '://', get_host(environ)] 18 cat = tmp.append 19 if host_only: 20 return ''.join(tmp) + '/' 21 cat(urllib.quote(environ.get('SCRIPT_NAME', '').rstrip('/'))) 22 if root_only: 23 cat('/') 24 else: 25 cat(urllib.quote('/' + environ.get('PATH_INFO', '').lstrip('/'))) 26 if not strip_querystring: 27 qs = environ.get('QUERY_STRING') 28 if qs: 29 cat('?' + qs) 30 return ''.join(tmp) 31 32 33 def get_host(environ): 34 if 'HTTP_X_FORWARDED_HOST' in environ: 35 return environ['HTTP_X_FORWARDED_HOST'] 36 if 'HTTP_HOST' in environ: 37 return environ['HTTP_HOST'] 38 result = environ['SERVER_NAME'] 39 if (environ['wsgi.url_scheme'], environ['SERVER_PORT']) not in (('https', '443'), ('http', '80')): 40 result += ':' + environ['SERVER_PORT'] 41 return result 42 43 44 def pop_path_info(environ): 45 path = environ.get('PATH_INFO') 46 if not path: 47 return None 48 script_name = environ.get('SCRIPT_NAME', '') 49 old_path = path 50 path = path.lstrip('/') 51 if path != old_path: 52 script_name += '/' * (len(old_path) - len(path)) 53 if '/' not in path: 54 environ['PATH_INFO'] = '' 55 environ['SCRIPT_NAME'] = script_name + path 56 return path 57 segment, path = path.split('/', 1) 58 environ['PATH_INFO'] = '/' + path 59 environ['SCRIPT_NAME'] = script_name + segment 60 return segment 61 62 63 def peek_path_info(environ): 64 segments = environ.get('PATH_INFO', '').lstrip('/').split('/', 1) 65 if segments: 66 return segments[0] 67 68 69 def extract_path_info(environ_or_baseurl, path_or_url, charset = 'utf-8', errors = 'ignore', collapse_http_schemes = True): 70 from werkzeug.urls import uri_to_iri, url_fix 71 72 def _as_iri(obj): 73 if not isinstance(obj, unicode): 74 return uri_to_iri(obj, charset, errors) 75 return obj 76 77 def _normalize_netloc(scheme, netloc): 78 parts = netloc.split(u'@', 1)[-1].split(u':', 1) 79 if len(parts) == 2: 80 netloc, port = parts 81 if scheme == u'http' and port == u'80' or scheme == u'https' and port == u'443': 82 port = None 83 else: 84 netloc = parts[0] 85 port = None 86 if port is not None: 87 netloc += u':' + port 88 return netloc 89 90 path = _as_iri(path_or_url) 91 if isinstance(environ_or_baseurl, dict): 92 environ_or_baseurl = get_current_url(environ_or_baseurl, root_only=True) 93 base_iri = _as_iri(environ_or_baseurl) 94 base_scheme, base_netloc, base_path = urlparse.urlsplit(base_iri)[:3] 95 cur_scheme, cur_netloc, cur_path = urlparse.urlsplit(urlparse.urljoin(base_iri, path))[:3] 96 base_netloc = _normalize_netloc(base_scheme, base_netloc) 97 cur_netloc = _normalize_netloc(cur_scheme, cur_netloc) 98 if collapse_http_schemes: 99 for scheme in (base_scheme, cur_scheme): 100 if scheme not in (u'http', u'https'): 101 return None 102 103 elif not (base_scheme in (u'http', u'https') and base_scheme == cur_scheme): 104 return None 105 if base_netloc != cur_netloc: 106 return None 107 base_path = base_path.rstrip(u'/') 108 if not cur_path.startswith(base_path): 109 return None 110 return u'/' + cur_path[len(base_path):].lstrip(u'/') 111 112 113 class SharedDataMiddleware(object): 114 115 def __init__(self, app, exports, disallow = None, cache = True, cache_timeout = 43200, fallback_mimetype = 'text/plain'): 116 self.app = app 117 self.exports = {} 118 self.cache = cache 119 self.cache_timeout = cache_timeout 120 for key, value in exports.iteritems(): 121 if isinstance(value, tuple): 122 loader = self.get_package_loader(*value) 123 elif isinstance(value, basestring): 124 if os.path.isfile(value): 125 loader = self.get_file_loader(value) 126 else: 127 loader = self.get_directory_loader(value) 128 else: 129 raise TypeError('unknown def %r' % value) 130 self.exports[key] = loader 131 132 if disallow is not None: 133 from fnmatch import fnmatch 134 self.is_allowed = lambda x: not fnmatch(x, disallow) 135 self.fallback_mimetype = fallback_mimetype 136 137 def is_allowed(self, filename): 138 return True 139 140 def _opener(self, filename): 141 return lambda : (open(filename, 'rb'), datetime.utcfromtimestamp(os.path.getmtime(filename)), int(os.path.getsize(filename))) 142 143 def get_file_loader(self, filename): 144 return lambda x: (os.path.basename(filename), self._opener(filename)) 145 146 def get_package_loader(self, package, package_path): 147 from pkg_resources import DefaultProvider, ResourceManager, get_provider 148 loadtime = datetime.utcnow() 149 provider = get_provider(package) 150 manager = ResourceManager() 151 filesystem_bound = isinstance(provider, DefaultProvider) 152 153 def loader(path): 154 path = posixpath.join(package_path, path) 155 if path is None or not provider.has_resource(path): 156 return (None, None) 157 basename = posixpath.basename(path) 158 if filesystem_bound: 159 return (basename, self._opener(provider.get_resource_filename(manager, path))) 160 return (basename, lambda : (provider.get_resource_stream(manager, path), loadtime, 0)) 161 162 return loader 163 164 def get_directory_loader(self, directory): 165 166 def loader(path): 167 if path is not None: 168 path = os.path.join(directory, path) 169 else: 170 path = directory 171 if os.path.isfile(path): 172 return (os.path.basename(path), self._opener(path)) 173 return (None, None) 174 175 return loader 176 177 def generate_etag(self, mtime, file_size, real_filename): 178 return 'wzsdm-%d-%s-%s' % (mktime(mtime.timetuple()), file_size, adler32(real_filename) & 4294967295L) 179 180 def __call__(self, environ, start_response): 181 cleaned_path = environ.get('PATH_INFO', '').strip('/') 182 for sep in (os.sep, os.altsep): 183 if sep and sep != '/': 184 cleaned_path = cleaned_path.replace(sep, '/') 185 186 path = '/'.join([''] + [ x for x in cleaned_path.split('/') if x and x != '..' ]) 187 file_loader = None 188 for search_path, loader in self.exports.iteritems(): 189 if search_path == path: 190 real_filename, file_loader = loader(None) 191 if file_loader is not None: 192 break 193 if not search_path.endswith('/'): 194 search_path += '/' 195 if path.startswith(search_path): 196 real_filename, file_loader = loader(path[len(search_path):]) 197 if file_loader is not None: 198 break 199 200 if file_loader is None or not self.is_allowed(real_filename): 201 return self.app(environ, start_response) 202 guessed_type = mimetypes.guess_type(real_filename) 203 mime_type = guessed_type[0] or self.fallback_mimetype 204 f, mtime, file_size = file_loader() 205 headers = [('Date', http_date())] 206 if self.cache: 207 timeout = self.cache_timeout 208 etag = self.generate_etag(mtime, file_size, real_filename) 209 headers += [('Etag', '"%s"' % etag), ('Cache-Control', 'max-age=%d, public' % timeout)] 210 if not is_resource_modified(environ, etag, last_modified=mtime): 211 f.close() 212 start_response('304 Not Modified', headers) 213 return [] 214 headers.append(('Expires', http_date(time() + timeout))) 215 else: 216 headers.append(('Cache-Control', 'public')) 217 headers.extend((('Content-Type', mime_type), ('Content-Length', str(file_size)), ('Last-Modified', http_date(mtime)))) 218 start_response('200 OK', headers) 219 return wrap_file(environ, f) 220 221 222 class DispatcherMiddleware(object): 223 224 def __init__(self, app, mounts = None): 225 self.app = app 226 self.mounts = mounts or {} 227 228 def __call__(self, environ, start_response): 229 script = environ.get('PATH_INFO', '') 230 path_info = '' 231 while '/' in script: 232 if script in self.mounts: 233 app = self.mounts[script] 234 break 235 items = script.split('/') 236 script = '/'.join(items[:-1]) 237 path_info = '/%s%s' % (items[-1], path_info) 238 else: 239 app = self.mounts.get(script, self.app) 240 241 original_script_name = environ.get('SCRIPT_NAME', '') 242 environ['SCRIPT_NAME'] = original_script_name + script 243 environ['PATH_INFO'] = path_info 244 return app(environ, start_response) 245 246 247 class ClosingIterator(object): 248 249 def __init__(self, iterable, callbacks = None): 250 iterator = iter(iterable) 251 self._next = iterator.next 252 if callbacks is None: 253 callbacks = [] 254 elif callable(callbacks): 255 callbacks = [callbacks] 256 else: 257 callbacks = list(callbacks) 258 iterable_close = getattr(iterator, 'close', None) 259 if iterable_close: 260 callbacks.insert(0, iterable_close) 261 self._callbacks = callbacks 262 263 def __iter__(self): 264 return self 265 266 def next(self): 267 return self._next() 268 269 def close(self): 270 for callback in self._callbacks: 271 callback() 272 273 274 def wrap_file(environ, file, buffer_size = 8192): 275 return environ.get('wsgi.file_wrapper', FileWrapper)(file, buffer_size) 276 277 278 class FileWrapper(object): 279 280 def __init__(self, file, buffer_size = 8192): 281 self.file = file 282 self.buffer_size = buffer_size 283 284 def close(self): 285 if hasattr(self.file, 'close'): 286 self.file.close() 287 288 def __iter__(self): 289 return self 290 291 def next(self): 292 data = self.file.read(self.buffer_size) 293 if data: 294 return data 295 raise StopIteration() 296 297 298 def make_line_iter(stream, limit = None, buffer_size = 10240): 299 if not isinstance(stream, LimitedStream): 300 if limit is None: 301 raise TypeError('stream not limited and no limit provided.') 302 stream = LimitedStream(stream, limit) 303 _read = stream.read 304 buffer = [] 305 while 1: 306 if len(buffer) > 1: 307 yield buffer.pop() 308 continue 309 chunks = _read(buffer_size).splitlines(True) 310 chunks.reverse() 311 first_chunk = buffer and buffer[0] or '' 312 if chunks: 313 first_chunk += chunks.pop() 314 if not first_chunk: 315 return 316 buffer = chunks 317 yield first_chunk 318 319 320 class LimitedStream(object): 321 322 def __init__(self, stream, limit, silent = True): 323 self._read = stream.read 324 self._readline = stream.readline 325 self._pos = 0 326 self.limit = limit 327 self.silent = silent 328 if not silent: 329 from warnings import warn 330 warn(DeprecationWarning('non-silent usage of the LimitedStream is deprecated. If you want to continue to use the stream in non-silent usage override on_exhausted.'), stacklevel=2) 331 332 def __iter__(self): 333 return self 334 335 @property 336 def is_exhausted(self): 337 return self._pos >= self.limit 338 339 def on_exhausted(self): 340 if self.silent: 341 return '' 342 from werkzeug.exceptions import BadRequest 343 raise BadRequest('input stream exhausted') 344 345 def exhaust(self, chunk_size = 16384): 346 to_read = self.limit - self._pos 347 chunk = chunk_size 348 while to_read > 0: 349 chunk = min(to_read, chunk) 350 self.read(chunk) 351 to_read -= chunk 352 353 def read(self, size = None): 354 if self._pos >= self.limit: 355 return self.on_exhausted() 356 if size is None: 357 size = self.limit 358 read = self._read(min(self.limit - self._pos, size)) 359 self._pos += len(read) 360 return read 361 362 def readline(self, size = None): 363 if self._pos >= self.limit: 364 return self.on_exhausted() 365 if size is None: 366 size = self.limit - self._pos 367 else: 368 size = min(size, self.limit - self._pos) 369 line = self._readline(size) 370 self._pos += len(line) 371 return line 372 373 def readlines(self, size = None): 374 last_pos = self._pos 375 result = [] 376 if size is not None: 377 end = min(self.limit, last_pos + size) 378 else: 379 end = self.limit 380 while 1: 381 if size is not None: 382 size -= last_pos - self._pos 383 if self._pos >= end: 384 break 385 result.append(self.readline(size)) 386 if size is not None: 387 last_pos = self._pos 388 389 return result 390 391 def next(self): 392 line = self.readline() 393 if line is None: 394 raise StopIteration() 395 return line 396 397 398 from werkzeug.utils import http_date 399 from werkzeug.http import is_resource_modified