17db96d56Sopenharmony_ci"""Base classes for server/gateway implementations"""
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_cifrom .util import FileWrapper, guess_scheme, is_hop_by_hop
47db96d56Sopenharmony_cifrom .headers import Headers
57db96d56Sopenharmony_ci
67db96d56Sopenharmony_ciimport sys, os, time
77db96d56Sopenharmony_ci
87db96d56Sopenharmony_ci__all__ = [
97db96d56Sopenharmony_ci    'BaseHandler', 'SimpleHandler', 'BaseCGIHandler', 'CGIHandler',
107db96d56Sopenharmony_ci    'IISCGIHandler', 'read_environ'
117db96d56Sopenharmony_ci]
127db96d56Sopenharmony_ci
137db96d56Sopenharmony_ci# Weekday and month names for HTTP date/time formatting; always English!
147db96d56Sopenharmony_ci_weekdayname = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
157db96d56Sopenharmony_ci_monthname = [None, # Dummy so we can use 1-based month numbers
167db96d56Sopenharmony_ci              "Jan", "Feb", "Mar", "Apr", "May", "Jun",
177db96d56Sopenharmony_ci              "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
187db96d56Sopenharmony_ci
197db96d56Sopenharmony_cidef format_date_time(timestamp):
207db96d56Sopenharmony_ci    year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
217db96d56Sopenharmony_ci    return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
227db96d56Sopenharmony_ci        _weekdayname[wd], day, _monthname[month], year, hh, mm, ss
237db96d56Sopenharmony_ci    )
247db96d56Sopenharmony_ci
257db96d56Sopenharmony_ci_is_request = {
267db96d56Sopenharmony_ci    'SCRIPT_NAME', 'PATH_INFO', 'QUERY_STRING', 'REQUEST_METHOD', 'AUTH_TYPE',
277db96d56Sopenharmony_ci    'CONTENT_TYPE', 'CONTENT_LENGTH', 'HTTPS', 'REMOTE_USER', 'REMOTE_IDENT',
287db96d56Sopenharmony_ci}.__contains__
297db96d56Sopenharmony_ci
307db96d56Sopenharmony_cidef _needs_transcode(k):
317db96d56Sopenharmony_ci    return _is_request(k) or k.startswith('HTTP_') or k.startswith('SSL_') \
327db96d56Sopenharmony_ci        or (k.startswith('REDIRECT_') and _needs_transcode(k[9:]))
337db96d56Sopenharmony_ci
347db96d56Sopenharmony_cidef read_environ():
357db96d56Sopenharmony_ci    """Read environment, fixing HTTP variables"""
367db96d56Sopenharmony_ci    enc = sys.getfilesystemencoding()
377db96d56Sopenharmony_ci    esc = 'surrogateescape'
387db96d56Sopenharmony_ci    try:
397db96d56Sopenharmony_ci        ''.encode('utf-8', esc)
407db96d56Sopenharmony_ci    except LookupError:
417db96d56Sopenharmony_ci        esc = 'replace'
427db96d56Sopenharmony_ci    environ = {}
437db96d56Sopenharmony_ci
447db96d56Sopenharmony_ci    # Take the basic environment from native-unicode os.environ. Attempt to
457db96d56Sopenharmony_ci    # fix up the variables that come from the HTTP request to compensate for
467db96d56Sopenharmony_ci    # the bytes->unicode decoding step that will already have taken place.
477db96d56Sopenharmony_ci    for k, v in os.environ.items():
487db96d56Sopenharmony_ci        if _needs_transcode(k):
497db96d56Sopenharmony_ci
507db96d56Sopenharmony_ci            # On win32, the os.environ is natively Unicode. Different servers
517db96d56Sopenharmony_ci            # decode the request bytes using different encodings.
527db96d56Sopenharmony_ci            if sys.platform == 'win32':
537db96d56Sopenharmony_ci                software = os.environ.get('SERVER_SOFTWARE', '').lower()
547db96d56Sopenharmony_ci
557db96d56Sopenharmony_ci                # On IIS, the HTTP request will be decoded as UTF-8 as long
567db96d56Sopenharmony_ci                # as the input is a valid UTF-8 sequence. Otherwise it is
577db96d56Sopenharmony_ci                # decoded using the system code page (mbcs), with no way to
587db96d56Sopenharmony_ci                # detect this has happened. Because UTF-8 is the more likely
597db96d56Sopenharmony_ci                # encoding, and mbcs is inherently unreliable (an mbcs string
607db96d56Sopenharmony_ci                # that happens to be valid UTF-8 will not be decoded as mbcs)
617db96d56Sopenharmony_ci                # always recreate the original bytes as UTF-8.
627db96d56Sopenharmony_ci                if software.startswith('microsoft-iis/'):
637db96d56Sopenharmony_ci                    v = v.encode('utf-8').decode('iso-8859-1')
647db96d56Sopenharmony_ci
657db96d56Sopenharmony_ci                # Apache mod_cgi writes bytes-as-unicode (as if ISO-8859-1) direct
667db96d56Sopenharmony_ci                # to the Unicode environ. No modification needed.
677db96d56Sopenharmony_ci                elif software.startswith('apache/'):
687db96d56Sopenharmony_ci                    pass
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_ci                # Python 3's http.server.CGIHTTPRequestHandler decodes
717db96d56Sopenharmony_ci                # using the urllib.unquote default of UTF-8, amongst other
727db96d56Sopenharmony_ci                # issues.
737db96d56Sopenharmony_ci                elif (
747db96d56Sopenharmony_ci                    software.startswith('simplehttp/')
757db96d56Sopenharmony_ci                    and 'python/3' in software
767db96d56Sopenharmony_ci                ):
777db96d56Sopenharmony_ci                    v = v.encode('utf-8').decode('iso-8859-1')
787db96d56Sopenharmony_ci
797db96d56Sopenharmony_ci                # For other servers, guess that they have written bytes to
807db96d56Sopenharmony_ci                # the environ using stdio byte-oriented interfaces, ending up
817db96d56Sopenharmony_ci                # with the system code page.
827db96d56Sopenharmony_ci                else:
837db96d56Sopenharmony_ci                    v = v.encode(enc, 'replace').decode('iso-8859-1')
847db96d56Sopenharmony_ci
857db96d56Sopenharmony_ci            # Recover bytes from unicode environ, using surrogate escapes
867db96d56Sopenharmony_ci            # where available (Python 3.1+).
877db96d56Sopenharmony_ci            else:
887db96d56Sopenharmony_ci                v = v.encode(enc, esc).decode('iso-8859-1')
897db96d56Sopenharmony_ci
907db96d56Sopenharmony_ci        environ[k] = v
917db96d56Sopenharmony_ci    return environ
927db96d56Sopenharmony_ci
937db96d56Sopenharmony_ci
947db96d56Sopenharmony_ciclass BaseHandler:
957db96d56Sopenharmony_ci    """Manage the invocation of a WSGI application"""
967db96d56Sopenharmony_ci
977db96d56Sopenharmony_ci    # Configuration parameters; can override per-subclass or per-instance
987db96d56Sopenharmony_ci    wsgi_version = (1,0)
997db96d56Sopenharmony_ci    wsgi_multithread = True
1007db96d56Sopenharmony_ci    wsgi_multiprocess = True
1017db96d56Sopenharmony_ci    wsgi_run_once = False
1027db96d56Sopenharmony_ci
1037db96d56Sopenharmony_ci    origin_server = True    # We are transmitting direct to client
1047db96d56Sopenharmony_ci    http_version  = "1.0"   # Version that should be used for response
1057db96d56Sopenharmony_ci    server_software = None  # String name of server software, if any
1067db96d56Sopenharmony_ci
1077db96d56Sopenharmony_ci    # os_environ is used to supply configuration from the OS environment:
1087db96d56Sopenharmony_ci    # by default it's a copy of 'os.environ' as of import time, but you can
1097db96d56Sopenharmony_ci    # override this in e.g. your __init__ method.
1107db96d56Sopenharmony_ci    os_environ= read_environ()
1117db96d56Sopenharmony_ci
1127db96d56Sopenharmony_ci    # Collaborator classes
1137db96d56Sopenharmony_ci    wsgi_file_wrapper = FileWrapper     # set to None to disable
1147db96d56Sopenharmony_ci    headers_class = Headers             # must be a Headers-like class
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_ci    # Error handling (also per-subclass or per-instance)
1177db96d56Sopenharmony_ci    traceback_limit = None  # Print entire traceback to self.get_stderr()
1187db96d56Sopenharmony_ci    error_status = "500 Internal Server Error"
1197db96d56Sopenharmony_ci    error_headers = [('Content-Type','text/plain')]
1207db96d56Sopenharmony_ci    error_body = b"A server error occurred.  Please contact the administrator."
1217db96d56Sopenharmony_ci
1227db96d56Sopenharmony_ci    # State variables (don't mess with these)
1237db96d56Sopenharmony_ci    status = result = None
1247db96d56Sopenharmony_ci    headers_sent = False
1257db96d56Sopenharmony_ci    headers = None
1267db96d56Sopenharmony_ci    bytes_sent = 0
1277db96d56Sopenharmony_ci
1287db96d56Sopenharmony_ci    def run(self, application):
1297db96d56Sopenharmony_ci        """Invoke the application"""
1307db96d56Sopenharmony_ci        # Note to self: don't move the close()!  Asynchronous servers shouldn't
1317db96d56Sopenharmony_ci        # call close() from finish_response(), so if you close() anywhere but
1327db96d56Sopenharmony_ci        # the double-error branch here, you'll break asynchronous servers by
1337db96d56Sopenharmony_ci        # prematurely closing.  Async servers must return from 'run()' without
1347db96d56Sopenharmony_ci        # closing if there might still be output to iterate over.
1357db96d56Sopenharmony_ci        try:
1367db96d56Sopenharmony_ci            self.setup_environ()
1377db96d56Sopenharmony_ci            self.result = application(self.environ, self.start_response)
1387db96d56Sopenharmony_ci            self.finish_response()
1397db96d56Sopenharmony_ci        except (ConnectionAbortedError, BrokenPipeError, ConnectionResetError):
1407db96d56Sopenharmony_ci            # We expect the client to close the connection abruptly from time
1417db96d56Sopenharmony_ci            # to time.
1427db96d56Sopenharmony_ci            return
1437db96d56Sopenharmony_ci        except:
1447db96d56Sopenharmony_ci            try:
1457db96d56Sopenharmony_ci                self.handle_error()
1467db96d56Sopenharmony_ci            except:
1477db96d56Sopenharmony_ci                # If we get an error handling an error, just give up already!
1487db96d56Sopenharmony_ci                self.close()
1497db96d56Sopenharmony_ci                raise   # ...and let the actual server figure it out.
1507db96d56Sopenharmony_ci
1517db96d56Sopenharmony_ci
1527db96d56Sopenharmony_ci    def setup_environ(self):
1537db96d56Sopenharmony_ci        """Set up the environment for one request"""
1547db96d56Sopenharmony_ci
1557db96d56Sopenharmony_ci        env = self.environ = self.os_environ.copy()
1567db96d56Sopenharmony_ci        self.add_cgi_vars()
1577db96d56Sopenharmony_ci
1587db96d56Sopenharmony_ci        env['wsgi.input']        = self.get_stdin()
1597db96d56Sopenharmony_ci        env['wsgi.errors']       = self.get_stderr()
1607db96d56Sopenharmony_ci        env['wsgi.version']      = self.wsgi_version
1617db96d56Sopenharmony_ci        env['wsgi.run_once']     = self.wsgi_run_once
1627db96d56Sopenharmony_ci        env['wsgi.url_scheme']   = self.get_scheme()
1637db96d56Sopenharmony_ci        env['wsgi.multithread']  = self.wsgi_multithread
1647db96d56Sopenharmony_ci        env['wsgi.multiprocess'] = self.wsgi_multiprocess
1657db96d56Sopenharmony_ci
1667db96d56Sopenharmony_ci        if self.wsgi_file_wrapper is not None:
1677db96d56Sopenharmony_ci            env['wsgi.file_wrapper'] = self.wsgi_file_wrapper
1687db96d56Sopenharmony_ci
1697db96d56Sopenharmony_ci        if self.origin_server and self.server_software:
1707db96d56Sopenharmony_ci            env.setdefault('SERVER_SOFTWARE',self.server_software)
1717db96d56Sopenharmony_ci
1727db96d56Sopenharmony_ci
1737db96d56Sopenharmony_ci    def finish_response(self):
1747db96d56Sopenharmony_ci        """Send any iterable data, then close self and the iterable
1757db96d56Sopenharmony_ci
1767db96d56Sopenharmony_ci        Subclasses intended for use in asynchronous servers will
1777db96d56Sopenharmony_ci        want to redefine this method, such that it sets up callbacks
1787db96d56Sopenharmony_ci        in the event loop to iterate over the data, and to call
1797db96d56Sopenharmony_ci        'self.close()' once the response is finished.
1807db96d56Sopenharmony_ci        """
1817db96d56Sopenharmony_ci        try:
1827db96d56Sopenharmony_ci            if not self.result_is_file() or not self.sendfile():
1837db96d56Sopenharmony_ci                for data in self.result:
1847db96d56Sopenharmony_ci                    self.write(data)
1857db96d56Sopenharmony_ci                self.finish_content()
1867db96d56Sopenharmony_ci        except:
1877db96d56Sopenharmony_ci            # Call close() on the iterable returned by the WSGI application
1887db96d56Sopenharmony_ci            # in case of an exception.
1897db96d56Sopenharmony_ci            if hasattr(self.result, 'close'):
1907db96d56Sopenharmony_ci                self.result.close()
1917db96d56Sopenharmony_ci            raise
1927db96d56Sopenharmony_ci        else:
1937db96d56Sopenharmony_ci            # We only call close() when no exception is raised, because it
1947db96d56Sopenharmony_ci            # will set status, result, headers, and environ fields to None.
1957db96d56Sopenharmony_ci            # See bpo-29183 for more details.
1967db96d56Sopenharmony_ci            self.close()
1977db96d56Sopenharmony_ci
1987db96d56Sopenharmony_ci
1997db96d56Sopenharmony_ci    def get_scheme(self):
2007db96d56Sopenharmony_ci        """Return the URL scheme being used"""
2017db96d56Sopenharmony_ci        return guess_scheme(self.environ)
2027db96d56Sopenharmony_ci
2037db96d56Sopenharmony_ci
2047db96d56Sopenharmony_ci    def set_content_length(self):
2057db96d56Sopenharmony_ci        """Compute Content-Length or switch to chunked encoding if possible"""
2067db96d56Sopenharmony_ci        try:
2077db96d56Sopenharmony_ci            blocks = len(self.result)
2087db96d56Sopenharmony_ci        except (TypeError,AttributeError,NotImplementedError):
2097db96d56Sopenharmony_ci            pass
2107db96d56Sopenharmony_ci        else:
2117db96d56Sopenharmony_ci            if blocks==1:
2127db96d56Sopenharmony_ci                self.headers['Content-Length'] = str(self.bytes_sent)
2137db96d56Sopenharmony_ci                return
2147db96d56Sopenharmony_ci        # XXX Try for chunked encoding if origin server and client is 1.1
2157db96d56Sopenharmony_ci
2167db96d56Sopenharmony_ci
2177db96d56Sopenharmony_ci    def cleanup_headers(self):
2187db96d56Sopenharmony_ci        """Make any necessary header changes or defaults
2197db96d56Sopenharmony_ci
2207db96d56Sopenharmony_ci        Subclasses can extend this to add other defaults.
2217db96d56Sopenharmony_ci        """
2227db96d56Sopenharmony_ci        if 'Content-Length' not in self.headers:
2237db96d56Sopenharmony_ci            self.set_content_length()
2247db96d56Sopenharmony_ci
2257db96d56Sopenharmony_ci    def start_response(self, status, headers,exc_info=None):
2267db96d56Sopenharmony_ci        """'start_response()' callable as specified by PEP 3333"""
2277db96d56Sopenharmony_ci
2287db96d56Sopenharmony_ci        if exc_info:
2297db96d56Sopenharmony_ci            try:
2307db96d56Sopenharmony_ci                if self.headers_sent:
2317db96d56Sopenharmony_ci                    raise
2327db96d56Sopenharmony_ci            finally:
2337db96d56Sopenharmony_ci                exc_info = None        # avoid dangling circular ref
2347db96d56Sopenharmony_ci        elif self.headers is not None:
2357db96d56Sopenharmony_ci            raise AssertionError("Headers already set!")
2367db96d56Sopenharmony_ci
2377db96d56Sopenharmony_ci        self.status = status
2387db96d56Sopenharmony_ci        self.headers = self.headers_class(headers)
2397db96d56Sopenharmony_ci        status = self._convert_string_type(status, "Status")
2407db96d56Sopenharmony_ci        assert len(status)>=4,"Status must be at least 4 characters"
2417db96d56Sopenharmony_ci        assert status[:3].isdigit(), "Status message must begin w/3-digit code"
2427db96d56Sopenharmony_ci        assert status[3]==" ", "Status message must have a space after code"
2437db96d56Sopenharmony_ci
2447db96d56Sopenharmony_ci        if __debug__:
2457db96d56Sopenharmony_ci            for name, val in headers:
2467db96d56Sopenharmony_ci                name = self._convert_string_type(name, "Header name")
2477db96d56Sopenharmony_ci                val = self._convert_string_type(val, "Header value")
2487db96d56Sopenharmony_ci                assert not is_hop_by_hop(name),\
2497db96d56Sopenharmony_ci                       f"Hop-by-hop header, '{name}: {val}', not allowed"
2507db96d56Sopenharmony_ci
2517db96d56Sopenharmony_ci        return self.write
2527db96d56Sopenharmony_ci
2537db96d56Sopenharmony_ci    def _convert_string_type(self, value, title):
2547db96d56Sopenharmony_ci        """Convert/check value type."""
2557db96d56Sopenharmony_ci        if type(value) is str:
2567db96d56Sopenharmony_ci            return value
2577db96d56Sopenharmony_ci        raise AssertionError(
2587db96d56Sopenharmony_ci            "{0} must be of type str (got {1})".format(title, repr(value))
2597db96d56Sopenharmony_ci        )
2607db96d56Sopenharmony_ci
2617db96d56Sopenharmony_ci    def send_preamble(self):
2627db96d56Sopenharmony_ci        """Transmit version/status/date/server, via self._write()"""
2637db96d56Sopenharmony_ci        if self.origin_server:
2647db96d56Sopenharmony_ci            if self.client_is_modern():
2657db96d56Sopenharmony_ci                self._write(('HTTP/%s %s\r\n' % (self.http_version,self.status)).encode('iso-8859-1'))
2667db96d56Sopenharmony_ci                if 'Date' not in self.headers:
2677db96d56Sopenharmony_ci                    self._write(
2687db96d56Sopenharmony_ci                        ('Date: %s\r\n' % format_date_time(time.time())).encode('iso-8859-1')
2697db96d56Sopenharmony_ci                    )
2707db96d56Sopenharmony_ci                if self.server_software and 'Server' not in self.headers:
2717db96d56Sopenharmony_ci                    self._write(('Server: %s\r\n' % self.server_software).encode('iso-8859-1'))
2727db96d56Sopenharmony_ci        else:
2737db96d56Sopenharmony_ci            self._write(('Status: %s\r\n' % self.status).encode('iso-8859-1'))
2747db96d56Sopenharmony_ci
2757db96d56Sopenharmony_ci    def write(self, data):
2767db96d56Sopenharmony_ci        """'write()' callable as specified by PEP 3333"""
2777db96d56Sopenharmony_ci
2787db96d56Sopenharmony_ci        assert type(data) is bytes, \
2797db96d56Sopenharmony_ci            "write() argument must be a bytes instance"
2807db96d56Sopenharmony_ci
2817db96d56Sopenharmony_ci        if not self.status:
2827db96d56Sopenharmony_ci            raise AssertionError("write() before start_response()")
2837db96d56Sopenharmony_ci
2847db96d56Sopenharmony_ci        elif not self.headers_sent:
2857db96d56Sopenharmony_ci            # Before the first output, send the stored headers
2867db96d56Sopenharmony_ci            self.bytes_sent = len(data)    # make sure we know content-length
2877db96d56Sopenharmony_ci            self.send_headers()
2887db96d56Sopenharmony_ci        else:
2897db96d56Sopenharmony_ci            self.bytes_sent += len(data)
2907db96d56Sopenharmony_ci
2917db96d56Sopenharmony_ci        # XXX check Content-Length and truncate if too many bytes written?
2927db96d56Sopenharmony_ci        self._write(data)
2937db96d56Sopenharmony_ci        self._flush()
2947db96d56Sopenharmony_ci
2957db96d56Sopenharmony_ci
2967db96d56Sopenharmony_ci    def sendfile(self):
2977db96d56Sopenharmony_ci        """Platform-specific file transmission
2987db96d56Sopenharmony_ci
2997db96d56Sopenharmony_ci        Override this method in subclasses to support platform-specific
3007db96d56Sopenharmony_ci        file transmission.  It is only called if the application's
3017db96d56Sopenharmony_ci        return iterable ('self.result') is an instance of
3027db96d56Sopenharmony_ci        'self.wsgi_file_wrapper'.
3037db96d56Sopenharmony_ci
3047db96d56Sopenharmony_ci        This method should return a true value if it was able to actually
3057db96d56Sopenharmony_ci        transmit the wrapped file-like object using a platform-specific
3067db96d56Sopenharmony_ci        approach.  It should return a false value if normal iteration
3077db96d56Sopenharmony_ci        should be used instead.  An exception can be raised to indicate
3087db96d56Sopenharmony_ci        that transmission was attempted, but failed.
3097db96d56Sopenharmony_ci
3107db96d56Sopenharmony_ci        NOTE: this method should call 'self.send_headers()' if
3117db96d56Sopenharmony_ci        'self.headers_sent' is false and it is going to attempt direct
3127db96d56Sopenharmony_ci        transmission of the file.
3137db96d56Sopenharmony_ci        """
3147db96d56Sopenharmony_ci        return False   # No platform-specific transmission by default
3157db96d56Sopenharmony_ci
3167db96d56Sopenharmony_ci
3177db96d56Sopenharmony_ci    def finish_content(self):
3187db96d56Sopenharmony_ci        """Ensure headers and content have both been sent"""
3197db96d56Sopenharmony_ci        if not self.headers_sent:
3207db96d56Sopenharmony_ci            # Only zero Content-Length if not set by the application (so
3217db96d56Sopenharmony_ci            # that HEAD requests can be satisfied properly, see #3839)
3227db96d56Sopenharmony_ci            self.headers.setdefault('Content-Length', "0")
3237db96d56Sopenharmony_ci            self.send_headers()
3247db96d56Sopenharmony_ci        else:
3257db96d56Sopenharmony_ci            pass # XXX check if content-length was too short?
3267db96d56Sopenharmony_ci
3277db96d56Sopenharmony_ci    def close(self):
3287db96d56Sopenharmony_ci        """Close the iterable (if needed) and reset all instance vars
3297db96d56Sopenharmony_ci
3307db96d56Sopenharmony_ci        Subclasses may want to also drop the client connection.
3317db96d56Sopenharmony_ci        """
3327db96d56Sopenharmony_ci        try:
3337db96d56Sopenharmony_ci            if hasattr(self.result,'close'):
3347db96d56Sopenharmony_ci                self.result.close()
3357db96d56Sopenharmony_ci        finally:
3367db96d56Sopenharmony_ci            self.result = self.headers = self.status = self.environ = None
3377db96d56Sopenharmony_ci            self.bytes_sent = 0; self.headers_sent = False
3387db96d56Sopenharmony_ci
3397db96d56Sopenharmony_ci
3407db96d56Sopenharmony_ci    def send_headers(self):
3417db96d56Sopenharmony_ci        """Transmit headers to the client, via self._write()"""
3427db96d56Sopenharmony_ci        self.cleanup_headers()
3437db96d56Sopenharmony_ci        self.headers_sent = True
3447db96d56Sopenharmony_ci        if not self.origin_server or self.client_is_modern():
3457db96d56Sopenharmony_ci            self.send_preamble()
3467db96d56Sopenharmony_ci            self._write(bytes(self.headers))
3477db96d56Sopenharmony_ci
3487db96d56Sopenharmony_ci
3497db96d56Sopenharmony_ci    def result_is_file(self):
3507db96d56Sopenharmony_ci        """True if 'self.result' is an instance of 'self.wsgi_file_wrapper'"""
3517db96d56Sopenharmony_ci        wrapper = self.wsgi_file_wrapper
3527db96d56Sopenharmony_ci        return wrapper is not None and isinstance(self.result,wrapper)
3537db96d56Sopenharmony_ci
3547db96d56Sopenharmony_ci
3557db96d56Sopenharmony_ci    def client_is_modern(self):
3567db96d56Sopenharmony_ci        """True if client can accept status and headers"""
3577db96d56Sopenharmony_ci        return self.environ['SERVER_PROTOCOL'].upper() != 'HTTP/0.9'
3587db96d56Sopenharmony_ci
3597db96d56Sopenharmony_ci
3607db96d56Sopenharmony_ci    def log_exception(self,exc_info):
3617db96d56Sopenharmony_ci        """Log the 'exc_info' tuple in the server log
3627db96d56Sopenharmony_ci
3637db96d56Sopenharmony_ci        Subclasses may override to retarget the output or change its format.
3647db96d56Sopenharmony_ci        """
3657db96d56Sopenharmony_ci        try:
3667db96d56Sopenharmony_ci            from traceback import print_exception
3677db96d56Sopenharmony_ci            stderr = self.get_stderr()
3687db96d56Sopenharmony_ci            print_exception(
3697db96d56Sopenharmony_ci                exc_info[0], exc_info[1], exc_info[2],
3707db96d56Sopenharmony_ci                self.traceback_limit, stderr
3717db96d56Sopenharmony_ci            )
3727db96d56Sopenharmony_ci            stderr.flush()
3737db96d56Sopenharmony_ci        finally:
3747db96d56Sopenharmony_ci            exc_info = None
3757db96d56Sopenharmony_ci
3767db96d56Sopenharmony_ci    def handle_error(self):
3777db96d56Sopenharmony_ci        """Log current error, and send error output to client if possible"""
3787db96d56Sopenharmony_ci        self.log_exception(sys.exc_info())
3797db96d56Sopenharmony_ci        if not self.headers_sent:
3807db96d56Sopenharmony_ci            self.result = self.error_output(self.environ, self.start_response)
3817db96d56Sopenharmony_ci            self.finish_response()
3827db96d56Sopenharmony_ci        # XXX else: attempt advanced recovery techniques for HTML or text?
3837db96d56Sopenharmony_ci
3847db96d56Sopenharmony_ci    def error_output(self, environ, start_response):
3857db96d56Sopenharmony_ci        """WSGI mini-app to create error output
3867db96d56Sopenharmony_ci
3877db96d56Sopenharmony_ci        By default, this just uses the 'error_status', 'error_headers',
3887db96d56Sopenharmony_ci        and 'error_body' attributes to generate an output page.  It can
3897db96d56Sopenharmony_ci        be overridden in a subclass to dynamically generate diagnostics,
3907db96d56Sopenharmony_ci        choose an appropriate message for the user's preferred language, etc.
3917db96d56Sopenharmony_ci
3927db96d56Sopenharmony_ci        Note, however, that it's not recommended from a security perspective to
3937db96d56Sopenharmony_ci        spit out diagnostics to any old user; ideally, you should have to do
3947db96d56Sopenharmony_ci        something special to enable diagnostic output, which is why we don't
3957db96d56Sopenharmony_ci        include any here!
3967db96d56Sopenharmony_ci        """
3977db96d56Sopenharmony_ci        start_response(self.error_status,self.error_headers[:],sys.exc_info())
3987db96d56Sopenharmony_ci        return [self.error_body]
3997db96d56Sopenharmony_ci
4007db96d56Sopenharmony_ci
4017db96d56Sopenharmony_ci    # Pure abstract methods; *must* be overridden in subclasses
4027db96d56Sopenharmony_ci
4037db96d56Sopenharmony_ci    def _write(self,data):
4047db96d56Sopenharmony_ci        """Override in subclass to buffer data for send to client
4057db96d56Sopenharmony_ci
4067db96d56Sopenharmony_ci        It's okay if this method actually transmits the data; BaseHandler
4077db96d56Sopenharmony_ci        just separates write and flush operations for greater efficiency
4087db96d56Sopenharmony_ci        when the underlying system actually has such a distinction.
4097db96d56Sopenharmony_ci        """
4107db96d56Sopenharmony_ci        raise NotImplementedError
4117db96d56Sopenharmony_ci
4127db96d56Sopenharmony_ci    def _flush(self):
4137db96d56Sopenharmony_ci        """Override in subclass to force sending of recent '_write()' calls
4147db96d56Sopenharmony_ci
4157db96d56Sopenharmony_ci        It's okay if this method is a no-op (i.e., if '_write()' actually
4167db96d56Sopenharmony_ci        sends the data.
4177db96d56Sopenharmony_ci        """
4187db96d56Sopenharmony_ci        raise NotImplementedError
4197db96d56Sopenharmony_ci
4207db96d56Sopenharmony_ci    def get_stdin(self):
4217db96d56Sopenharmony_ci        """Override in subclass to return suitable 'wsgi.input'"""
4227db96d56Sopenharmony_ci        raise NotImplementedError
4237db96d56Sopenharmony_ci
4247db96d56Sopenharmony_ci    def get_stderr(self):
4257db96d56Sopenharmony_ci        """Override in subclass to return suitable 'wsgi.errors'"""
4267db96d56Sopenharmony_ci        raise NotImplementedError
4277db96d56Sopenharmony_ci
4287db96d56Sopenharmony_ci    def add_cgi_vars(self):
4297db96d56Sopenharmony_ci        """Override in subclass to insert CGI variables in 'self.environ'"""
4307db96d56Sopenharmony_ci        raise NotImplementedError
4317db96d56Sopenharmony_ci
4327db96d56Sopenharmony_ci
4337db96d56Sopenharmony_ciclass SimpleHandler(BaseHandler):
4347db96d56Sopenharmony_ci    """Handler that's just initialized with streams, environment, etc.
4357db96d56Sopenharmony_ci
4367db96d56Sopenharmony_ci    This handler subclass is intended for synchronous HTTP/1.0 origin servers,
4377db96d56Sopenharmony_ci    and handles sending the entire response output, given the correct inputs.
4387db96d56Sopenharmony_ci
4397db96d56Sopenharmony_ci    Usage::
4407db96d56Sopenharmony_ci
4417db96d56Sopenharmony_ci        handler = SimpleHandler(
4427db96d56Sopenharmony_ci            inp,out,err,env, multithread=False, multiprocess=True
4437db96d56Sopenharmony_ci        )
4447db96d56Sopenharmony_ci        handler.run(app)"""
4457db96d56Sopenharmony_ci
4467db96d56Sopenharmony_ci    def __init__(self,stdin,stdout,stderr,environ,
4477db96d56Sopenharmony_ci        multithread=True, multiprocess=False
4487db96d56Sopenharmony_ci    ):
4497db96d56Sopenharmony_ci        self.stdin = stdin
4507db96d56Sopenharmony_ci        self.stdout = stdout
4517db96d56Sopenharmony_ci        self.stderr = stderr
4527db96d56Sopenharmony_ci        self.base_env = environ
4537db96d56Sopenharmony_ci        self.wsgi_multithread = multithread
4547db96d56Sopenharmony_ci        self.wsgi_multiprocess = multiprocess
4557db96d56Sopenharmony_ci
4567db96d56Sopenharmony_ci    def get_stdin(self):
4577db96d56Sopenharmony_ci        return self.stdin
4587db96d56Sopenharmony_ci
4597db96d56Sopenharmony_ci    def get_stderr(self):
4607db96d56Sopenharmony_ci        return self.stderr
4617db96d56Sopenharmony_ci
4627db96d56Sopenharmony_ci    def add_cgi_vars(self):
4637db96d56Sopenharmony_ci        self.environ.update(self.base_env)
4647db96d56Sopenharmony_ci
4657db96d56Sopenharmony_ci    def _write(self,data):
4667db96d56Sopenharmony_ci        result = self.stdout.write(data)
4677db96d56Sopenharmony_ci        if result is None or result == len(data):
4687db96d56Sopenharmony_ci            return
4697db96d56Sopenharmony_ci        from warnings import warn
4707db96d56Sopenharmony_ci        warn("SimpleHandler.stdout.write() should not do partial writes",
4717db96d56Sopenharmony_ci            DeprecationWarning)
4727db96d56Sopenharmony_ci        while True:
4737db96d56Sopenharmony_ci            data = data[result:]
4747db96d56Sopenharmony_ci            if not data:
4757db96d56Sopenharmony_ci                break
4767db96d56Sopenharmony_ci            result = self.stdout.write(data)
4777db96d56Sopenharmony_ci
4787db96d56Sopenharmony_ci    def _flush(self):
4797db96d56Sopenharmony_ci        self.stdout.flush()
4807db96d56Sopenharmony_ci        self._flush = self.stdout.flush
4817db96d56Sopenharmony_ci
4827db96d56Sopenharmony_ci
4837db96d56Sopenharmony_ciclass BaseCGIHandler(SimpleHandler):
4847db96d56Sopenharmony_ci
4857db96d56Sopenharmony_ci    """CGI-like systems using input/output/error streams and environ mapping
4867db96d56Sopenharmony_ci
4877db96d56Sopenharmony_ci    Usage::
4887db96d56Sopenharmony_ci
4897db96d56Sopenharmony_ci        handler = BaseCGIHandler(inp,out,err,env)
4907db96d56Sopenharmony_ci        handler.run(app)
4917db96d56Sopenharmony_ci
4927db96d56Sopenharmony_ci    This handler class is useful for gateway protocols like ReadyExec and
4937db96d56Sopenharmony_ci    FastCGI, that have usable input/output/error streams and an environment
4947db96d56Sopenharmony_ci    mapping.  It's also the base class for CGIHandler, which just uses
4957db96d56Sopenharmony_ci    sys.stdin, os.environ, and so on.
4967db96d56Sopenharmony_ci
4977db96d56Sopenharmony_ci    The constructor also takes keyword arguments 'multithread' and
4987db96d56Sopenharmony_ci    'multiprocess' (defaulting to 'True' and 'False' respectively) to control
4997db96d56Sopenharmony_ci    the configuration sent to the application.  It sets 'origin_server' to
5007db96d56Sopenharmony_ci    False (to enable CGI-like output), and assumes that 'wsgi.run_once' is
5017db96d56Sopenharmony_ci    False.
5027db96d56Sopenharmony_ci    """
5037db96d56Sopenharmony_ci
5047db96d56Sopenharmony_ci    origin_server = False
5057db96d56Sopenharmony_ci
5067db96d56Sopenharmony_ci
5077db96d56Sopenharmony_ciclass CGIHandler(BaseCGIHandler):
5087db96d56Sopenharmony_ci
5097db96d56Sopenharmony_ci    """CGI-based invocation via sys.stdin/stdout/stderr and os.environ
5107db96d56Sopenharmony_ci
5117db96d56Sopenharmony_ci    Usage::
5127db96d56Sopenharmony_ci
5137db96d56Sopenharmony_ci        CGIHandler().run(app)
5147db96d56Sopenharmony_ci
5157db96d56Sopenharmony_ci    The difference between this class and BaseCGIHandler is that it always
5167db96d56Sopenharmony_ci    uses 'wsgi.run_once' of 'True', 'wsgi.multithread' of 'False', and
5177db96d56Sopenharmony_ci    'wsgi.multiprocess' of 'True'.  It does not take any initialization
5187db96d56Sopenharmony_ci    parameters, but always uses 'sys.stdin', 'os.environ', and friends.
5197db96d56Sopenharmony_ci
5207db96d56Sopenharmony_ci    If you need to override any of these parameters, use BaseCGIHandler
5217db96d56Sopenharmony_ci    instead.
5227db96d56Sopenharmony_ci    """
5237db96d56Sopenharmony_ci
5247db96d56Sopenharmony_ci    wsgi_run_once = True
5257db96d56Sopenharmony_ci    # Do not allow os.environ to leak between requests in Google App Engine
5267db96d56Sopenharmony_ci    # and other multi-run CGI use cases.  This is not easily testable.
5277db96d56Sopenharmony_ci    # See http://bugs.python.org/issue7250
5287db96d56Sopenharmony_ci    os_environ = {}
5297db96d56Sopenharmony_ci
5307db96d56Sopenharmony_ci    def __init__(self):
5317db96d56Sopenharmony_ci        BaseCGIHandler.__init__(
5327db96d56Sopenharmony_ci            self, sys.stdin.buffer, sys.stdout.buffer, sys.stderr,
5337db96d56Sopenharmony_ci            read_environ(), multithread=False, multiprocess=True
5347db96d56Sopenharmony_ci        )
5357db96d56Sopenharmony_ci
5367db96d56Sopenharmony_ci
5377db96d56Sopenharmony_ciclass IISCGIHandler(BaseCGIHandler):
5387db96d56Sopenharmony_ci    """CGI-based invocation with workaround for IIS path bug
5397db96d56Sopenharmony_ci
5407db96d56Sopenharmony_ci    This handler should be used in preference to CGIHandler when deploying on
5417db96d56Sopenharmony_ci    Microsoft IIS without having set the config allowPathInfo option (IIS>=7)
5427db96d56Sopenharmony_ci    or metabase allowPathInfoForScriptMappings (IIS<7).
5437db96d56Sopenharmony_ci    """
5447db96d56Sopenharmony_ci    wsgi_run_once = True
5457db96d56Sopenharmony_ci    os_environ = {}
5467db96d56Sopenharmony_ci
5477db96d56Sopenharmony_ci    # By default, IIS gives a PATH_INFO that duplicates the SCRIPT_NAME at
5487db96d56Sopenharmony_ci    # the front, causing problems for WSGI applications that wish to implement
5497db96d56Sopenharmony_ci    # routing. This handler strips any such duplicated path.
5507db96d56Sopenharmony_ci
5517db96d56Sopenharmony_ci    # IIS can be configured to pass the correct PATH_INFO, but this causes
5527db96d56Sopenharmony_ci    # another bug where PATH_TRANSLATED is wrong. Luckily this variable is
5537db96d56Sopenharmony_ci    # rarely used and is not guaranteed by WSGI. On IIS<7, though, the
5547db96d56Sopenharmony_ci    # setting can only be made on a vhost level, affecting all other script
5557db96d56Sopenharmony_ci    # mappings, many of which break when exposed to the PATH_TRANSLATED bug.
5567db96d56Sopenharmony_ci    # For this reason IIS<7 is almost never deployed with the fix. (Even IIS7
5577db96d56Sopenharmony_ci    # rarely uses it because there is still no UI for it.)
5587db96d56Sopenharmony_ci
5597db96d56Sopenharmony_ci    # There is no way for CGI code to tell whether the option was set, so a
5607db96d56Sopenharmony_ci    # separate handler class is provided.
5617db96d56Sopenharmony_ci    def __init__(self):
5627db96d56Sopenharmony_ci        environ= read_environ()
5637db96d56Sopenharmony_ci        path = environ.get('PATH_INFO', '')
5647db96d56Sopenharmony_ci        script = environ.get('SCRIPT_NAME', '')
5657db96d56Sopenharmony_ci        if (path+'/').startswith(script+'/'):
5667db96d56Sopenharmony_ci            environ['PATH_INFO'] = path[len(script):]
5677db96d56Sopenharmony_ci        BaseCGIHandler.__init__(
5687db96d56Sopenharmony_ci            self, sys.stdin.buffer, sys.stdout.buffer, sys.stderr,
5697db96d56Sopenharmony_ci            environ, multithread=False, multiprocess=True
5707db96d56Sopenharmony_ci        )
571