annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/wsgiref/util.py @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
jpayne@69 1 """Miscellaneous WSGI-related Utilities"""
jpayne@69 2
jpayne@69 3 import posixpath
jpayne@69 4
jpayne@69 5 __all__ = [
jpayne@69 6 'FileWrapper', 'guess_scheme', 'application_uri', 'request_uri',
jpayne@69 7 'shift_path_info', 'setup_testing_defaults',
jpayne@69 8 ]
jpayne@69 9
jpayne@69 10
jpayne@69 11 class FileWrapper:
jpayne@69 12 """Wrapper to convert file-like objects to iterables"""
jpayne@69 13
jpayne@69 14 def __init__(self, filelike, blksize=8192):
jpayne@69 15 self.filelike = filelike
jpayne@69 16 self.blksize = blksize
jpayne@69 17 if hasattr(filelike,'close'):
jpayne@69 18 self.close = filelike.close
jpayne@69 19
jpayne@69 20 def __getitem__(self,key):
jpayne@69 21 import warnings
jpayne@69 22 warnings.warn(
jpayne@69 23 "FileWrapper's __getitem__ method ignores 'key' parameter. "
jpayne@69 24 "Use iterator protocol instead.",
jpayne@69 25 DeprecationWarning,
jpayne@69 26 stacklevel=2
jpayne@69 27 )
jpayne@69 28 data = self.filelike.read(self.blksize)
jpayne@69 29 if data:
jpayne@69 30 return data
jpayne@69 31 raise IndexError
jpayne@69 32
jpayne@69 33 def __iter__(self):
jpayne@69 34 return self
jpayne@69 35
jpayne@69 36 def __next__(self):
jpayne@69 37 data = self.filelike.read(self.blksize)
jpayne@69 38 if data:
jpayne@69 39 return data
jpayne@69 40 raise StopIteration
jpayne@69 41
jpayne@69 42 def guess_scheme(environ):
jpayne@69 43 """Return a guess for whether 'wsgi.url_scheme' should be 'http' or 'https'
jpayne@69 44 """
jpayne@69 45 if environ.get("HTTPS") in ('yes','on','1'):
jpayne@69 46 return 'https'
jpayne@69 47 else:
jpayne@69 48 return 'http'
jpayne@69 49
jpayne@69 50 def application_uri(environ):
jpayne@69 51 """Return the application's base URI (no PATH_INFO or QUERY_STRING)"""
jpayne@69 52 url = environ['wsgi.url_scheme']+'://'
jpayne@69 53 from urllib.parse import quote
jpayne@69 54
jpayne@69 55 if environ.get('HTTP_HOST'):
jpayne@69 56 url += environ['HTTP_HOST']
jpayne@69 57 else:
jpayne@69 58 url += environ['SERVER_NAME']
jpayne@69 59
jpayne@69 60 if environ['wsgi.url_scheme'] == 'https':
jpayne@69 61 if environ['SERVER_PORT'] != '443':
jpayne@69 62 url += ':' + environ['SERVER_PORT']
jpayne@69 63 else:
jpayne@69 64 if environ['SERVER_PORT'] != '80':
jpayne@69 65 url += ':' + environ['SERVER_PORT']
jpayne@69 66
jpayne@69 67 url += quote(environ.get('SCRIPT_NAME') or '/', encoding='latin1')
jpayne@69 68 return url
jpayne@69 69
jpayne@69 70 def request_uri(environ, include_query=True):
jpayne@69 71 """Return the full request URI, optionally including the query string"""
jpayne@69 72 url = application_uri(environ)
jpayne@69 73 from urllib.parse import quote
jpayne@69 74 path_info = quote(environ.get('PATH_INFO',''), safe='/;=,', encoding='latin1')
jpayne@69 75 if not environ.get('SCRIPT_NAME'):
jpayne@69 76 url += path_info[1:]
jpayne@69 77 else:
jpayne@69 78 url += path_info
jpayne@69 79 if include_query and environ.get('QUERY_STRING'):
jpayne@69 80 url += '?' + environ['QUERY_STRING']
jpayne@69 81 return url
jpayne@69 82
jpayne@69 83 def shift_path_info(environ):
jpayne@69 84 """Shift a name from PATH_INFO to SCRIPT_NAME, returning it
jpayne@69 85
jpayne@69 86 If there are no remaining path segments in PATH_INFO, return None.
jpayne@69 87 Note: 'environ' is modified in-place; use a copy if you need to keep
jpayne@69 88 the original PATH_INFO or SCRIPT_NAME.
jpayne@69 89
jpayne@69 90 Note: when PATH_INFO is just a '/', this returns '' and appends a trailing
jpayne@69 91 '/' to SCRIPT_NAME, even though empty path segments are normally ignored,
jpayne@69 92 and SCRIPT_NAME doesn't normally end in a '/'. This is intentional
jpayne@69 93 behavior, to ensure that an application can tell the difference between
jpayne@69 94 '/x' and '/x/' when traversing to objects.
jpayne@69 95 """
jpayne@69 96 path_info = environ.get('PATH_INFO','')
jpayne@69 97 if not path_info:
jpayne@69 98 return None
jpayne@69 99
jpayne@69 100 path_parts = path_info.split('/')
jpayne@69 101 path_parts[1:-1] = [p for p in path_parts[1:-1] if p and p != '.']
jpayne@69 102 name = path_parts[1]
jpayne@69 103 del path_parts[1]
jpayne@69 104
jpayne@69 105 script_name = environ.get('SCRIPT_NAME','')
jpayne@69 106 script_name = posixpath.normpath(script_name+'/'+name)
jpayne@69 107 if script_name.endswith('/'):
jpayne@69 108 script_name = script_name[:-1]
jpayne@69 109 if not name and not script_name.endswith('/'):
jpayne@69 110 script_name += '/'
jpayne@69 111
jpayne@69 112 environ['SCRIPT_NAME'] = script_name
jpayne@69 113 environ['PATH_INFO'] = '/'.join(path_parts)
jpayne@69 114
jpayne@69 115 # Special case: '/.' on PATH_INFO doesn't get stripped,
jpayne@69 116 # because we don't strip the last element of PATH_INFO
jpayne@69 117 # if there's only one path part left. Instead of fixing this
jpayne@69 118 # above, we fix it here so that PATH_INFO gets normalized to
jpayne@69 119 # an empty string in the environ.
jpayne@69 120 if name=='.':
jpayne@69 121 name = None
jpayne@69 122 return name
jpayne@69 123
jpayne@69 124 def setup_testing_defaults(environ):
jpayne@69 125 """Update 'environ' with trivial defaults for testing purposes
jpayne@69 126
jpayne@69 127 This adds various parameters required for WSGI, including HTTP_HOST,
jpayne@69 128 SERVER_NAME, SERVER_PORT, REQUEST_METHOD, SCRIPT_NAME, PATH_INFO,
jpayne@69 129 and all of the wsgi.* variables. It only supplies default values,
jpayne@69 130 and does not replace any existing settings for these variables.
jpayne@69 131
jpayne@69 132 This routine is intended to make it easier for unit tests of WSGI
jpayne@69 133 servers and applications to set up dummy environments. It should *not*
jpayne@69 134 be used by actual WSGI servers or applications, since the data is fake!
jpayne@69 135 """
jpayne@69 136
jpayne@69 137 environ.setdefault('SERVER_NAME','127.0.0.1')
jpayne@69 138 environ.setdefault('SERVER_PROTOCOL','HTTP/1.0')
jpayne@69 139
jpayne@69 140 environ.setdefault('HTTP_HOST',environ['SERVER_NAME'])
jpayne@69 141 environ.setdefault('REQUEST_METHOD','GET')
jpayne@69 142
jpayne@69 143 if 'SCRIPT_NAME' not in environ and 'PATH_INFO' not in environ:
jpayne@69 144 environ.setdefault('SCRIPT_NAME','')
jpayne@69 145 environ.setdefault('PATH_INFO','/')
jpayne@69 146
jpayne@69 147 environ.setdefault('wsgi.version', (1,0))
jpayne@69 148 environ.setdefault('wsgi.run_once', 0)
jpayne@69 149 environ.setdefault('wsgi.multithread', 0)
jpayne@69 150 environ.setdefault('wsgi.multiprocess', 0)
jpayne@69 151
jpayne@69 152 from io import StringIO, BytesIO
jpayne@69 153 environ.setdefault('wsgi.input', BytesIO())
jpayne@69 154 environ.setdefault('wsgi.errors', StringIO())
jpayne@69 155 environ.setdefault('wsgi.url_scheme',guess_scheme(environ))
jpayne@69 156
jpayne@69 157 if environ['wsgi.url_scheme']=='http':
jpayne@69 158 environ.setdefault('SERVER_PORT', '80')
jpayne@69 159 elif environ['wsgi.url_scheme']=='https':
jpayne@69 160 environ.setdefault('SERVER_PORT', '443')
jpayne@69 161
jpayne@69 162
jpayne@69 163
jpayne@69 164 _hoppish = {
jpayne@69 165 'connection', 'keep-alive', 'proxy-authenticate',
jpayne@69 166 'proxy-authorization', 'te', 'trailers', 'transfer-encoding',
jpayne@69 167 'upgrade'
jpayne@69 168 }.__contains__
jpayne@69 169
jpayne@69 170 def is_hop_by_hop(header_name):
jpayne@69 171 """Return true if 'header_name' is an HTTP/1.1 "Hop-by-Hop" header"""
jpayne@69 172 return _hoppish(header_name.lower())