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