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